Require fresh State Anchor absence before lease release
This commit is contained in:
parent
4dd2b150d2
commit
3b76b59bdf
3 changed files with 543 additions and 194 deletions
|
|
@ -530,33 +530,39 @@ pub struct RuntimeStateAnchorAuthority {
|
|||
merged_descriptor: OwnedFd,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RuntimeStateAnchorInherited<'a> {
|
||||
pub lower_fd: i32,
|
||||
pub lower_label: &'a Path,
|
||||
pub persistent_root_fd: i32,
|
||||
pub persistent_anchor_fd: i32,
|
||||
pub persistent_anchor_label: &'a Path,
|
||||
pub runtime_root_fd: i32,
|
||||
pub runtime_anchor_fd: i32,
|
||||
pub runtime_anchor_label: &'a Path,
|
||||
pub expected_lower: (u64, u64),
|
||||
}
|
||||
|
||||
impl RuntimeStateAnchorAuthority {
|
||||
/// Reopens inherited authorities without falling back to ambient configured paths.
|
||||
///
|
||||
/// # Errors
|
||||
/// Refuses missing, replaced, or incorrectly typed descriptors and fixed children.
|
||||
pub fn from_inherited(
|
||||
lower_fd: i32,
|
||||
lower_label: &Path,
|
||||
persistent_root_fd: i32,
|
||||
persistent_anchor_fd: i32,
|
||||
persistent_anchor_label: &Path,
|
||||
runtime_root_fd: i32,
|
||||
runtime_anchor_fd: i32,
|
||||
runtime_anchor_label: &Path,
|
||||
expected_lower: (u64, u64),
|
||||
inherited: RuntimeStateAnchorInherited<'_>,
|
||||
) -> Result<Self, StateAnchorError> {
|
||||
let retained_lower = reopen_inherited_directory(lower_fd)?;
|
||||
require_directory_descriptor_identity(&retained_lower, expected_lower)?;
|
||||
if !lower_label.is_absolute() {
|
||||
let retained_lower = reopen_inherited_directory(inherited.lower_fd)?;
|
||||
require_directory_descriptor_identity(&retained_lower, inherited.expected_lower)?;
|
||||
if !inherited.lower_label.is_absolute() {
|
||||
return Err(StateAnchorError::InvalidLayout(
|
||||
"prepared lower label is not absolute",
|
||||
));
|
||||
}
|
||||
let lower = open_exact_directory_path(lower_label, expected_lower)?;
|
||||
let persistent_root = reopen_inherited_directory(persistent_root_fd)?;
|
||||
let retained_persistent_anchor = reopen_inherited_directory(persistent_anchor_fd)?;
|
||||
if !persistent_anchor_label.is_absolute() {
|
||||
let lower = open_exact_directory_path(inherited.lower_label, inherited.expected_lower)?;
|
||||
let persistent_root = reopen_inherited_directory(inherited.persistent_root_fd)?;
|
||||
let retained_persistent_anchor =
|
||||
reopen_inherited_directory(inherited.persistent_anchor_fd)?;
|
||||
if !inherited.persistent_anchor_label.is_absolute() {
|
||||
return Err(StateAnchorError::InvalidLayout(
|
||||
"persistent State Anchor label is not absolute",
|
||||
));
|
||||
|
|
@ -570,12 +576,12 @@ impl RuntimeStateAnchorAuthority {
|
|||
(retained_persistent.st_dev, retained_persistent.st_ino),
|
||||
)?;
|
||||
let persistent_anchor = open_exact_directory_path(
|
||||
persistent_anchor_label,
|
||||
inherited.persistent_anchor_label,
|
||||
(retained_persistent.st_dev, retained_persistent.st_ino),
|
||||
)?;
|
||||
let runtime_root = reopen_inherited_directory(runtime_root_fd)?;
|
||||
let retained_runtime_anchor = reopen_inherited_directory(runtime_anchor_fd)?;
|
||||
if !runtime_anchor_label.is_absolute() {
|
||||
let runtime_root = reopen_inherited_directory(inherited.runtime_root_fd)?;
|
||||
let retained_runtime_anchor = reopen_inherited_directory(inherited.runtime_anchor_fd)?;
|
||||
if !inherited.runtime_anchor_label.is_absolute() {
|
||||
return Err(StateAnchorError::InvalidLayout(
|
||||
"runtime State Anchor label is not absolute",
|
||||
));
|
||||
|
|
@ -586,13 +592,15 @@ impl RuntimeStateAnchorAuthority {
|
|||
&confined_runtime_anchor,
|
||||
(retained.st_dev, retained.st_ino),
|
||||
)?;
|
||||
let runtime_anchor =
|
||||
open_exact_directory_path(runtime_anchor_label, (retained.st_dev, retained.st_ino))?;
|
||||
let runtime_anchor = open_exact_directory_path(
|
||||
inherited.runtime_anchor_label,
|
||||
(retained.st_dev, retained.st_ino),
|
||||
)?;
|
||||
for name in ["upper", "work"] {
|
||||
drop(open_directory(&persistent_anchor, Path::new(name))?);
|
||||
}
|
||||
let merged_descriptor = open_directory(&runtime_anchor, Path::new("merged"))?;
|
||||
let merged_label = runtime_anchor_label.join("merged");
|
||||
let merged_label = inherited.runtime_anchor_label.join("merged");
|
||||
Ok(Self {
|
||||
lower,
|
||||
persistent_anchor,
|
||||
|
|
@ -699,6 +707,32 @@ pub struct RuntimeStateAnchorMount {
|
|||
merged: OwnedFd,
|
||||
}
|
||||
|
||||
pub struct RuntimeStateAnchorAbsenceAuthority {
|
||||
runtime_anchor: OwnedFd,
|
||||
mount: MountIdentity,
|
||||
}
|
||||
|
||||
impl RuntimeStateAnchorAbsenceAuthority {
|
||||
/// Reopens the exact runtime anchor to produce a fresh post-unmount observation.
|
||||
///
|
||||
/// # Errors
|
||||
/// Refuses an unreadable anchor or the continued presence of the retired mount.
|
||||
pub fn observe_absence(
|
||||
&self,
|
||||
expected_mount: MountIdentity,
|
||||
) -> Result<MountIdentity, StateAnchorError> {
|
||||
if expected_mount != self.mount {
|
||||
return Err(StateAnchorError::OwnershipMismatch);
|
||||
}
|
||||
let observed = mount_identity(&open_directory(&self.runtime_anchor, Path::new("merged"))?)?;
|
||||
if observed == self.mount {
|
||||
Err(StateAnchorError::OwnershipMismatch)
|
||||
} else {
|
||||
Ok(observed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeStateAnchorMount {
|
||||
#[must_use]
|
||||
pub const fn mount_identity(&self) -> MountIdentity {
|
||||
|
|
@ -756,7 +790,7 @@ impl RuntimeStateAnchorMount {
|
|||
///
|
||||
/// # Errors
|
||||
/// Refuses mount replacement, unmount failure, or exact child cleanup failure.
|
||||
pub fn unmount(self) -> Result<(), StateAnchorError> {
|
||||
pub fn unmount(self) -> Result<RuntimeStateAnchorAbsenceAuthority, StateAnchorError> {
|
||||
let Self {
|
||||
authority,
|
||||
process,
|
||||
|
|
@ -777,7 +811,10 @@ impl RuntimeStateAnchorMount {
|
|||
return Err(StateAnchorError::OwnershipMismatch);
|
||||
}
|
||||
child_broker().reap(process)?;
|
||||
Ok(())
|
||||
Ok(RuntimeStateAnchorAbsenceAuthority {
|
||||
runtime_anchor: authority.runtime_anchor,
|
||||
mount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1195,12 +1232,10 @@ fn append_fuse_option_path(target: &mut Vec<u8>, path: &Path) {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_methods, clippy::disallowed_types)]
|
||||
fn rootless_unmount(layout: &StateAnchorLayout<'_>) -> Result<(), StateAnchorError> {
|
||||
rootless_unmount_anchor(&layout.runtime_anchor)
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_methods, clippy::disallowed_types)]
|
||||
fn rootless_unmount_anchor(runtime_anchor: impl AsFd) -> Result<(), StateAnchorError> {
|
||||
let inherited_anchor = inherit_for_exec(runtime_anchor)?;
|
||||
let path = descriptor_child_path(&inherited_anchor, "merged");
|
||||
|
|
@ -1208,17 +1243,14 @@ fn rootless_unmount_anchor(runtime_anchor: impl AsFd) -> Result<(), StateAnchorE
|
|||
}
|
||||
|
||||
fn rootless_unmount_path(path: &Path) -> Result<(), StateAnchorError> {
|
||||
let status = std::process::Command::new("fusermount3")
|
||||
.arg("-u")
|
||||
.arg(path)
|
||||
.status()?;
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(StateAnchorError::Io(io::Error::other(format!(
|
||||
"fusermount3 exited with {status}"
|
||||
))))
|
||||
}
|
||||
child_broker()
|
||||
.run_transient(
|
||||
&TargetCommand::new("fusermount3")
|
||||
.argument("-u")
|
||||
.argument(path),
|
||||
)
|
||||
.map(drop)
|
||||
.map_err(StateAnchorError::from)
|
||||
}
|
||||
|
||||
fn require_mount_record(
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use lazaret_core::identity::{
|
|||
};
|
||||
use lazaret_core::records::{ArtifactState, PreparedArtifact};
|
||||
use lazaret_state_anchor::{
|
||||
ProtectedRuntimeFiles, RuntimeStateAnchorAuthority, StateAnchorLaunchAuthority,
|
||||
StateAnchorLayout,
|
||||
ProtectedRuntimeFiles, RuntimeStateAnchorAuthority, RuntimeStateAnchorInherited,
|
||||
StateAnchorLaunchAuthority, StateAnchorLayout,
|
||||
};
|
||||
|
||||
static SEQUENCE: AtomicU64 = AtomicU64::new(0);
|
||||
|
|
@ -78,30 +78,12 @@ fn inherited_runtime_authority_accepts_only_the_exact_prepared_lower() {
|
|||
};
|
||||
let launch = StateAnchorLaunchAuthority::capture(&layout, &artifact).unwrap();
|
||||
let expected = launch.expected_lower();
|
||||
RuntimeStateAnchorAuthority::from_inherited(
|
||||
launch.lower_fd(),
|
||||
launch.lower_label(),
|
||||
launch.persistent_root_fd(),
|
||||
launch.persistent_anchor_fd(),
|
||||
launch.persistent_anchor_label(),
|
||||
launch.runtime_root_fd(),
|
||||
launch.runtime_anchor_fd(),
|
||||
launch.runtime_anchor_label(),
|
||||
expected,
|
||||
)
|
||||
.unwrap();
|
||||
RuntimeStateAnchorAuthority::from_inherited(inherited(&launch, expected)).unwrap();
|
||||
assert!(
|
||||
RuntimeStateAnchorAuthority::from_inherited(
|
||||
launch.lower_fd(),
|
||||
launch.lower_label(),
|
||||
launch.persistent_root_fd(),
|
||||
launch.persistent_anchor_fd(),
|
||||
launch.persistent_anchor_label(),
|
||||
launch.runtime_root_fd(),
|
||||
launch.runtime_anchor_fd(),
|
||||
launch.runtime_anchor_label(),
|
||||
RuntimeStateAnchorAuthority::from_inherited(inherited(
|
||||
&launch,
|
||||
(expected.0, expected.1 + 1),
|
||||
)
|
||||
))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
|
@ -150,18 +132,8 @@ fn namespace_local_rebound_refuses_a_replaced_state_anchor() {
|
|||
fs::create_dir(&anchor).unwrap();
|
||||
|
||||
assert!(
|
||||
RuntimeStateAnchorAuthority::from_inherited(
|
||||
launch.lower_fd(),
|
||||
launch.lower_label(),
|
||||
launch.persistent_root_fd(),
|
||||
launch.persistent_anchor_fd(),
|
||||
launch.persistent_anchor_label(),
|
||||
launch.runtime_root_fd(),
|
||||
launch.runtime_anchor_fd(),
|
||||
launch.runtime_anchor_label(),
|
||||
launch.expected_lower(),
|
||||
)
|
||||
.is_err()
|
||||
RuntimeStateAnchorAuthority::from_inherited(inherited(&launch, launch.expected_lower(),))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -178,18 +150,8 @@ fn namespace_local_rebound_refuses_a_replaced_prepared_lower() {
|
|||
fs::create_dir(&lower).unwrap();
|
||||
|
||||
assert!(
|
||||
RuntimeStateAnchorAuthority::from_inherited(
|
||||
launch.lower_fd(),
|
||||
launch.lower_label(),
|
||||
launch.persistent_root_fd(),
|
||||
launch.persistent_anchor_fd(),
|
||||
launch.persistent_anchor_label(),
|
||||
launch.runtime_root_fd(),
|
||||
launch.runtime_anchor_fd(),
|
||||
launch.runtime_anchor_label(),
|
||||
launch.expected_lower(),
|
||||
)
|
||||
.is_err()
|
||||
RuntimeStateAnchorAuthority::from_inherited(inherited(&launch, launch.expected_lower(),))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -207,18 +169,8 @@ fn namespace_local_rebound_refuses_a_replaced_persistent_anchor() {
|
|||
fs::create_dir(&anchor).unwrap();
|
||||
|
||||
assert!(
|
||||
RuntimeStateAnchorAuthority::from_inherited(
|
||||
launch.lower_fd(),
|
||||
launch.lower_label(),
|
||||
launch.persistent_root_fd(),
|
||||
launch.persistent_anchor_fd(),
|
||||
launch.persistent_anchor_label(),
|
||||
launch.runtime_root_fd(),
|
||||
launch.runtime_anchor_fd(),
|
||||
launch.runtime_anchor_label(),
|
||||
launch.expected_lower(),
|
||||
)
|
||||
.is_err()
|
||||
RuntimeStateAnchorAuthority::from_inherited(inherited(&launch, launch.expected_lower(),))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -237,21 +189,28 @@ fn namespace_local_rebound_refuses_a_state_anchor_symlink() {
|
|||
std::os::unix::fs::symlink(&retained, &anchor).unwrap();
|
||||
|
||||
assert!(
|
||||
RuntimeStateAnchorAuthority::from_inherited(
|
||||
launch.lower_fd(),
|
||||
launch.lower_label(),
|
||||
launch.persistent_root_fd(),
|
||||
launch.persistent_anchor_fd(),
|
||||
launch.persistent_anchor_label(),
|
||||
launch.runtime_root_fd(),
|
||||
launch.runtime_anchor_fd(),
|
||||
launch.runtime_anchor_label(),
|
||||
launch.expected_lower(),
|
||||
)
|
||||
.is_err()
|
||||
RuntimeStateAnchorAuthority::from_inherited(inherited(&launch, launch.expected_lower(),))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
fn inherited(
|
||||
launch: &StateAnchorLaunchAuthority,
|
||||
expected_lower: (u64, u64),
|
||||
) -> RuntimeStateAnchorInherited<'_> {
|
||||
RuntimeStateAnchorInherited {
|
||||
lower_fd: launch.lower_fd(),
|
||||
lower_label: launch.lower_label(),
|
||||
persistent_root_fd: launch.persistent_root_fd(),
|
||||
persistent_anchor_fd: launch.persistent_anchor_fd(),
|
||||
persistent_anchor_label: launch.persistent_anchor_label(),
|
||||
runtime_root_fd: launch.runtime_root_fd(),
|
||||
runtime_anchor_fd: launch.runtime_anchor_fd(),
|
||||
runtime_anchor_label: launch.runtime_anchor_label(),
|
||||
expected_lower,
|
||||
}
|
||||
}
|
||||
|
||||
fn prepared_artifact(lower: &Path) -> PreparedArtifact {
|
||||
let metadata = lower.metadata().unwrap();
|
||||
PreparedArtifact {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ use lazaret_linux::process::{
|
|||
ProcessOwnershipContext, current_process_identity, dispatch_private_process_role,
|
||||
};
|
||||
use lazaret_state_anchor::{
|
||||
FuseOverlayfsMount, ProtectedRuntimeFiles, RuntimeStateAnchorAuthority, StateAnchorAcquisition,
|
||||
StateAnchorLaunchAuthority, StateAnchorLayout, StateAnchorSandboxView,
|
||||
unmount_after_coordinator_loss,
|
||||
FuseOverlayfsMount, ProtectedRuntimeFiles, RuntimeStateAnchorAuthority,
|
||||
RuntimeStateAnchorInherited, StateAnchorAcquisition, StateAnchorLaunchAuthority,
|
||||
StateAnchorLayout, StateAnchorSandboxView, unmount_after_coordinator_loss,
|
||||
};
|
||||
use rustix::process::Signal;
|
||||
|
||||
|
|
@ -80,18 +80,19 @@ fn run() -> io::Result<()> {
|
|||
|
||||
let launch =
|
||||
StateAnchorLaunchAuthority::capture(&layout, &artifact).map_err(io::Error::other)?;
|
||||
let runtime_authority = RuntimeStateAnchorAuthority::from_inherited(
|
||||
launch.lower_fd(),
|
||||
launch.lower_label(),
|
||||
launch.persistent_root_fd(),
|
||||
launch.persistent_anchor_fd(),
|
||||
launch.persistent_anchor_label(),
|
||||
launch.runtime_root_fd(),
|
||||
launch.runtime_anchor_fd(),
|
||||
launch.runtime_anchor_label(),
|
||||
launch.expected_lower(),
|
||||
)
|
||||
.map_err(io::Error::other)?;
|
||||
let runtime_authority =
|
||||
RuntimeStateAnchorAuthority::from_inherited(RuntimeStateAnchorInherited {
|
||||
lower_fd: launch.lower_fd(),
|
||||
lower_label: launch.lower_label(),
|
||||
persistent_root_fd: launch.persistent_root_fd(),
|
||||
persistent_anchor_fd: launch.persistent_anchor_fd(),
|
||||
persistent_anchor_label: launch.persistent_anchor_label(),
|
||||
runtime_root_fd: launch.runtime_root_fd(),
|
||||
runtime_anchor_fd: launch.runtime_anchor_fd(),
|
||||
runtime_anchor_label: launch.runtime_anchor_label(),
|
||||
expected_lower: launch.expected_lower(),
|
||||
})
|
||||
.map_err(io::Error::other)?;
|
||||
let expected = std::cell::Cell::new(None);
|
||||
let runtime_mount = runtime_authority
|
||||
.acquire(
|
||||
|
|
|
|||
Loading…
Reference in a new issue