summaryrefslogtreecommitdiff
path: root/tests/rootfs_finalization.rs
blob: fa0cf3b7273bd7b8d036ebe773438f82610d2721 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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"));
}