From 3773e8a2c45f18efacc4d88fe0d7340fb09d1ec0 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Tue, 16 Mar 2021 13:23:36 +0000 Subject: [PATCH] mount: fix caching of old directories after renaming them It was discovered `rclone mount` (but not `rclone cmount`) cached directories after rename which it shouldn't have done. This caused IO errors when trying to access files in renamed directories on bucket based file systems. This turned out to be the kernel caching the directories as basil/fuse sets their expiry time to 60s for some reason. This fix invalidates the relevant kernel cache entries in the for the directories which fixes the problem. Fixes: #4977 See: https://forum.rclone.org/t/after-a-directory-renmane-using-mv-files-are-not-visible-any-longer/22797 --- cmd/mount/dir.go | 16 ++++++++++++++++ cmd/mount/fs.go | 5 +++-- cmd/mount/mount.go | 4 ++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/cmd/mount/dir.go b/cmd/mount/dir.go index cf83da13ba4d9..f03e3baf68f59 100644 --- a/cmd/mount/dir.go +++ b/cmd/mount/dir.go @@ -181,6 +181,15 @@ func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) { return nil } +// Invalidate a leaf in a directory +func (d *Dir) invalidateEntry(dirNode fusefs.Node, leaf string) { + fs.Debugf(dirNode, "Invalidating %q", leaf) + err := d.fsys.server.InvalidateEntry(dirNode, leaf) + if err != nil { + fs.Debugf(dirNode, "Failed to invalidate %q: %v", leaf, err) + } +} + // Check interface satisfied var _ fusefs.NodeRenamer = (*Dir)(nil) @@ -197,6 +206,13 @@ func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fusefs return translateError(err) } + // Invalidate the new directory entry so it gets re-read (in + // the background otherwise we cause a deadlock) + // + // See https://github.com/rclone/rclone/issues/4977 for why + go d.invalidateEntry(newDir, req.NewName) + //go d.invalidateEntry(d, req.OldName) + return nil } diff --git a/cmd/mount/fs.go b/cmd/mount/fs.go index 78a99a6fcee48..5dbd74efe49c7 100644 --- a/cmd/mount/fs.go +++ b/cmd/mount/fs.go @@ -20,8 +20,9 @@ import ( // FS represents the top level filing system type FS struct { *vfs.VFS - f fs.Fs - opt *mountlib.Options + f fs.Fs + opt *mountlib.Options + server *fusefs.Server } // Check interface satisfied diff --git a/cmd/mount/mount.go b/cmd/mount/mount.go index 88aac32deeef2..2b42af4721c52 100644 --- a/cmd/mount/mount.go +++ b/cmd/mount/mount.go @@ -91,12 +91,12 @@ func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error } filesys := NewFS(VFS, opt) - server := fusefs.New(c, nil) + filesys.server = fusefs.New(c, nil) // Serve the mount point in the background returning error to errChan errChan := make(chan error, 1) go func() { - err := server.Serve(filesys) + err := filesys.server.Serve(filesys) closeErr := c.Close() if err == nil { err = closeErr