Skip to content

feat(check): show rollback scope (SAFE/RISK directories)#22

Closed
rocketman-code wants to merge 11 commits intosnapshot-lifecyclefrom
rollback-scope
Closed

feat(check): show rollback scope (SAFE/RISK directories)#22
rocketman-code wants to merge 11 commits intosnapshot-lifecyclefrom
rollback-scope

Conversation

@rocketman-code
Copy link
Copy Markdown
Owner

Stacked on #21. GitHub will auto-retarget to main when #21 merges.

Summary

  • check and rollback now show which directories are protected (separate btrfs subvolumes, labeled SAFE) and which are inside root and will revert on rollback (labeled RISK).
  • Derived from fstab subvol= entries and is_mountpoint checks on top-level directories.

Test plan

  • VM (Fedora 43 btrfs): check produces "Boot chain is valid" + Rollback scope section with SAFE listing /home and /var, RISK listing non-empty root-internal directories
  • rollback call site present at src/rollback.rs:44, calls the same print_rollback_scope function verified via check (transit-verified: same function, same fstab input)
  • 23/23 unit tests pass

The libdnf5 actions plugin and the RPM plugin both fire on dnf5
transactions, producing duplicate snapshot messages. The RPM plugin's
coverage strictly subsumes the libdnf5 plugin: it catches dnf5, dnf4,
pure rpm, PackageKit, and anything else that routes through librpm.
The libdnf5 plugin catches only dnf5.

This was a failed cleanup from v0.3.7 when the RPM plugin was added.

Remove the .actions file, the libdnf5-plugin-actions runtime dependency,
and all references to "the dnf plugin" in code comments, documentation,
and function doc comments.
The kernel-install hook (90-atomic-rollback.install) was owned by the
RPM package. If the package was uninstalled after migrate had permanently
changed the on-disk layout, the hook was removed but the migrated layout
persisted. Subsequent kernel installs produced BLS entries with paths
GRUB could not resolve, making new kernels unbootable.

The hook is now a migration artifact: migrate writes it to
/usr/lib/kernel/install.d/ alongside its other persistent changes
(fstab markers, root symlinks, ESP grub.cfg rewrites). The hook
persists after package removal because RPM no longer owns it.

Add internal ensure-hooks command (not in --help, same pattern as
kernel-hook) that checks migration state using the existing detection
logic (btrfs root + /boot not a separate mount) and writes the hook
if migrated and missing. %posttrans calls ensure-hooks to restore the
hook for existing migrated users upgrading to this version.

Fixes #17.
Replace the fixed-name idempotent snapshot with a uniquely-named
snapshot per invocation. The auto-generated name uses %Y-%m-%d_%H-%M-%S
in local time.

Each RPM transaction now creates a new snapshot instead of no-oping on
the existing root.pre-update. User-named snapshots (snapshot create
<name>) continue to work through the same code path.

DEFAULT_SNAPSHOT_NAME remains in consts.rs for rollback no-args default;
that dependency is removed in a later commit.
Add a keep-last-N retention policy for automatic snapshots. After
creating an auto-named snapshot, if the count of automatic snapshots
exceeds MAX_SNAPSHOTS, the oldest are evicted. User-named snapshots
are never counted and never evicted.

Automatic snapshots are identified by their name matching the
YYYY-MM-DD_HH-MM-SS format. User-supplied names matching this format
are rejected at creation time to prevent collision.

MAX_SNAPSHOTS defaults to 50 and is configurable in
/etc/atomic-rollback.conf using shell key=value format (derived from
snapper, openSUSE/snapper data/default-config).

Retention failure is non-fatal: errors are logged but the snapshot
creation succeeds and the RPM transaction is not blocked.
Thread the btrfs-assigned subvolume ID through SnapshotResult and the
delete return type so all user-facing messages display it. The ID is
looked up via the existing btrfs_subvol_id_by_name after creation, and
surfaced from delete which already looked it up internally.

Create:  Snapshot '<name>' with ID <id> created.
Existed: Snapshot '<name>' with ID <id> already exists.
Delete:  Snapshot '<name>' with ID <id> deleted.
Replace the bare-names list with a table showing btrfs subvolume ID,
filesystem name, and creation time. Sorted by ID ascending
(chronological order).

Creation time is read from btrfs subvolume show via a new
btrfs_subvol_creation_time helper in tools.rs, with the timezone
suffix stripped to produce local-time %Y-%m-%d %H:%M:%S output.

Column widths are computed from the data so the table adapts to
varying ID digit counts and name lengths.
Rollback and delete now accept btrfs subvolume IDs (numeric) in
addition to names. Rollback with no arguments targets the most recent
snapshot by highest btrfs subvolume ID instead of the removed
DEFAULT_SNAPSHOT_NAME constant.

Add resolve_snapshot and most_recent_snapshot to snapshot.rs, both
pure composition on the existing btrfs_subvol_list output.

Remove DEFAULT_SNAPSHOT_NAME from consts.rs (zero consumers remain).
Replace all user-facing output, doc comments, and documentation
instances of "bootable" and "system is bootable" with "boot chain"
language that matches what the formal model actually verifies. The
tool verifies boot chain structural validity (ESP, GRUB, BLS entries,
root mount), not system bootability in the broader sense (kernel
bugs, runtime failures, etc. are outside the verification scope).

Internal function names (verify_bootable, BootStatus) and formal
model terminology (proof.rs theorem names, BOOTS predicate) are
unchanged. These are internal API and the formal model's own domain
language, not user-facing claims.
Systems that had atomic-rollback v0.3.x installed carry a
`root.pre-update` subvolume created by the old fixed-name plugin. On
upgrade to the rolling-history naming, this subvolume survives but is
treated as user-named: it appears in snapshot list alongside
timestamped entries with an inconsistent name, and retention never
touches it (is_auto_name rejects it, so it is preserved indefinitely).

Add a one-shot migration that renames root.pre-update to its creation
timestamp formatted as YYYY-MM-DD_HH-MM-SS (matching the auto-name
format). The btrfs subvolume ID is preserved across the rename, so
rollback behavior for that snapshot is unchanged.

The migration is invoked from %posttrans via a hidden
rename-legacy-snapshot subcommand, matching the ensure-hooks pattern.
Idempotent no-op if root.pre-update is absent (fresh install, already
migrated, non-btrfs root).
Print the package version so users and scripts can query it without
going through rpm -q. Output format: `atomic-rollback v<version>`,
matching btrfs-progs style. Single line on stdout, exit 0.

Both --version and -V are accepted. -v (lowercase) is deliberately
not matched, keeping it free for a potential future --verbose flag.

Version string resolves from env!("CARGO_PKG_VERSION") at compile
time, so it always reflects the Cargo.toml version baked into the
binary.
check and rollback now show which directories are protected
(separate btrfs subvolumes) and which are inside root and will
revert on rollback. Derived from fstab subvol= entries and
mountpoint checks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant