diff --git a/fs/operations.go b/fs/operations.go index 5284b01ff..a57ee8aab 100644 --- a/fs/operations.go +++ b/fs/operations.go @@ -12,7 +12,6 @@ import ( "strings" "sync" "sync/atomic" - "time" "github.com/pkg/errors" "github.com/spf13/pflag" @@ -121,34 +120,53 @@ func Equal(src, dst Object) bool { return true } - var srcModTime time.Time - if !Config.CheckSum { - if Config.ModifyWindow == ModTimeNotSupported { - Debug(src, "Sizes identical") - return true + // Assert: Size is equal or being ignored + + // If checking checksum and not modtime + if Config.CheckSum { + // Check the hash + same, hash, _ := CheckHashes(src, dst) + if !same { + Debug(src, "%v differ", hash) + return false } - // Size the same so check the mtime - srcModTime = src.ModTime() - dstModTime := dst.ModTime() - dt := dstModTime.Sub(srcModTime) - ModifyWindow := Config.ModifyWindow - if dt >= ModifyWindow || dt <= -ModifyWindow { - Debug(src, "Modification times differ by %s: %v, %v", dt, srcModTime, dstModTime) + if hash == HashNone { + Debug(src, "Size of src and dst objects identical") } else { - Debug(src, "Size and modification time the same (differ by %s, within tolerance %s)", dt, ModifyWindow) - return true + Debug(src, "Size and %v of src and dst objects identical", hash) } + return true } - // mtime is unreadable or different but size is the same so - // check the hash + // Sizes the same so check the mtime + if Config.ModifyWindow == ModTimeNotSupported { + Debug(src, "Sizes identical") + return true + } + srcModTime := src.ModTime() + dstModTime := dst.ModTime() + dt := dstModTime.Sub(srcModTime) + ModifyWindow := Config.ModifyWindow + if dt < ModifyWindow && dt > -ModifyWindow { + Debug(src, "Size and modification time the same (differ by %s, within tolerance %s)", dt, ModifyWindow) + return true + } + + Debug(src, "Modification times differ by %s: %v, %v", dt, srcModTime, dstModTime) + + // Check if the hashes are the same same, hash, _ := CheckHashes(src, dst) if !same { - Debug(src, "Hash differ") + Debug(src, "%v differ", hash) + return false + } + if hash == HashNone { + // if couldn't check hash, return that they differ return false } - if !(Config.CheckSum || Config.NoUpdateModTime) { + // mod time differs but hash is the same to reset mod time if required + if !Config.NoUpdateModTime { // Size and hash the same but mtime different so update the // mtime of the dst object here err := dst.SetModTime(srcModTime) @@ -157,15 +175,11 @@ func Equal(src, dst Object) bool { return false } else if err != nil { Stats.Error() - ErrorLog(dst, "Failed to read set modification time: %v", err) + ErrorLog(dst, "Failed to set modification time: %v", err) + } else { + Debug(src, "Updated modification time in destination") } } - - if hash == HashNone { - Debug(src, "Size of src and dst objects identical") - } else { - Debug(src, "Size and %v of src and dst objects identical", hash) - } return true } diff --git a/fs/sync_test.go b/fs/sync_test.go index 84fbfc70c..df2227176 100644 --- a/fs/sync_test.go +++ b/fs/sync_test.go @@ -336,6 +336,30 @@ func TestSyncAfterChangingModtimeOnlyWithNoUpdateModTime(t *testing.T) { fstest.CheckItems(t, r.fremote, file2) } +func TestSyncDoesntUpdateModtime(t *testing.T) { + if fs.Config.ModifyWindow == fs.ModTimeNotSupported { + t.Skip("Can't run this test on fs which doesn't support mod time") + } + r := NewRun(t) + defer r.Finalise() + + file1 := r.WriteFile("foo", "foo", t2) + file2 := r.WriteObject("foo", "bar", t1) + + fstest.CheckItems(t, r.flocal, file1) + fstest.CheckItems(t, r.fremote, file2) + + fs.Stats.ResetCounters() + err := fs.Sync(r.fremote, r.flocal) + require.NoError(t, err) + + fstest.CheckItems(t, r.flocal, file1) + fstest.CheckItems(t, r.fremote, file1) + + // We should have transferred exactly one file, not set the mod time + assert.Equal(t, int64(1), fs.Stats.GetTransfers()) +} + func TestSyncAfterAddingAFile(t *testing.T) { r := NewRun(t) defer r.Finalise()