use rm and find instead of remove_tree()

* remove_tree() requires the CWD to be accessible or fails with
   cannot chdir to $CWD from $DIR_TO_DELETE: Permission denied, aborting.
 * CWD is not always accessible -- example: run mmdebstrap from a
   directory only accessible by the current user (like a tempdir) in
   unshare mode
 * find from findutils *also* requires CWD to be accessible but it's
   easier to temporarily change CWD in a subprocess because using
   there is no utility in perl core that changes CWD temporarily and
   cleans up after itself
 * we need to use find from findutils instead of rm in unshare mode
   because the root directory itself might not be removable by the
   unshared user so we only want to remove its subdirectories
This commit is contained in:
Johannes Schauer Marin Rodrigues 2021-10-06 21:20:41 +02:00
parent c2d988b475
commit 4f278deadf
Signed by untrusted user: josch
GPG key ID: F2CBA5C78FBD83E1
2 changed files with 67 additions and 50 deletions

View file

@ -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

View file

@ -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}";
}