Add crash-consistent State Anchor maintenance
This commit is contained in:
parent
5cd0772866
commit
24d7d5b6e0
1 changed files with 1590 additions and 7 deletions
|
|
@ -16,11 +16,74 @@ use lazaret_core::identity::{
|
|||
use lazaret_core::records::{ArtifactState, PreparedArtifact};
|
||||
use lazaret_state_anchor::{
|
||||
ProtectedRuntimeFiles, RuntimeStateAnchorAuthority, RuntimeStateAnchorInherited,
|
||||
StateAnchorLaunchAuthority, StateAnchorLayout,
|
||||
StateAnchorLaunchAuthority, StateAnchorLayout, StateAnchorResetScope,
|
||||
};
|
||||
|
||||
static SEQUENCE: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[test]
|
||||
fn selected_reset_removes_exact_state_and_whiteout_without_touching_siblings() {
|
||||
let tree = TestTree::new();
|
||||
let configuration = configuration(&tree);
|
||||
let layout = StateAnchorLayout::create(&configuration).unwrap();
|
||||
let upper = domain_path(&configuration).join("state-anchor/upper");
|
||||
fs::create_dir_all(upper.join("usr/bin")).unwrap();
|
||||
fs::write(upper.join("usr/bin/tool"), b"state").unwrap();
|
||||
fs::write(upper.join(".wh.usr"), b"").unwrap();
|
||||
fs::create_dir(upper.join("var")).unwrap();
|
||||
fs::write(upper.join("var/unrelated"), b"unchanged").unwrap();
|
||||
|
||||
let inspection = layout.inspect_state().unwrap();
|
||||
assert!(
|
||||
inspection
|
||||
.entries
|
||||
.iter()
|
||||
.any(|entry| entry.path == b"usr/bin/tool")
|
||||
);
|
||||
layout
|
||||
.complete_state_reset(StateAnchorResetScope::Selected(Path::new("/usr")))
|
||||
.unwrap();
|
||||
|
||||
assert!(!upper.join("usr").exists());
|
||||
assert!(!upper.join(".wh.usr").exists());
|
||||
assert_eq!(fs::read(upper.join("var/unrelated")).unwrap(), b"unchanged");
|
||||
layout.require_no_reset_quarantine().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_reset_unlinks_symlink_state_without_following_it() {
|
||||
let tree = TestTree::new();
|
||||
let configuration = configuration(&tree);
|
||||
let layout = StateAnchorLayout::create(&configuration).unwrap();
|
||||
let upper = domain_path(&configuration).join("state-anchor/upper");
|
||||
let foreign = tree.path.join("foreign");
|
||||
fs::create_dir(&foreign).unwrap();
|
||||
fs::write(foreign.join("value"), b"foreign").unwrap();
|
||||
std::os::unix::fs::symlink(&foreign, upper.join("link")).unwrap();
|
||||
|
||||
layout
|
||||
.complete_state_reset(StateAnchorResetScope::All)
|
||||
.unwrap();
|
||||
|
||||
assert!(upper.read_dir().unwrap().next().is_none());
|
||||
assert_eq!(fs::read(foreign.join("value")).unwrap(), b"foreign");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arbitrary_target_and_orphan_quarantine_are_refused() {
|
||||
let tree = TestTree::new();
|
||||
let configuration = configuration(&tree);
|
||||
let layout = StateAnchorLayout::create(&configuration).unwrap();
|
||||
assert!(
|
||||
layout
|
||||
.complete_state_reset(StateAnchorResetScope::Selected(Path::new("/etc")))
|
||||
.is_err()
|
||||
);
|
||||
fs::create_dir(domain_path(&configuration).join("state-anchor/reset-quarantine")).unwrap();
|
||||
assert!(layout.require_no_reset_quarantine().is_err());
|
||||
assert!(layout.inspect_state().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replaced_runtime_root_is_refused_before_runtime_file_write() {
|
||||
let tree = TestTree::new();
|
||||
|
|
@ -226,6 +289,18 @@ fn prepared_artifact(lower: &Path) -> PreparedArtifact {
|
|||
}
|
||||
}
|
||||
|
||||
fn domain_path(
|
||||
configuration: &lazaret_core::configuration::ResolvedDomainConfiguration,
|
||||
) -> PathBuf {
|
||||
PathBuf::from(OsString::from_vec(
|
||||
configuration
|
||||
.persistent_domain_root()
|
||||
.canonical_label()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
))
|
||||
}
|
||||
|
||||
struct TestTree {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue