Skip to content

Commit ceef78c

Browse files
lorenzncw
authored andcommitted
vfs: fix directory cache serving stale data
The VFS directory cache layer didn't update directory entry properties if they are reused after cache invalidation. Update them unconditionally as newDir sets them to the same value and setting a pointer is cheaper in both LoC as well as CPU cycles than a branch. Also add a test exercising this behavior. Fixes rclone#6335
1 parent 6560ea9 commit ceef78c

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

vfs/dir.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree
760760
dir := node.(*Dir)
761761
dir.mu.Lock()
762762
dir.modTime = item.ModTime(context.TODO())
763+
dir.entry = item
763764
if dirTree != nil {
764765
err = dir._readDirFromDirTree(dirTree, when)
765766
if err != nil {

vfs/dir_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"runtime"
78
"sort"
89
"testing"
910
"time"
@@ -655,3 +656,34 @@ func TestDirFileOpen(t *testing.T) {
655656
require.NoError(t, err)
656657
assert.Equal(t, int64(12), fi.Size())
657658
}
659+
660+
func TestDirEntryModTimeInvalidation(t *testing.T) {
661+
if runtime.GOOS == "windows" {
662+
t.Skip("dirent modtime is unreliable on Windows filesystems")
663+
}
664+
r, vfs := newTestVFS(t)
665+
666+
// Needs to be less than 2x the wait time below, othewrwise the entry
667+
// gets cleared out before it had a chance to be updated.
668+
vfs.Opt.DirCacheTime = fs.Duration(50 * time.Millisecond)
669+
670+
r.WriteObject(context.Background(), "dir/file1", "file1 contents", t1)
671+
672+
node, err := vfs.Stat("dir")
673+
require.NoError(t, err)
674+
modTime1 := node.(*Dir).DirEntry().ModTime(context.Background())
675+
676+
// Wait some time, then write another file which must update ModTime of
677+
// the directory.
678+
time.Sleep(75 * time.Millisecond)
679+
r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
680+
681+
node2, err := vfs.Stat("dir")
682+
require.NoError(t, err)
683+
modTime2 := node2.(*Dir).DirEntry().ModTime(context.Background())
684+
685+
// ModTime of directory must be different after second file was written.
686+
if modTime1.Equal(modTime2) {
687+
t.Error("ModTime not invalidated")
688+
}
689+
}

0 commit comments

Comments
 (0)