Snapshot lifecycle: rolling history, retention, btrfs IDs#21
Merged
rocketman-code merged 10 commits intomainfrom Apr 14, 2026
Merged
Snapshot lifecycle: rolling history, retention, btrfs IDs#21rocketman-code merged 10 commits intomainfrom
rocketman-code merged 10 commits intomainfrom
Conversation
This was referenced Apr 14, 2026
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.
893f017 to
a168dfd
Compare
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
%Y-%m-%d_%H-%M-%S) with configurable retention viaMAX_SNAPSHOTSin/etc/atomic-rollback.conf(default 50). User-named snapshots never counted, never evicted.snapshot listshows a three-column table: btrfs subvolume ID, name, creation time.rollback [id|name]andsnapshot delete <id|name>accept btrfs subvolume IDs in addition to names.rollbackwith no arguments defaults to the most recent snapshot (highest ID).migrate. The hook now persists across atomic-rollback uninstall, so removing the tool while on a migrated layout does not break future kernel upgrades.checkoutput uses "boot chain" terminology instead of "system bootable." The formal model verifies boot chain structural validity, not system bootability in the broader sense.root.pre-updatesnapshot renamed to its creation timestamp on upgrade, so it joins the rolling history and participates in retention instead of persisting indefinitely.--version/-Vprint the package version.Closes #9, #16, #17.
Test plan
cargo test --release)snapshotcreate /snapshot list/snapshot deleteby name and by IDMAX_SNAPSHOTSexceeded; user-named untouchedrename-legacy-snapshotsilent no-op, exit 0, no mount attempts on non-btrfs--version,-V, captured via$()(stdout verified); rejects-v(lowercase),--VERSION(case), bareversion; trailing args ignored;--helpandcheckunaffected