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

@ -1,9 +1,9 @@
use std::collections::BTreeMap;
use std::ffi::OsString;
use std::fs;
use std::fs::{self, File};
use std::os::unix::ffi::OsStringExt;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use lazaret_core::configuration::{
@ -80,22 +80,193 @@ fn inherited_runtime_authority_accepts_only_the_exact_prepared_lower() {
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();
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(),
(expected.0, expected.1 + 1),
)
.is_err()
);
}
#[test]
fn retained_prepared_lower_authority_refuses_a_replacement_descriptor() {
let tree = TestTree::new();
let configuration = configuration(&tree);
let layout = StateAnchorLayout::create(&configuration).unwrap();
let lower = tree.path.join("prepared-lower");
let replacement = tree.path.join("replacement-lower");
fs::create_dir(&lower).unwrap();
fs::create_dir(&replacement).unwrap();
let metadata = lower.metadata().unwrap();
let artifact = PreparedArtifact {
prepared_artifact_id: PreparedArtifactId::new([8; 32]),
source_identity: SourceIdentity::new(b"fixture".to_vec()),
selected_manifest_digest: SelectedManifestDigest::new([9; 32]),
policy_id: PolicyId::new([10; 32]),
id_map_digest: IdMapDigest::new([11; 32]),
path: HostPath::new(b"artifacts/fixture".to_vec()),
device: DeviceId::new(metadata.dev()),
inode: InodeId::new(metadata.ino()),
state: ArtifactState::Available,
};
let exact = File::open(&lower).unwrap();
StateAnchorLaunchAuthority::capture_retained(&layout, &artifact, &lower, &exact).unwrap();
let replacement = File::open(&replacement).unwrap();
assert!(
StateAnchorLaunchAuthority::capture_retained(&layout, &artifact, &lower, &replacement)
.is_err()
);
}
#[test]
fn namespace_local_rebound_refuses_a_replaced_state_anchor() {
let tree = TestTree::new();
let configuration = configuration(&tree);
let layout = StateAnchorLayout::create(&configuration).unwrap();
let lower = tree.path.join("prepared-lower");
fs::create_dir(&lower).unwrap();
let artifact = prepared_artifact(&lower);
let launch = StateAnchorLaunchAuthority::capture(&layout, &artifact).unwrap();
let anchor = launch.runtime_anchor_label().to_owned();
fs::rename(&anchor, anchor.with_extension("retained")).unwrap();
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()
);
}
#[test]
fn namespace_local_rebound_refuses_a_replaced_prepared_lower() {
let tree = TestTree::new();
let configuration = configuration(&tree);
let layout = StateAnchorLayout::create(&configuration).unwrap();
let lower = tree.path.join("prepared-lower");
fs::create_dir(&lower).unwrap();
let artifact = prepared_artifact(&lower);
let launch = StateAnchorLaunchAuthority::capture(&layout, &artifact).unwrap();
fs::rename(&lower, lower.with_extension("retained")).unwrap();
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()
);
}
#[test]
fn namespace_local_rebound_refuses_a_replaced_persistent_anchor() {
let tree = TestTree::new();
let configuration = configuration(&tree);
let layout = StateAnchorLayout::create(&configuration).unwrap();
let lower = tree.path.join("prepared-lower");
fs::create_dir(&lower).unwrap();
let artifact = prepared_artifact(&lower);
let launch = StateAnchorLaunchAuthority::capture(&layout, &artifact).unwrap();
let anchor = launch.persistent_anchor_label().to_owned();
fs::rename(&anchor, anchor.with_extension("retained")).unwrap();
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()
);
}
#[test]
fn namespace_local_rebound_refuses_a_state_anchor_symlink() {
let tree = TestTree::new();
let configuration = configuration(&tree);
let layout = StateAnchorLayout::create(&configuration).unwrap();
let lower = tree.path.join("prepared-lower");
fs::create_dir(&lower).unwrap();
let artifact = prepared_artifact(&lower);
let launch = StateAnchorLaunchAuthority::capture(&layout, &artifact).unwrap();
let anchor = launch.runtime_anchor_label().to_owned();
let retained = anchor.with_extension("retained");
fs::rename(&anchor, &retained).unwrap();
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()
);
}
fn prepared_artifact(lower: &Path) -> PreparedArtifact {
let metadata = lower.metadata().unwrap();
PreparedArtifact {
prepared_artifact_id: PreparedArtifactId::new([8; 32]),
source_identity: SourceIdentity::new(b"fixture".to_vec()),
selected_manifest_digest: SelectedManifestDigest::new([9; 32]),
policy_id: PolicyId::new([10; 32]),
id_map_digest: IdMapDigest::new([11; 32]),
path: HostPath::new(lower.as_os_str().as_encoded_bytes().to_vec()),
device: DeviceId::new(metadata.dev()),
inode: InodeId::new(metadata.ino()),
state: ArtifactState::Available,
}
}
struct TestTree {
path: PathBuf,
}