diff --git a/coverage.sh b/coverage.sh index 5bbdc60..807ee38 100755 --- a/coverage.sh +++ b/coverage.sh @@ -127,7 +127,7 @@ if [ ! -e shared/hooks/eatmydata/customize.sh ] || [ hooks/eatmydata/customize.s fi fi starttime= -total=212 +total=213 skipped=0 runtests=0 i=1 @@ -1055,6 +1055,39 @@ else runtests=$((runtests+1)) fi +print_header "mode=unshare,variant=apt: CWD directory not accessible by unshared user" +cat << END > shared/test.sh +#!/bin/sh +set -eu +export LC_ALL=C.UTF-8 +if [ ! -e /mmdebstrap-testenv ]; then + echo "this test modifies the system and should only be run inside a container" >&2 + exit 1 +fi +adduser --gecos user --disabled-password user +sysctl -w kernel.unprivileged_userns_clone=1 +mkdir /tmp/debian-chroot +chmod 700 /tmp/debian-chroot +chown user:user /tmp/debian-chroot +if [ "$CMD" = "./mmdebstrap" ]; then + CMD=\$(realpath --canonicalize-existing ./mmdebstrap) +elif [ "$CMD" = "perl -MDevel::Cover=-silent,-nogcov ./mmdebstrap" ]; then + CMD="perl -MDevel::Cover=-silent,-nogcov \$(realpath --canonicalize-existing ./mmdebstrap)" +else + CMD="$CMD" +fi +env --chdir=/tmp/debian-chroot runuser -u user -- \$CMD --mode=unshare --variant=apt $DEFAULT_DIST /tmp/debian-chroot.tar $mirror +tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt - +rm /tmp/debian-chroot.tar +END +if [ "$HAVE_QEMU" = "yes" ]; then + ./run_qemu.sh + runtests=$((runtests+1)) +else + echo "HAVE_QEMU != yes -- Skipping test..." >&2 + skipped=$((skipped+1)) +fi + print_header "mode=unshare,variant=apt: create gzip compressed tarball" cat << END > shared/test.sh #!/bin/sh diff --git a/mmdebstrap b/mmdebstrap index 0122411..0d23f26 100755 --- a/mmdebstrap +++ b/mmdebstrap @@ -29,7 +29,7 @@ use English; use Getopt::Long; use Pod::Usage; use File::Copy; -use File::Path qw(make_path remove_tree); +use File::Path qw(make_path); use File::Temp qw(tempfile tempdir); use File::Basename; use File::Find; @@ -3057,15 +3057,14 @@ sub run_cleanup() { # apt since 1.6 creates the auxfiles directory. If apt inside the # chroot is older than that, then it will not know how to clean it. if (-e "$options->{root}/var/lib/apt/lists/auxfiles") { - remove_tree("$options->{root}/var/lib/apt/lists/auxfiles", - { error => \my $err }); - if (@$err) { - for my $diag (@$err) { - my ($file, $message) = %$diag; - if ($file eq '') { warning "general error: $message"; } - else { warning "problem unlinking $file: $message"; } - } - } + 0 == system( + 'rm', + '--interactive=never', + '--recursive', + '--preserve-root', + '--one-file-system', + "$options->{root}/var/lib/apt/lists/auxfiles" + ) or error "rm failed: $?"; } } @@ -3135,15 +3134,11 @@ sub run_cleanup() { next if $entry eq "."; next if $entry eq ".."; warning "deleting files in /tmp: $entry"; - remove_tree("$options->{root}/tmp/$entry", - { error => \my $err }); - if (@$err) { - for my $diag (@$err) { - my ($file, $message) = %$diag; - if ($file eq '') { warning "general error: $message"; } - else { warning "problem unlinking $file: $message"; } - } - } + 0 == system( + 'rm', '--interactive=never', + '--recursive', '--preserve-root', + '--one-file-system', "$options->{root}/tmp/$entry" + ) or error "rm failed: $?"; } closedir($dh); } @@ -4519,8 +4514,11 @@ sub main() { $ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin"; } - foreach my $tool ('dpkg', 'dpkg-deb', 'apt-get', 'apt-cache', 'apt-config', - 'tar') { + foreach my $tool ( + 'dpkg', 'dpkg-deb', 'apt-get', 'apt-cache', + 'apt-config', 'tar', 'rm', 'find', + 'env' + ) { my $found = 0; foreach my $path (split /:/, $ENV{PATH}) { if (-f "$path/$tool" && -x _ ) { @@ -5922,31 +5920,18 @@ sub main() { # no risk of removing anything important. $pid = get_unshare_cmd( sub { - # File::Path will produce the error "cannot stat initial - # working directory" if the working directory cannot be - # accessed by the unprivileged unshared user. Thus, we - # first navigate to the parent of the root directory. - chdir "$options->{root}/.." - or error "unable to chdir() to parent directory of" - . " $options->{root}: $!"; - remove_tree($options->{root}, { error => \my $err }); - if (@$err) { - for my $diag (@$err) { - my ($file, $message) = %$diag; - if ($file eq '') { - warning "general error: $message"; - } else { - # the unshared process might not have - # sufficient permissions to remove the root - # directory. We attempt its removal later, so - # don't warn if its removal fails now. - if ($file ne $options->{root}) { - warning - "problem unlinking $file: $message"; - } - } - } - } + # change CWD to chroot directory because find tries to + # chdir to the current directory which might not be + # accessible by the unshared user: + # find: Failed to restore initial working directory + 0 == system('env', "--chdir=$options->{root}", 'find', + $options->{root}, '-mount', + '-mindepth', '1', '-delete') + or error "rm failed: $?"; + # ignore failure in case the unshared user doesn't have the + # required permissions -- we attempt again later if + # necessary + rmdir "$options->{root}"; }, \@idmap ); @@ -5954,8 +5939,7 @@ sub main() { $? == 0 or error "remove_tree failed"; # in unshare mode, the toplevel directory might've been created in # a directory that the unshared user cannot change and thus cannot - # delete. If the root directory still exists after remove_tree - # above, we attempt its removal again outside as the normal user. + # delete. We attempt its removal again outside as the normal user. if (-e $options->{root}) { rmdir "$options->{root}" or error "cannot rmdir $options->{root}: $!"; @@ -5973,7 +5957,7 @@ sub main() { # be removed. 0 == system('rm', '--interactive=never', '--recursive', '--preserve-root', '--one-file-system', $options->{root}) - or error "rm failed: $!"; + or error "rm failed: $?"; } else { error "unknown mode: $options->{mode}"; }