fix: populate ctime from actual filesystem stats instead of mtime fallback
All checks were successful
check / check (pull_request) Successful in 2m27s

Replace the mtime fallback for ctime in the scanner with platform-specific
implementations that extract the real ctime from syscall.Stat_t:

- macOS/Darwin: uses Birthtimespec (file birth/creation time)
- Linux: uses Ctim (inode change time)
- Falls back to mtime when syscall stats are unavailable (e.g. afero.MemMapFs)

Also:
- Document platform-specific ctime semantics in README
- Document ctime restore limitations (cannot be set on either platform)
- Add ctime field documentation to File model
- Update README files table schema to match actual schema (adds ctime,
  source_path, link_target columns)
- Add comprehensive tests for fileCTime on real files and mock FileInfo

closes #13
This commit is contained in:
user
2026-03-17 13:49:51 -07:00
parent c24e7e6360
commit 25860c03a9
8 changed files with 228 additions and 11 deletions

View File

@@ -194,8 +194,9 @@ vaultik [--config <path>] store info
* Requires `VAULTIK_AGE_SECRET_KEY` environment variable with age private key
* Optional path arguments to restore specific files/directories (default: all)
* Downloads and decrypts metadata, fetches required blobs, reconstructs files
* Preserves file permissions, timestamps, and ownership (ownership requires root)
* Preserves file permissions, mtime, and ownership (ownership requires root)
* Handles symlinks and directories
* Note: ctime cannot be restored (see [platform notes](#platform-specific-ctime-semantics))
**prune**: Remove unreferenced blobs from remote storage
* Scans all snapshots for referenced blobs
@@ -247,11 +248,14 @@ Snapshot IDs follow the format `<hostname>_<snapshot-name>_<timestamp>` (e.g., `
CREATE TABLE files (
id TEXT PRIMARY KEY,
path TEXT NOT NULL UNIQUE,
source_path TEXT NOT NULL DEFAULT '',
mtime INTEGER NOT NULL,
ctime INTEGER NOT NULL,
size INTEGER NOT NULL,
mode INTEGER NOT NULL,
uid INTEGER NOT NULL,
gid INTEGER NOT NULL
gid INTEGER NOT NULL,
link_target TEXT
);
CREATE TABLE file_chunks (
@@ -339,7 +343,25 @@ CREATE TABLE snapshot_blobs (
1. For each file, get ordered chunk list from file_chunks
1. Download required blobs, decrypt, decompress
1. Extract chunks and reconstruct files
1. Restore permissions, mtime, uid/gid
1. Restore permissions, mtime, uid/gid (ctime cannot be restored — see platform notes above)
### platform-specific ctime semantics
The `ctime` field in the files table stores a platform-dependent timestamp:
* **macOS (Darwin)**: `ctime` is the file's **birth time** — when the file was
first created on disk. This value never changes after file creation, even if
the file's content or metadata is modified.
* **Linux**: `ctime` is the **inode change time** — the last time the file's
metadata (permissions, ownership, link count, etc.) was modified. This is NOT
the file creation time. Linux did not expose birth time (via `statx(2)`) until
kernel 4.11, and Go's `syscall` package does not yet surface it.
**Restore limitation**: `ctime` cannot be restored on either platform. On Linux,
the kernel manages the inode change time and userspace cannot set it. On macOS,
there is no standard POSIX API to set birth time. The `ctime` value is preserved
in the snapshot database for informational/forensic purposes only.
#### prune