Enforce exact recovery ownership before cold start

This commit is contained in:
ms2k 2026-07-17 06:48:49 +09:00
commit 2bc5982a62
3 changed files with 1076 additions and 238 deletions

View file

@ -1033,6 +1033,7 @@ pub struct HelperOwnership {
pub struct SandboxOwnership {
pub container_id: Option<ContainerId>,
pub runtime_root: Option<HostPath>,
pub runtime_root_identity: Option<MountIdentity>,
pub bundle_digest: Option<BundleDigest>,
pub host_coordinator: PersistedHostProcessOwnership,
pub runtime_supervisor: PersistedHostProcessOwnership,
@ -1041,8 +1042,12 @@ pub struct SandboxOwnership {
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublicationOwnership {
pub staged_socket_path: Option<HostPath>,
pub staged_identity_path: Option<HostPath>,
pub socket_path: Option<HostPath>,
pub identity_path: Option<HostPath>,
pub socket_identity: Option<MountIdentity>,
pub identity_file_identity: Option<MountIdentity>,
}
impl PreparedLowerOwnership {
@ -1309,6 +1314,7 @@ impl SandboxOwnership {
Self {
container_id: None,
runtime_root: None,
runtime_root_identity: None,
bundle_digest: None,
host_coordinator: PersistedHostProcessOwnership::unassigned(),
runtime_supervisor: PersistedHostProcessOwnership::unassigned(),
@ -1319,6 +1325,7 @@ impl SandboxOwnership {
fn is_empty(&self) -> bool {
self.container_id.is_none()
&& self.runtime_root.is_none()
&& self.runtime_root_identity.is_none()
&& self.bundle_digest.is_none()
&& self.host_coordinator.is_unassigned()
&& self.runtime_supervisor.is_unassigned()
@ -1328,6 +1335,7 @@ impl SandboxOwnership {
fn is_committed(&self) -> bool {
self.container_id.is_some()
&& self.runtime_root.is_some()
&& self.runtime_root_identity.is_some()
&& self.bundle_digest.is_some()
&& self.host_coordinator.is_current_committed()
&& self.runtime_supervisor.is_current_committed()
@ -1337,6 +1345,10 @@ impl SandboxOwnership {
fn validate_progress_to(&self, next: &Self) -> Result<(), RecordTransitionError> {
validate_option_progress(self.container_id.as_ref(), next.container_id.as_ref())?;
validate_option_progress(self.runtime_root.as_ref(), next.runtime_root.as_ref())?;
validate_option_progress(
self.runtime_root_identity.as_ref(),
next.runtime_root_identity.as_ref(),
)?;
validate_option_progress(self.bundle_digest.as_ref(), next.bundle_digest.as_ref())?;
validate_existing_process_progress(&self.host_coordinator, &next.host_coordinator)?;
validate_process_progress(&self.runtime_supervisor, &next.runtime_supervisor)?;
@ -1347,22 +1359,49 @@ impl SandboxOwnership {
impl PublicationOwnership {
fn empty() -> Self {
Self {
staged_socket_path: None,
staged_identity_path: None,
socket_path: None,
identity_path: None,
socket_identity: None,
identity_file_identity: None,
}
}
fn is_empty(&self) -> bool {
self.socket_path.is_none() && self.identity_path.is_none()
self.staged_socket_path.is_none()
&& self.staged_identity_path.is_none()
&& self.socket_path.is_none()
&& self.identity_path.is_none()
&& self.socket_identity.is_none()
&& self.identity_file_identity.is_none()
}
fn is_committed(&self) -> bool {
self.socket_path.is_some() && self.identity_path.is_some()
self.staged_socket_path.is_some()
&& self.staged_identity_path.is_some()
&& self.socket_path.is_some()
&& self.identity_path.is_some()
&& self.socket_identity.is_some()
&& self.identity_file_identity.is_some()
}
fn validate_progress_to(&self, next: &Self) -> Result<(), RecordTransitionError> {
validate_option_progress(
self.staged_socket_path.as_ref(),
next.staged_socket_path.as_ref(),
)?;
validate_option_progress(
self.staged_identity_path.as_ref(),
next.staged_identity_path.as_ref(),
)?;
validate_option_progress(self.socket_path.as_ref(), next.socket_path.as_ref())?;
validate_option_progress(self.identity_path.as_ref(), next.identity_path.as_ref())
validate_option_progress(self.identity_path.as_ref(), next.identity_path.as_ref())?;
validate_option_progress(self.socket_identity.as_ref(), next.socket_identity.as_ref())?;
validate_option_progress(
self.identity_file_identity.as_ref(),
next.identity_file_identity.as_ref(),
)
}
}

View file

@ -457,14 +457,19 @@ fn complete_ownership(role: ParticipantRole, state: ParticipantState) -> Partici
ParticipantRole::Sandbox => ParticipantOwnership::Sandbox(SandboxOwnership {
container_id: Some(ContainerId::new(opaque(29))),
runtime_root: Some(HostPath::new(b"runtime".to_vec())),
runtime_root_identity: Some(mount()),
bundle_digest: Some(BundleDigest::new(opaque(43))),
host_coordinator: owned_process(30),
runtime_supervisor: owned_process(21),
attached_runsc: owned_process(44),
}),
ParticipantRole::Publication => ParticipantOwnership::Publication(PublicationOwnership {
staged_socket_path: Some(HostPath::new(b"staged-socket".to_vec())),
staged_identity_path: Some(HostPath::new(b"staged-identity".to_vec())),
socket_path: Some(HostPath::new(b"socket".to_vec())),
identity_path: Some(HostPath::new(b"identity".to_vec())),
socket_identity: Some(mount()),
identity_file_identity: Some(mount()),
}),
}
}
@ -558,6 +563,7 @@ fn participant_edge_snapshot(
ParticipantOwnership::Sandbox(SandboxOwnership {
container_id: Some(ContainerId::new(opaque(29))),
runtime_root: None,
runtime_root_identity: None,
bundle_digest: None,
host_coordinator: process_ownership,
runtime_supervisor: PersistedHostProcessOwnership::unassigned(),
@ -569,6 +575,7 @@ fn participant_edge_snapshot(
ParticipantOwnership::Sandbox(SandboxOwnership {
container_id: Some(ContainerId::new(opaque(29))),
runtime_root: None,
runtime_root_identity: None,
bundle_digest: None,
host_coordinator: owned_process(122),
runtime_supervisor: process_ownership,
@ -580,6 +587,7 @@ fn participant_edge_snapshot(
ParticipantOwnership::Sandbox(SandboxOwnership {
container_id: Some(ContainerId::new(opaque(29))),
runtime_root: None,
runtime_root_identity: None,
bundle_digest: None,
host_coordinator: owned_process(122),
runtime_supervisor: owned_process(123),

View file

@ -375,6 +375,11 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
SandboxOwnership {
container_id: Some(ContainerId::new(opaque(29))),
runtime_root: Some(HostPath::new(b"runtime".to_vec())),
runtime_root_identity: Some(MountIdentity {
mount_id: MountId::new(68),
device: DeviceId::new(69),
inode: InodeId::new(70),
}),
bundle_digest: Some(BundleDigest::new(opaque(66))),
host_coordinator: owned_process(30),
runtime_supervisor: owned_process(21),
@ -384,8 +389,20 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
);
verify_shape(
PublicationOwnership {
staged_socket_path: Some(HostPath::new(b"staged-socket".to_vec())),
staged_identity_path: Some(HostPath::new(b"staged-identity".to_vec())),
socket_path: Some(HostPath::new(b"socket".to_vec())),
identity_path: Some(HostPath::new(b"identity".to_vec())),
socket_identity: Some(MountIdentity {
mount_id: MountId::new(71),
device: DeviceId::new(72),
inode: InodeId::new(73),
}),
identity_file_identity: Some(MountIdentity {
mount_id: MountId::new(74),
device: DeviceId::new(75),
inode: InodeId::new(76),
}),
},
&mut seen,
);