vfs: fix directory renaming by renaming dirs cached in memory

Before this change, if a directory was renamed and it or any children
had virtual entries in it they weren't flushed.

The consequence of this was that the directory path got out sync with
the actual position of the directory in the tree, leading to listings
of the old directory rather than the new one.

The fix renames any directories remaining after the ForgetAll to have
the correct path which fixes the problem.

See: https://forum.rclone.org/t/after-a-directory-renmane-using-mv-files-are-not-visible-any-longer/22797
This commit is contained in:
Nick Craig-Wood 2021-03-17 15:01:35 +00:00
parent a4c4ddf052
commit b47d6001a9

View File

@ -312,12 +312,35 @@ func (d *Dir) _age(when time.Time) (age time.Duration, stale bool) {
return return
} }
// renameTree renames the directories under this directory
//
// path should be the desired path
func (d *Dir) renameTree(dirPath string) {
d.mu.Lock()
defer d.mu.Unlock()
// Make sure the path is correct for each node
if d.path != dirPath {
fs.Debugf(d.path, "Renaming to %q", dirPath)
d.path = dirPath
d.entry = fs.NewDirCopy(context.TODO(), d.entry).SetRemote(dirPath)
}
// Do the same to any child directories
for leaf, node := range d.items {
if dir, ok := node.(*Dir); ok {
dir.renameTree(path.Join(dirPath, leaf))
}
}
}
// rename should be called after the directory is renamed // rename should be called after the directory is renamed
// //
// Reset the directory to new state, discarding all the objects and // Reset the directory to new state, discarding all the objects and
// reading everything again // reading everything again
func (d *Dir) rename(newParent *Dir, fsDir fs.Directory) { func (d *Dir) rename(newParent *Dir, fsDir fs.Directory) {
d.ForgetAll() d.ForgetAll()
d.modTimeMu.Lock() d.modTimeMu.Lock()
d.modTime = fsDir.ModTime(context.TODO()) d.modTime = fsDir.ModTime(context.TODO())
d.modTimeMu.Unlock() d.modTimeMu.Unlock()
@ -330,6 +353,9 @@ func (d *Dir) rename(newParent *Dir, fsDir fs.Directory) {
d.read = time.Time{} d.read = time.Time{}
d.mu.Unlock() d.mu.Unlock()
// Rename any remaining items in the tree that we couldn't forget
d.renameTree(d.path)
// Rename in the cache // Rename in the cache
if d.vfs.cache != nil && d.vfs.cache.DirExists(oldPath) { if d.vfs.cache != nil && d.vfs.cache.DirExists(oldPath) {
if err := d.vfs.cache.DirRename(oldPath, newPath); err != nil { if err := d.vfs.cache.DirRename(oldPath, newPath); err != nil {
@ -930,6 +956,7 @@ func (d *Dir) RemoveName(name string) error {
// Rename the file // Rename the file
func (d *Dir) Rename(oldName, newName string, destDir *Dir) error { func (d *Dir) Rename(oldName, newName string, destDir *Dir) error {
// fs.Debugf(d, "BEFORE\n%s", d.dump())
if d.vfs.Opt.ReadOnly { if d.vfs.Opt.ReadOnly {
return EROFS return EROFS
} }
@ -996,6 +1023,7 @@ func (d *Dir) Rename(oldName, newName string, destDir *Dir) error {
destDir.addObject(oldNode) destDir.addObject(oldNode)
// fs.Debugf(newPath, "Dir.Rename renamed from %q", oldPath) // fs.Debugf(newPath, "Dir.Rename renamed from %q", oldPath)
// fs.Debugf(d, "AFTER\n%s", d.dump())
return nil return nil
} }