hive-c0re: hivectl subvol upgrade — migrate an agent state dir to a btrfs subvolume

New agents get a btrfs subvolume state root automatically when the host
FS is btrfs, but agents that predate that migration are left on plain
dirs and miss the subvolume feature set (snapshots, per-subvol
usage/quota, send/receive migration). Add an opt-in operator verb to
convert an existing plain-dir agent in place.

btrfs cannot promote a directory to a subvolume in place, so the new
privileged op stages a sibling subvolume mirroring the dir (create +
`cp -a --reflink=auto` preserving ownership/permissions/xattrs + match
the root's owner and mode), then atomically renames the original aside
and the subvolume into place, then removes the original. Any failure
before the swap leaves the original untouched; idempotent (no-op if
already a subvolume) and btrfs-gated.

The `hivectl subvol upgrade <agent> --yes` verb composes it client-side
like `restart`: stop the agent so its state bind-mount is released, run
the migration via hive-priv, then restart it — the restart is attempted
regardless of the migration outcome so a failed migration never leaves
the agent down.

- hive-sh4re: UpgradeAgentSubvolume priv request variant.
- hive-priv: the migration handler plus stage/cleanup helpers.
- hive-c0re: priv_client wrapper and the hivectl verb; regen CLI docs.
This commit is contained in:
atlas 2026-06-21 14:12:43 +02:00 committed by mara
commit 6b1dbebe5a
5 changed files with 351 additions and 0 deletions

View file

@ -406,6 +406,29 @@ pub enum PrivRequest {
/// Byte cap on referenced usage; `None` clears the limit.
limit_bytes: Option<u64>,
},
/// Convert an existing **plain-directory** agent state root into a btrfs
/// subvolume in place. The operator opt-in counterpart to the progressive
/// `EnsureAgentSubvolume` (which only ever makes *new* agents subvolumes):
/// it migrates an already-existing plain dir so the agent gains the
/// subvolume feature set (snapshots, per-subvol usage/quota, send/receive).
///
/// btrfs cannot promote a directory in place, so the helper does the move:
/// create a fresh subvolume, copy the dir's contents into it preserving
/// ownership/permissions/xattrs (`cp -a --reflink=auto`), then atomically
/// rename the original aside and the subvolume into place, and finally
/// remove the original. The caller (hivectl) MUST stop the agent first so
/// its state bind-mount is gone before the host dir moves, and restart it
/// after. Behaviour:
/// - path missing → error (nothing to upgrade);
/// - already a subvolume → no-op success (idempotent);
/// - parent FS not btrfs → error (subvolumes unsupported here);
/// - any failure before the final swap leaves the original dir untouched
/// (no half-migration). Requires root.
UpgradeAgentSubvolume {
/// Logical agent name (validated by `validate_agent_name`).
agent_name: String,
},
}
/// Response from the privileged helper.