vfs: stop setting a file's modtime from discarding its cached data

Setting the modtime of a closed file refreshed the cache item's
fingerprint before the new modtime had been applied to the remote
object. The next open then saw a fingerprint mismatch, logged "removed
cache file as stale (remote is different)" and downloaded the whole
file again, even though only the modtime had changed.

Refresh the cache fingerprint again once the remote modtime has been
set.
This commit is contained in:
Neil Cawse authored and Nick Craig-Wood committed 2026-09-30 17:51:49 +01:00
1 parent e0fb01084d
commit 9380daed0d
2 files changed
+22

No files matched your search

+5
View File
@@ -495,6 +495,11 @@ func (f *File) _applyPendingModTime() error {
switch err {
case nil:
fs.Debugf(f.o, "Applied pending mod time %v OK", f.pendingModTime)
// The cache fingerprint predates the new modtime, so refresh
// it or the next open discards the cached data as stale.
if f.d.vfs.cache != nil && f.d.vfs.cache.Exists(f._cachePath()) {
f.d.vfs.cache.SetModTime(f._cachePath(), f.pendingModTime)
}
case fs.ErrorCantSetModTime, fs.ErrorCantSetModTimeWithoutDelete:
// do nothing, in order to not break "touch somefile" if it exists already
default:
+17
View File
@@ -13,6 +13,7 @@ import (
"github.com/rclone/rclone/fstest"
"github.com/rclone/rclone/fstest/mockfs"
"github.com/rclone/rclone/fstest/mockobject"
"github.com/rclone/rclone/lib/ranges"
"github.com/rclone/rclone/vfs/vfscommon"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -163,6 +164,22 @@ func TestFileSetModTime(t *testing.T) {
}
}
// Touching a closed, fully cached file must keep its cached data: the
// remote modtime change it makes is not a change to the content.
func TestFileSetModTimeKeepsCache(t *testing.T) {
_, vfs, file, _ := fileCreate(t, vfscommon.CacheModeFull)
fileCheckContents(t, file)
whole := ranges.Range{Pos: 0, Size: int64(len("file1 contents"))}
item := vfs.cache.Item(file.Path())
require.True(t, item.HasRange(whole))
require.NoError(t, file.SetModTime(t2))
require.NoError(t, item.Open(file.getObject()))
defer func() { require.NoError(t, item.Close(nil)) }()
assert.True(t, item.HasRange(whole), "touch discarded the cached data")
}
func fileCheckContents(t *testing.T, file *File) {
fd, err := file.Open(os.O_RDONLY)
require.NoError(t, err)