vfs: fix incorrect modtime for mv into mount with --vfs-cache-modes write

When a file has its modtime set while it is open we delay setting the
modtime until the file is closed.

The file is then uploaded in Flush. In Release we check the cached
file has been uploaded by comparing modtimes and or hashes and upload
it again if it has changed.

Before this change we forgot to change the time on the cached file
when we updated the time file on the object, so this mean that Release
reset the time to the wrong time and uploaded the file again on
remotes which don't support hashes (eg crypt).

The fix was to set the modtime of the cached file at the same time we
set the modtime of the remote object. This means that the files check
as identical in Release so it doesn't try to upload the file.

This means that we avoid a double upload and the modtime is correct.

See: https://forum.rclone.org/t/modification-time-with-vfs-cache/13906/8
This commit is contained in:
Nick Craig-Wood 2020-01-19 12:52:48 +00:00
parent 7cf8ea354c
commit 84191ac6dc
3 changed files with 33 additions and 3 deletions

View file

@ -381,6 +381,15 @@ func (c *cache) removeDir(dir string) bool {
return false
}
// setModTime should be called to set the modification time of the cache file
func (c *cache) setModTime(name string, modTime time.Time) {
osPath := c.toOSPath(name)
err := os.Chtimes(osPath, modTime, modTime)
if err != nil {
fs.Errorf(name, "Failed to set modification time of cached file: %v", err)
}
}
// cleanUp empties the cache of everything
func (c *cache) cleanUp() error {
return os.RemoveAll(c.root)

View file

@ -88,11 +88,17 @@ func (f *File) Name() (name string) {
return f.leaf
}
// _path returns the full path of the file
// use when lock is held
func (f *File) _path() string {
return path.Join(f.d.path, f.leaf)
}
// Path returns the full path of the file
func (f *File) Path() string {
f.mu.RLock()
defer f.mu.RUnlock()
return path.Join(f.d.path, f.leaf)
return f._path()
}
// osPath returns the full path of the file in the cache in OS format
@ -372,6 +378,12 @@ func (f *File) _applyPendingModTime() error {
return errors.New("Cannot apply ModTime, file object is not available")
}
// set the time of the file in the cache
if f.d.vfs.Opt.CacheMode != CacheModeOff {
f.d.vfs.cache.setModTime(f._path(), f.pendingModTime)
}
// set the time of the object
err := f.o.SetModTime(context.TODO(), f.pendingModTime)
switch err {
case nil:

View file

@ -2,6 +2,7 @@ package vfs
import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
@ -687,7 +688,12 @@ func TestRWFileModTimeWithOpenWriters(t *testing.T) {
err = fh.Node().SetModTime(mtime)
require.NoError(t, err)
err = fh.Close()
// Using Flush/Release to mimic mount instead of Close
err = fh.Flush()
require.NoError(t, err)
err = fh.Release()
require.NoError(t, err)
info, err := vfs.Stat("file1")
@ -695,8 +701,11 @@ func TestRWFileModTimeWithOpenWriters(t *testing.T) {
if r.Fremote.Precision() != fs.ModTimeNotSupported {
// avoid errors because of timezone differences
assert.Equal(t, info.ModTime().Unix(), mtime.Unix())
assert.Equal(t, info.ModTime().Unix(), mtime.Unix(), fmt.Sprintf("Time mismatch: %v != %v", info.ModTime(), mtime))
}
file1 := fstest.NewItem("file1", "hi", mtime)
fstest.CheckItems(t, r.Fremote, file1)
}
func TestRWCacheRename(t *testing.T) {