Relationships
#3546 @swamp/s3-datastore and @swamp/gcs-datastore: pull's same-size fast path trusts the pushing machine's mtime, so a conflicting local file can be kept
Opened by stack72 · 10/16/2026· Shipped 10/16/2026
Background
@swamp/s3-datastore and @swamp/gcs-datastore sync a v2 datastore by pulling files whose remote index entry differs from the local cache. swamp-extensions has issues turned off, so this is tracked here.
Found by the randomized model test written for swamp-club#3377 (create-only data versions, since closed as covered). It exists on main today, independent of #3377.
The bug
pullChanged's same-size fast path skips the content check when the local file's mtime equals the index entry's localMtime. In S3 that's datastore/s3/extensions/datastores/_lib/s3_cache_sync.ts, around line 2681; GCS has the equivalent:
if (stat.size === entry.size) {
const mtimeMatch = entry.localMtime && stat.mtime &&
entry.localMtime === stat.mtime.toISOString();
if (!mtimeMatch && entry.sha256) {
// hash-compare, pull on mismatch
}
continue; // treated as up to date
}entry.localMtime comes from the remote index, so it's the pushing machine's mtime for that file, not this copy's. When this copy's local file has the same size and the same mtime string, the pull keeps the local bytes without hashing, even when they differ from the remote's sha256.
When it can happen:
- a copy whose cache files were restored or copied with mtimes preserved (
cp -p, rsync, a backup restore), then edited to same-size content; - clocks or tools that write identical mtimes.
What results: this copy keeps a stale or conflicting file. A later push may then upload it over the remote's newer content.
What to do
- Make the fast path compare this copy's own recorded mtime, not the pusher's. Either keep a per-cache record of the mtime this copy last wrote or verified (never synced), or drop the mtime shortcut whenever the index carries a
sha256and hash-compare same-size files. The hash is cheap next to a download. - Keep the
localMtimereconciliation that stopspushChangedre-uploading unchanged files, but base it on this copy's own state. - Same change on S3 and GCS.
Tests
- A same-size local file whose mtime equals the remote entry's
localMtimebut whose bytes differ from the entry'ssha256is pulled (replaced), on both providers. It must fail onmain. - An unchanged same-size file is still not re-downloaded: count requests.
Blocked by: nothing. Low severity: it needs preserved or identical mtimes and same-size differing content.
Shipped
Click a lifecycle step above to view its details.
system commented 10/16/2026, 3:32:16 PM
Classified automatically when this issue was filed.
- Source: Extensions
If you feel this classification is incorrect, add a ripple to tell us so.
Sign in to post a ripple.