mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
s3: fix delete of directory marker on non-empty directory (#8731)
When deleting an S3 key ending with "/" (a directory marker) where the directory has children, strip the marker metadata instead of returning InternalError. This matches AWS S3 behavior where deleting a directory marker only removes that object, not child objects under the prefix.
This commit is contained in:
@@ -78,6 +78,43 @@ func doDeleteEntry(client filer_pb.SeaweedFilerClient, parentDirectoryPath strin
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeDirectoryKeyObjectMarker clears the MIME type and S3-specific metadata from a
|
||||
// directory entry so it no longer appears as an object in S3 listings (IsDirectoryKeyObject()
|
||||
// returns false). This handles the case where a directory marker (key ending with "/") is
|
||||
// deleted on a non-empty directory — matching AWS S3 behavior where deleting the marker
|
||||
// only removes that object, not the child objects under the prefix.
|
||||
func removeDirectoryKeyObjectMarker(client filer_pb.SeaweedFilerClient, parentDir, entryName string) error {
|
||||
ctx := context.Background()
|
||||
resp, err := filer_pb.LookupEntry(ctx, client, &filer_pb.LookupDirectoryEntryRequest{
|
||||
Directory: parentDir,
|
||||
Name: entryName,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, filer_pb.ErrNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
entry := resp.Entry
|
||||
if entry == nil || !entry.IsDirectory || entry.Attributes == nil || entry.Attributes.Mime == "" {
|
||||
return nil // Not a directory key object, nothing to remove
|
||||
}
|
||||
|
||||
// Clear marker metadata so IsDirectoryKeyObject() returns false
|
||||
entry.Attributes.Mime = ""
|
||||
entry.Attributes.Md5 = nil
|
||||
entry.Content = nil
|
||||
if entry.Extended != nil {
|
||||
delete(entry.Extended, s3_constants.ExtETagKey)
|
||||
delete(entry.Extended, s3_constants.ExtAmzOwnerKey)
|
||||
}
|
||||
|
||||
return filer_pb.UpdateEntry(ctx, client, &filer_pb.UpdateEntryRequest{
|
||||
Directory: parentDir,
|
||||
Entry: entry,
|
||||
})
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) exists(parentDirectoryPath string, entryName string, isDirectory bool) (exists bool, err error) {
|
||||
|
||||
return filer_pb.Exists(context.Background(), s3a, parentDirectoryPath, entryName, isDirectory)
|
||||
|
||||
@@ -130,7 +130,15 @@ func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Reque
|
||||
dir, name := target.DirAndName()
|
||||
|
||||
err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
|
||||
return doDeleteEntry(client, dir, name, true, false)
|
||||
deleteErr := doDeleteEntry(client, dir, name, true, false)
|
||||
if deleteErr != nil && strings.HasSuffix(object, "/") &&
|
||||
strings.Contains(deleteErr.Error(), filer.MsgFailDelNonEmptyFolder) {
|
||||
// Deleting a directory key object (S3 key ending with "/") on a non-empty
|
||||
// directory. Strip the marker metadata so it no longer appears as an S3 object,
|
||||
// matching AWS S3 behavior where only the marker is removed, not child objects.
|
||||
return removeDirectoryKeyObjectMarker(client, dir, name)
|
||||
}
|
||||
return deleteErr
|
||||
// Note: Empty folder cleanup is now handled asynchronously by EmptyFolderCleaner
|
||||
// which listens to metadata events and uses consistent hashing for coordination
|
||||
})
|
||||
@@ -349,6 +357,11 @@ func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *h
|
||||
if err == nil {
|
||||
deletedObjects = append(deletedObjects, object)
|
||||
} else if strings.Contains(err.Error(), filer.MsgFailDelNonEmptyFolder) {
|
||||
if strings.HasSuffix(object.Key, "/") {
|
||||
if markerErr := removeDirectoryKeyObjectMarker(client, parentDirectoryPath, entryName); markerErr != nil {
|
||||
glog.Warningf("DeleteMultipleObjectsHandler: failed to remove directory marker %s/%s: %v", parentDirectoryPath, entryName, markerErr)
|
||||
}
|
||||
}
|
||||
deletedObjects = append(deletedObjects, object)
|
||||
} else {
|
||||
deleteErrors = append(deleteErrors, DeleteError{
|
||||
|
||||
Reference in New Issue
Block a user