From 5db15cb1570eb4a2146334821c280491a78bc954 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Tue, 23 Jun 2020 13:04:27 +0100 Subject: [PATCH] vfs: make dir.ForgetAll and friends not forget virtual entries Before this change dir.ForgetAll and vfs/forget would forget about virtual directory entries. This change preserves them. --- vfs/dir.go | 55 +++++++++++++++++++++++++++++++------------------ vfs/dir_test.go | 17 ++++++++------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/vfs/dir.go b/vfs/dir.go index 8caae4ec8..4cba1375e 100644 --- a/vfs/dir.go +++ b/vfs/dir.go @@ -127,33 +127,48 @@ func (d *Dir) Node() Node { return d } +// ForgetAll forgets directory entries for this directory and any children. +// +// It does not invalidate or clear the cache of the parent directory. +// +// It returns true if the directory or any of its children had virtual entries +// so could not be forgotten. Children which didn't have virtual entries and +// children with virtual entries will be forgotten even if true is returned. +func (d *Dir) ForgetAll() (hasVirtual bool) { + d.mu.Lock() + defer d.mu.Unlock() + fs.Debugf(d.path, "forgetting directory cache") + for _, node := range d.items { + if dir, ok := node.(*Dir); ok { + if dir.ForgetAll() { + hasVirtual = true + } + } + } + d.read = time.Time{} + // Check if this dir has virtual entries + if len(d.virtual) != 0 { + hasVirtual = true + } + // Don't clear directory entries if there are virtual entries in this + // directory or any children + if !hasVirtual { + d.items = make(map[string]Node) + } + return hasVirtual +} + // 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) { - // this is called with the mutex held - fs.Debugf(dir.path, "forgetting directory cache") - dir.read = time.Time{} - // Don't clear directory entries if there are virtual - // items in there. - if len(dir.virtual) == 0 { - dir.items = make(map[string]Node) - dir.virtual = nil - } - }) + dir := d.cachedDir(relativePath) + if dir == nil { + return } -} - -// 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.forgetDirPath("") + dir.ForgetAll() } // invalidateDir invalidates the directory cache for absPath relative to the root diff --git a/vfs/dir_test.go b/vfs/dir_test.go index 493227904..a3bb581d4 100644 --- a/vfs/dir_test.go +++ b/vfs/dir_test.go @@ -269,21 +269,17 @@ func TestDirReadDirAll(t *testing.T) { node, err = vfs.Stat("") require.NoError(t, err) - dir = node.(*Dir) + root := node.(*Dir) - checkListing(t, dir, []string{"dir,0,true"}) + checkListing(t, root, []string{"dir,0,true"}) node, err = vfs.Stat("dir/subdir") require.NoError(t, err) - dir = node.(*Dir) + subdir := node.(*Dir) - checkListing(t, dir, []string{"file3,16,false"}) + checkListing(t, subdir, []string{"file3,16,false"}) t.Run("Virtual", func(t *testing.T) { - node, err := vfs.Stat("dir") - require.NoError(t, err) - dir := node.(*Dir) - // Add some virtual entries and check what happens dir.AddVirtual("virtualFile", 17, false) dir.AddVirtual("virtualDir", 0, true) @@ -298,6 +294,11 @@ func TestDirReadDirAll(t *testing.T) { checkListing(t, dir, []string{"file1,14,false", "virtualDir,0,true", "virtualFile,17,false"}) + // Check that forgetting the root doesn't invalidate the virtual entries + root.ForgetAll() + + checkListing(t, dir, []string{"file1,14,false", "virtualDir,0,true", "virtualFile,17,false"}) + // Now action the deletes and uploads _ = r.WriteObject(context.Background(), "dir/virtualFile", "virtualFile contents", t1) _ = r.WriteObject(context.Background(), "dir/virtualDir/testFile", "testFile contents", t1)