From 1fe1a19339ebfe140a43a6812372810fe630d860 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Tue, 8 Oct 2019 07:45:02 +0100 Subject: [PATCH] vfs: stop empty dirs disappearing when renamed on bucket based remotes Before this change when we renamed a directory this cleared the directory cache for the parent directory too. If the directory was remaining in the same parent this wasn't necessary and caused the empty directory to fall out of the cache. Fixes #3597 --- vfs/dir.go | 27 +++++++++++++++++++-------- vfs/dir_test.go | 18 +++++++++++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/vfs/dir.go b/vfs/dir.go index 55ab396fe..9f96b02da 100644 --- a/vfs/dir.go +++ b/vfs/dir.go @@ -96,10 +96,27 @@ func (d *Dir) Node() Node { return d } +// forgetDirPath clears the cache for itself and all subdirectories if +// they match the given path. The path is specified relative from the +// directory it is called from. +// +// It does not invalidate or clear the cache of the parent directory. +func (d *Dir) forgetDirPath(relativePath string) { + if dir := d.cachedDir(relativePath); dir != nil { + dir.walk(func(dir *Dir) { + fs.Debugf(dir.path, "forgetting directory cache") + dir.read = time.Time{} + dir.items = make(map[string]Node) + }) + } +} + // ForgetAll ensures the directory and all its children are purged // from the cache. +// +// It does not invalidate or clear the cache of the parent directory. func (d *Dir) ForgetAll() { - d.ForgetPath("", fs.EntryDirectory) + d.forgetDirPath("") } // ForgetPath clears the cache for itself and all subdirectories if @@ -126,13 +143,7 @@ func (d *Dir) ForgetPath(relativePath string, entryType fs.EntryType) { } if entryType == fs.EntryDirectory { - if dir := d.cachedDir(relativePath); dir != nil { - dir.walk(func(dir *Dir) { - fs.Debugf(dir.path, "forgetting directory cache") - dir.read = time.Time{} - dir.items = make(map[string]Node) - }) - } + d.forgetDirPath(relativePath) } } diff --git a/vfs/dir_test.go b/vfs/dir_test.go index fe0d10e0c..61fe85715 100644 --- a/vfs/dir_test.go +++ b/vfs/dir_test.go @@ -96,7 +96,7 @@ func TestDirForgetAll(t *testing.T) { dir.ForgetAll() assert.Equal(t, 1, len(root.items)) assert.Equal(t, 0, len(dir.items)) - assert.True(t, root.read.IsZero()) + assert.False(t, root.read.IsZero()) assert.True(t, dir.read.IsZero()) root.ForgetAll() @@ -521,6 +521,22 @@ func TestDirRename(t *testing.T) { file1.Path = "dir2/file3" fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{"dir2"}, r.Fremote.Precision()) + // rename an empty directory + _, err = root.Mkdir("empty directory") + assert.NoError(t, err) + checkListing(t, root, []string{ + "dir2,0,true", + "empty directory,0,true", + }) + err = root.Rename("empty directory", "renamed empty directory", root) + assert.NoError(t, err) + checkListing(t, root, []string{ + "dir2,0,true", + "renamed empty directory,0,true", + }) + // ...we don't check the underlying f.Fremote because on + // bucket based remotes the directory won't be there + // read only check vfs.Opt.ReadOnly = true err = dir.Rename("potato", "tuba", dir)