Fix S3 delete for non-empty directory markers (#8740)
* Fix S3 delete for non-empty directory markers * Address review feedback on directory marker deletes * Stabilize FUSE concurrent directory operations
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
"github.com/seaweedfs/seaweedfs/weed/filer"
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
||||
@@ -31,8 +32,8 @@ type FileHandle struct {
|
||||
asyncFlushPending bool // set in writebackCache mode to defer flush to Release
|
||||
asyncFlushUid uint32 // saved uid for deferred metadata flush
|
||||
asyncFlushGid uint32 // saved gid for deferred metadata flush
|
||||
asyncFlushDir string // saved directory at defer time (fallback if inode forgotten)
|
||||
asyncFlushName string // saved file name at defer time (fallback if inode forgotten)
|
||||
savedDir string // last known parent path if inode-to-path state is forgotten
|
||||
savedName string // last known file name if inode-to-path state is forgotten
|
||||
|
||||
isDeleted bool
|
||||
|
||||
@@ -73,8 +74,20 @@ func newFileHandle(wfs *WFS, handleId FileHandleId, inode uint64, entry *filer_p
|
||||
}
|
||||
|
||||
func (fh *FileHandle) FullPath() util.FullPath {
|
||||
fp, _ := fh.wfs.inodeToPath.GetPath(fh.inode)
|
||||
return fp
|
||||
if fp, status := fh.wfs.inodeToPath.GetPath(fh.inode); status == fuse.OK {
|
||||
return fp
|
||||
}
|
||||
if fh.savedName != "" {
|
||||
return util.FullPath(fh.savedDir).Child(fh.savedName)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (fh *FileHandle) RememberPath(fullPath util.FullPath) {
|
||||
if fullPath == "" {
|
||||
return
|
||||
}
|
||||
fh.savedDir, fh.savedName = fullPath.DirAndName()
|
||||
}
|
||||
|
||||
func (fh *FileHandle) GetEntry() *LockedEntry {
|
||||
|
||||
52
weed/mount/filehandle_test.go
Normal file
52
weed/mount/filehandle_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
)
|
||||
|
||||
func TestFileHandleFullPathFallsBackAfterForget(t *testing.T) {
|
||||
wfs := &WFS{
|
||||
inodeToPath: NewInodeToPath(util.FullPath("/"), 0),
|
||||
}
|
||||
|
||||
fullPath := util.FullPath("/worker_0/subdir_0/test.txt")
|
||||
inode := wfs.inodeToPath.Lookup(fullPath, 1, false, false, 0, true)
|
||||
|
||||
fh := &FileHandle{
|
||||
inode: inode,
|
||||
wfs: wfs,
|
||||
}
|
||||
fh.RememberPath(fullPath)
|
||||
|
||||
wfs.inodeToPath.Forget(inode, 1, nil)
|
||||
|
||||
if got := fh.FullPath(); got != fullPath {
|
||||
t.Fatalf("FullPath() after forget = %q, want %q", got, fullPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileHandleFullPathUsesSavedRenamePathAfterForget(t *testing.T) {
|
||||
wfs := &WFS{
|
||||
inodeToPath: NewInodeToPath(util.FullPath("/"), 0),
|
||||
}
|
||||
|
||||
oldPath := util.FullPath("/worker_0/subdir_0/test.txt")
|
||||
newPath := util.FullPath("/worker_0/subdir_1/test.txt")
|
||||
inode := wfs.inodeToPath.Lookup(oldPath, 1, false, false, 0, true)
|
||||
|
||||
fh := &FileHandle{
|
||||
inode: inode,
|
||||
wfs: wfs,
|
||||
}
|
||||
fh.RememberPath(oldPath)
|
||||
|
||||
wfs.inodeToPath.MovePath(oldPath, newPath)
|
||||
fh.RememberPath(newPath)
|
||||
wfs.inodeToPath.Forget(inode, 1, nil)
|
||||
|
||||
if got := fh.FullPath(); got != newPath {
|
||||
t.Fatalf("FullPath() after rename+forget = %q, want %q", got, newPath)
|
||||
}
|
||||
}
|
||||
@@ -41,12 +41,12 @@ func (wfs *WFS) completeAsyncFlush(fh *FileHandle) {
|
||||
// Try GetPath first — it reflects any rename that happened
|
||||
// after close(). If the inode mapping is gone (Forget
|
||||
// dropped it after the kernel's lookup count hit zero), fall
|
||||
// back to the dir/name saved at doFlush time. Rename also
|
||||
// updates the saved path, so the fallback is always current.
|
||||
// back to the last path saved on the handle. Rename keeps
|
||||
// that fallback current, so it is always the newest known path.
|
||||
//
|
||||
// Forget does NOT mean the file was deleted — it only means
|
||||
// the kernel evicted its cache entry.
|
||||
dir, name := fh.asyncFlushDir, fh.asyncFlushName
|
||||
dir, name := fh.savedDir, fh.savedName
|
||||
fileFullPath := util.FullPath(dir).Child(name)
|
||||
|
||||
if resolvedPath, status := wfs.inodeToPath.GetPath(fh.inode); status == fuse.OK {
|
||||
|
||||
@@ -97,6 +97,7 @@ func (wfs *WFS) doFlush(fh *FileHandle, uid, gid uint32, allowAsync bool) fuse.S
|
||||
|
||||
// flush works at fh level
|
||||
fileFullPath := fh.FullPath()
|
||||
fh.RememberPath(fileFullPath)
|
||||
dir, name := fileFullPath.DirAndName()
|
||||
// send the data to the OS
|
||||
glog.V(4).Infof("doFlush %s fh %d", fileFullPath, fh.fh)
|
||||
@@ -112,8 +113,6 @@ func (wfs *WFS) doFlush(fh *FileHandle, uid, gid uint32, allowAsync bool) fuse.S
|
||||
fh.asyncFlushPending = true
|
||||
fh.asyncFlushUid = uid
|
||||
fh.asyncFlushGid = gid
|
||||
fh.asyncFlushDir = dir
|
||||
fh.asyncFlushName = name
|
||||
glog.V(3).Infof("doFlush async deferred %s fh %d", fileFullPath, fh.fh)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ func (wfs *WFS) AcquireHandle(inode uint64, flags, uid, gid uint32) (fileHandle
|
||||
}
|
||||
// need to AcquireFileHandle again to ensure correct handle counter
|
||||
fileHandle = wfs.fhMap.AcquireFileHandle(wfs, inode, entry)
|
||||
fileHandle.RememberPath(path)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -253,12 +253,9 @@ func (wfs *WFS) handleRenameResponse(ctx context.Context, resp *filer_pb.StreamR
|
||||
if entry := fh.GetEntry(); entry != nil {
|
||||
entry.Name = newName
|
||||
}
|
||||
// Keep the saved async-flush path current so the fallback
|
||||
// Keep the saved handle path current so any flush fallback
|
||||
// after Forget uses the post-rename location, not the old one.
|
||||
if fh.asyncFlushPending {
|
||||
fh.asyncFlushDir = string(newParent)
|
||||
fh.asyncFlushName = newName
|
||||
}
|
||||
fh.RememberPath(newPath)
|
||||
}
|
||||
// invalidate attr and data
|
||||
// wfs.fuseServer.InodeNotify(sourceInode, 0, -1)
|
||||
|
||||
Reference in New Issue
Block a user