From fcb4a36f3090a75ee5856fb9b3335d31390bc679 Mon Sep 17 00:00:00 2001 From: Filippo Squillace Date: Fri, 7 Apr 2017 22:38:47 +0100 Subject: [PATCH] Issue #174: Add option -n in groot and umount directories in order --- bin/groot | 30 ++++++++++++++++++++---------- lib/core/namespace.sh | 7 +++++-- tests/unit-tests/test-groot.sh | 24 ++++++++++++++++++++++++ tests/unit-tests/test-namespace.sh | 16 ++++++++-------- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/bin/groot b/bin/groot index 1d6e26b..97ccfef 100755 --- a/bin/groot +++ b/bin/groot @@ -19,6 +19,11 @@ UMOUNT=umount MOUNTPOINT=mountpoint MKDIR=mkdir TOUCH=touch +CUT=cut +SORT=sort +UNIQ=uniq +GREP=grep +MOUNTS_FILE=/proc/self/mounts NOT_EXISTING_FILE=103 NOT_ABSOLUTE_PATH=111 @@ -29,28 +34,29 @@ source "${JUNEST_BASE}/lib/utils/utils.sh" ################################ MAIN FUNCTIONS ########################### -function chroot_add_mount() { - $MOUNT "$@" && CHROOT_ACTIVE_MOUNTS=("${@: -1}" "${CHROOT_ACTIVE_MOUNTS[@]}") -} - function chroot_teardown() { - $UMOUNT "${CHROOT_ACTIVE_MOUNTS[@]}" - unset CHROOT_ACTIVE_MOUNTS + # Remove all mounts starting from the most nested ones. + # Suffix the CHROOTDIR with / to avoid umounting directories not belonging + # to CHROOTDIR. + for mp in $($GREP "${CHROOTDIR%/}/" $MOUNTS_FILE | $CUT -f2 -d' ' | $SORT -r | $UNIQ) + do + $UMOUNT $mp + done + $UMOUNT ${CHROOTDIR%/} } function chroot_maybe_add_mount() { local cond=$1 shift if eval "$cond"; then - chroot_add_mount "$@" + $MOUNT "$@" return fi return 1 } function chroot_setup() { - CHROOT_ACTIVE_MOUNTS=() - check_and_trap 'chroot_teardown' EXIT + $OPT_NO_UMOUNT || check_and_trap 'chroot_teardown' EXIT if ! chroot_maybe_add_mount "! $MOUNTPOINT -q '$CHROOTDIR'" --bind "$CHROOTDIR" "$CHROOTDIR" then @@ -73,7 +79,7 @@ function chroot_setup() { fi create_node "${host_path}" "${CHROOTDIR}${guest_path}" - chroot_add_mount --rbind "${host_path}" "${CHROOTDIR}${guest_path}" + $MOUNT --rbind "${host_path}" "${CHROOTDIR}${guest_path}" done } @@ -112,6 +118,8 @@ Options: path in the guest rootfs but users can specify any other location with the syntax: -b :. This option can be invoked multiple times and the paths specified must be absolutes. + -n, --no-umount + Do not umount after chroot session finished. -h, --help Print this help message @@ -129,12 +137,14 @@ version() { function parse_arguments() { OPT_BIND=false BINDINGS=() + OPT_NO_UMOUNT=false OPT_HELP=false OPT_VERSION=false for opt in "$@" do case "$1" in -b|--bind) OPT_BIND=true ; shift ; BINDINGS+=("$1") ; shift ;; + -n|--no-umount) OPT_NO_UMOUNT=true ; shift ;; -h|--help) OPT_HELP=true ; shift ;; -V|--version) OPT_VERSION=true ; shift ;; -*) die "Invalid option $1" ;; diff --git a/lib/core/namespace.sh b/lib/core/namespace.sh index 6c44bbb..8854c43 100644 --- a/lib/core/namespace.sh +++ b/lib/core/namespace.sh @@ -46,11 +46,14 @@ function _run_env_with_namespace(){ local backend_args="$1" shift + # Use option -n in groot because umount do not work sometimes. + # As soon as the process terminates, the namespace + # will terminate too with its own mounted directories. if [[ "$1" != "" ]] then - JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")" + JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT -n $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")" else - JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" + JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT -n $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" fi } diff --git a/tests/unit-tests/test-groot.sh b/tests/unit-tests/test-groot.sh index 02f05ec..472e027 100755 --- a/tests/unit-tests/test-groot.sh +++ b/tests/unit-tests/test-groot.sh @@ -133,5 +133,29 @@ function test_groot_with_bind_and_command(){ assertEquals 0 $? assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)" } +function test_groot_with_bind_no_umount(){ + assertCommandSuccess main -n chrootdir + assertEquals "$(echo -e "mountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_chroot_teardown(){ + echo -e "1 /home/mychroot/dev\n1 /home/mychroot/proc/fs1\n1 /home/mychroot\n1 /home/mychroot-no/dev\n1 /home/mychroot/dev/shm\n1 /home/mychroot/proc\n" > ./mounts + MOUNTS_FILE=./mounts + CHROOTDIR=/home/mychroot assertCommandSuccess chroot_teardown + assertEquals "$(echo -e "umount(/home/mychroot/proc/fs1) +umount(/home/mychroot/proc) +umount(/home/mychroot/dev/shm) +umount(/home/mychroot/dev) +umount(/home/mychroot)")" "$(cat $STDOUTF)" +} +function test_groot_with_chroot_teardown_with_trailing_slash(){ + echo -e "1 /home/mychroot/dev\n1 /home/mychroot/proc/fs1\n1 /home/mychroot\n1 /home/mychroot-no/dev\n1 /home/mychroot/dev/shm\n1 /home/mychroot/proc\n" > ./mounts + MOUNTS_FILE=./mounts + CHROOTDIR=/home/mychroot assertCommandSuccess chroot_teardown + assertEquals "$(echo -e "umount(/home/mychroot/proc/fs1) +umount(/home/mychroot/proc) +umount(/home/mychroot/dev/shm) +umount(/home/mychroot/dev) +umount(/home/mychroot)")" "$(cat $STDOUTF)" +} source $(dirname $0)/../utils/shunit2 diff --git a/tests/unit-tests/test-namespace.sh b/tests/unit-tests/test-namespace.sh index b1e52d5..e09db0a 100755 --- a/tests/unit-tests/test-namespace.sh +++ b/tests/unit-tests/test-namespace.sh @@ -80,7 +80,7 @@ function test_is_user_namespace_enabled_with_config(){ function test_run_env_as_user_with_namespace() { assertCommandSuccess run_env_as_user_with_namespace "" "" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" _test_copy_common_files _test_copy_remaining_files @@ -88,7 +88,7 @@ function test_run_env_as_user_with_namespace() { function test_run_env_as_user_with_namespace_with_bindings() { assertCommandSuccess run_env_as_user_with_namespace "-b /usr -b /lib:/tmp/lib" "" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" _test_copy_common_files _test_copy_remaining_files @@ -96,7 +96,7 @@ function test_run_env_as_user_with_namespace_with_bindings() { function test_run_env_as_user_with_namespace_with_command() { assertCommandSuccess run_env_as_user_with_namespace "" "ls -la" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" _test_copy_common_files _test_copy_remaining_files @@ -104,7 +104,7 @@ function test_run_env_as_user_with_namespace_with_command() { function test_run_env_as_user_with_namespace_with_bindings_and_command() { assertCommandSuccess run_env_as_user_with_namespace "-b /usr -b /lib:/tmp/lib" "ls -la" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" _test_copy_common_files _test_copy_remaining_files @@ -112,28 +112,28 @@ function test_run_env_as_user_with_namespace_with_bindings_and_command() { function test_run_env_as_fakeroot_with_namespace() { assertCommandSuccess run_env_as_fakeroot_with_namespace "" "" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" _test_copy_common_files } function test_run_env_as_fakeroot_with_namespace_with_bindings() { assertCommandSuccess run_env_as_fakeroot_with_namespace "-b /usr -b /lib:/tmp/lib" "" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" _test_copy_common_files } function test_run_env_as_fakeroot_with_namespace_with_command() { assertCommandSuccess run_env_as_fakeroot_with_namespace "" "ls -la" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" _test_copy_common_files } function test_run_env_as_fakeroot_with_namespace_with_bindings_and_command() { assertCommandSuccess run_env_as_fakeroot_with_namespace "-b /usr -b /lib:/tmp/lib" "ls -la" - assertEquals "unshare --mount --user --map-root-user $GROOT -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" _test_copy_common_files }