diff --git a/go.mod b/go.mod index 00b2085a..17d5cf20 100644 --- a/go.mod +++ b/go.mod @@ -289,6 +289,6 @@ require ( replace ( github.com/opencontainers/umoci => github.com/project-stacker/umoci v0.0.0-20240906174318-e9397ba4ced0 - machinerun.io/atomfs => github.com/rchincha/atomfs v0.0.0-20241118222046-f23b7d3df267 + machinerun.io/atomfs => github.com/rchincha/atomfs v0.0.0-20241118224201-3b5276847a13 stackerbuild.io/stacker-bom => github.com/project-stacker/stacker-bom v0.0.0-20240509203427-4d685e046780 ) diff --git a/go.sum b/go.sum index cfec720c..40beebc7 100644 --- a/go.sum +++ b/go.sum @@ -818,8 +818,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rchincha/atomfs v0.0.0-20241118222046-f23b7d3df267 h1:m+sM2UvDXMCRIl5e+6zQsw37KRceg0tEcuvPxQ69BSc= -github.com/rchincha/atomfs v0.0.0-20241118222046-f23b7d3df267/go.mod h1:woheEy3EVXE+AFLGwmBRSMtmcOKBM71qiDEYaq7Nwng= +github.com/rchincha/atomfs v0.0.0-20241118224201-3b5276847a13 h1:PZWE0mq+wI6zqUsV1pA0yUmhkl3UWs0I7duypR92NQw= +github.com/rchincha/atomfs v0.0.0-20241118224201-3b5276847a13/go.mod h1:woheEy3EVXE+AFLGwmBRSMtmcOKBM71qiDEYaq7Nwng= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/install-build-deps.sh b/install-build-deps.sh index bae0a887..e34fdb77 100755 --- a/install-build-deps.sh +++ b/install-build-deps.sh @@ -43,6 +43,7 @@ installdeps_ubuntu() { squashfs-tools squashfuse libarchive-tools + erofs-utils erofsfuse ) case "$VERSION_ID" in diff --git a/test/atomfs-erofs.bats b/test/atomfs-erofs.bats new file mode 100644 index 00000000..6df71b14 --- /dev/null +++ b/test/atomfs-erofs.bats @@ -0,0 +1,164 @@ +load helpers + +function setup() { + stacker_setup +} + +function teardown() { + cleanup +} + +function verity_checkusedloops() { + # search for loopdevices which have backing files with the current + # BATS_TEST_DIRNAME value and complain if they're present. + local usedloops="" found="" x="" + for ((x=0; x<5; x++)); do + usedloops=$(losetup -a | grep $BATS_TEST_DIRNAME || echo) + if [ -n "$usedloops" ]; then + found=1 + udevadm settle + else + return 0 + fi + done + echo "found used loops in testdir=$BATS_TEST_DIRNAME :$usedloops" >&3 + [ $found = 1 ] +} + +function basic_test() { + require_privilege priv + local verity_arg=$1 + + cat > stacker.yaml <<"EOF" +test: + from: + type: oci + url: ${{BUSYBOX_OCI}} + run: | + touch /hello +EOF + stacker build --layer-type=erofs $verity_arg --substitute BUSYBOX_OCI=${BUSYBOX_OCI} + mkdir mountpoint + stacker internal-go atomfs mount test-erofs mountpoint + + [ -f mountpoint/hello ] + stacker internal-go atomfs umount mountpoint +} + +@test "--no-verity works" { + basic_test --no-verity + verity_checkusedloops +} + +@test "mount + umount works" { + basic_test + + # last layer shouldn't exist any more, since it is unique + manifest=$(cat oci/index.json | jq -r .manifests[0].digest | cut -f2 -d:) + last_layer_num=$(($(cat oci/blobs/sha256/$manifest | jq -r '.layers | length')-1)) + last_layer_hash=$(cat oci/blobs/sha256/$manifest | jq -r .layers[$last_layer].digest | cut -f2 -d:) + [ ! -b "/dev/mapper/$last_layer_hash-verity" ] + verity_checkusedloops +} + +@test "mount + umount + mount a tree of images works" { + require_privilege priv + cat > stacker.yaml <<"EOF" +base: + from: + type: oci + url: ${{BUSYBOX_OCI}} + run: touch /base +a: + from: + type: built + tag: base + run: touch /a +b: + from: + type: built + tag: base + run: touch /b +c: + from: + type: built + tag: base + run: touch /c +EOF + stacker build --layer-type=erofs --substitute BUSYBOX_OCI=${BUSYBOX_OCI} + + mkdir a + stacker internal-go atomfs mount a-erofs a + [ -f a/a ] + + mkdir b + stacker internal-go atomfs mount b-erofs b + [ -f b/b ] + + cat /proc/self/mountinfo + echo "mountinfo after b^" + + stacker internal-go atomfs umount b + + # first layer should still exist since a is still mounted + manifest=$(cat oci/index.json | jq -r .manifests[0].digest | cut -f2 -d:) + first_layer_hash=$(cat oci/blobs/sha256/$manifest | jq -r .layers[0].digest | cut -f2 -d:) + [ ! -b "/dev/mapper/$last_layer_hash-verity" ] + + mkdir c + stacker internal-go atomfs mount c-erofs c + [ -f c/c ] + + cat /proc/self/mountinfo + echo "mountinfo after c^" + + stacker internal-go atomfs umount a + + cat /proc/self/mountinfo + echo "mountinfo after umount a^" + + # first layer should still exist since c is still mounted + manifest=$(cat oci/index.json | jq -r .manifests[0].digest | cut -f2 -d:) + first_layer_hash=$(cat oci/blobs/sha256/$manifest | jq -r .layers[0].digest | cut -f2 -d:) + [ ! -b "/dev/mapper/$last_layer_hash-verity" ] + + # c should still be ok + [ -f c/c ] + [ -f c/bin/sh ] + stacker internal-go atomfs umount c + + # c's last layer shouldn't exist any more, since it is unique + manifest=$(cat oci/index.json | jq -r .manifests[0].digest | cut -f2 -d:) + last_layer_num=$(($(cat oci/blobs/sha256/$manifest | jq -r '.layers | length')-1)) + last_layer_hash=$(cat oci/blobs/sha256/$manifest | jq -r .layers[$last_layer].digest | cut -f2 -d:) + [ ! -b "/dev/mapper/$last_layer_hash-verity" ] + verity_checkusedloops +} + +@test "bad existing verity device is rejected" { + require_privilege priv + cat > stacker.yaml <<"EOF" +test: + from: + type: oci + url: ${{BUSYBOX_OCI}} + run: | + touch /hello +EOF + stacker build --layer-type=erofs --substitute BUSYBOX_OCI=${BUSYBOX_OCI} + + manifest=$(cat oci/index.json | jq -r .manifests[0].digest | cut -f2 -d:) + first_layer_hash=$(cat oci/blobs/sha256/$manifest | jq -r .layers[0].digest | cut -f2 -d:) + devname="$first_layer_hash-verity" + + # make an evil device and fake it as an existing verity device + dd if=/dev/random of=mydev bs=50K count=1 + root_hash=$(veritysetup format mydev mydev.hash | grep "Root hash:" | awk '{print $NF}') + echo "root hash $root_hash" + veritysetup open mydev "$devname" mydev.hash "$root_hash" + + mkdir mountpoint + bad_stacker internal-go atomfs mount test-erofs mountpoint | grep "invalid root hash" + veritysetup close "$devname" + verity_checkusedloops +} diff --git a/test/helpers.bash b/test/helpers.bash index f61000eb..ac82cae4 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -8,6 +8,17 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi +function give_user_ownership() { + if [ "$PRIVILEGE_LEVEL" = "priv" ]; then + return + fi + if [ -z "$SUDO_UID" ]; then + echo "PRIVILEGE_LEVEL=$PRIVILEGE_LEVEL but empty SUDO_USER" + exit 1 + fi + chown -R "$SUDO_USER:$SUDO_USER" "$@" +} + function skip_if_no_unpriv_overlay { local wdir="" # use a workdir to ensure no side effects to the caller @@ -80,17 +91,6 @@ function stacker_setup() { chown -R $SUDO_USER:$SUDO_USER . } -function give_user_ownership() { - if [ "$PRIVILEGE_LEVEL" = "priv" ]; then - return - fi - if [ -z "$SUDO_UID" ]; then - echo "PRIVILEGE_LEVEL=$PRIVILEGE_LEVEL but empty SUDO_USER" - exit 1 - fi - chown -R "$SUDO_USER:$SUDO_USER" "$@" - } - function cleanup() { cd "$ROOT_DIR/test" umount_under "$TEST_TMPDIR"