From e405ca7733b19b9430afc344324eee3b608c1466 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 8 Mar 2023 12:10:50 +0000 Subject: [PATCH] vfs: make uploaded files retain modtime with non-modtime backends Before this change if a file was uploaded to a backend which didn't support modtimes, the time of the file read after the upload had completed would change to the time the file was uploaded on the backend. When using `--vfs-cache-mode writes` or `full` this time would be different by the `--vfs-write-back` delay which would cause applications to think the file had been modified. This changes uses the last modification time read by the OS as a virtual modtime for backends which don't support setting modtimes. It does not change the modtime to that actually uploaded. This means that as long as the file remains in the directory cache it will have the expected modtime. See: https://forum.rclone.org/t/saving-files-causes-wrong-modified-time-to-be-set-for-a-few-seconds-on-webdav-mount-with-bitrix24/36451 --- vfs/file.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/vfs/file.go b/vfs/file.go index 44a4ebb88..09b8a85d8 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -47,6 +47,7 @@ type File struct { o fs.Object // NB o may be nil if file is being written leaf string // leaf name of the object writers []Handle // writers for this file + virtualModTime *time.Time // modtime for backends with Precision == fs.ModTimeNotSupported pendingModTime time.Time // will be applied once o becomes available, i.e. after file was written pendingRenameFun func(ctx context.Context) error // will be run/renamed after all writers close sys atomic.Value // user defined info to be attached here @@ -314,9 +315,20 @@ func (f *File) _roundModTime(modTime time.Time) time.Time { // if NoModTime is set then it returns the mod time of the directory func (f *File) ModTime() (modTime time.Time) { f.mu.RLock() - d, o, pendingModTime := f.d, f.o, f.pendingModTime + d, o, pendingModTime, virtualModTime := f.d, f.o, f.pendingModTime, f.virtualModTime f.mu.RUnlock() + // Set the virtual modtime up for backends which don't support setting modtime + // + // Note that we only cache modtime values that we have returned to the OS + // if we haven't returned a value to the OS then we can change it + defer func() { + if f.d.f.Precision() == fs.ModTimeNotSupported && (virtualModTime == nil || !virtualModTime.Equal(modTime)) { + f.virtualModTime = &modTime + fs.Debugf(f._path(), "Set virtual modtime to %v", f.virtualModTime) + } + }() + if d.vfs.Opt.NoModTime { return d.ModTime() } @@ -334,6 +346,10 @@ func (f *File) ModTime() (modTime time.Time) { if !pendingModTime.IsZero() { return f._roundModTime(pendingModTime) } + if virtualModTime != nil && !virtualModTime.IsZero() { + fs.Debugf(f._path(), "Returning virtual modtime %v", f.virtualModTime) + return f._roundModTime(*virtualModTime) + } if o == nil { return time.Now() } @@ -477,6 +493,8 @@ func (f *File) setObject(o fs.Object) { func (f *File) setObjectNoUpdate(o fs.Object) { f.mu.Lock() f.o = o + f.virtualModTime = nil + fs.Debugf(f._path(), "Reset virtual modtime") f.mu.Unlock() }