Compose selected State Anchor paths over prepared roots

This commit is contained in:
ms2k 2026-07-17 03:58:27 +09:00
commit 4dd2b150d2

View file

@ -3,6 +3,7 @@ use std::fs::File;
use std::io;
use std::os::fd::{AsFd, AsRawFd, OwnedFd};
use std::os::unix::fs::FileExt;
use std::path::PathBuf;
use std::time::{Duration, Instant};
use lazaret_core::codec::{decode, encode};
@ -32,8 +33,8 @@ use rustix::thread::CapabilitySet;
use serde::{Deserialize, Serialize};
use crate::network::{
NetworkAttestationContext, NetworkProbeRequest, PreparedNetwork, prepare_network,
probe_current_network,
NetworkAttestationContext, NetworkProbeRequest, PreparedNetwork,
enter_generation_filesystem_namespaces, prepare_network, probe_current_network,
};
use crate::sandbox::RunscCommand;
@ -64,6 +65,8 @@ pub enum SupervisorEvent {
StateAnchorStaged(StagedProcessOwnership),
StateAnchorReady {
staged: StagedProcessOwnership,
user_namespace: lazaret_core::identity::NamespaceIdentity,
mount_namespace: lazaret_core::identity::NamespaceIdentity,
mount: lazaret_core::identity::MountIdentity,
},
SidecarStarted,
@ -80,7 +83,9 @@ pub enum SupervisorEvent {
SignalApplied(AttachedRunscSignal),
SandboxStopped,
SidecarStopped,
StateAnchorUnmounted,
StateAnchorUnmounted {
mount: lazaret_core::identity::MountIdentity,
},
Exited,
AttachedRunscExited,
Stopped,
@ -362,14 +367,31 @@ impl RuntimeSupervisorLaunch {
target = target
.argument("--state-lower-fd")
.argument(authority.lower_fd().to_string())
.argument("--state-lower-label")
.argument(authority.lower_label())
.argument("--state-persistent-root-fd")
.argument(authority.persistent_root_fd().to_string())
.argument("--state-persistent-fd")
.argument(authority.persistent_anchor_fd().to_string())
.argument("--state-persistent-label")
.argument(authority.persistent_anchor_label())
.argument("--state-runtime-root-fd")
.argument(authority.runtime_root_fd().to_string())
.argument("--state-runtime-fd")
.argument(authority.runtime_anchor_fd().to_string())
.argument("--state-runtime-label")
.argument(authority.runtime_anchor_label())
.argument("--state-lower-device")
.argument(device.to_string())
.argument("--state-lower-inode")
.argument(inode.to_string());
.argument(inode.to_string())
.argument("--state-persistent-count")
.argument(authority.persistent_destinations().len().to_string());
for destination in authority.persistent_destinations() {
target = target
.argument("--state-persistent-destination")
.argument(destination);
}
}
if let (Some(policy_file), Some(digest), Some(fingerprint), Some(sidecar)) = (
&self.policy_file,
@ -468,9 +490,15 @@ struct RuntimeArguments {
struct RuntimeStateAnchorArguments {
lower_fd: i32,
lower_label: PathBuf,
persistent_root_fd: i32,
persistent_anchor_fd: i32,
persistent_anchor_label: PathBuf,
runtime_root_fd: i32,
runtime_anchor_fd: i32,
runtime_anchor_label: PathBuf,
expected_lower: (u64, u64),
persistent_destinations: Vec<PathBuf>,
}
struct RuntimeNetworkArguments {
@ -492,14 +520,35 @@ fn parse_runtime_arguments(arguments: &[OsString]) -> io::Result<RuntimeArgument
.peek()
.is_some_and(|value| *value == "--state-lower-fd")
{
let lower_fd = parse_number(named(&mut arguments, "--state-lower-fd")?)?;
let lower_label = PathBuf::from(named(&mut arguments, "--state-lower-label")?);
let persistent_root_fd =
parse_number(named(&mut arguments, "--state-persistent-root-fd")?)?;
let persistent_anchor_fd = parse_number(named(&mut arguments, "--state-persistent-fd")?)?;
let persistent_anchor_label =
PathBuf::from(named(&mut arguments, "--state-persistent-label")?);
let runtime_root_fd = parse_number(named(&mut arguments, "--state-runtime-root-fd")?)?;
let runtime_anchor_fd = parse_number(named(&mut arguments, "--state-runtime-fd")?)?;
let runtime_anchor_label = PathBuf::from(named(&mut arguments, "--state-runtime-label")?);
let expected_lower = (
parse_number(named(&mut arguments, "--state-lower-device")?)?,
parse_number(named(&mut arguments, "--state-lower-inode")?)?,
);
let count: usize = parse_number(named(&mut arguments, "--state-persistent-count")?)?;
let persistent_destinations = (0..count)
.map(|_| named(&mut arguments, "--state-persistent-destination").map(PathBuf::from))
.collect::<io::Result<Vec<_>>>()?;
Some(RuntimeStateAnchorArguments {
lower_fd: parse_number(named(&mut arguments, "--state-lower-fd")?)?,
persistent_anchor_fd: parse_number(named(&mut arguments, "--state-persistent-fd")?)?,
runtime_anchor_fd: parse_number(named(&mut arguments, "--state-runtime-fd")?)?,
expected_lower: (
parse_number(named(&mut arguments, "--state-lower-device")?)?,
parse_number(named(&mut arguments, "--state-lower-inode")?)?,
),
lower_fd,
lower_label,
persistent_root_fd,
persistent_anchor_fd,
persistent_anchor_label,
runtime_root_fd,
runtime_anchor_fd,
runtime_anchor_label,
expected_lower,
persistent_destinations,
})
} else {
None
@ -604,9 +653,10 @@ fn parse_fixed_hex(value: &OsStr) -> io::Result<[u8; 32]> {
fn run_supervisor(arguments: &RuntimeArguments) -> io::Result<()> {
require_lifetime_held()?;
let namespaces = enter_generation_filesystem_namespaces()?;
let control = std::io::stdout();
let parent = current_process_identity()?;
let mut state_anchor = prepare_state_anchor(arguments, parent, &control)?;
let mut state_anchor = prepare_state_anchor(arguments, parent, namespaces, &control)?;
let result = run_supervisor_with_state(arguments, parent, &control, &mut state_anchor);
let cleanup = state_anchor.take().map(RuntimeStateAnchorMount::unmount);
match (result, cleanup) {
@ -623,6 +673,10 @@ fn run_supervisor(arguments: &RuntimeArguments) -> io::Result<()> {
fn prepare_state_anchor(
arguments: &RuntimeArguments,
parent: lazaret_core::identity::PidfsProcessIdentityV1,
namespaces: (
lazaret_core::identity::NamespaceIdentity,
lazaret_core::identity::NamespaceIdentity,
),
control: &std::io::Stdout,
) -> io::Result<Option<RuntimeStateAnchorMount>> {
let Some(state) = &arguments.state_anchor else {
@ -636,8 +690,13 @@ fn prepare_state_anchor(
}
let authority = RuntimeStateAnchorAuthority::from_inherited(
state.lower_fd,
&state.lower_label,
state.persistent_root_fd,
state.persistent_anchor_fd,
&state.persistent_anchor_label,
state.runtime_root_fd,
state.runtime_anchor_fd,
&state.runtime_anchor_label,
state.expected_lower,
)
.map_err(io::Error::other)?;
@ -645,7 +704,7 @@ fn prepare_state_anchor(
slot: HostProcessOwnershipSlot::StateAnchorDaemon,
..arguments.ownership
};
authority
let mount = authority
.acquire(
parent,
ownership,
@ -665,13 +724,18 @@ fn prepare_state_anchor(
control,
&SupervisorEvent::StateAnchorReady {
staged: readiness.staged(),
user_namespace: namespaces.0,
mount_namespace: namespaces.1,
mount: readiness.mount(),
},
)
},
)
.map(Some)
.map_err(io::Error::other)
.map_err(io::Error::other)?;
mount
.require_persistent_destinations(&state.persistent_destinations)
.map_err(io::Error::other)?;
Ok(Some(mount))
}
fn run_supervisor_with_state(
@ -698,6 +762,7 @@ fn run_supervisor_with_state(
let pid_file = File::from(memfd_create("lazaret-runsc-pid", MemfdFlags::empty())?);
fcntl_setfd(pid_file.as_fd(), FdFlags::empty())?;
let runsc = runsc_target(arguments, pid_file.as_raw_fd())?;
let delete = runsc_delete_target(arguments)?;
let staged = std::cell::Cell::new(None);
let process = match child_broker().acquire_with_stdio(
parent,
@ -762,7 +827,7 @@ fn run_supervisor_with_state(
io::Error::other("sandbox commit acknowledgment was not received"),
);
}
supervise(control, process, network, state_anchor)
supervise(control, process, network, state_anchor, &delete)
}
fn prepare_runtime_network(
@ -858,6 +923,29 @@ fn runsc_target(arguments: &RuntimeArguments, pid_file: i32) -> io::Result<Targe
Ok(target.with_parent_death_kill_edge())
}
fn runsc_delete_target(arguments: &RuntimeArguments) -> io::Result<TargetCommand> {
let run = arguments
.runsc_arguments
.iter()
.position(|argument| argument == "run")
.ok_or_else(|| io::Error::other("attached runsc command has no run operation"))?;
let container = arguments
.runsc_arguments
.last()
.filter(|container| {
!container.is_empty() && !container.as_encoded_bytes().starts_with(b"-")
})
.ok_or_else(|| io::Error::other("attached runsc command has no container ID"))?;
let mut target = TargetCommand::new(arguments.runsc_program.clone());
for argument in &arguments.runsc_arguments[..run] {
target = target.argument(argument);
}
Ok(target
.argument("delete")
.argument("--force")
.argument(container))
}
fn attached_runsc_standard_io(target: &TargetCommand) -> io::Result<ChildStandardIo> {
if target.parent_edge() != ParentEdge::ParentDeathKill {
return Err(io::Error::other(
@ -896,6 +984,7 @@ fn supervise(
process: RetainedProcess,
mut network: Option<PreparedNetwork>,
state_anchor: &mut Option<RuntimeStateAnchorMount>,
delete: &TargetCommand,
) -> io::Result<()> {
let mut process = Some(process);
loop {
@ -912,6 +1001,7 @@ fn supervise(
.take()
.ok_or_else(|| io::Error::other("attached runsc handle is absent"))?,
)?;
child_broker().run_transient(delete)?;
stop_network(network)?;
send_message(control, &SupervisorEvent::AttachedRunscExited)?;
return Ok(());
@ -933,6 +1023,7 @@ fn supervise(
if lease_lost {
if let Some(process) = process.take() {
terminate_and_reap(process)?;
child_broker().run_transient(delete)?;
}
stop_network(network)?;
return Ok(());
@ -969,6 +1060,7 @@ fn supervise(
&mut process,
&mut network,
state_anchor,
delete,
) {
Ok(true) => return Ok(()),
Ok(false) => {}
@ -984,6 +1076,7 @@ fn handle_supervisor_message(
process: &mut Option<RetainedProcess>,
network: &mut Option<PreparedNetwork>,
state_anchor: &mut Option<RuntimeStateAnchorMount>,
delete: &TargetCommand,
) -> io::Result<bool> {
match message {
CoordinatorMessage::SignalAttachedRunsc(signal) => {
@ -999,6 +1092,7 @@ fn handle_supervisor_message(
.take()
.ok_or_else(|| io::Error::other("attached runsc is already stopped"))?,
)?;
child_broker().run_transient(delete)?;
send_message(control, &SupervisorEvent::SandboxStopped)?;
}
CoordinatorMessage::StopSidecar => {
@ -1014,12 +1108,16 @@ fn handle_supervisor_message(
"State Anchor unmount preceded runtime resource stop",
));
}
let mount = state_anchor
.as_ref()
.ok_or_else(|| io::Error::other("State Anchor is absent"))?
.mount_identity();
state_anchor
.take()
.ok_or_else(|| io::Error::other("State Anchor is absent"))?
.unmount()
.map_err(io::Error::other)?;
send_message(control, &SupervisorEvent::StateAnchorUnmounted)?;
send_message(control, &SupervisorEvent::StateAnchorUnmounted { mount })?;
}
CoordinatorMessage::Exit => {
if process.is_some() || network.is_some() || state_anchor.is_some() {
@ -1307,6 +1405,14 @@ mod tests {
attached_runsc_standard_io(&runsc).unwrap().policy(),
ChildStandardIoPolicy::Silenced
);
let delete = runsc_delete_target(&runsc_arguments).unwrap();
assert_eq!(delete.program(), "runsc");
assert_eq!(
delete.arguments(),
["-root=/runtime", "delete", "--force", "container"]
.map(OsString::from)
.as_slice()
);
}
#[test]