From aea8776a4368ee8ab02b436717d219d0e868d701 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 12 Mar 2021 16:15:28 +0000 Subject: [PATCH] vfs: fix modtimes not updating when writing via cache - fixes #4763 This reads modtime from a dirty cache item if it exists. This mirrors the way reading the size works. This fixes the mod time not updating when the file is written, only when the writeback completes. See: https://forum.rclone.org/t/rclone-mount-and-changing-timestamps-after-writes/22629 --- vfs/file.go | 11 +++++++++++ vfs/vfscache/cache.go | 3 ++- vfs/vfscache/item.go | 31 ++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/vfs/file.go b/vfs/file.go index f06b35bb1..535aa414f 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -300,6 +300,17 @@ func (f *File) ModTime() (modTime time.Time) { if d.vfs.Opt.NoModTime { return d.ModTime() } + // Read the modtime from a dirty item if it exists + if f.d.vfs.Opt.CacheMode >= vfscommon.CacheModeMinimal { + if item := f.d.vfs.cache.DirtyItem(f._path()); item != nil { + modTime, err := item.GetModTime() + if err != nil { + fs.Errorf(f._path(), "ModTime: Item GetModTime failed: %v", err) + } else { + return modTime + } + } + } if !pendingModTime.IsZero() { return pendingModTime } diff --git a/vfs/vfscache/cache.go b/vfs/vfscache/cache.go index 2d1950ab0..45fc98554 100644 --- a/vfs/vfscache/cache.go +++ b/vfs/vfscache/cache.go @@ -237,7 +237,8 @@ func (c *Cache) InUse(name string) bool { return item.inUse() } -// DirtyItem the Item if it exists in the cache and is Dirty +// DirtyItem returns the Item if it exists in the cache **and** is +// dirty otherwise it returns nil. // // name should be a remote path not an osPath func (c *Cache) DirtyItem(name string) (item *Item) { diff --git a/vfs/vfscache/item.go b/vfs/vfscache/item.go index 8fffb2bc5..b7f6f0700 100644 --- a/vfs/vfscache/item.go +++ b/vfs/vfscache/item.go @@ -359,17 +359,22 @@ func (item *Item) Truncate(size int64) (err error) { return nil } +// _stat gets the current stat of the backing file +// +// Call with mutex held +func (item *Item) _stat() (fi os.FileInfo, err error) { + if item.fd != nil { + return item.fd.Stat() + } + osPath := item.c.toOSPath(item.name) // No locking in Cache + return os.Stat(osPath) +} + // _getSize gets the current size of the item and updates item.info.Size // // Call with mutex held func (item *Item) _getSize() (size int64, err error) { - var fi os.FileInfo - if item.fd != nil { - fi, err = item.fd.Stat() - } else { - osPath := item.c.toOSPath(item.name) // No locking in Cache - fi, err = os.Stat(osPath) - } + fi, err := item._stat() if err != nil { if os.IsNotExist(err) && item.o != nil { size = item.o.Size() @@ -1187,6 +1192,18 @@ func (item *Item) setModTime(modTime time.Time) { item.mu.Unlock() } +// GetModTime of the cache file +func (item *Item) GetModTime() (modTime time.Time, err error) { + // defer log.Trace(item.name, "modTime=%v", modTime)("") + item.mu.Lock() + defer item.mu.Unlock() + fi, err := item._stat() + if err == nil { + modTime = fi.ModTime() + } + return modTime, nil +} + // ReadAt bytes from the file at off func (item *Item) ReadAt(b []byte, off int64) (n int, err error) { n = 0