Restore applies directory modes and times before writing the contents, and file modes before ownership #219

Closed
opened 2026-10-06 01:49:42 +02:00 by clawbot · 1 comment
Collaborator

Restore gets three ordering steps wrong. Each one leaves a restore that differs from what the README promises: "Preserves file permissions, timestamps, ownership (ownership requires root), symlinks, and empty directories" (README.md:367-368, also :489).

  • Directory mode is set before the children are written. Directories go on the ready queue first (internal/vaultik/restore_plan.go:58-65) and get their stored mode at once (internal/vaultik/restore.go:1003-1013). Their children are created later (restore.go:1080-1086). Measured on next at 0700901: restoring a 0555 directory holding one file, as uid 1000, fails with creating output file: ... permission denied. With --skip-errors, the files inside are simply not restored. Read-only trees such as a Go module cache are common.
  • Directory time is set before the children are written (restore.go:1015). Creating the children then bumps the directory's mtime. Measured: a non-empty directory came back with the time of the restore, while the file inside kept its stored 2001-02-03.
  • Mode is applied before ownership (restore.go:1117-1118; the chown is at :1030). On Linux, chown clears the setuid and setgid bits of a regular file, so a restore as root loses them on sudo, ping and the like. Observed unprivileged on this host: mode 4755 became 755 after a same-owner chown. The root case was not run.
  • Symlinks get no owner and no time. restoreSymlink (internal/vaultik/restore.go:977-996) skips both, so a restore as root leaves every symlink owned by root. Since #216 the scanner records the right symlink owner.

Definition of done

  1. Ownership is applied before mode, so setuid and setgid survive a restore as root.
  2. A directory's mode and times are applied after everything inside it has been written.
  3. A symlink restored as root gets its recorded owner (Lchown), and its recorded mtime where the platform supports it.
  4. Tests, run as a normal user: a read-only directory with a file inside restores completely; a non-empty directory keeps its stored mtime; a setuid file restored by a same-owner restore keeps its mode bits.
  5. make check passes.

Model: fable-5-1 (audit); opus-5-5 (issue)

Restore gets three ordering steps wrong. Each one leaves a restore that differs from what the README promises: "Preserves file permissions, timestamps, ownership (ownership requires root), symlinks, and empty directories" (`README.md:367-368`, also `:489`). - **Directory mode is set before the children are written.** Directories go on the ready queue first (`internal/vaultik/restore_plan.go:58-65`) and get their stored mode at once (`internal/vaultik/restore.go:1003-1013`). Their children are created later (`restore.go:1080-1086`). Measured on `next` at `0700901`: restoring a `0555` directory holding one file, as uid 1000, fails with `creating output file: ... permission denied`. With `--skip-errors`, the files inside are simply not restored. Read-only trees such as a Go module cache are common. - **Directory time is set before the children are written** (`restore.go:1015`). Creating the children then bumps the directory's mtime. Measured: a non-empty directory came back with the time of the restore, while the file inside kept its stored 2001-02-03. - **Mode is applied before ownership** (`restore.go:1117-1118`; the chown is at `:1030`). On Linux, `chown` clears the setuid and setgid bits of a regular file, so a restore as root loses them on `sudo`, `ping` and the like. Observed unprivileged on this host: mode 4755 became 755 after a same-owner chown. The root case was not run. - **Symlinks get no owner and no time.** `restoreSymlink` (`internal/vaultik/restore.go:977-996`) skips both, so a restore as root leaves every symlink owned by root. Since https://git.eeqj.de/sneak/vaultik/issues/216 the scanner records the right symlink owner. ## Definition of done 1. Ownership is applied before mode, so setuid and setgid survive a restore as root. 2. A directory's mode and times are applied after everything inside it has been written. 3. A symlink restored as root gets its recorded owner (`Lchown`), and its recorded mtime where the platform supports it. 4. Tests, run as a normal user: a read-only directory with a file inside restores completely; a non-empty directory keeps its stored mtime; a setuid file restored by a same-owner restore keeps its mode bits. 5. `make check` passes. Model: fable-5-1 (audit); opus-5-5 (issue)
clawbot self-assigned this 2026-10-06 01:49:42 +02:00
Author
Collaborator

Fixed in #245. Directories are created owner-only and get their stored owner, mtime and mode after the restore loop, deepest first. A file is chowned before its mode is set, and a symlink gets its owner (as root) and mtime on the link itself.

Model: opus-5-5

Fixed in https://git.eeqj.de/sneak/vaultik/pulls/245. Directories are created owner-only and get their stored owner, mtime and mode after the restore loop, deepest first. A file is chowned before its mode is set, and a symlink gets its owner (as root) and mtime on the link itself. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/vaultik#219