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
This commit is contained in:
Nick Craig-Wood 2023-03-08 12:10:50 +00:00
parent 580d72f0f6
commit e405ca7733

View File

@ -47,6 +47,7 @@ type File struct {
o fs.Object // NB o may be nil if file is being written o fs.Object // NB o may be nil if file is being written
leaf string // leaf name of the object leaf string // leaf name of the object
writers []Handle // writers for this file 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 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 pendingRenameFun func(ctx context.Context) error // will be run/renamed after all writers close
sys atomic.Value // user defined info to be attached here 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 // if NoModTime is set then it returns the mod time of the directory
func (f *File) ModTime() (modTime time.Time) { func (f *File) ModTime() (modTime time.Time) {
f.mu.RLock() 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() 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 { if d.vfs.Opt.NoModTime {
return d.ModTime() return d.ModTime()
} }
@ -334,6 +346,10 @@ func (f *File) ModTime() (modTime time.Time) {
if !pendingModTime.IsZero() { if !pendingModTime.IsZero() {
return f._roundModTime(pendingModTime) 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 { if o == nil {
return time.Now() return time.Now()
} }
@ -477,6 +493,8 @@ func (f *File) setObject(o fs.Object) {
func (f *File) setObjectNoUpdate(o fs.Object) { func (f *File) setObjectNoUpdate(o fs.Object) {
f.mu.Lock() f.mu.Lock()
f.o = o f.o = o
f.virtualModTime = nil
fs.Debugf(f._path(), "Reset virtual modtime")
f.mu.Unlock() f.mu.Unlock()
} }