implement dpkg-realpath in perl so that we don't need to run tar inside the chroot anymore for modes other than fakechroot and proot
This commit is contained in:
parent
dc67c1f4be
commit
c2c270390b
1 changed files with 105 additions and 30 deletions
131
mmdebstrap
131
mmdebstrap
|
@ -2585,6 +2585,49 @@ sub checkokthx {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# resolve a path inside a chroot
|
||||||
|
sub chrooted_realpath {
|
||||||
|
my $root = shift;
|
||||||
|
my $src = shift;
|
||||||
|
my $result = $root;
|
||||||
|
my $prefix;
|
||||||
|
|
||||||
|
# relative paths are relative to the root of the chroot
|
||||||
|
# remove prefixed slashes
|
||||||
|
$src =~ s{^/+}{};
|
||||||
|
my $loop = 0;
|
||||||
|
while (length $src) {
|
||||||
|
if ($loop > 25) {
|
||||||
|
error "too many levels of symbolic links";
|
||||||
|
}
|
||||||
|
# Get the first directory component.
|
||||||
|
($prefix, $src) = split m{/+}, $src, 2;
|
||||||
|
# Resolve the first directory component.
|
||||||
|
if ($prefix eq ".") {
|
||||||
|
# Ignore, stay at the same directory.
|
||||||
|
} elsif ($prefix eq "..") {
|
||||||
|
# Go up one directory.
|
||||||
|
$result =~ s{(.*)/[^/]*}{$1};
|
||||||
|
# but not further than the root
|
||||||
|
if ($result !~ m/^\Q$root\E/) {
|
||||||
|
$result = $root;
|
||||||
|
}
|
||||||
|
} elsif (-l "$result/$prefix") {
|
||||||
|
my $dst = readlink "$result/$prefix";
|
||||||
|
if ($dst =~ s{^/+}{}) {
|
||||||
|
# Absolute pathname, reset result back to $root.
|
||||||
|
$result = $root;
|
||||||
|
}
|
||||||
|
$src = length $src ? "$dst/$src" : $dst;
|
||||||
|
$loop++;
|
||||||
|
} else {
|
||||||
|
# Otherwise append the prefix.
|
||||||
|
$result = "$result/$prefix";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
sub hookhelper {
|
sub hookhelper {
|
||||||
# we put everything in an eval block because that way we can easily handle
|
# we put everything in an eval block because that way we can easily handle
|
||||||
# errors without goto labels or much code duplication: the error handler
|
# errors without goto labels or much code duplication: the error handler
|
||||||
|
@ -2597,15 +2640,6 @@ sub hookhelper {
|
||||||
$verbosity_level = $ARGV[5];
|
$verbosity_level = $ARGV[5];
|
||||||
my $command = $ARGV[6];
|
my $command = $ARGV[6];
|
||||||
|
|
||||||
# unless we are in the setup hook (where there is no tar inside the
|
|
||||||
# chroot) we need to run tar on the inside because otherwise, possible
|
|
||||||
# absolute symlinks in the path given via --directory are not
|
|
||||||
# correctly resolved
|
|
||||||
#
|
|
||||||
# FIXME: the issue above can be fixed by a function that is able to
|
|
||||||
# resolve absolute symlinks even inside the chroot directory to a full
|
|
||||||
# path that is valid on the outside -- fakechroot and proot have their
|
|
||||||
# own reasons, see below
|
|
||||||
my @cmdprefix = ();
|
my @cmdprefix = ();
|
||||||
my @tarcmd = (
|
my @tarcmd = (
|
||||||
'tar', '--numeric-owner', '--xattrs', '--format=pax',
|
'tar', '--numeric-owner', '--xattrs', '--format=pax',
|
||||||
|
@ -2634,7 +2668,7 @@ sub hookhelper {
|
||||||
push @cmdprefix, 'proot', '--root-id', "--rootfs=$root",
|
push @cmdprefix, 'proot', '--root-id', "--rootfs=$root",
|
||||||
'--cwd=/', "--qemu=$qemu";
|
'--cwd=/', "--qemu=$qemu";
|
||||||
} elsif (any { $_ eq $mode } ('root', 'chrootless', 'unshare')) {
|
} elsif (any { $_ eq $mode } ('root', 'chrootless', 'unshare')) {
|
||||||
push @cmdprefix, '/usr/sbin/chroot', $root;
|
# not chrooting in this case
|
||||||
} else {
|
} else {
|
||||||
error "unknown mode: $mode";
|
error "unknown mode: $mode";
|
||||||
}
|
}
|
||||||
|
@ -2657,21 +2691,43 @@ sub hookhelper {
|
||||||
# outside
|
# outside
|
||||||
my $directory;
|
my $directory;
|
||||||
if ($hook eq 'setup') {
|
if ($hook eq 'setup') {
|
||||||
$directory = "$root/$outpath";
|
# tar runs outside, so acquire the correct path
|
||||||
|
$directory = chrooted_realpath $root, $outpath;
|
||||||
} elsif (
|
} elsif (
|
||||||
any { $_ eq $hook }
|
any { $_ eq $hook }
|
||||||
('extract', 'essential', 'customize')
|
('extract', 'essential', 'customize')
|
||||||
) {
|
) {
|
||||||
|
if (any { $_ eq $mode } ('fakechroot', 'proot')) {
|
||||||
|
# tar will run inside the chroot
|
||||||
$directory = $outpath;
|
$directory = $outpath;
|
||||||
|
} elsif (
|
||||||
|
any { $_ eq $mode }
|
||||||
|
('root', 'chrootless', 'unshare')
|
||||||
|
) {
|
||||||
|
$directory = chrooted_realpath $root, $outpath;
|
||||||
|
} else {
|
||||||
|
error "unknown mode: $mode";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error "unknown hook: $hook";
|
error "unknown hook: $hook";
|
||||||
}
|
}
|
||||||
|
|
||||||
# FIXME: here we would like to check if the path inside the
|
# if chrooted_realpath was used and if neither fakechroot or
|
||||||
# chroot given by $directory actually exists but we cannot
|
# proot were used (absolute symlinks will be broken) we can
|
||||||
# because we are missing a function that can resolve even
|
# check and potentially fail early if the target does not exist
|
||||||
# paths including absolute symlinks to paths that are valid
|
if (none { $_ eq $mode } ('fakechroot', 'proot')) {
|
||||||
# outside the chroot
|
my $dirtocheck = $directory;
|
||||||
|
if ($command eq 'upload') {
|
||||||
|
# check the parent directory instead
|
||||||
|
$dirtocheck =~ s/(.*)\/[^\/]*/$1/;
|
||||||
|
}
|
||||||
|
if (!-e $dirtocheck) {
|
||||||
|
error "path does not exist: $dirtocheck";
|
||||||
|
}
|
||||||
|
if (!-d $dirtocheck) {
|
||||||
|
error "path is not a directory: $dirtocheck";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $fh;
|
my $fh;
|
||||||
if ($command eq 'upload') {
|
if ($command eq 'upload') {
|
||||||
|
@ -2779,21 +2835,40 @@ sub hookhelper {
|
||||||
# outside
|
# outside
|
||||||
my $directory;
|
my $directory;
|
||||||
if ($hook eq 'setup') {
|
if ($hook eq 'setup') {
|
||||||
$directory = "$root/$ARGV[$i]";
|
# tar runs outside, so acquire the correct path
|
||||||
|
$directory = chrooted_realpath $root, $ARGV[$i];
|
||||||
} elsif (
|
} elsif (
|
||||||
any { $_ eq $hook }
|
any { $_ eq $hook }
|
||||||
('extract', 'essential', 'customize')
|
('extract', 'essential', 'customize')
|
||||||
) {
|
) {
|
||||||
|
if (any { $_ eq $mode } ('fakechroot', 'proot')) {
|
||||||
|
# tar will run inside the chroot
|
||||||
$directory = $ARGV[$i];
|
$directory = $ARGV[$i];
|
||||||
|
} elsif (
|
||||||
|
any { $_ eq $mode }
|
||||||
|
('root', 'chrootless', 'unshare')
|
||||||
|
) {
|
||||||
|
$directory = chrooted_realpath $root, $ARGV[$i];
|
||||||
|
} else {
|
||||||
|
error "unknown mode: $mode";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error "unknown hook: $hook";
|
error "unknown hook: $hook";
|
||||||
}
|
}
|
||||||
|
|
||||||
# FIXME: here we would like to check if the path inside the
|
# if chrooted_realpath was used and if neither fakechroot or
|
||||||
# chroot given by $directory actually exists but we cannot
|
# proot were used (absolute symlinks will be broken) we can
|
||||||
# because we are missing a function that can resolve even
|
# check and potentially fail early if the source does not exist
|
||||||
# paths including absolute symlinks to paths that are valid
|
if (none { $_ eq $mode } ('fakechroot', 'proot')) {
|
||||||
# outside the chroot
|
if (!-e $directory) {
|
||||||
|
error "path does not exist: $directory";
|
||||||
|
}
|
||||||
|
if ($command eq 'download') {
|
||||||
|
if (!-f $directory) {
|
||||||
|
error "path is not a file: $directory";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $fh;
|
my $fh;
|
||||||
if ($command eq 'download') {
|
if ($command eq 'download') {
|
||||||
|
@ -5444,12 +5519,12 @@ The path on the outside is relative to current directory of the original
|
||||||
B<mmdebstrap> invocation. The path inside the chroot must already exist. Paths
|
B<mmdebstrap> invocation. The path inside the chroot must already exist. Paths
|
||||||
outside the chroot are created as necessary.
|
outside the chroot are created as necessary.
|
||||||
|
|
||||||
To be able to resolve even absolute symlinks, C<tar> or C<sh> and C<cat> are
|
In B<fakechroot> and B<proot> mode, C<tar>, or C<sh> and C<cat> have to be run
|
||||||
executed inside the chroot for the B<essential> and B<customize> hooks. This
|
inside the chroot or otherwise, symlinks will be wrongly resolved and/or
|
||||||
means that the special hooks might fail for the B<extract> and B<custom>
|
permissions will be off. This means that the special hooks might fail in
|
||||||
variants if no C<tar> or C<sh> and C<cat> is available inside the chroot.
|
B<fakechroot> and B<proot> mode for the B<setup> hook or for the B<extract> and
|
||||||
Since nothing is yet installed at the time of the B<setup> hook, no absolute
|
B<custom> variants if no C<tar> or C<sh> and C<cat> is available inside the
|
||||||
symlinks in paths inside the chroot are supported during that hook.
|
chroot.
|
||||||
|
|
||||||
=over 8
|
=over 8
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue