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
This commit is contained in:
Nick Craig-Wood 2021-03-12 16:15:28 +00:00
parent c387eb8c09
commit aea8776a43
3 changed files with 37 additions and 8 deletions

View file

@ -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
}

View file

@ -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) {

View file

@ -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