From 956c2963fd1d7352e58913b4bb6282310c1c5f1e Mon Sep 17 00:00:00 2001 From: nielash Date: Fri, 23 Aug 2024 11:42:36 -0400 Subject: [PATCH] bisync: don't convert modtime precision in listings - fixes #8025 Before this change, bisync proactively converted modtime precision when greater than what the destination backend supported. This dates back to a time before bisync considered the modifyWindow for same-side comparisons. Back then, it was problematic to save a listing with 12:54:49.7 for a backend that can't handle that precision, as on the next run the backend would report the time as 12:54:50 and bisync would think the file had changed. So the truncation was a workaround to anticipate this and proactively record the time with the precision we expect to receive next time. However, this caused problems for backends (such as dropbox) that round instead of truncating as bisync expected. After this change, bisync preserves the original precision in the listing (without conversion), even when greater than what the backend supports, to avoid rounding error. On the next run, bisync will compare it to the rounded time reported by the backend, and if it's within the modifyWindow, it will treat them as equivalent. --- cmd/bisync/listing.go | 36 ++++++++++-------------------------- docs/content/bisync.md | 6 ++++++ 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/cmd/bisync/listing.go b/cmd/bisync/listing.go index 8f6d8eb19..a95029abe 100644 --- a/cmd/bisync/listing.go +++ b/cmd/bisync/listing.go @@ -43,8 +43,10 @@ var lineRegex = regexp.MustCompile(`^(\S) +(-?\d+) (\S+) (\S+) (\d{4}-\d\d-\d\dT const timeFormat = "2006-01-02T15:04:05.000000000-0700" // TZ defines time zone used in listings -var TZ = time.UTC -var tzLocal = false +var ( + TZ = time.UTC + tzLocal = false +) // fileInfo describes a file type fileInfo struct { @@ -83,7 +85,7 @@ func (ls *fileList) has(file string) bool { } _, found := ls.info[file] if !found { - //try unquoting + // try unquoting file, _ = strconv.Unquote(`"` + file + `"`) _, found = ls.info[file] } @@ -93,7 +95,7 @@ func (ls *fileList) has(file string) bool { func (ls *fileList) get(file string) *fileInfo { info, found := ls.info[file] if !found { - //try unquoting + // try unquoting file, _ = strconv.Unquote(`"` + file + `"`) info = ls.info[fmt.Sprint(file)] } @@ -420,7 +422,7 @@ func (b *bisyncRun) loadListingNum(listingNum int) (*fileList, error) { func (b *bisyncRun) listDirsOnly(listingNum int) (*fileList, error) { var fulllisting *fileList - var dirsonly = newFileList() + dirsonly := newFileList() var err error if !b.opt.CreateEmptySrcDirs { @@ -450,24 +452,6 @@ func (b *bisyncRun) listDirsOnly(listingNum int) (*fileList, error) { return dirsonly, err } -// ConvertPrecision returns the Modtime rounded to Dest's precision if lower, otherwise unchanged -// Need to use the other fs's precision (if lower) when copying -// Note: we need to use Truncate rather than Round so that After() is reliable. -// (2023-11-02 20:22:45.552679442 +0000 < UTC 2023-11-02 20:22:45.553 +0000 UTC) -func ConvertPrecision(Modtime time.Time, dst fs.Fs) time.Time { - DestPrecision := dst.Precision() - - // In case it's wrapping an Fs with lower precision, try unwrapping and use the lowest. - if Modtime.Truncate(DestPrecision).After(Modtime.Truncate(fs.UnWrapFs(dst).Precision())) { - DestPrecision = fs.UnWrapFs(dst).Precision() - } - - if Modtime.After(Modtime.Truncate(DestPrecision)) { - return Modtime.Truncate(DestPrecision) - } - return Modtime -} - // modifyListing will modify the listing based on the results of the sync func (b *bisyncRun) modifyListing(ctx context.Context, src fs.Fs, dst fs.Fs, results []Results, queues queues, is1to2 bool) (err error) { queue := queues.copy2to1 @@ -533,13 +517,13 @@ func (b *bisyncRun) modifyListing(ctx context.Context, src fs.Fs, dst fs.Fs, res // build src winners list if result.IsSrc && result.Src != "" && (result.Winner.Err == nil || result.Flags == "d") { - srcWinners.put(result.Name, result.Size, ConvertPrecision(result.Modtime, src), result.Hash, "-", result.Flags) + srcWinners.put(result.Name, result.Size, result.Modtime, result.Hash, "-", result.Flags) prettyprint(result, "winner: copy to src", fs.LogLevelDebug) } // build dst winners list if result.IsWinner && result.Winner.Side != "none" && (result.Winner.Err == nil || result.Flags == "d") { - dstWinners.put(result.Name, result.Size, ConvertPrecision(result.Modtime, dst), result.Hash, "-", result.Flags) + dstWinners.put(result.Name, result.Size, result.Modtime, result.Hash, "-", result.Flags) prettyprint(result, "winner: copy to dst", fs.LogLevelDebug) } @@ -623,7 +607,7 @@ func (b *bisyncRun) modifyListing(ctx context.Context, src fs.Fs, dst fs.Fs, res } if srcNewName != "" { // if it was renamed and not deleted srcList.put(srcNewName, new.size, new.time, new.hash, new.id, new.flags) - dstList.put(srcNewName, new.size, ConvertPrecision(new.time, src), new.hash, new.id, new.flags) + dstList.put(srcNewName, new.size, new.time, new.hash, new.id, new.flags) } if srcNewName != srcOldName { srcList.remove(srcOldName) diff --git a/docs/content/bisync.md b/docs/content/bisync.md index c51030df1..8f0bf4883 100644 --- a/docs/content/bisync.md +++ b/docs/content/bisync.md @@ -1812,6 +1812,12 @@ about _Unison_ and synchronization in general. ## Changelog +### `v1.68` +* Fixed an issue affecting backends that round modtimes to a lower precision. + +### `v1.67` +* Added integration tests against all backends. + ### `v1.66` * Copies and deletes are now handled in one operation instead of two * `--track-renames` and `--backup-dir` are now supported