forked from josch/mmdebstrap
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:
parent
c2d988b475
commit
4f278deadf
2 changed files with 67 additions and 50 deletions
35
coverage.sh
35
coverage.sh
|
@ -127,7 +127,7 @@ if [ ! -e shared/hooks/eatmydata/customize.sh ] || [ hooks/eatmydata/customize.s
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
starttime=
|
starttime=
|
||||||
total=212
|
total=213
|
||||||
skipped=0
|
skipped=0
|
||||||
runtests=0
|
runtests=0
|
||||||
i=1
|
i=1
|
||||||
|
@ -1055,6 +1055,39 @@ else
|
||||||
runtests=$((runtests+1))
|
runtests=$((runtests+1))
|
||||||
fi
|
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"
|
print_header "mode=unshare,variant=apt: create gzip compressed tarball"
|
||||||
cat << END > shared/test.sh
|
cat << END > shared/test.sh
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
82
mmdebstrap
82
mmdebstrap
|
@ -29,7 +29,7 @@ use English;
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
use Pod::Usage;
|
use Pod::Usage;
|
||||||
use File::Copy;
|
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::Temp qw(tempfile tempdir);
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
use File::Find;
|
use File::Find;
|
||||||
|
@ -3057,15 +3057,14 @@ sub run_cleanup() {
|
||||||
# apt since 1.6 creates the auxfiles directory. If apt inside the
|
# 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.
|
# chroot is older than that, then it will not know how to clean it.
|
||||||
if (-e "$options->{root}/var/lib/apt/lists/auxfiles") {
|
if (-e "$options->{root}/var/lib/apt/lists/auxfiles") {
|
||||||
remove_tree("$options->{root}/var/lib/apt/lists/auxfiles",
|
0 == system(
|
||||||
{ error => \my $err });
|
'rm',
|
||||||
if (@$err) {
|
'--interactive=never',
|
||||||
for my $diag (@$err) {
|
'--recursive',
|
||||||
my ($file, $message) = %$diag;
|
'--preserve-root',
|
||||||
if ($file eq '') { warning "general error: $message"; }
|
'--one-file-system',
|
||||||
else { warning "problem unlinking $file: $message"; }
|
"$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 ".";
|
||||||
next if $entry eq "..";
|
next if $entry eq "..";
|
||||||
warning "deleting files in /tmp: $entry";
|
warning "deleting files in /tmp: $entry";
|
||||||
remove_tree("$options->{root}/tmp/$entry",
|
0 == system(
|
||||||
{ error => \my $err });
|
'rm', '--interactive=never',
|
||||||
if (@$err) {
|
'--recursive', '--preserve-root',
|
||||||
for my $diag (@$err) {
|
'--one-file-system', "$options->{root}/tmp/$entry"
|
||||||
my ($file, $message) = %$diag;
|
) or error "rm failed: $?";
|
||||||
if ($file eq '') { warning "general error: $message"; }
|
|
||||||
else { warning "problem unlinking $file: $message"; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
closedir($dh);
|
closedir($dh);
|
||||||
}
|
}
|
||||||
|
@ -4519,8 +4514,11 @@ sub main() {
|
||||||
$ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin";
|
$ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin";
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $tool ('dpkg', 'dpkg-deb', 'apt-get', 'apt-cache', 'apt-config',
|
foreach my $tool (
|
||||||
'tar') {
|
'dpkg', 'dpkg-deb', 'apt-get', 'apt-cache',
|
||||||
|
'apt-config', 'tar', 'rm', 'find',
|
||||||
|
'env'
|
||||||
|
) {
|
||||||
my $found = 0;
|
my $found = 0;
|
||||||
foreach my $path (split /:/, $ENV{PATH}) {
|
foreach my $path (split /:/, $ENV{PATH}) {
|
||||||
if (-f "$path/$tool" && -x _ ) {
|
if (-f "$path/$tool" && -x _ ) {
|
||||||
|
@ -5922,31 +5920,18 @@ sub main() {
|
||||||
# no risk of removing anything important.
|
# no risk of removing anything important.
|
||||||
$pid = get_unshare_cmd(
|
$pid = get_unshare_cmd(
|
||||||
sub {
|
sub {
|
||||||
# File::Path will produce the error "cannot stat initial
|
# change CWD to chroot directory because find tries to
|
||||||
# working directory" if the working directory cannot be
|
# chdir to the current directory which might not be
|
||||||
# accessed by the unprivileged unshared user. Thus, we
|
# accessible by the unshared user:
|
||||||
# first navigate to the parent of the root directory.
|
# find: Failed to restore initial working directory
|
||||||
chdir "$options->{root}/.."
|
0 == system('env', "--chdir=$options->{root}", 'find',
|
||||||
or error "unable to chdir() to parent directory of"
|
$options->{root}, '-mount',
|
||||||
. " $options->{root}: $!";
|
'-mindepth', '1', '-delete')
|
||||||
remove_tree($options->{root}, { error => \my $err });
|
or error "rm failed: $?";
|
||||||
if (@$err) {
|
# ignore failure in case the unshared user doesn't have the
|
||||||
for my $diag (@$err) {
|
# required permissions -- we attempt again later if
|
||||||
my ($file, $message) = %$diag;
|
# necessary
|
||||||
if ($file eq '') {
|
rmdir "$options->{root}";
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
\@idmap
|
\@idmap
|
||||||
);
|
);
|
||||||
|
@ -5954,8 +5939,7 @@ sub main() {
|
||||||
$? == 0 or error "remove_tree failed";
|
$? == 0 or error "remove_tree failed";
|
||||||
# in unshare mode, the toplevel directory might've been created in
|
# in unshare mode, the toplevel directory might've been created in
|
||||||
# a directory that the unshared user cannot change and thus cannot
|
# a directory that the unshared user cannot change and thus cannot
|
||||||
# delete. If the root directory still exists after remove_tree
|
# delete. We attempt its removal again outside as the normal user.
|
||||||
# above, we attempt its removal again outside as the normal user.
|
|
||||||
if (-e $options->{root}) {
|
if (-e $options->{root}) {
|
||||||
rmdir "$options->{root}"
|
rmdir "$options->{root}"
|
||||||
or error "cannot rmdir $options->{root}: $!";
|
or error "cannot rmdir $options->{root}: $!";
|
||||||
|
@ -5973,7 +5957,7 @@ sub main() {
|
||||||
# be removed.
|
# be removed.
|
||||||
0 == system('rm', '--interactive=never', '--recursive',
|
0 == system('rm', '--interactive=never', '--recursive',
|
||||||
'--preserve-root', '--one-file-system', $options->{root})
|
'--preserve-root', '--one-file-system', $options->{root})
|
||||||
or error "rm failed: $!";
|
or error "rm failed: $?";
|
||||||
} else {
|
} else {
|
||||||
error "unknown mode: $options->{mode}";
|
error "unknown mode: $options->{mode}";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue