You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
5.2 KiB

9 months ago
  1. package s3api
  2. import (
  3. "encoding/xml"
  4. "fmt"
  5. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  6. "golang.org/x/exp/slices"
  7. "io"
  8. "net/http"
  9. "strings"
  10. "github.com/seaweedfs/seaweedfs/weed/filer"
  11. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  12. "github.com/seaweedfs/seaweedfs/weed/glog"
  13. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  14. "github.com/seaweedfs/seaweedfs/weed/util"
  15. )
  16. const (
  17. deleteMultipleObjectsLimit = 1000
  18. )
  19. func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
  20. bucket, object := s3_constants.GetBucketAndObject(r)
  21. glog.V(3).Infof("DeleteObjectHandler %s %s", bucket, object)
  22. destUrl := s3a.toFilerUrl(bucket, object)
  23. s3a.proxyToFiler(w, r, destUrl, true, func(proxyResponse *http.Response, w http.ResponseWriter) (statusCode int) {
  24. statusCode = http.StatusNoContent
  25. for k, v := range proxyResponse.Header {
  26. w.Header()[k] = v
  27. }
  28. w.WriteHeader(statusCode)
  29. return statusCode
  30. })
  31. }
  32. // / ObjectIdentifier carries key name for the object to delete.
  33. type ObjectIdentifier struct {
  34. ObjectName string `xml:"Key"`
  35. }
  36. // DeleteObjectsRequest - xml carrying the object key names which needs to be deleted.
  37. type DeleteObjectsRequest struct {
  38. // Element to enable quiet mode for the request
  39. Quiet bool
  40. // List of objects to be deleted
  41. Objects []ObjectIdentifier `xml:"Object"`
  42. }
  43. // DeleteError structure.
  44. type DeleteError struct {
  45. Code string
  46. Message string
  47. Key string
  48. }
  49. // DeleteObjectsResponse container for multiple object deletes.
  50. type DeleteObjectsResponse struct {
  51. XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-"`
  52. // Collection of all deleted objects
  53. DeletedObjects []ObjectIdentifier `xml:"Deleted,omitempty"`
  54. // Collection of errors deleting certain objects.
  55. Errors []DeleteError `xml:"Error,omitempty"`
  56. }
  57. // DeleteMultipleObjectsHandler - Delete multiple objects
  58. func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
  59. bucket, _ := s3_constants.GetBucketAndObject(r)
  60. glog.V(3).Infof("DeleteMultipleObjectsHandler %s", bucket)
  61. deleteXMLBytes, err := io.ReadAll(r.Body)
  62. if err != nil {
  63. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  64. return
  65. }
  66. deleteObjects := &DeleteObjectsRequest{}
  67. if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil {
  68. s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
  69. return
  70. }
  71. if len(deleteObjects.Objects) > deleteMultipleObjectsLimit {
  72. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxDeleteObjects)
  73. return
  74. }
  75. var deletedObjects []ObjectIdentifier
  76. var deleteErrors []DeleteError
  77. var auditLog *s3err.AccessLog
  78. directoriesWithDeletion := make(map[string]int)
  79. if s3err.Logger != nil {
  80. auditLog = s3err.GetAccessLog(r, http.StatusNoContent, s3err.ErrNone)
  81. }
  82. s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  83. // delete file entries
  84. for _, object := range deleteObjects.Objects {
  85. if object.ObjectName == "" {
  86. continue
  87. }
  88. lastSeparator := strings.LastIndex(object.ObjectName, "/")
  89. parentDirectoryPath, entryName, isDeleteData, isRecursive := "", object.ObjectName, true, false
  90. if lastSeparator > 0 && lastSeparator+1 < len(object.ObjectName) {
  91. entryName = object.ObjectName[lastSeparator+1:]
  92. parentDirectoryPath = "/" + object.ObjectName[:lastSeparator]
  93. }
  94. parentDirectoryPath = fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, bucket, parentDirectoryPath)
  95. err := doDeleteEntry(client, parentDirectoryPath, entryName, isDeleteData, isRecursive)
  96. if err == nil {
  97. directoriesWithDeletion[parentDirectoryPath]++
  98. deletedObjects = append(deletedObjects, object)
  99. } else if strings.Contains(err.Error(), filer.MsgFailDelNonEmptyFolder) {
  100. deletedObjects = append(deletedObjects, object)
  101. } else {
  102. delete(directoriesWithDeletion, parentDirectoryPath)
  103. deleteErrors = append(deleteErrors, DeleteError{
  104. Code: "",
  105. Message: err.Error(),
  106. Key: object.ObjectName,
  107. })
  108. }
  109. if auditLog != nil {
  110. auditLog.Key = entryName
  111. s3err.PostAccessLog(*auditLog)
  112. }
  113. }
  114. // purge empty folders, only checking folders with deletions
  115. for len(directoriesWithDeletion) > 0 {
  116. directoriesWithDeletion = s3a.doDeleteEmptyDirectories(client, directoriesWithDeletion)
  117. }
  118. return nil
  119. })
  120. deleteResp := DeleteObjectsResponse{}
  121. if !deleteObjects.Quiet {
  122. deleteResp.DeletedObjects = deletedObjects
  123. }
  124. deleteResp.Errors = deleteErrors
  125. writeSuccessResponseXML(w, r, deleteResp)
  126. }
  127. func (s3a *S3ApiServer) doDeleteEmptyDirectories(client filer_pb.SeaweedFilerClient, directoriesWithDeletion map[string]int) (newDirectoriesWithDeletion map[string]int) {
  128. var allDirs []string
  129. for dir := range directoriesWithDeletion {
  130. allDirs = append(allDirs, dir)
  131. }
  132. slices.SortFunc(allDirs, func(a, b string) int {
  133. return len(b) - len(a)
  134. })
  135. newDirectoriesWithDeletion = make(map[string]int)
  136. for _, dir := range allDirs {
  137. parentDir, dirName := util.FullPath(dir).DirAndName()
  138. if parentDir == s3a.option.BucketsPath {
  139. continue
  140. }
  141. if err := doDeleteEntry(client, parentDir, dirName, false, false); err != nil {
  142. glog.V(4).Infof("directory %s has %d deletion but still not empty: %v", dir, directoriesWithDeletion[dir], err)
  143. } else {
  144. newDirectoriesWithDeletion[parentDir]++
  145. }
  146. }
  147. return
  148. }