mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-13 15:29:12 +01:00
mount: pick a flush's name again once its locks are held
An unlink or rename that lands between the name pick and the locks would otherwise send the flush to the removed name, where the create guard fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
357a698425
commit
482b6a03ca
1 file changed
+31
-12
@@ -205,24 +205,30 @@ func (wfs *WFS) doFlush(ctx context.Context, fh *FileHandle, uid, gid uint32, al
|
||||
// from open-for-write through close, so no additional distributed lock is
|
||||
// needed here. The local fhLockTable lock below serializes within this mount.
|
||||
func (wfs *WFS) flushMetadataToFiler(ctx context.Context, fh *FileHandle, dir, name string, uid, gid uint32) error {
|
||||
fileFullPath := fh.FullPath()
|
||||
// The caller's name may have been unlinked or renamed away since it
|
||||
// resolved it; the file is reached through the name it has now.
|
||||
if resolved := util.FullPath(dir).Child(name); fileFullPath != "" && fileFullPath != resolved {
|
||||
if inode, found := wfs.inodeToPath.GetInode(resolved); !found || inode != fh.inode {
|
||||
dir, name = fileFullPath.DirAndName()
|
||||
}
|
||||
}
|
||||
fileFullPath, dir, name := wfs.flushTarget(fh, dir, name)
|
||||
// Attribute acknowledgement and local retention must complete before a
|
||||
// whole-entry flush can publish. Take this before the handle's active lock.
|
||||
unlock := wfs.lockMetadataPublication(fileFullPath)
|
||||
defer unlock()
|
||||
fhActiveLock := fh.wfs.fhLockTable.AcquireLock("doFlush", fh.fh, util.ExclusiveLock)
|
||||
// An unlink or rename can land before the locks are held.
|
||||
for attempt := 0; attempt < 3; attempt++ {
|
||||
nowPath, nowDir, nowName := wfs.flushTarget(fh, dir, name)
|
||||
if nowPath == fileFullPath && nowDir == dir && nowName == name {
|
||||
break
|
||||
}
|
||||
fh.wfs.fhLockTable.ReleaseLock(fh.fh, fhActiveLock)
|
||||
unlock()
|
||||
fileFullPath, dir, name = nowPath, nowDir, nowName
|
||||
unlock = wfs.lockMetadataPublication(fileFullPath)
|
||||
fhActiveLock = fh.wfs.fhLockTable.AcquireLock("doFlush", fh.fh, util.ExclusiveLock)
|
||||
}
|
||||
defer func() {
|
||||
fh.wfs.fhLockTable.ReleaseLock(fh.fh, fhActiveLock)
|
||||
unlock()
|
||||
}()
|
||||
|
||||
glog.V(4).Infof("flushMetadataToFiler %s/%s inode %d fh %d", dir, name, fh.inode, fh.fh)
|
||||
|
||||
fhActiveLock := fh.wfs.fhLockTable.AcquireLock("doFlush", fh.fh, util.ExclusiveLock)
|
||||
defer fh.wfs.fhLockTable.ReleaseLock(fh.fh, fhActiveLock)
|
||||
|
||||
// Re-check under the lock: Unlink sets the flag under it, so a flush that
|
||||
// was already past the earlier check cannot write the entry back after
|
||||
// the delete removed it.
|
||||
@@ -323,6 +329,19 @@ func (wfs *WFS) flushMetadataToFiler(ctx context.Context, fh *FileHandle, dir, n
|
||||
return err
|
||||
}
|
||||
|
||||
// flushTarget returns the lock path and the name a flush of fh publishes
|
||||
// through: the caller's name while it still names the file, else the name the
|
||||
// file has now.
|
||||
func (wfs *WFS) flushTarget(fh *FileHandle, dir, name string) (util.FullPath, string, string) {
|
||||
fileFullPath := fh.FullPath()
|
||||
if resolved := util.FullPath(dir).Child(name); fileFullPath != "" && fileFullPath != resolved {
|
||||
if inode, found := wfs.inodeToPath.GetInode(resolved); !found || inode != fh.inode {
|
||||
dir, name = fileFullPath.DirAndName()
|
||||
}
|
||||
}
|
||||
return fileFullPath, dir, name
|
||||
}
|
||||
|
||||
// createContentEntry publishes a handle's whole entry only over the filer
|
||||
// state the handle observed. Never refresh the guard and replay a stale whole
|
||||
// entry after a conflict: that would discard a peer's acknowledged chunks.
|
||||
|
||||
Reference in new issue
Block a user