feat(#1763): local-file half of btrfs send/receive migration transport

SendAgentSnapshotToFile priv op: btrfs send [-p <parent>] <snapshot> to a
file under MIGRATE_STAGING_ROOT. Standalone-useful as a point-in-time
snapshot export/backup today; the cross-hive ssh-piped leg (auth/trust
design posted on #1763, awaiting mara/damocles steer) is a later,
separate piece this doesn't block on.

- hive-sh4re: PrivRequest::SendAgentSnapshotToFile + MIGRATE_STAGING_ROOT
- hive-priv: validates names, refuses to overwrite an existing export,
  cleans up a partial file on btrfs send failure
- hive-c0re: priv_client::send_agent_snapshot_to_file
- hivectl: `hivectl subvol snapshot send <agent> <label> [--parent <label>] --dest <file>`
This commit is contained in:
atlas 2026-07-14 19:09:44 +02:00 committed by mara
commit 90af0edab0
4 changed files with 201 additions and 2 deletions

View file

@ -121,6 +121,13 @@ pub const AGENT_STATE_ROOT: &str = "/var/lib/hyperhive/agents";
/// across the crate.
pub const AGENT_RUNTIME_ROOT: &str = "/run/hyperhive/agents";
/// Root of the local staging area for `btrfs send` archives
/// (`SendAgentSnapshotToFile`). A sibling of `AGENT_STATE_ROOT`, not inside
/// it — these are exported streams, not live/subvolume state, and don't
/// belong in the tree btrfs quota accounting or the subvolume-per-agent
/// layout cares about. Root-owned; hive-priv creates it on first use.
pub const MIGRATE_STAGING_ROOT: &str = "/var/lib/hyperhive/migrate-staging";
/// Output format for `ReadContainerJournal`. Maps to journalctl
/// `--output=<...>`. Restricted to the two formats hive callers use so
/// the wire type can't smuggle an arbitrary `--output` value.
@ -616,6 +623,36 @@ pub enum PrivRequest {
snapshot_name: String,
},
/// Stream a previously-created read-only snapshot to a local file via
/// `btrfs send [-p <parent>] <snapshot> > <MIGRATE_STAGING_ROOT>/<dest_file_name>`.
/// The local-file half of the inter-hive migration transport: the
/// cross-hive leg (piping into `ssh <peer> btrfs receive`) is a later,
/// separate piece pending the auth/trust design — this variant is
/// useful standalone today as a point-in-time export/backup of a
/// snapshot (full send, no parent) or to validate the incremental
/// (`-p`) path locally before wiring up the network leg.
///
/// `dest_file_name` is a bare filename (not a path) under
/// `MIGRATE_STAGING_ROOT`, which hive-priv creates on first use.
/// Fails if the snapshot doesn't exist, `parent_snapshot_name` is given
/// but doesn't exist, or `dest_file_name` already exists (never
/// silently overwrites an export). Requires root.
SendAgentSnapshotToFile {
/// Logical agent name (validated by `validate_agent_name`).
agent_name: String,
/// Snapshot label to send, same validation as `SnapshotAgentSubvolume`.
snapshot_name: String,
/// Optional parent snapshot label for an incremental
/// (`btrfs send -p`) send — must be an older read-only snapshot of
/// the same agent, still present on disk. `None` sends the full
/// snapshot.
parent_snapshot_name: Option<String>,
/// Bare filename (no path separators) for the exported stream,
/// written under `MIGRATE_STAGING_ROOT`. Same charset as a
/// credential name (`[A-Za-z0-9_-]`).
dest_file_name: String,
},
/// Write `/etc/tmpfiles.d/hyperhive-agents.conf` for the given agent set
/// and immediately apply it with `systemd-tmpfiles --create`. Each entry
/// declares the per-agent runtime dirs (`/run/hyperhive/agents/<name>` and