diff options
| author | Hermes Agent <hermes@localhost> | 2026-08-12 00:56:41 +0000 |
|---|---|---|
| committer | Hermes Agent <hermes@localhost> | 2026-08-12 00:56:41 +0000 |
| commit | 5e840ed4ce8097f0c53abdc3c5b2a25d052611fd (patch) | |
| tree | 450fdad5657a121cbb4f585604f4abde51f7a1b7 /tests | |
| parent | c24c00c1d89990d44d796c4fd2b84e92162b044a (diff) | |
Add native rootfs finalization
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/rootfs_finalization.rs | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/tests/rootfs_finalization.rs b/tests/rootfs_finalization.rs new file mode 100644 index 0000000..fa0cf3b --- /dev/null +++ b/tests/rootfs_finalization.rs @@ -0,0 +1,81 @@ +use std::fs; +use std::os::unix::fs::symlink; +use std::path::Path; + +use alt_controller_image::rootfs::{CopyTree, InitrdOem, RootfsFinalization, ServiceName}; +use tempfile::tempdir; + +#[test] +fn finalization_copies_trees_generates_oem_recipe_and_enables_services() { + let fixture = tempdir().expect("fixture directory"); + let source = fixture.path().join("overlay"); + fs::create_dir_all(source.join("nested")).expect("create source tree"); + fs::write(source.join("nested/controller.conf"), "mode = controller\n").expect("write source file"); + symlink("nested/controller.conf", source.join("controller.conf")) + .expect("create relative source symlink"); + + let rootfs = tempdir().expect("rootfs directory"); + let finalization = RootfsFinalization::new( + vec![CopyTree::new(&source, "etc/controller").expect("valid copy destination")], + InitrdOem::new(["rootfs", "qemu"], ["ext4", "virtio_blk.ko"]), + vec![ServiceName::new("chronyd.service").expect("valid service")], + ); + + let manifest = finalization.apply(rootfs.path()).expect("finalize rootfs"); + + assert_eq!( + fs::read_to_string(rootfs.path().join("etc/controller/nested/controller.conf")) + .expect("copied file"), + "mode = controller\n" + ); + assert_eq!( + fs::read_link(rootfs.path().join("etc/controller/controller.conf")).expect("copied symlink"), + Path::new("nested/controller.conf") + ); + assert_eq!( + fs::read_to_string(rootfs.path().join("etc/initrd.mk.oem")).expect("OEM recipe"), + "FEATURES += qemu rootfs\nMODULES += ext4 virtio_blk.ko\n" + ); + assert_eq!( + fs::read_link( + rootfs + .path() + .join("etc/systemd/system/multi-user.target.wants/chronyd.service") + ) + .expect("enabled service"), + Path::new("/usr/lib/systemd/system/chronyd.service") + ); + assert_eq!( + manifest.created_paths(), + [ + "etc/controller", + "etc/controller/controller.conf", + "etc/controller/nested", + "etc/controller/nested/controller.conf", + "etc/initrd.mk.oem", + "etc/systemd/system/multi-user.target.wants/chronyd.service", + ] + ); +} + +#[test] +fn finalization_rejects_rootfs_escaping_destinations_and_symlinks() { + assert!(CopyTree::new("fixtures/overlay", "/etc/controller").is_err()); + assert!(CopyTree::new("fixtures/overlay", "../etc/controller").is_err()); + + let fixture = tempdir().expect("fixture directory"); + let source = fixture.path().join("overlay"); + fs::create_dir_all(&source).expect("create source tree"); + symlink("../../outside", source.join("unsafe-link")).expect("create unsafe symlink"); + let rootfs = tempdir().expect("rootfs directory"); + let finalization = RootfsFinalization::new( + vec![CopyTree::new(&source, "etc/controller").expect("valid destination")], + InitrdOem::new(std::iter::empty::<&str>(), std::iter::empty::<&str>()), + vec![], + ); + + let error = finalization + .apply(rootfs.path()) + .expect_err("unsafe symlink must be rejected"); + assert!(error.to_string().contains("escapes copied tree")); +} |