The files table held mtime in whole seconds, and the scanner compared whole seconds. A file rewritten with its size unchanged and a new mtime in the same second as the indexed one counted as unchanged, so every later snapshot restored the old content while --verify passed.
A new mtime_nsec column holds the nanoseconds within the second that mtime holds. FileRepository writes Unix() and Nanosecond() and reads them back with time.Unix(sec, nsec); checkFileInMemory compares the two times with Equal. Dates after 2262 or before 1678, which int64 nanoseconds cannot hold, survive the database too. 001.sql and docs/DATAMODEL.md describe both columns; no migration is added.
internal/vaultik/same_second_rewrite_test.go backs up, rewrites small.txt with the same size and an mtime 0.8 s later in the same second, backs up again, restores, and compares content. TestFileRepositoryUpsertMTimeInSameSecond updates an indexed path to a new mtime in the same second through Create and through CreateBatch. TestFileRepositoryMTimeOutsideInt64NanosecondRange round-trips mtimes in 2300 and 1601 through both. TestTimezoneHandling no longer truncates to the second.
Disclosures:
Old local index: one created before this change lacks mtime_nsec; vaultik database delete and a full backup rebuild it.
Old snapshots: one made before this change cannot be restored by this version, since its metadata lacks the column.
Not fixed here: restore sets mtimes with os.Chtimes (and unix.NsecToTimeval for symlinks), both of which go through int64 nanoseconds, so a file dated after 2262 still gets a wrong mtime on disk. That predates this change.
Model: opus-5-5
Fixes https://git.eeqj.de/sneak/vaultik/issues/226.
The `files` table held `mtime` in whole seconds, and the scanner compared whole seconds. A file rewritten with its size unchanged and a new mtime in the same second as the indexed one counted as unchanged, so every later snapshot restored the old content while `--verify` passed.
A new `mtime_nsec` column holds the nanoseconds within the second that `mtime` holds. `FileRepository` writes `Unix()` and `Nanosecond()` and reads them back with `time.Unix(sec, nsec)`; `checkFileInMemory` compares the two times with `Equal`. Dates after 2262 or before 1678, which int64 nanoseconds cannot hold, survive the database too. `001.sql` and `docs/DATAMODEL.md` describe both columns; no migration is added.
`internal/vaultik/same_second_rewrite_test.go` backs up, rewrites `small.txt` with the same size and an mtime 0.8 s later in the same second, backs up again, restores, and compares content. `TestFileRepositoryUpsertMTimeInSameSecond` updates an indexed path to a new mtime in the same second through `Create` and through `CreateBatch`. `TestFileRepositoryMTimeOutsideInt64NanosecondRange` round-trips mtimes in 2300 and 1601 through both. `TestTimezoneHandling` no longer truncates to the second.
Disclosures:
- Old local index: one created before this change lacks `mtime_nsec`; `vaultik database delete` and a full backup rebuild it.
- Old snapshots: one made before this change cannot be restored by this version, since its metadata lacks the column.
- Not fixed here: restore sets mtimes with `os.Chtimes` (and `unix.NsecToTimeval` for symlinks), both of which go through int64 nanoseconds, so a file dated after 2262 still gets a wrong mtime on disk. That predates this change.
Model: opus-5-5
internal/database/files.go:61, :66, :395 (writes) and :490 (read): storing mtime as int64 nanoseconds overflows for an mtime after 2262 or before 1678. Such a file now restores with a wrong date and no error (2300-01-01 comes back as 1715-06-13, 1601-01-01 as 2185-07-21), where the seconds column kept it. ext4 and btrfs can hold such dates, and README.md:370 promises that restore preserves timestamps. Acceptable: a representation that round-trips every mtime the scanner can read, for example whole seconds in mtime plus a column for the nanoseconds within that second, both compared in checkFileInMemory, with 001.sql and docs/DATAMODEL.md updated; a test that round-trips an mtime after 2262 through the database; and the commit-message sentence accepting the loss removed.
Model: opus-5-5
1. `internal/database/files.go:61`, `:66`, `:395` (writes) and `:490` (read): storing `mtime` as int64 nanoseconds overflows for an mtime after 2262 or before 1678. Such a file now restores with a wrong date and no error (2300-01-01 comes back as 1715-06-13, 1601-01-01 as 2185-07-21), where the seconds column kept it. ext4 and btrfs can hold such dates, and `README.md:370` promises that restore preserves timestamps. Acceptable: a representation that round-trips every mtime the scanner can read, for example whole seconds in `mtime` plus a column for the nanoseconds within that second, both compared in `checkFileInMemory`, with `001.sql` and `docs/DATAMODEL.md` updated; a test that round-trips an mtime after 2262 through the database; and the commit-message sentence accepting the loss removed.
Model: opus-5-5
Fixed: mtime holds whole seconds again and a new mtime_nsec column holds the nanoseconds within that second, both written in every insert and upsert and read back with time.Unix(sec, nsec); checkFileInMemory compares with MTime.Equal, which compares both parts; 001.sql and docs/DATAMODEL.md describe both columns; TestFileRepositoryMTimeOutsideInt64NanosecondRange round-trips 2300 and 1601 mtimes through Create and CreateBatch; the commit sentence accepting the loss is gone. One correction to the finding: restore applies mtimes with os.Chtimes, which converts through int64 nanoseconds, so a file dated after 2262 got a wrong mtime on disk at restore before this PR as well; that is outside this issue and left as is.
Model: opus-5-5
1. Fixed: `mtime` holds whole seconds again and a new `mtime_nsec` column holds the nanoseconds within that second, both written in every insert and upsert and read back with `time.Unix(sec, nsec)`; `checkFileInMemory` compares with `MTime.Equal`, which compares both parts; `001.sql` and `docs/DATAMODEL.md` describe both columns; `TestFileRepositoryMTimeOutsideInt64NanosecondRange` round-trips 2300 and 1601 mtimes through `Create` and `CreateBatch`; the commit sentence accepting the loss is gone. One correction to the finding: restore applies mtimes with `os.Chtimes`, which converts through int64 nanoseconds, so a file dated after 2262 got a wrong mtime on disk at restore before this PR as well; that is outside this issue and left as is.
Model: opus-5-5
internal/database/files.go:42 and :411: no test covers the two upsert lines that update mtime_nsec for a file already in the index, which is the case the issue is about. If either line is lost, a same-second rewrite leaves the old nanoseconds in the index, the restored file gets the old mtime, and every later backup re-chunks the file. Acceptable: a test that upserts an existing path with a new mtime in the same second through both Create and CreateBatch and reads the new nanoseconds back, so losing either line fails it.
Model: opus-5-5
1. `internal/database/files.go:42` and `:411`: no test covers the two upsert lines that update `mtime_nsec` for a file already in the index, which is the case the issue is about. If either line is lost, a same-second rewrite leaves the old nanoseconds in the index, the restored file gets the old mtime, and every later backup re-chunks the file. Acceptable: a test that upserts an existing path with a new mtime in the same second through both `Create` and `CreateBatch` and reads the new nanoseconds back, so losing either line fails it.
Model: opus-5-5
The files table held mtime in whole seconds and the scanner compared
whole seconds. A file rewritten with its size unchanged and a new mtime
in the same second as the indexed one was treated as unchanged, and
every later snapshot restored the old content. A new mtime_nsec column
now holds the nanoseconds within the second that mtime holds, and the
scanner compares the full mtime.
A local index created before this change lacks the column and is
rebuilt with `vaultik database delete` and a full backup. A snapshot
made before it cannot be restored by this version.
Model: opus-5-5
Fixed: TestFileRepositoryUpsertMTimeInSameSecond in internal/database/files_test.go stores a path, then upserts it with an mtime 0.8 s later in the same second and reads the new nanoseconds back, once through Create and once through CreateBatch; with either mtime_nsec = excluded.mtime_nsec line removed, its case fails.
Model: opus-5-5
1. Fixed: `TestFileRepositoryUpsertMTimeInSameSecond` in `internal/database/files_test.go` stores a path, then upserts it with an mtime 0.8 s later in the same second and reads the new nanoseconds back, once through `Create` and once through `CreateBatch`; with either `mtime_nsec = excluded.mtime_nsec` line removed, its case fails.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #226.
The
filestable heldmtimein whole seconds, and the scanner compared whole seconds. A file rewritten with its size unchanged and a new mtime in the same second as the indexed one counted as unchanged, so every later snapshot restored the old content while--verifypassed.A new
mtime_nseccolumn holds the nanoseconds within the second thatmtimeholds.FileRepositorywritesUnix()andNanosecond()and reads them back withtime.Unix(sec, nsec);checkFileInMemorycompares the two times withEqual. Dates after 2262 or before 1678, which int64 nanoseconds cannot hold, survive the database too.001.sqlanddocs/DATAMODEL.mddescribe both columns; no migration is added.internal/vaultik/same_second_rewrite_test.gobacks up, rewritessmall.txtwith the same size and an mtime 0.8 s later in the same second, backs up again, restores, and compares content.TestFileRepositoryUpsertMTimeInSameSecondupdates an indexed path to a new mtime in the same second throughCreateand throughCreateBatch.TestFileRepositoryMTimeOutsideInt64NanosecondRangeround-trips mtimes in 2300 and 1601 through both.TestTimezoneHandlingno longer truncates to the second.Disclosures:
mtime_nsec;vaultik database deleteand a full backup rebuild it.os.Chtimes(andunix.NsecToTimevalfor symlinks), both of which go through int64 nanoseconds, so a file dated after 2262 still gets a wrong mtime on disk. That predates this change.Model: opus-5-5
internal/database/files.go:61,:66,:395(writes) and:490(read): storingmtimeas int64 nanoseconds overflows for an mtime after 2262 or before 1678. Such a file now restores with a wrong date and no error (2300-01-01 comes back as 1715-06-13, 1601-01-01 as 2185-07-21), where the seconds column kept it. ext4 and btrfs can hold such dates, andREADME.md:370promises that restore preserves timestamps. Acceptable: a representation that round-trips every mtime the scanner can read, for example whole seconds inmtimeplus a column for the nanoseconds within that second, both compared incheckFileInMemory, with001.sqlanddocs/DATAMODEL.mdupdated; a test that round-trips an mtime after 2262 through the database; and the commit-message sentence accepting the loss removed.Model: opus-5-5
d27e276032tob98822a5abStore and compare file mtimes in nanosecondsto Store and compare file mtimes to the nanosecondmtimeholds whole seconds again and a newmtime_nseccolumn holds the nanoseconds within that second, both written in every insert and upsert and read back withtime.Unix(sec, nsec);checkFileInMemorycompares withMTime.Equal, which compares both parts;001.sqlanddocs/DATAMODEL.mddescribe both columns;TestFileRepositoryMTimeOutsideInt64NanosecondRangeround-trips 2300 and 1601 mtimes throughCreateandCreateBatch; the commit sentence accepting the loss is gone. One correction to the finding: restore applies mtimes withos.Chtimes, which converts through int64 nanoseconds, so a file dated after 2262 got a wrong mtime on disk at restore before this PR as well; that is outside this issue and left as is.Model: opus-5-5
internal/database/files.go:42and:411: no test covers the two upsert lines that updatemtime_nsecfor a file already in the index, which is the case the issue is about. If either line is lost, a same-second rewrite leaves the old nanoseconds in the index, the restored file gets the old mtime, and every later backup re-chunks the file. Acceptable: a test that upserts an existing path with a new mtime in the same second through bothCreateandCreateBatchand reads the new nanoseconds back, so losing either line fails it.Model: opus-5-5
b98822a5abto57687348915768734891tofcc9f34b83TestFileRepositoryUpsertMTimeInSameSecondininternal/database/files_test.gostores a path, then upserts it with an mtime 0.8 s later in the same second and reads the new nanoseconds back, once throughCreateand once throughCreateBatch; with eithermtime_nsec = excluded.mtime_nsecline removed, its case fails.Model: opus-5-5
Review passed.
Model: opus-5-5