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.
This commit is contained in:
Nick Craig-Wood 2020-06-23 13:04:27 +01:00
parent 06a12f5e27
commit 5db15cb157
2 changed files with 44 additions and 28 deletions

View File

@ -127,33 +127,48 @@ func (d *Dir) Node() Node {
return d 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 // forgetDirPath clears the cache for itself and all subdirectories if
// they match the given path. The path is specified relative from the // they match the given path. The path is specified relative from the
// directory it is called from. // directory it is called from.
// //
// It does not invalidate or clear the cache of the parent directory. // It does not invalidate or clear the cache of the parent directory.
func (d *Dir) forgetDirPath(relativePath string) { func (d *Dir) forgetDirPath(relativePath string) {
if dir := d.cachedDir(relativePath); dir != nil { dir := d.cachedDir(relativePath)
dir.walk(func(dir *Dir) { if dir == nil {
// this is called with the mutex held return
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.ForgetAll()
// 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("")
} }
// invalidateDir invalidates the directory cache for absPath relative to the root // invalidateDir invalidates the directory cache for absPath relative to the root

View File

@ -269,21 +269,17 @@ func TestDirReadDirAll(t *testing.T) {
node, err = vfs.Stat("") node, err = vfs.Stat("")
require.NoError(t, err) 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") node, err = vfs.Stat("dir/subdir")
require.NoError(t, err) 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) { 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 // Add some virtual entries and check what happens
dir.AddVirtual("virtualFile", 17, false) dir.AddVirtual("virtualFile", 17, false)
dir.AddVirtual("virtualDir", 0, true) 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"}) 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 // Now action the deletes and uploads
_ = r.WriteObject(context.Background(), "dir/virtualFile", "virtualFile contents", t1) _ = r.WriteObject(context.Background(), "dir/virtualFile", "virtualFile contents", t1)
_ = r.WriteObject(context.Background(), "dir/virtualDir/testFile", "testFile contents", t1) _ = r.WriteObject(context.Background(), "dir/virtualDir/testFile", "testFile contents", t1)