A backup while a volume mounted below a configured path is not mounted deletes the older snapshots holding its files under --prune #395

Closed
opened 2026-10-15 00:17:50 +02:00 by clawbot · 1 comment
Collaborator

Severity: high

What is wrong: When a volume is normally mounted below a configured source path (configured path /data, an NFS share or USB disk mounted at /data/nas) and is not mounted at backup time, snapshot create finds /data/nas as an empty directory, counts every file the previous snapshot held under it as deleted, and succeeds. With --prune (default retention, keep the latest) that run then purges the older snapshot that holds the volume's files and prunes their blobs; with --keep-newer-than 4w the same happens once the volume has been away for the window. This is the data loss of #388 one level down: that fix (#394) catches a configured path that is empty, but here the configured path still holds other files. Found by the reviewer of #394; shown by reading the code (the scan crosses onto other filesystems and records nothing about them), not run.

Where: internal/snapshot/scanner.go (the walk); internal/vaultik/snapshot.go CreateSnapshot (the switch that decides the post-backup prune).

Plan:

  • The scan records, for each snapshot, the directories at or below a configured path that sit on a different filesystem from their parent directory (device number differs), the configured path itself included. They are stored in the local index with the snapshot, in a new table in internal/database/schema/001.sql (vaultik is pre-1.0: edit the schema in place).
  • Under --prune, snapshot create skips the post-backup prune when an earlier snapshot of that name in the local index recorded such a directory that this run did not find on a different filesystem. It warns naming the directory and snapshot, keeps every older snapshot and its blobs, and exits non-zero, as for an empty configured path.
  • A volume retired on purpose is cleared by removing the older snapshots by hand with snapshot purge; the warning and the README's --prune text say so.
  • The scanner reads the device number through one small function the tests can replace, since the in-memory test filesystem has none.

Definition of done

  • snapshot create --prune does not prune after a backup in which a volume mounted at or below a configured path in an earlier snapshot of that name is not mounted; it keeps the older snapshots and their blobs, says why, and exits non-zero.
  • The README's --prune text says so.
  • A test covers a backup with such a volume missing under --prune keeping the earlier snapshot, over two consecutive runs, and a run with the volume present still pruning.

Model: opus-5-5

**Severity:** high **What is wrong:** When a volume is normally mounted below a configured source path (configured path `/data`, an NFS share or USB disk mounted at `/data/nas`) and is not mounted at backup time, `snapshot create` finds `/data/nas` as an empty directory, counts every file the previous snapshot held under it as deleted, and succeeds. With `--prune` (default retention, keep the latest) that run then purges the older snapshot that holds the volume's files and prunes their blobs; with `--keep-newer-than 4w` the same happens once the volume has been away for the window. This is the data loss of https://git.eeqj.de/sneak/vaultik/issues/388 one level down: that fix (https://git.eeqj.de/sneak/vaultik/pulls/394) catches a configured path that is empty, but here the configured path still holds other files. Found by the reviewer of https://git.eeqj.de/sneak/vaultik/pulls/394; shown by reading the code (the scan crosses onto other filesystems and records nothing about them), not run. **Where:** `internal/snapshot/scanner.go` (the walk); `internal/vaultik/snapshot.go` `CreateSnapshot` (the `switch` that decides the post-backup prune). **Plan:** - The scan records, for each snapshot, the directories at or below a configured path that sit on a different filesystem from their parent directory (device number differs), the configured path itself included. They are stored in the local index with the snapshot, in a new table in `internal/database/schema/001.sql` (vaultik is pre-1.0: edit the schema in place). - Under `--prune`, `snapshot create` skips the post-backup prune when an earlier snapshot of that name in the local index recorded such a directory that this run did not find on a different filesystem. It warns naming the directory and snapshot, keeps every older snapshot and its blobs, and exits non-zero, as for an empty configured path. - A volume retired on purpose is cleared by removing the older snapshots by hand with `snapshot purge`; the warning and the README's `--prune` text say so. - The scanner reads the device number through one small function the tests can replace, since the in-memory test filesystem has none. **Definition of done** - `snapshot create --prune` does not prune after a backup in which a volume mounted at or below a configured path in an earlier snapshot of that name is not mounted; it keeps the older snapshots and their blobs, says why, and exits non-zero. - The README's `--prune` text says so. - A test covers a backup with such a volume missing under `--prune` keeping the earlier snapshot, over two consecutive runs, and a run with the volume present still pruning. Model: opus-5-5
Author
Collaborator

Built as planned in #396 (base next): each snapshot now records in the local index the directories it found on a different filesystem from their parent, and snapshot create --prune skips the prune, keeps the older snapshots and exits non-zero when one an earlier snapshot of that name recorded is not found as one. The PR body lists the one judgement call and the one rule suppressed.

Model: opus-5-5

Built as planned in https://git.eeqj.de/sneak/vaultik/pulls/396 (base `next`): each snapshot now records in the local index the directories it found on a different filesystem from their parent, and `snapshot create --prune` skips the prune, keeps the older snapshots and exits non-zero when one an earlier snapshot of that name recorded is not found as one. The PR body lists the one judgement call and the one rule suppressed. Model: opus-5-5
Sign in to join this conversation.