diff --git a/tests/integration/mounts.bats b/tests/integration/mounts.bats index a4d997ca765..5dafb655c92 100644 --- a/tests/integration/mounts.bats +++ b/tests/integration/mounts.bats @@ -24,6 +24,109 @@ function test_ro_cgroup_mount() { for line in "${lines[@]}"; do [[ "${line}" == *'ro,'* ]]; done } +# Parse an "optstring" of the form foo,bar into $is_foo and $is_bar variables. +# Usage: parse_optstring foo,bar foo bar baz +function parse_optstring() { + optstring="$1" + shift + + for flag in "$@"; do + is_set= + if grep -wq "$flag" <<<"$optstring"; then + is_set=1 + fi + eval "is_$flag=$is_set" + done +} + +function config_add_bind_mount() { + src="$1" + dst="$2" + parse_optstring "${3:-}" rbind idmap + + bindtype=bind + if [ -n "$is_rbind" ]; then + bindtype=rbind + fi + + mappings="" + if [ -n "$is_idmap" ]; then + mappings=' + "uidMappings": [{"containerID": 0, "hostID": 100000, "size": 65536}], + "gidMappings": [{"containerID": 0, "hostID": 100000, "size": 65536}], + ' + fi + + update_config '.mounts += [{ + "source": "'"$src"'", + "destination": "'"$dst"'", + "type": "bind", + '"$mappings"' + "options": [ "'"$bindtype"'", "rprivate" ] + }]' +} + +# This needs to be placed at the top of the bats file to work around +# a shellcheck bug. See . +function test_mount_order() { + parse_optstring "${1:-}" userns idmap + + if [ -n "$is_userns" ]; then + requires root + + update_config '.linux.namespaces += [{"type": "user"}] + | .linux.uidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}] + | .linux.gidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]' + remap_rootfs + fi + + ctr_src_opts="rbind" + if [ -n "$is_idmap" ]; then + requires root + requires_kernel 5.12 + requires_idmap_fs . + + ctr_src_opts+=",idmap" + fi + + mkdir -p rootfs/{mnt,final} + # Create a set of directories we can create a mount tree with. + for subdir in a/x b/y c/z; do + dir="bind-src/$subdir" + mkdir -p "$dir" + echo "$subdir" >"$dir/file" + # Add a symlink to make sure + topdir="$(dirname "$subdir")" + ln -s "$topdir" "bind-src/sym-$topdir" + done + # In userns tests, make sure that the source directory cannot be accessed, + # to make sure we're exercising the bind-mount source fd logic. + chmod o-rwx bind-src + + rootfs="$(pwd)/rootfs" + rm -rf rootfs/mnt + mkdir rootfs/mnt + + # Create a bind-mount tree. + config_add_bind_mount "$PWD/bind-src/a" "/mnt" + config_add_bind_mount "$PWD/bind-src/sym-b" "/mnt/x" + config_add_bind_mount "$PWD/bind-src/c" "/mnt/x/y" + config_add_bind_mount "$PWD/bind-src/sym-a" "/mnt/x/y/z" + # Create a recursive bind-mount that uses part of the current tree in the + # container. + config_add_bind_mount "$rootfs/mnt/x" "$rootfs/mnt/x/y/z/x" "$ctr_src_opts" + config_add_bind_mount "$rootfs/mnt/x/y" "$rootfs/mnt/x/y/z" "$ctr_src_opts" + # Finally, bind-mount the whole thing on top of /final. + config_add_bind_mount "$rootfs/mnt" "$rootfs/final" "$ctr_src_opts" + + # Check that the entire tree was copied and the mounts were done in the + # expected order. + update_config '.process.args = ["cat", "/final/x/y/z/z/x/y/z/x/file"]' + runc run test_busybox + [ "$status" -eq 0 ] + [[ "$output" == *"a/x"* ]] # the final "file" was from a/x. +} + # https://github.com/opencontainers/runc/issues/3991 @test "runc run [tmpcopyup]" { mkdir -p rootfs/dir1/dir2 @@ -108,3 +211,19 @@ function test_ro_cgroup_mount() { update_config '.linux.namespaces |= if index({"type": "cgroup"}) then . else . + [{"type": "cgroup"}] end' test_ro_cgroup_mount } + +@test "runc run [mount order, container bind-mount source]" { + test_mount_order +} + +@test "runc run [mount order, container bind-mount source] (userns)" { + test_mount_order userns +} + +@test "runc run [mount order, container idmap source]" { + test_mount_order idmap +} + +@test "runc run [mount order, container idmap source] (userns)" { + test_mount_order userns,idmap +}