mirror of
https://github.com/rclone/rclone.git
synced 2025-12-11 22:14:05 +01:00
vfs: fix rename of open files when using the VFS cache
Before this change, renaming an open file when using the VFS cache was delayed until the file was closed. This meant that the file was not readable after a rename even though it is was in the cache. After this change we rename the local cache file and the in memory cache, delaying only the rename of the file in object storage. See: https://forum.rclone.org/t/xen-orchestra-ebadf-bad-file-descriptor-write/13104
This commit is contained in:
54
vfs/file.go
54
vfs/file.go
@@ -137,29 +137,42 @@ func (f *File) applyPendingRename() {
|
||||
func (f *File) rename(ctx context.Context, destDir *Dir, newName string) error {
|
||||
f.mu.RLock()
|
||||
d := f.d
|
||||
o := f.o
|
||||
oldPendingRenameFun := f.pendingRenameFun
|
||||
f.mu.RUnlock()
|
||||
|
||||
if features := d.f.Features(); features.Move == nil && features.Copy == nil {
|
||||
err := errors.Errorf("Fs %q can't rename files (no server side Move or Copy)", d.f)
|
||||
fs.Errorf(f.Path(), "Dir.Rename error: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
newPath := path.Join(destDir.path, newName)
|
||||
|
||||
renameCall := func(ctx context.Context) error {
|
||||
newPath := path.Join(destDir.path, newName)
|
||||
f.mu.RLock()
|
||||
o := f.o
|
||||
f.mu.RUnlock()
|
||||
if o == nil {
|
||||
return errors.New("Cannot rename: file object is not available")
|
||||
}
|
||||
|
||||
// chain rename calls if any
|
||||
if oldPendingRenameFun != nil {
|
||||
err := oldPendingRenameFun(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// do the move of the remote object
|
||||
dstOverwritten, _ := d.f.NewObject(ctx, newPath)
|
||||
newObject, err := operations.Move(ctx, d.f, dstOverwritten, newPath, f.o)
|
||||
newObject, err := operations.Move(ctx, d.f, dstOverwritten, newPath, o)
|
||||
if err != nil {
|
||||
fs.Errorf(f.Path(), "File.Rename error: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename in the cache too if it exists
|
||||
if f.d.vfs.Opt.CacheMode >= CacheModeWrites && f.d.vfs.cache.exists(f.Path()) {
|
||||
if err := f.d.vfs.cache.rename(f.Path(), newPath); err != nil {
|
||||
fs.Infof(f.Path(), "File.Rename failed in Cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// newObject can be nil here for example if --dry-run
|
||||
if newObject == nil {
|
||||
err = errors.New("rename failed: nil object returned")
|
||||
@@ -167,25 +180,32 @@ func (f *File) rename(ctx context.Context, destDir *Dir, newName string) error {
|
||||
return err
|
||||
}
|
||||
// Update the node with the new details
|
||||
fs.Debugf(f.o, "Updating file with %v %p", newObject, f)
|
||||
fs.Debugf(o, "Updating file with %v %p", newObject, f)
|
||||
// f.rename(destDir, newObject)
|
||||
f.mu.Lock()
|
||||
f.o = newObject
|
||||
f.d = destDir
|
||||
f.leaf = path.Base(newObject.Remote())
|
||||
f.pendingRenameFun = nil
|
||||
f.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
f.mu.RLock()
|
||||
// Rename in the cache if it exists
|
||||
if f.d.vfs.Opt.CacheMode != CacheModeOff && f.d.vfs.cache.exists(f.Path()) {
|
||||
if err := f.d.vfs.cache.rename(f.Path(), newPath); err != nil {
|
||||
fs.Infof(f.Path(), "File.Rename failed in Cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// rename the file object
|
||||
f.mu.Lock()
|
||||
f.d = destDir
|
||||
f.leaf = newName
|
||||
writing := f._writingInProgress()
|
||||
f.mu.RUnlock()
|
||||
f.mu.Unlock()
|
||||
|
||||
if writing {
|
||||
fs.Debugf(f.o, "File is currently open, delaying rename %p", f)
|
||||
fs.Debugf(o, "File is currently open, delaying rename %p", f)
|
||||
f.mu.Lock()
|
||||
f.d = destDir
|
||||
f.leaf = newName
|
||||
f.pendingRenameFun = renameCall
|
||||
f.mu.Unlock()
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user