From 4ba82a41cfde15b8e31bcfe5f00eff08c29bc587 Mon Sep 17 00:00:00 2001 From: Johannes 'josch' Schauer Date: Wed, 8 Jan 2020 17:44:07 +0100 Subject: [PATCH] format code with perltidy --- .perltidyrc | 15 + coverage.sh | 10 + mmdebstrap | 1641 ++++++++++++++++++++++++++++++++------------------- 3 files changed, 1075 insertions(+), 591 deletions(-) create mode 100644 .perltidyrc diff --git a/.perltidyrc b/.perltidyrc new file mode 100644 index 0000000..bbc8b84 --- /dev/null +++ b/.perltidyrc @@ -0,0 +1,15 @@ +# mmdebstrap is a tool focused on Debian and derivatives (it relies on apt +# after all). Thus, we use a perl style used in other Debian Perl code. The +# following options are used in Lintian and devscripts + +--break-before-all-operators +--noblanks-before-comments +--cuddled-else +--maximum-line-length=79 +--paren-tightness=2 +--square-bracket-tightness=2 +--space-for-semicolon +--opening-brace-always-on-right +--stack-opening-tokens +--stack-closing-tokens +--format-skipping diff --git a/coverage.sh b/coverage.sh index 42856ab..edad84a 100755 --- a/coverage.sh +++ b/coverage.sh @@ -2,6 +2,16 @@ set -eu +perltidy < mmdebstrap > mmdebstrap.tdy +ret=0 +diff -u mmdebstrap mmdebstrap.tdy || ret=$? +if [ "$ret" -ne 0 ]; then + echo "perltidy failed" >&2 + rm mmdebstrap.tdy + exit 1 +fi +rm mmdebstrap.tdy + mirrordir="./shared/cache/debian" if [ ! -e "$mirrordir" ]; then diff --git a/mmdebstrap b/mmdebstrap index bf7e3d7..7e0f424 100755 --- a/mmdebstrap +++ b/mmdebstrap @@ -60,21 +60,21 @@ use constant { # 5 -> directory my @devfiles = ( # filename mode type link target major minor - [ "./dev/", 0755, 5, '', undef, undef ], - [ "./dev/console", 0666, 3, '', 5, 1 ], - [ "./dev/fd", 0777, 2, '/proc/self/fd', undef, undef ], - [ "./dev/full", 0666, 3, '', 1, 7 ], - [ "./dev/null", 0666, 3, '', 1, 3 ], - [ "./dev/ptmx", 0666, 3, '', 5, 2 ], - [ "./dev/pts/", 0755, 5, '', undef, undef ], - [ "./dev/random", 0666, 3, '', 1, 8 ], - [ "./dev/shm/", 0755, 5, '', undef, undef ], - [ "./dev/stderr", 0777, 2, '/proc/self/fd/2', undef, undef ], - [ "./dev/stdin", 0777, 2, '/proc/self/fd/0', undef, undef ], - [ "./dev/stdout", 0777, 2, '/proc/self/fd/1', undef, undef ], - [ "./dev/tty", 0666, 3, '', 5, 0 ], - [ "./dev/urandom", 0666, 3, '', 1, 9 ], - [ "./dev/zero", 0666, 3, '', 1, 5 ], + ["./dev/", 0755, 5, '', undef, undef], + ["./dev/console", 0666, 3, '', 5, 1], + ["./dev/fd", 0777, 2, '/proc/self/fd', undef, undef], + ["./dev/full", 0666, 3, '', 1, 7], + ["./dev/null", 0666, 3, '', 1, 3], + ["./dev/ptmx", 0666, 3, '', 5, 2], + ["./dev/pts/", 0755, 5, '', undef, undef], + ["./dev/random", 0666, 3, '', 1, 8], + ["./dev/shm/", 0755, 5, '', undef, undef], + ["./dev/stderr", 0777, 2, '/proc/self/fd/2', undef, undef], + ["./dev/stdin", 0777, 2, '/proc/self/fd/0', undef, undef], + ["./dev/stdout", 0777, 2, '/proc/self/fd/1', undef, undef], + ["./dev/tty", 0666, 3, '', 5, 0], + ["./dev/urandom", 0666, 3, '', 1, 9], + ["./dev/zero", 0666, 3, '', 1, 5], ); # verbosity levels: @@ -93,8 +93,8 @@ sub debug { my $msg = shift; my ($package, $filename, $line) = caller; $msg = "D: $PID $line $msg"; - if ( -t STDERR ) { - $msg = colored($msg, 'clear') + if (-t STDERR) { + $msg = colored($msg, 'clear'); } print STDERR "$msg\n"; } @@ -106,11 +106,11 @@ sub info { my $msg = shift; if ($verbosity_level >= 3) { my ($package, $filename, $line) = caller; - $msg = "$PID $line $msg" + $msg = "$PID $line $msg"; } $msg = "I: $msg"; - if ( -t STDERR ) { - $msg = colored($msg, 'green') + if (-t STDERR) { + $msg = colored($msg, 'green'); } print STDERR "$msg\n"; } @@ -121,8 +121,8 @@ sub warning { } my $msg = shift; $msg = "W: $msg"; - if ( -t STDERR ) { - $msg = colored($msg, 'bold yellow') + if (-t STDERR) { + $msg = colored($msg, 'bold yellow'); } print STDERR "$msg\n"; } @@ -134,13 +134,13 @@ sub error { # if error() is called with the string from a previous error() that was # caught inside an eval(), then the string will have a newline which we # are stripping here - chomp (my $msg = shift); + chomp(my $msg = shift); $msg = "E: $msg"; - if ( -t STDERR ) { - $msg = colored($msg, 'bold red') + if (-t STDERR) { + $msg = colored($msg, 'bold red'); } if ($verbosity_level == 3) { - croak $msg; # produces a backtrace + croak $msg; # produces a backtrace } else { die "$msg\n"; } @@ -150,7 +150,7 @@ sub error { # directory itself with its parent sub is_mountpoint($) { my $dir = shift; - if (! -e $dir) { + if (!-e $dir) { return 0; } my @a = stat "$dir/."; @@ -172,9 +172,9 @@ sub is_mountpoint($) { sub get_tar_compressor($) { my $filename = shift; if ($filename eq '-') { - return undef + return undef; } elsif ($filename =~ /\.tar$/) { - return undef + return undef; } elsif ($filename =~ /\.(gz|tgz|taz)$/) { return ['gzip']; } elsif ($filename =~ /\.(Z|taZ)$/) { @@ -194,7 +194,7 @@ sub get_tar_compressor($) { } elsif ($filename =~ /\.zst$/) { return ['zstd']; } - return undef + return undef; } sub test_unshare($) { @@ -280,16 +280,17 @@ sub read_subuid_subgid() { my ($subid, $num_subid, $fh, $n); my @result = (); - if (! -e "/etc/subuid") { + if (!-e "/etc/subuid") { warning "/etc/subuid doesn't exist"; return; } - if (! -r "/etc/subuid") { + if (!-r "/etc/subuid") { warning "/etc/subuid is not readable"; return; } - open $fh, "<", "/etc/subuid" or error "cannot open /etc/subuid for reading: $!"; + open $fh, "<", "/etc/subuid" + or error "cannot open /etc/subuid for reading: $!"; while (my $line = <$fh>) { ($n, $subid, $num_subid) = split(/:/, $line, 3); last if ($n eq $username); @@ -306,7 +307,8 @@ sub read_subuid_subgid() { return; } - open $fh, "<", "/etc/subgid" or error "cannot open /etc/subgid for reading: $!"; + open $fh, "<", "/etc/subgid" + or error "cannot open /etc/subgid for reading: $!"; while (my $line = <$fh>) { ($n, $subid, $num_subid) = split(/:/, $line, 3); last if ($n eq $username); @@ -370,10 +372,12 @@ sub read_subuid_subgid() { # alive or otherwise any child processes cannot fork() anymore themselves. So # we keep F as PID 1 and finally call exec() in G. sub get_unshare_cmd(&$) { - my $cmd = shift; + my $cmd = shift; my $idmap = shift; - my $unshare_flags = CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWIPC; + my $unshare_flags + = CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS + | CLONE_NEWIPC; if (0) { $unshare_flags |= CLONE_NEWNET; @@ -449,10 +453,12 @@ sub get_unshare_cmd(&$) { } my $idmapcmd = ''; if ($uidmapcmd ne "") { - 0 == system "newuidmap $ppid $uidmapcmd" or error "newuidmap $ppid $uidmapcmd failed: $!"; + 0 == system "newuidmap $ppid $uidmapcmd" + or error "newuidmap $ppid $uidmapcmd failed: $!"; } if ($gidmapcmd ne "") { - 0 == system "newgidmap $ppid $gidmapcmd" or error "newgidmap $ppid $gidmapcmd failed: $!"; + 0 == system "newgidmap $ppid $gidmapcmd" + or error "newgidmap $ppid $gidmapcmd failed: $!"; } exit 0; } @@ -460,7 +466,8 @@ sub get_unshare_cmd(&$) { # parent # After fork()-ing, the parent immediately calls unshare... - 0 == syscall &SYS_unshare, $unshare_flags or error "unshare() failed: $!"; + 0 == syscall &SYS_unshare, $unshare_flags + or error "unshare() failed: $!"; # .. and then signals the child process that we are done with the # unshare() call by sending an EOF. @@ -502,7 +509,7 @@ sub get_unshare_cmd(&$) { # important because pid 1 must never die or otherwise nothing # new can be forked. $cpid == waitpid $cpid, 0 or error "waitpid() failed: $!"; - exit ($? >> 8); + exit($? >> 8); } } @@ -516,12 +523,12 @@ sub get_unshare_cmd(&$) { } sub havemknod($) { - my $root = shift; + my $root = shift; my $havemknod = 0; if (-e "$root/test-dev-null") { error "/test-dev-null already exists"; } - TEST: { + TEST: { # we fork so that we can read STDERR my $pid = open my $fh, '-|' // error "failed to fork(): $!"; if ($pid == 0) { @@ -530,7 +537,9 @@ sub havemknod($) { # right dev_t argument requires makedev(3) exec 'mknod', "$root/test-dev-null", 'c', '1', '3'; } - chomp (my $content = do { local $/; <$fh> }); + chomp( + my $content = do { local $/; <$fh> } + ); close $fh; { last TEST unless $? == 0 and $content eq ''; @@ -541,7 +550,8 @@ sub havemknod($) { $havemknod = 1; } if (-e "$root/test-dev-null") { - unlink "$root/test-dev-null" or error "cannot unlink /test-dev-null: $!"; + unlink "$root/test-dev-null" + or error "cannot unlink /test-dev-null: $!"; } return $havemknod; } @@ -551,7 +561,7 @@ sub print_progress { return; } my $perc = shift; - if (! -t STDERR) { + if (!-t STDERR) { return; } if ($perc eq "done") { @@ -563,8 +573,8 @@ sub print_progress { $perc = 100; } my $width = 50; - my $num_x = int($perc*$width/100); - my $bar = '=' x $num_x; + my $num_x = int($perc * $width / 100); + my $bar = '=' x $num_x; if ($num_x != $width) { $bar .= '>'; $bar .= ' ' x ($width - $num_x - 1); @@ -576,7 +586,7 @@ sub run_progress { my ($get_exec, $line_handler, $line_has_error, $chdir) = @_; pipe my $rfh, my $wfh; my $got_signal = 0; - my $ignore = sub { + my $ignore = sub { info "run_progress() received signal $_[0]: waiting for child..."; }; @@ -589,19 +599,21 @@ sub run_progress { if ($pid1 == 0) { # child: default signal handlers - $SIG{'INT'} = 'DEFAULT'; - $SIG{'HUP'} = 'DEFAULT'; + $SIG{'INT'} = 'DEFAULT'; + $SIG{'HUP'} = 'DEFAULT'; $SIG{'PIPE'} = 'DEFAULT'; $SIG{'TERM'} = 'DEFAULT'; # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; close $rfh; # Unset the close-on-exec flag, so that the file descriptor does not # get closed when we exec - my $flags = fcntl( $wfh, F_GETFD, 0 ) or error "fcntl F_GETFD: $!"; - fcntl($wfh, F_SETFD, $flags & ~FD_CLOEXEC ) or error "fcntl F_SETFD: $!"; + my $flags = fcntl($wfh, F_GETFD, 0) or error "fcntl F_GETFD: $!"; + fcntl($wfh, F_SETFD, $flags & ~FD_CLOEXEC) + or error "fcntl F_SETFD: $!"; my $fd = fileno $wfh; # redirect stderr to stdout so that we can capture it open(STDERR, '>&', STDOUT) or error "cannot open STDOUT: $!"; @@ -614,7 +626,8 @@ sub run_progress { chdir $chdir or error "failed chdir() to $chdir: $!"; } eval 'Devel::Cover::set_coverage("none")' if $is_covering; - exec { $execargs[0] } @execargs or error 'cannot exec() ' . (join ' ', @execargs); + exec { $execargs[0] } @execargs + or error 'cannot exec() ' . (join ' ', @execargs); } close $wfh; @@ -624,13 +637,14 @@ sub run_progress { my $pid2 = fork() // error "failed to fork(): $!"; if ($pid2 == 0) { # child: default signal handlers - $SIG{'INT'} = 'IGNORE'; - $SIG{'HUP'} = 'IGNORE'; + $SIG{'INT'} = 'IGNORE'; + $SIG{'HUP'} = 'IGNORE'; $SIG{'PIPE'} = 'IGNORE'; $SIG{'TERM'} = 'IGNORE'; # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; print_progress 0.0; while (my $line = <$rfh>) { @@ -646,15 +660,16 @@ sub run_progress { # parent: ignore signals # by using "local", the original is automatically restored once the # function returns - local $SIG{'INT'} = $ignore; - local $SIG{'HUP'} = $ignore; + local $SIG{'INT'} = $ignore; + local $SIG{'HUP'} = $ignore; local $SIG{'PIPE'} = $ignore; local $SIG{'TERM'} = $ignore; # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; - my $output = ''; + my $output = ''; my $has_error = 0; while (my $line = <$pipe>) { $has_error = $line_has_error->($line); @@ -685,41 +700,43 @@ sub run_progress { if ($verbosity_level >= 1) { print STDERR $output; } - error ((join ' ', $get_exec->('<$fd>')) . ' failed'); + error((join ' ', $get_exec->('<$fd>')) . ' failed'); } } sub run_dpkg_progress { my $options = shift; - my @debs = @{$options->{PKGS} // []}; - my $get_exec = sub { return @{$options->{ARGV}}, "--status-fd=$_[0]", @debs; }; + my @debs = @{ $options->{PKGS} // [] }; + my $get_exec + = sub { return @{ $options->{ARGV} }, "--status-fd=$_[0]", @debs; }; my $line_has_error = sub { return 0; }; - my $num = 0; + my $num = 0; # each package has one install and one configure step, thus the total # number is twice the number of packages - my $total = (scalar @debs) * 2; + my $total = (scalar @debs) * 2; my $line_handler = sub { if ($_[0] =~ /^processing: (install|configure): /) { $num += 1; } - return $num/$total*100; + return $num / $total * 100; }; run_progress $get_exec, $line_handler, $line_has_error; } sub run_apt_progress { - my $options = shift; - my @debs = @{$options->{PKGS} // []}; + my $options = shift; + my @debs = @{ $options->{PKGS} // [] }; my $get_exec = sub { return ( - @{$options->{ARGV}}, + @{ $options->{ARGV} }, "-oAPT::Status-Fd=$_[0]", # prevent apt from messing up the terminal and allow dpkg to # receive SIGINT and quit immediately without waiting for # maintainer script to finish '-oDpkg::Use-Pty=false', @debs - )}; + ); + }; my $line_has_error = sub { return 0; }; if ($options->{FIND_APT_WARNINGS}) { $line_has_error = sub { @@ -741,7 +758,7 @@ sub run_apt_progress { } sub run_chroot(&$) { - my $cmd = shift; + my $cmd = shift; my $options = shift; my @cleanup_tasks = (); @@ -757,8 +774,8 @@ sub run_chroot(&$) { } }; - local $SIG{INT} = $cleanup; - local $SIG{HUP} = $cleanup; + local $SIG{INT} = $cleanup; + local $SIG{HUP} = $cleanup; local $SIG{PIPE} = $cleanup; local $SIG{TERM} = $cleanup; @@ -768,15 +785,17 @@ sub run_chroot(&$) { # more like a real one by creating or bind-mounting the device # nodes foreach my $file (@devfiles) { - my ($fname, $mode, $type, $linkname, $devmajor, $devminor) = @{$file}; + my ($fname, $mode, $type, $linkname, $devmajor, $devminor) + = @{$file}; next if $fname eq './dev/'; - if ($type == 0) { # normal file + if ($type == 0) { # normal file error "type 0 not implemented"; - } elsif ($type == 1) { # hardlink + } elsif ($type == 1) { # hardlink error "type 1 not implemented"; - } elsif ($type == 2) { # symlink + } elsif ($type == 2) { # symlink if (!$options->{havemknod}) { - if ($options->{mode} eq 'fakechroot' and $linkname =~ /^\/proc/) { + if ( $options->{mode} eq 'fakechroot' + and $linkname =~ /^\/proc/) { # there is no /proc in fakechroot mode next; } @@ -785,73 +804,103 @@ sub run_chroot(&$) { ('root', 'unshare') ) { push @cleanup_tasks, sub { - unlink "$options->{root}/$fname" or warn "cannot unlink $fname: $!"; + unlink "$options->{root}/$fname" + or warn "cannot unlink $fname: $!"; } } - symlink $linkname, "$options->{root}/$fname" or error "cannot create symlink $fname"; + symlink $linkname, "$options->{root}/$fname" + or error "cannot create symlink $fname"; } } elsif ($type == 3 or $type == 4) { # character/block special if (!$options->{havemknod}) { - open my $fh, '>', "$options->{root}/$fname" or error "cannot open $options->{root}/$fname: $!"; + open my $fh, '>', "$options->{root}/$fname" + or error "cannot open $options->{root}/$fname: $!"; close $fh; if ($options->{mode} eq 'unshare') { push @cleanup_tasks, sub { - 0 == system('umount', '--no-mtab', "$options->{root}/$fname") or warn "umount $fname failed: $?"; - unlink "$options->{root}/$fname" or warn "cannot unlink $fname: $!"; + 0 == system('umount', '--no-mtab', + "$options->{root}/$fname") + or warn "umount $fname failed: $?"; + unlink "$options->{root}/$fname" + or warn "cannot unlink $fname: $!"; }; } elsif ($options->{mode} eq 'root') { push @cleanup_tasks, sub { - 0 == system('umount', "$options->{root}/$fname") or warn "umount failed: $?"; - unlink "$options->{root}/$fname" or warn "cannot unlink $fname: $!"; + 0 == system('umount', + "$options->{root}/$fname") + or warn "umount failed: $?"; + unlink "$options->{root}/$fname" + or warn "cannot unlink $fname: $!"; }; } else { error "unknown mode: $options->{mode}"; } - 0 == system('mount', '-o', 'bind', "/$fname", "$options->{root}/$fname") or error "mount $fname failed: $?"; + 0 == system('mount', '-o', 'bind', "/$fname", + "$options->{root}/$fname") + or error "mount $fname failed: $?"; } - } elsif ($type == 5) { # directory + } elsif ($type == 5) { # directory if (!$options->{havemknod}) { if ( any { $_ eq $options->{mode} } ('root', 'unshare') ) { push @cleanup_tasks, sub { - rmdir "$options->{root}/$fname" or warn "cannot rmdir $fname: $!"; + rmdir "$options->{root}/$fname" + or warn "cannot rmdir $fname: $!"; } } if (-e "$options->{root}/$fname") { - if (! -d "$options->{root}/$fname") { + if (!-d "$options->{root}/$fname") { error "$fname already exists but is not a" . " directory"; } } else { - my $num_created = make_path "$options->{root}/$fname", {error => \my $err}; + my $num_created + = make_path "$options->{root}/$fname", + { error => \my $err }; if ($err && @$err) { - error (join "; ", (map {"cannot create " . (join ": ", %{$_})} @$err)); + error( + join "; ", + ( + map { + "cannot create " + . (join ": ", %{$_}) + } @$err + )); } elsif ($num_created == 0) { error "cannot create $options->{root}/$fname"; } } - chmod $mode, "$options->{root}/$fname" or error "cannot chmod $fname: $!"; + chmod $mode, "$options->{root}/$fname" + or error "cannot chmod $fname: $!"; } if ($options->{mode} eq 'unshare') { push @cleanup_tasks, sub { - 0 == system('umount', '--no-mtab', "$options->{root}/$fname") or warn "umount $fname failed: $?"; + 0 == system('umount', '--no-mtab', + "$options->{root}/$fname") + or warn "umount $fname failed: $?"; }; } elsif ($options->{mode} eq 'root') { push @cleanup_tasks, sub { - 0 == system('umount', "$options->{root}/$fname") or warn "umount $fname failed: $?"; + 0 == system('umount', "$options->{root}/$fname") + or warn "umount $fname failed: $?"; }; } else { error "unknown mode: $options->{mode}"; } - 0 == system('mount', '-o', 'bind', "/$fname", "$options->{root}/$fname") or error "mount $fname failed: $?"; + 0 == system('mount', '-o', 'bind', "/$fname", + "$options->{root}/$fname") + or error "mount $fname failed: $?"; } else { error "unsupported type: $type"; } } - } elsif (any { $_ eq $options->{mode} } ('proot', 'fakechroot', 'chrootless')) { + } elsif ( + any { $_ eq $options->{mode} } + ('proot', 'fakechroot', 'chrootless') + ) { # we cannot mount in fakechroot and proot mode # in proot mode we have /dev bind-mounted already through # --bind=/dev @@ -863,9 +912,13 @@ sub run_chroot(&$) { # to extract those if ($options->{mode} eq 'root') { push @cleanup_tasks, sub { - 0 == system('umount', "$options->{root}/sys") or warn "umount /sys failed: $?"; + 0 == system('umount', "$options->{root}/sys") + or warn "umount /sys failed: $?"; }; - 0 == system('mount', '-t', 'sysfs', '-o', 'nosuid,nodev,noexec', 'sys', "$options->{root}/sys") or error "mount /sys failed: $?"; + 0 == system( + 'mount', '-t', 'sysfs', '-o', + 'nosuid,nodev,noexec', 'sys', "$options->{root}/sys" + ) or error "mount /sys failed: $?"; } elsif ($options->{mode} eq 'unshare') { # naturally we have to clean up after ourselves in sudo mode where # we do a real mount. But we also need to unmount in unshare mode @@ -875,15 +928,21 @@ sub run_chroot(&$) { push @cleanup_tasks, sub { # since we cannot write to /etc/mtab we need --no-mtab # unmounting /sys only seems to be successful with --lazy - 0 == system('umount', '--no-mtab', '--lazy', "$options->{root}/sys") or warn "umount /sys failed: $?"; + 0 == system('umount', '--no-mtab', '--lazy', + "$options->{root}/sys") + or warn "umount /sys failed: $?"; }; # without the network namespace unshared, we cannot mount a new # sysfs. Since we need network, we just bind-mount. # # we have to rbind because just using bind results in "wrong fs # type, bad option, bad superblock" error - 0 == system('mount', '-o', 'rbind', '/sys', "$options->{root}/sys") or error "mount /sys failed: $?"; - } elsif (any { $_ eq $options->{mode} } ('proot', 'fakechroot', 'chrootless')) { + 0 == system('mount', '-o', 'rbind', '/sys', "$options->{root}/sys") + or error "mount /sys failed: $?"; + } elsif ( + any { $_ eq $options->{mode} } + ('proot', 'fakechroot', 'chrootless') + ) { # we cannot mount in fakechroot and proot mode # in proot mode we have /proc bind-mounted already through # --bind=/proc @@ -894,12 +953,17 @@ sub run_chroot(&$) { push @cleanup_tasks, sub { # some maintainer scripts mount additional stuff into /proc # which we need to unmount beforehand - if (is_mountpoint("$options->{root}/proc/sys/fs/binfmt_misc")) { - 0 == system('umount', "$options->{root}/proc/sys/fs/binfmt_misc") or error "umount /proc/sys/fs/binfmt_misc failed: $?"; + if (is_mountpoint("$options->{root}/proc/sys/fs/binfmt_misc")) + { + 0 == system('umount', + "$options->{root}/proc/sys/fs/binfmt_misc") + or error "umount /proc/sys/fs/binfmt_misc failed: $?"; } - 0 == system('umount', "$options->{root}/proc") or error "umount /proc failed: $?"; + 0 == system('umount', "$options->{root}/proc") + or error "umount /proc failed: $?"; }; - 0 == system('mount', '-t', 'proc', 'proc', "$options->{root}/proc") or error "mount /proc failed: $?"; + 0 == system('mount', '-t', 'proc', 'proc', "$options->{root}/proc") + or error "mount /proc failed: $?"; } elsif ($options->{mode} eq 'unshare') { # naturally we have to clean up after ourselves in sudo mode where # we do a real mount. But we also need to unmount in unshare mode @@ -908,10 +972,15 @@ sub run_chroot(&$) { # mount target (the directory) push @cleanup_tasks, sub { # since we cannot write to /etc/mtab we need --no-mtab - 0 == system('umount', '--no-mtab', "$options->{root}/proc") or error "umount /proc failed: $?"; + 0 == system('umount', '--no-mtab', "$options->{root}/proc") + or error "umount /proc failed: $?"; }; - 0 == system('mount', '-t', 'proc', 'proc', "$options->{root}/proc") or error "mount /proc failed: $?"; - } elsif (any { $_ eq $options->{mode} } ('proot', 'fakechroot', 'chrootless')) { + 0 == system('mount', '-t', 'proc', 'proc', "$options->{root}/proc") + or error "mount /proc failed: $?"; + } elsif ( + any { $_ eq $options->{mode} } + ('proot', 'fakechroot', 'chrootless') + ) { # we cannot mount in fakechroot and proot mode # in proot mode we have /sys bind-mounted already through # --bind=/sys @@ -922,11 +991,13 @@ sub run_chroot(&$) { # prevent daemons from starting # the directory might not exist in custom variant, for example if (-d "$options->{root}/usr/sbin/") { - open my $fh, '>', "$options->{root}/usr/sbin/policy-rc.d" or error "cannot open policy-rc.d: $!"; + open my $fh, '>', "$options->{root}/usr/sbin/policy-rc.d" + or error "cannot open policy-rc.d: $!"; print $fh "#!/bin/sh\n"; print $fh "exit 101\n"; close $fh; - chmod 0755, "$options->{root}/usr/sbin/policy-rc.d" or error "cannot chmod policy-rc.d: $!"; + chmod 0755, "$options->{root}/usr/sbin/policy-rc.d" + or error "cannot chmod policy-rc.d: $!"; } # the file might not exist if it was removed in a hook @@ -935,23 +1006,32 @@ sub run_chroot(&$) { error "$options->{root}/sbin/start-stop-daemon.REAL already" . " exists"; } - move("$options->{root}/sbin/start-stop-daemon", "$options->{root}/sbin/start-stop-daemon.REAL") or error "cannot move start-stop-daemon: $!"; - open my $fh, '>', "$options->{root}/sbin/start-stop-daemon" or error "cannot open start-stop-daemon: $!"; + move( + "$options->{root}/sbin/start-stop-daemon", + "$options->{root}/sbin/start-stop-daemon.REAL" + ) or error "cannot move start-stop-daemon: $!"; + open my $fh, '>', "$options->{root}/sbin/start-stop-daemon" + or error "cannot open start-stop-daemon: $!"; print $fh "#!/bin/sh\n"; print $fh "echo \"Warning: Fake start-stop-daemon called, doing" . " nothing\">&2\n"; close $fh; - chmod 0755, "$options->{root}/sbin/start-stop-daemon" or error "cannot chmod start-stop-daemon: $!"; + chmod 0755, "$options->{root}/sbin/start-stop-daemon" + or error "cannot chmod start-stop-daemon: $!"; } &{$cmd}(); # cleanup if (-e "$options->{root}/sbin/start-stop-daemon.REAL") { - move("$options->{root}/sbin/start-stop-daemon.REAL", "$options->{root}/sbin/start-stop-daemon") or error "cannot move start-stop-daemon: $!"; + move( + "$options->{root}/sbin/start-stop-daemon.REAL", + "$options->{root}/sbin/start-stop-daemon" + ) or error "cannot move start-stop-daemon: $!"; } if (-e "$options->{root}/usr/sbin/policy-rc.d") { - unlink "$options->{root}/usr/sbin/policy-rc.d" or error "cannot unlink policy-rc.d: $!"; + unlink "$options->{root}/usr/sbin/policy-rc.d" + or error "cannot unlink policy-rc.d: $!"; } }; @@ -967,20 +1047,23 @@ sub run_chroot(&$) { } sub run_hooks($$) { - my $name = shift; + my $name = shift; my $options = shift; - if (scalar @{$options->{"${name}_hook"}} == 0) { + if (scalar @{ $options->{"${name}_hook"} } == 0) { return; } my $runner = sub { - foreach my $script (@{$options->{"${name}_hook"}}) { - if ($script =~ /^(copy-in|copy-out|tar-in|tar-out|upload|download) /) { + foreach my $script (@{ $options->{"${name}_hook"} }) { + if ($script + =~ /^(copy-in|copy-out|tar-in|tar-out|upload|download) /) { info "running special hook: $script"; - if (any { $_ eq $options->{variant} } ('extract', 'custom') - and any { $_ eq $options->{mode} } ('fakechroot', 'proot') - and $name ne 'setup') { + if ( + any { $_ eq $options->{variant} } ('extract', 'custom') + and any { $_ eq $options->{mode} } + ('fakechroot', 'proot') and $name ne 'setup' + ) { info "the copy-in, copy-out, tar-in and tar-out commands" . " in fakechroot mode or proot mode might fail in" . " extract and custom variants because there might be" @@ -992,34 +1075,45 @@ sub run_hooks($$) { # whatever the script writes on stdout is sent to the # socket # whatever is written to the socket, send to stdin - open(STDOUT, '>&', $options->{hooksock}) or error "cannot open STDOUT: $!"; - open(STDIN, '<&', $options->{hooksock}) or error "cannot open STDIN: $!"; + open(STDOUT, '>&', $options->{hooksock}) + or error "cannot open STDOUT: $!"; + open(STDIN, '<&', $options->{hooksock}) + or error "cannot open STDIN: $!"; # we execute ourselves under sh to avoid having to # implement a clever parser of the quoting used in $script # for the filenames my $prefix = ""; - if($is_covering) { - $prefix = "$EXECUTABLE_NAME -MDevel::Cover=-silent,-nogcov "; + if ($is_covering) { + $prefix + = "$EXECUTABLE_NAME -MDevel::Cover=-silent,-nogcov "; } exec 'sh', '-c', "$prefix$PROGRAM_NAME --hook-helper" . " \"\$1\" \"\$2\" \"\$3\" \"\$4\" \"\$5\" $script", - 'exec', $options->{root}, $options->{mode}, $name, (defined $options->{qemu} ? "qemu-$options->{qemu}" : 'env', $verbosity_level); + 'exec', $options->{root}, $options->{mode}, $name, + ( + defined $options->{qemu} + ? "qemu-$options->{qemu}" + : 'env', + $verbosity_level + ); } waitpid($pid, 0); $? == 0 or error "special hook failed with exit code $?"; - } elsif ( -x $script || $script !~ m/[^\w@\%+=:,.\/-]/a) { + } elsif (-x $script || $script !~ m/[^\w@\%+=:,.\/-]/a) { info "running --$name-hook directly: $script $options->{root}"; # execute it directly if it's an executable file # or if it there are no shell metacharacters # (the /a regex modifier makes \w match only ASCII) - 0 == system($script, $options->{root}) or error "command failed: $script"; + 0 == system($script, $options->{root}) + or error "command failed: $script"; } else { info "running --$name-hook in shell: sh -c '$script' exec" . " $options->{root}"; # otherwise, wrap everything in sh -c - 0 == system('sh', '-c', $script, 'exec', $options->{root}) or error "command failed: $script"; + 0 == system('sh', '-c', $script, 'exec', $options->{root}) + or error "command failed: $script"; } } }; @@ -1053,13 +1147,14 @@ sub setup { } } - my ($conf, $tmpfile) = tempfile(UNLINK => 1) or error "cannot open apt.conf: $!"; + my ($conf, $tmpfile) = tempfile(UNLINK => 1) + or error "cannot open apt.conf: $!"; print $conf "Apt::Architecture \"$options->{nativearch}\";\n"; # the host system might have configured additional architectures # force only the native architecture - if (scalar @{$options->{foreignarchs}} > 0) { + if (scalar @{ $options->{foreignarchs} } > 0) { print $conf "Apt::Architectures { \"$options->{nativearch}\"; "; - foreach my $arch (@{$options->{foreignarchs}}) { + foreach my $arch (@{ $options->{foreignarchs} }) { print $conf "\"$arch\"; "; } print $conf "};\n"; @@ -1068,7 +1163,8 @@ sub setup { } print $conf "Dir \"$options->{root}\";\n"; # not needed anymore for apt 1.3 and newer - print $conf "Dir::State::Status \"$options->{root}/var/lib/dpkg/status\";\n"; + print $conf + "Dir::State::Status \"$options->{root}/var/lib/dpkg/status\";\n"; # for authentication, use the keyrings from the host print $conf "Dir::Etc::Trusted \"$options->{apttrusted}\";\n"; print $conf "Dir::Etc::TrustedParts \"$options->{apttrustedparts}\";\n"; @@ -1088,27 +1184,35 @@ sub setup { close $conf; { - my @directories = ('/etc/apt/apt.conf.d', '/etc/apt/sources.list.d', - '/etc/apt/preferences.d', '/var/cache/apt', + my @directories = ( + '/etc/apt/apt.conf.d', '/etc/apt/sources.list.d', + '/etc/apt/preferences.d', '/var/cache/apt', '/var/lib/apt/lists/partial', '/var/lib/dpkg', - '/etc/dpkg/dpkg.cfg.d/'); + '/etc/dpkg/dpkg.cfg.d/' + ); # if dpkg and apt operate from the outside we need some more # directories because dpkg and apt might not even be installed inside # the chroot if ($options->{mode} eq 'chrootless') { - push @directories, ('/var/log/apt', '/var/lib/dpkg/triggers', + push @directories, + ( + '/var/log/apt', '/var/lib/dpkg/triggers', '/var/lib/dpkg/info', '/var/lib/dpkg/alternatives', - '/var/lib/dpkg/updates'); + '/var/lib/dpkg/updates' + ); } foreach my $dir (@directories) { if (-e "$options->{root}/$dir") { - if (! -d "$options->{root}/$dir") { + if (!-d "$options->{root}/$dir") { error "$dir already exists but is not a directory"; } } else { - my $num_created = make_path "$options->{root}/$dir", {error => \my $err}; + my $num_created = make_path "$options->{root}/$dir", + { error => \my $err }; if ($err && @$err) { - error (join "; ", (map {"cannot create " . (join ": ", %{$_})} @$err)); + error( + join "; ", + (map { "cannot create " . (join ": ", %{$_}) } @$err)); } elsif ($num_created == 0) { error "cannot create $options->{root}/$dir"; } @@ -1122,28 +1226,32 @@ sub setup { # The config filename is chosen such that any settings in it will be # overridden by what the user specified with --aptopt. { - open my $fh, '>', "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap" or error "cannot open /etc/apt/apt.conf.d/00mmdebstrap: $!"; + open my $fh, '>', "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap" + or error "cannot open /etc/apt/apt.conf.d/00mmdebstrap: $!"; print $fh "Apt::Install-Recommends false;\n"; print $fh "Acquire::Languages \"none\";\n"; close $fh; } { - open my $fh, '>', "$options->{root}/var/lib/dpkg/status" or error "failed to open(): $!"; + open my $fh, '>', "$options->{root}/var/lib/dpkg/status" + or error "failed to open(): $!"; close $fh; } # /var/lib/dpkg/available is required to exist or otherwise package # removals will fail { - open my $fh, '>', "$options->{root}/var/lib/dpkg/available" or error "failed to open(): $!"; + open my $fh, '>', "$options->{root}/var/lib/dpkg/available" + or error "failed to open(): $!"; close $fh; } # /var/lib/dpkg/cmethopt is used by dselect # see #930788 { - open my $fh, '>', "$options->{root}/var/lib/dpkg/cmethopt" or error "failed to open(): $!"; + open my $fh, '>', "$options->{root}/var/lib/dpkg/cmethopt" + or error "failed to open(): $!"; print $fh "apt apt\n"; close $fh; } @@ -1152,20 +1260,25 @@ sub setup { # than the native architecture in the chroot or if chrootless mode is # used to create a chroot of a different architecture than the native # architecture outside the chroot. - chomp (my $hostarch = `dpkg --print-architecture`); - if (scalar @{$options->{foreignarchs}} > 0 or ( - $options->{mode} eq 'chrootless' and $hostarch ne $options->{nativearch})) { - open my $fh, '>', "$options->{root}/var/lib/dpkg/arch" or error "cannot open /var/lib/dpkg/arch: $!"; + chomp(my $hostarch = `dpkg --print-architecture`); + if ( + scalar @{ $options->{foreignarchs} } > 0 + or ( $options->{mode} eq 'chrootless' + and $hostarch ne $options->{nativearch}) + ) { + open my $fh, '>', "$options->{root}/var/lib/dpkg/arch" + or error "cannot open /var/lib/dpkg/arch: $!"; print $fh "$options->{nativearch}\n"; - foreach my $arch (@{$options->{foreignarchs}}) { + foreach my $arch (@{ $options->{foreignarchs} }) { print $fh "$arch\n"; } close $fh; } - if (scalar @{$options->{aptopts}} > 0) { - open my $fh, '>', "$options->{root}/etc/apt/apt.conf.d/99mmdebstrap" or error "cannot open /etc/apt/apt.conf.d/99mmdebstrap: $!"; - foreach my $opt (@{$options->{aptopts}}) { + if (scalar @{ $options->{aptopts} } > 0) { + open my $fh, '>', "$options->{root}/etc/apt/apt.conf.d/99mmdebstrap" + or error "cannot open /etc/apt/apt.conf.d/99mmdebstrap: $!"; + foreach my $opt (@{ $options->{aptopts} }) { if (-r $opt) { # flush handle because copy() uses syswrite() which bypasses # buffered IO @@ -1184,11 +1297,12 @@ sub setup { close $fh; } - if (scalar @{$options->{dpkgopts}} > 0) { + if (scalar @{ $options->{dpkgopts} } > 0) { # FIXME: in chrootless mode, dpkg will only read the configuration # from the host - open my $fh, '>', "$options->{root}/etc/dpkg/dpkg.cfg.d/99mmdebstrap" or error "cannot open /etc/dpkg/dpkg.cfg.d/99mmdebstrap: $!"; - foreach my $opt (@{$options->{dpkgopts}}) { + open my $fh, '>', "$options->{root}/etc/dpkg/dpkg.cfg.d/99mmdebstrap" + or error "cannot open /etc/dpkg/dpkg.cfg.d/99mmdebstrap: $!"; + foreach my $opt (@{ $options->{dpkgopts} }) { if (-r $opt) { # flush handle because copy() uses syswrite() which bypasses # buffered IO @@ -1214,60 +1328,78 @@ sub setup { #} { - open my $fh, '>', "$options->{root}/etc/fstab" or error "cannot open fstab: $!"; + open my $fh, '>', "$options->{root}/etc/fstab" + or error "cannot open fstab: $!"; print $fh "# UNCONFIGURED FSTAB FOR BASE SYSTEM\n"; close $fh; - chmod 0644, "$options->{root}/etc/fstab" or error "cannot chmod fstab: $!"; + chmod 0644, "$options->{root}/etc/fstab" + or error "cannot chmod fstab: $!"; } # write /etc/apt/sources.list { - open my $fh, '>', "$options->{root}/etc/apt/sources.list" or error "cannot open /etc/apt/sources.list: $!"; + open my $fh, '>', "$options->{root}/etc/apt/sources.list" + or error "cannot open /etc/apt/sources.list: $!"; print $fh $options->{sourceslist}; close $fh; } # allow network access from within if (-e "/etc/resolv.conf") { - copy("/etc/resolv.conf", "$options->{root}/etc/resolv.conf") or error "cannot copy /etc/resolv.conf: $!"; + copy("/etc/resolv.conf", "$options->{root}/etc/resolv.conf") + or error "cannot copy /etc/resolv.conf: $!"; } else { warning("Host system does not have a /etc/resolv.conf to copy into the" - . " rootfs.") + . " rootfs."); } if (-e "/etc/hostname") { - copy("/etc/hostname", "$options->{root}/etc/hostname") or error "cannot copy /etc/hostname: $!"; + copy("/etc/hostname", "$options->{root}/etc/hostname") + or error "cannot copy /etc/hostname: $!"; } else { warning("Host system does not have a /etc/hostname to copy into the" - . " rootfs.") + . " rootfs."); } if ($options->{havemknod}) { foreach my $file (@devfiles) { - my ($fname, $mode, $type, $linkname, $devmajor, $devminor) = @{$file}; - if ($type == 0) { # normal file + my ($fname, $mode, $type, $linkname, $devmajor, $devminor) + = @{$file}; + if ($type == 0) { # normal file error "type 0 not implemented"; - } elsif ($type == 1) { # hardlink + } elsif ($type == 1) { # hardlink error "type 1 not implemented"; - } elsif ($type == 2) { # symlink - if ($options->{mode} eq 'fakechroot' and $linkname =~ /^\/proc/) { + } elsif ($type == 2) { # symlink + if ( $options->{mode} eq 'fakechroot' + and $linkname =~ /^\/proc/) { # there is no /proc in fakechroot mode next; } - symlink $linkname, "$options->{root}/$fname" or error "cannot create symlink $fname"; - next; # chmod cannot work on symlinks - } elsif ($type == 3) { # character special - 0 == system('mknod', "$options->{root}/$fname", 'c', $devmajor, $devminor) or error "mknod failed: $?"; - } elsif ($type == 4) { # block special - 0 == system('mknod', "$options->{root}/$fname", 'b', $devmajor, $devminor) or error "mknod failed: $?"; - } elsif ($type == 5) { # directory + symlink $linkname, "$options->{root}/$fname" + or error "cannot create symlink $fname"; + next; # chmod cannot work on symlinks + } elsif ($type == 3) { # character special + 0 == system('mknod', "$options->{root}/$fname", 'c', + $devmajor, $devminor) + or error "mknod failed: $?"; + } elsif ($type == 4) { # block special + 0 == system('mknod', "$options->{root}/$fname", 'b', + $devmajor, $devminor) + or error "mknod failed: $?"; + } elsif ($type == 5) { # directory if (-e "$options->{root}/$fname") { - if (! -d "$options->{root}/$fname") { + if (!-d "$options->{root}/$fname") { error "$fname already exists but is not a directory"; } } else { - my $num_created = make_path "$options->{root}/$fname", {error => \my $err}; + my $num_created = make_path "$options->{root}/$fname", + { error => \my $err }; if ($err && @$err) { - error (join "; ", (map {"cannot create " . (join ": ", %{$_})} @$err)); + error( + join "; ", + ( + map { "cannot create " . (join ": ", %{$_}) } + @$err + )); } elsif ($num_created == 0) { error "cannot create $options->{root}/$fname"; } @@ -1275,7 +1407,8 @@ sub setup { } else { error "unsupported type: $type"; } - chmod $mode, "$options->{root}/$fname" or error "cannot chmod $fname: $!"; + chmod $mode, "$options->{root}/$fname" + or error "cannot chmod $fname: $!"; } } @@ -1292,10 +1425,14 @@ sub setup { # to disable apt sandboxing. if ($options->{mode} eq 'root') { my $partial = '/var/lib/apt/lists/partial'; - if (system('/usr/lib/apt/apt-helper', 'drop-privs', '--', 'test', '-r', "$options->{root}$partial") != 0) { + if ( + system('/usr/lib/apt/apt-helper', 'drop-privs', '--', 'test', + '-r', "$options->{root}$partial") != 0 + ) { warning "Download is performed unsandboxed as root as file" . " $options->{root}$partial couldn't be accessed by user _apt"; - open my $fh, '>>', $tmpfile or error "cannot open $tmpfile for appending: $!"; + open my $fh, '>>', $tmpfile + or error "cannot open $tmpfile for appending: $!"; print $fh "APT::Sandbox::User \"root\";\n"; close $fh; } @@ -1312,14 +1449,19 @@ sub setup { run_hooks('setup', $options); info "running apt-get update..."; - run_apt_progress({ ARGV => ['apt-get', 'update'], - CHDIR => $options->{root}, - FIND_APT_WARNINGS => 1 }); + run_apt_progress({ + ARGV => ['apt-get', 'update'], + CHDIR => $options->{root}, + FIND_APT_WARNINGS => 1 + }); # check if anything was downloaded at all { - open my $fh, '-|', 'apt-get', 'indextargets' // error "failed to fork(): $!"; - chomp (my $indextargets = do { local $/; <$fh> }); + open my $fh, '-|', 'apt-get', + 'indextargets' // error "failed to fork(): $!"; + chomp( + my $indextargets = do { local $/; <$fh> } + ); close $fh; if ($indextargets eq '') { info "content of /etc/apt/sources.list:"; @@ -1331,7 +1473,7 @@ sub setup { } my @pkgs_to_install; - for my $incl (@{$options->{include}}) { + for my $incl (@{ $options->{include} }) { for my $pkg (split /[,\s]+/, $incl) { # strip leading and trailing whitespace $pkg =~ s/^\s+|\s+$//g; @@ -1340,7 +1482,7 @@ sub setup { next; } # do not append component if it's already in the list - if (any {$_ eq $pkg} @pkgs_to_install) { + if (any { $_ eq $pkg } @pkgs_to_install) { next; } push @pkgs_to_install, $pkg; @@ -1358,9 +1500,10 @@ sub setup { if (any { $_ eq $options->{variant} } ('extract', 'custom')) { info "downloading packages with apt..."; run_apt_progress({ - ARGV => ['apt-get', '--yes', - '-oApt::Get::Download-Only=true', - 'install'], + ARGV => [ + 'apt-get', '--yes', + '-oApt::Get::Download-Only=true', 'install' + ], PKGS => [@pkgs_to_install], }); } elsif ($options->{variant} eq 'apt') { @@ -1379,19 +1522,29 @@ sub setup { # right mind would even suggest depending on it!") info "downloading packages with apt..."; run_apt_progress({ - ARGV => ['apt-get', '--yes', - '-oApt::Get::Download-Only=true', - 'dist-upgrade'], + ARGV => [ + 'apt-get', '--yes', + '-oApt::Get::Download-Only=true', 'dist-upgrade' + ], }); - } elsif (any { $_ eq $options->{variant} } ('essential', 'standard', 'important', 'required', 'buildd', 'minbase')) { + } elsif ( + any { $_ eq $options->{variant} } ( + 'essential', 'standard', 'important', 'required', 'buildd', + 'minbase' + ) + ) { my %ess_pkgs; - open(my $pipe_apt, '-|', 'apt-get', 'indextargets', '--format', '$(FILENAME)', 'Created-By: Packages') or error "cannot start apt-get indextargets: $!"; + open(my $pipe_apt, '-|', 'apt-get', 'indextargets', '--format', + '$(FILENAME)', 'Created-By: Packages') + or error "cannot start apt-get indextargets: $!"; while (my $fname = <$pipe_apt>) { chomp $fname; - open (my $pipe_cat, '-|', '/usr/lib/apt/apt-helper', 'cat-file', $fname) or error "cannot start apt-helper cat-file: $!"; + open(my $pipe_cat, '-|', '/usr/lib/apt/apt-helper', 'cat-file', + $fname) + or error "cannot start apt-helper cat-file: $!"; my $pkgname; - my $ess = ''; + my $ess = ''; my $prio = 'optional'; my $arch = ''; while (my $line = <$pipe_cat>) { @@ -1403,7 +1556,7 @@ sub setup { if ($line =~ /^Package: (.*)/) { $pkgname = $1; } elsif ($line =~ /^Essential: yes$/) { - $ess = 'yes' + $ess = 'yes'; } elsif ($line =~ /^Priority: (.*)/) { $prio = $1; } elsif ($line =~ /^Architecture: (.*)/) { @@ -1421,16 +1574,27 @@ sub setup { } elsif ($options->{variant} eq 'essential') { # for this variant we are only interested in the # essential packages - } elsif (any { $_ eq $options->{variant} } ('standard', 'important', 'required', 'buildd', 'minbase')) { + } elsif ( + any { $_ eq $options->{variant} } ( + 'standard', 'important', 'required', 'buildd', + 'minbase' + ) + ) { if ($prio eq 'optional' or $prio eq 'extra') { # always ignore packages of priority optional and # extra } elsif ($prio eq 'standard') { - if (none { $_ eq $options->{variant} } ('important', 'required', 'buildd', 'minbase')) { + if ( + none { $_ eq $options->{variant} } + ('important', 'required', 'buildd', 'minbase') + ) { push @pkgs_to_install, $pkgname; } } elsif ($prio eq 'important') { - if (none { $_ eq $options->{variant} } ('required', 'buildd', 'minbase')) { + if ( + none { $_ eq $options->{variant} } + ('required', 'buildd', 'minbase') + ) { push @pkgs_to_install, $pkgname; } } elsif ($prio eq 'required') { @@ -1446,7 +1610,7 @@ sub setup { } # reset values undef $pkgname; - $ess = ''; + $ess = ''; $prio = 'optional'; $arch = ''; } @@ -1464,9 +1628,10 @@ sub setup { info "downloading packages with apt..."; run_apt_progress({ - ARGV => ['apt-get', '--yes', - '-oApt::Get::Download-Only=true', - 'install'], + ARGV => [ + 'apt-get', '--yes', + '-oApt::Get::Download-Only=true', 'install' + ], PKGS => [keys %ess_pkgs], }); } else { @@ -1477,13 +1642,14 @@ sub setup { my @essential_pkgs; { my $apt_archives = "/var/cache/apt/archives/"; - opendir my $dh, "$options->{root}/$apt_archives" or error "cannot read $apt_archives"; + opendir my $dh, "$options->{root}/$apt_archives" + or error "cannot read $apt_archives"; while (my $deb = readdir $dh) { if ($deb !~ /\.deb$/) { next; } $deb = "$apt_archives/$deb"; - if (! -f "$options->{root}/$deb") { + if (!-f "$options->{root}/$deb") { next; } push @essential_pkgs, $deb; @@ -1493,10 +1659,13 @@ sub setup { if (scalar @essential_pkgs == 0) { # check if a file:// URI was used - open(my $pipe_apt, '-|', 'apt-get', 'indextargets', '--format', '$(URI)', 'Created-By: Packages') or error "cannot start apt-get indextargets: $!"; + open(my $pipe_apt, '-|', 'apt-get', 'indextargets', '--format', + '$(URI)', 'Created-By: Packages') + or error "cannot start apt-get indextargets: $!"; while (my $uri = <$pipe_apt>) { if ($uri =~ /^file:\/\//) { - error "nothing got downloaded -- use copy:// instead of file://"; + error + "nothing got downloaded -- use copy:// instead of file://"; } } error "nothing got downloaded"; @@ -1506,13 +1675,14 @@ sub setup { # chrootless mode and extract variant or in any other mode. # In other words, the only scenario in which the @essential_pkgs are not # extracted are in chrootless mode in any other than the extract variant. - if ($options->{mode} eq 'chrootless' and $options->{variant} ne 'extract') { + if ($options->{mode} eq 'chrootless' and $options->{variant} ne 'extract') + { # nothing to do } else { info "extracting archives..."; print_progress 0.0; my $counter = 0; - my $total = scalar @essential_pkgs; + my $total = scalar @essential_pkgs; foreach my $deb (@essential_pkgs) { $counter += 1; # not using dpkg-deb --extract as that would replace the @@ -1531,13 +1701,14 @@ sub setup { debug( "running tar -C $options->{root}" . " --keep-directory-symlink --extract --file -"); eval 'Devel::Cover::set_coverage("none")' if $is_covering; - exec 'tar', '-C', $options->{root}, '--keep-directory-symlink', '--extract', '--file', '-'; + exec 'tar', '-C', $options->{root}, + '--keep-directory-symlink', '--extract', '--file', '-'; } waitpid($pid1, 0); $? == 0 or error "dpkg-deb --fsys-tarfile failed: $?"; waitpid($pid2, 0); $? == 0 or error "tar --extract failed: $?"; - print_progress ($counter/$total*100); + print_progress($counter / $total * 100); } print_progress "done"; } @@ -1552,7 +1723,8 @@ sub setup { '-oDPkg::Options::=--force-not-root', '-oDPkg::Options::=--force-script-chrootless', '-oDPkg::Options::=--root=' . $options->{root}, - '-oDPkg::Options::=--log=' . "$options->{root}/var/log/dpkg.log"); + '-oDPkg::Options::=--log=' . "$options->{root}/var/log/dpkg.log" + ); if (defined $options->{qemu}) { # The binfmt support on the outside is used, so qemu needs to know # where it has to look for shared libraries @@ -1567,56 +1739,71 @@ sub setup { # nothing to do } else { run_apt_progress({ - ARGV => ['apt-get', '--yes', - @chrootless_opts, - 'install'], - PKGS => [map { "$options->{root}/$_" } @essential_pkgs], - }); + ARGV => ['apt-get', '--yes', @chrootless_opts, 'install'], + PKGS => [map { "$options->{root}/$_" } @essential_pkgs], + }); } if (any { $_ eq $options->{variant} } ('extract', 'custom')) { # nothing to do - } elsif (any { $_ eq $options->{variant} } ('essential', 'apt', 'standard', 'important', 'required', 'buildd', 'minbase')) { + } elsif ( + any { $_ eq $options->{variant} } ( + 'essential', 'apt', 'standard', 'important', + 'required', 'buildd', 'minbase' + ) + ) { # run essential hooks run_hooks('essential', $options); if (scalar @pkgs_to_install > 0) { run_apt_progress({ - ARGV => ['apt-get', '--yes', - @chrootless_opts, - 'install'], - PKGS => [@pkgs_to_install], - }); + ARGV => + ['apt-get', '--yes', @chrootless_opts, 'install'], + PKGS => [@pkgs_to_install], + }); } } else { error "unknown variant: $options->{variant}"; } - } elsif (any { $_ eq $options->{mode} } ('root', 'unshare', 'fakechroot', 'proot')) { + } elsif ( + any { $_ eq $options->{mode} } + ('root', 'unshare', 'fakechroot', 'proot') + ) { if (any { $_ eq $options->{variant} } ('extract')) { # nothing to do - } elsif (any { $_ eq $options->{variant} } ('custom', 'essential', 'apt', 'standard', 'important', 'required', 'buildd', 'minbase')) { + } elsif ( + any { $_ eq $options->{variant} } ( + 'custom', 'essential', 'apt', 'standard', + 'important', 'required', 'buildd', 'minbase' + ) + ) { if ($options->{mode} eq 'fakechroot') { # this borrows from and extends # /etc/fakechroot/debootstrap.env and # /etc/fakechroot/chroot.env { my @fakechrootsubst = (); - foreach my $dir ('/usr/sbin', '/usr/bin', '/sbin', '/bin') { - push @fakechrootsubst, "$dir/chroot=/usr/sbin/chroot.fakechroot"; + foreach my $dir ('/usr/sbin', '/usr/bin', '/sbin', '/bin') + { + push @fakechrootsubst, + "$dir/chroot=/usr/sbin/chroot.fakechroot"; push @fakechrootsubst, "$dir/mkfifo=/bin/true"; push @fakechrootsubst, "$dir/ldconfig=/bin/true"; - push @fakechrootsubst, "$dir/ldd=/usr/bin/ldd.fakechroot"; + push @fakechrootsubst, + "$dir/ldd=/usr/bin/ldd.fakechroot"; push @fakechrootsubst, "$dir/ischroot=/bin/true"; } if (defined $ENV{FAKECHROOT_CMD_SUBST} && $ENV{FAKECHROOT_CMD_SUBST} ne "") { - push @fakechrootsubst, split /:/, $ENV{FAKECHROOT_CMD_SUBST}; + push @fakechrootsubst, split /:/, + $ENV{FAKECHROOT_CMD_SUBST}; } $ENV{FAKECHROOT_CMD_SUBST} = join ':', @fakechrootsubst; } if (defined $ENV{FAKECHROOT_EXCLUDE_PATH} && $ENV{FAKECHROOT_EXCLUDE_PATH} ne "") { - $ENV{FAKECHROOT_EXCLUDE_PATH} = "$ENV{FAKECHROOT_EXCLUDE_PATH}:/dev:/proc:/sys"; + $ENV{FAKECHROOT_EXCLUDE_PATH} + = "$ENV{FAKECHROOT_EXCLUDE_PATH}:/dev:/proc:/sys"; } else { $ENV{FAKECHROOT_EXCLUDE_PATH} = '/dev:/proc:/sys'; } @@ -1625,7 +1812,8 @@ sub setup { $ENV{FAKECHROOT_AF_UNIX_PATH} = "/tmp"; { my @ldsoconf = ('/etc/ld.so.conf'); - opendir(my $dh, '/etc/ld.so.conf.d') or error "Can't opendir(/etc/ld.so.conf.d): $!"; + opendir(my $dh, '/etc/ld.so.conf.d') + or error "Can't opendir(/etc/ld.so.conf.d): $!"; while (my $entry = readdir $dh) { # skip the "." and ".." entries next if $entry eq "."; @@ -1643,7 +1831,8 @@ sub setup { # live in fakechroot, see #917920 push @ldlibpath, "/lib/systemd"; foreach my $fname (@ldsoconf) { - open my $fh, "<", $fname or error "cannot open $fname for reading: $!"; + open my $fh, "<", $fname + or error "cannot open $fname for reading: $!"; while (my $line = <$fh>) { next if $line !~ /^\//; push @ldlibpath, $line; @@ -1658,15 +1847,17 @@ sub setup { # inside the chroot my @chrootcmd = (); if ($options->{mode} eq 'proot') { - push @chrootcmd, ( - 'proot', - '--root-id', - '--bind=/dev', - '--bind=/proc', - '--bind=/sys', - "--rootfs=$options->{root}", - '--cwd=/'); - } elsif (any { $_ eq $options->{mode} } ('root', 'unshare', 'fakechroot')) { + push @chrootcmd, + ( + 'proot', '--root-id', + '--bind=/dev', '--bind=/proc', + '--bind=/sys', "--rootfs=$options->{root}", + '--cwd=/' + ); + } elsif ( + any { $_ eq $options->{mode} } + ('root', 'unshare', 'fakechroot') + ) { push @chrootcmd, ('/usr/sbin/chroot', $options->{root}); } else { error "unknown mode: $options->{mode}"; @@ -1682,26 +1873,33 @@ sub setup { # to know where it has to look for shared libraries if (defined $ENV{QEMU_LD_PREFIX} && $ENV{QEMU_LD_PREFIX} ne "") { - $ENV{QEMU_LD_PREFIX} = "$ENV{QEMU_LD_PREFIX}:$options->{root}"; + $ENV{QEMU_LD_PREFIX} + = "$ENV{QEMU_LD_PREFIX}:$options->{root}"; } else { $ENV{QEMU_LD_PREFIX} = $options->{root}; } # Make sure that the fakeroot and fakechroot shared # libraries exist for the right architecture - open my $fh, '-|', 'dpkg-architecture', '-a', $options->{nativearch}, '-qDEB_HOST_MULTIARCH' // error "failed to fork(): $!"; - chomp (my $deb_host_multiarch = do { local $/; <$fh> }); + open my $fh, '-|', 'dpkg-architecture', '-a', + $options->{nativearch}, + '-qDEB_HOST_MULTIARCH' // error "failed to fork(): $!"; + chomp( + my $deb_host_multiarch = do { local $/; <$fh> } + ); close $fh; if ($? != 0 or !$deb_host_multiarch) { error "dpkg-architecture failed: $?"; } - my $fakechrootdir = "/usr/lib/$deb_host_multiarch/fakechroot"; - if (! -e "$fakechrootdir/libfakechroot.so") { + my $fakechrootdir + = "/usr/lib/$deb_host_multiarch/fakechroot"; + if (!-e "$fakechrootdir/libfakechroot.so") { error "$fakechrootdir/libfakechroot.so doesn't exist." . " Install libfakechroot:$options->{nativearch}" . " outside the chroot"; } - my $fakerootdir = "/usr/lib/$deb_host_multiarch/libfakeroot"; - if (! -e "$fakerootdir/libfakeroot-sysv.so") { + my $fakerootdir + = "/usr/lib/$deb_host_multiarch/libfakeroot"; + if (!-e "$fakerootdir/libfakeroot-sysv.so") { error "$fakerootdir/libfakeroot-sysv.so doesn't exist." . " Install libfakeroot:$options->{nativearch}" . " outside the chroot"; @@ -1713,15 +1911,17 @@ sub setup { } elsif (any { $_ eq $options->{mode} } ('root', 'unshare')) { # other modes require a static qemu-user binary my $qemubin = "/usr/bin/qemu-$options->{qemu}-static"; - if (! -e $qemubin) { + if (!-e $qemubin) { error "cannot find $qemubin"; } - copy $qemubin, "$options->{root}/$qemubin" or error "cannot copy $qemubin: $!"; + copy $qemubin, "$options->{root}/$qemubin" + or error "cannot copy $qemubin: $!"; # File::Copy does not retain permissions but on some # platforms (like Travis CI) the binfmt interpreter must # have the executable bit set or otherwise execve will # fail with EACCES - chmod 0755, "$options->{root}/$qemubin" or error "cannot chmod $qemubin: $!"; + chmod 0755, "$options->{root}/$qemubin" + or error "cannot chmod $qemubin: $!"; } else { error "unknown mode: $options->{mode}"; } @@ -1730,28 +1930,36 @@ sub setup { # some versions of coreutils use the renameat2 system call in mv. # This breaks certain versions of fakechroot and proot. Here we do # a sanity check and warn the user in case things might break. - if (any { $_ eq $options->{mode} } ('fakechroot', 'proot') and -e "$options->{root}/bin/mv") { - mkdir "$options->{root}/000-move-me" or error "cannot create directory: $!"; - my $ret = system @chrootcmd, '/bin/mv', '/000-move-me', '/001-delete-me'; + if (any { $_ eq $options->{mode} } ('fakechroot', 'proot') + and -e "$options->{root}/bin/mv") { + mkdir "$options->{root}/000-move-me" + or error "cannot create directory: $!"; + my $ret = system @chrootcmd, '/bin/mv', '/000-move-me', + '/001-delete-me'; if ($ret != 0) { if ($options->{mode} eq 'proot') { info "the /bin/mv binary inside the chroot doesn't" . " work under proot"; info "this is likely due to missing support for" . " renameat2 in proot"; - info "see https://github.com/proot-me/PRoot/issues/147"; + info + "see https://github.com/proot-me/PRoot/issues/147"; } else { info "the /bin/mv binary inside the chroot doesn't" . " work under fakechroot"; info "with certain versions of coreutils and glibc," . " this is due to missing support for renameat2 in" . " fakechroot"; - info "see https://github.com/dex4er/fakechroot/issues/60"; + info + "see https://github.com/dex4er/fakechroot/issues/60"; } - info "expect package post installation scripts not to work"; - rmdir "$options->{root}/000-move-me" or error "cannot rmdir: $!"; + info + "expect package post installation scripts not to work"; + rmdir "$options->{root}/000-move-me" + or error "cannot rmdir: $!"; } else { - rmdir "$options->{root}/001-delete-me" or error "cannot rmdir: $!"; + rmdir "$options->{root}/001-delete-me" + or error "cannot rmdir: $!"; } } @@ -1762,16 +1970,22 @@ sub setup { info "installing packages..."; run_chroot { run_dpkg_progress({ - ARGV => [@chrootcmd, 'env', '--unset=TMPDIR', - 'dpkg', '--install', '--force-depends'], + ARGV => [ + @chrootcmd, 'env', + '--unset=TMPDIR', 'dpkg', + '--install', '--force-depends' + ], PKGS => \@essential_pkgs, }); - } $options; + } + $options; # if the path-excluded option was added to the dpkg config, # reinstall all packages if (-e "$options->{root}/etc/dpkg/dpkg.cfg.d/99mmdebstrap") { - open(my $fh, '<', "$options->{root}/etc/dpkg/dpkg.cfg.d/99mmdebstrap") or error "cannot open /etc/dpkg/dpkg.cfg.d/99mmdebstrap: $!"; + open(my $fh, '<', + "$options->{root}/etc/dpkg/dpkg.cfg.d/99mmdebstrap") + or error "cannot open /etc/dpkg/dpkg.cfg.d/99mmdebstrap: $!"; my $num_matches = grep /^path-exclude=/, <$fh>; close $fh; if ($num_matches > 0) { @@ -1779,15 +1993,19 @@ sub setup { # packages even though they are already installed info "re-installing packages because of path-exclude..."; run_dpkg_progress({ - ARGV => [@chrootcmd, 'env', '--unset=TMPDIR', - 'dpkg', '--install', '--force-depends'], + ARGV => [ + @chrootcmd, 'env', + '--unset=TMPDIR', 'dpkg', + '--install', '--force-depends' + ], PKGS => \@essential_pkgs, }); } } foreach my $deb (@essential_pkgs) { - unlink "$options->{root}/$deb" or error "cannot unlink $deb: $!"; + unlink "$options->{root}/$deb" + or error "cannot unlink $deb: $!"; } # run essential hooks @@ -1795,7 +2013,8 @@ sub setup { run_hooks('essential', $options); } - if ($options->{variant} ne 'custom' and scalar @pkgs_to_install > 0) { + if ($options->{variant} ne 'custom' + and scalar @pkgs_to_install > 0) { # some packages have to be installed from the outside before # anything can be installed from the inside. # @@ -1818,17 +2037,21 @@ sub setup { # since apt will be run inside the chroot, make sure that # apt-transport-https and ca-certificates gets installed first # if any mirror is a https URI - open(my $pipe_apt, '-|', 'apt-get', 'indextargets', '--format', '$(URI)', 'Created-By: Packages') or error "cannot start apt-get indextargets: $!"; + open(my $pipe_apt, '-|', 'apt-get', 'indextargets', + '--format', '$(URI)', 'Created-By: Packages') + or error "cannot start apt-get indextargets: $!"; while (my $uri = <$pipe_apt>) { if ($uri =~ /^https:\/\//) { # FIXME: support for https is part of apt >= 1.5 - push @pkgs_to_install_from_outside, 'apt-transport-https'; + push @pkgs_to_install_from_outside, + 'apt-transport-https'; push @pkgs_to_install_from_outside, 'ca-certificates'; last; } elsif ($uri =~ /^tor(\+[a-z]+)*:\/\//) { # tor URIs can be tor+http://, tor+https:// or even # tor+mirror+file:// - push @pkgs_to_install_from_outside, 'apt-transport-tor'; + push @pkgs_to_install_from_outside, + 'apt-transport-tor'; last; } } @@ -1836,22 +2059,25 @@ sub setup { $? == 0 or error "apt-get indextargets failed"; if (scalar @pkgs_to_install_from_outside > 0) { - info 'downloading ' . (join ', ', @pkgs_to_install_from_outside) . "..."; + info 'downloading ' + . (join ', ', @pkgs_to_install_from_outside) . "..."; run_apt_progress({ - ARGV => ['apt-get', '--yes', - '-oApt::Get::Download-Only=true', - 'install'], + ARGV => [ + 'apt-get', '--yes', + '-oApt::Get::Download-Only=true', 'install' + ], PKGS => [@pkgs_to_install_from_outside], }); my @debs_to_install; my $apt_archives = "/var/cache/apt/archives/"; - opendir my $dh, "$options->{root}/$apt_archives" or error "cannot read $apt_archives"; + opendir my $dh, "$options->{root}/$apt_archives" + or error "cannot read $apt_archives"; while (my $deb = readdir $dh) { if ($deb !~ /\.deb$/) { next; } $deb = "$apt_archives/$deb"; - if (! -f "$options->{root}/$deb") { + if (!-f "$options->{root}/$deb") { next; } push @debs_to_install, $deb; @@ -1864,14 +2090,19 @@ sub setup { # we need --force-depends because dpkg does not take # Pre-Depends into account and thus doesn't install # them in the right order - info 'installing ' . (join ', ', @pkgs_to_install_from_outside) . "..."; + info 'installing ' + . (join ', ', @pkgs_to_install_from_outside) . "..."; run_dpkg_progress({ - ARGV => [@chrootcmd, 'env', '--unset=TMPDIR', - 'dpkg', '--install', '--force-depends'], + ARGV => [ + @chrootcmd, 'env', + '--unset=TMPDIR', 'dpkg', + '--install', '--force-depends' + ], PKGS => \@debs_to_install, }); foreach my $deb (@debs_to_install) { - unlink "$options->{root}/$deb" or error "cannot unlink $deb: $!"; + unlink "$options->{root}/$deb" + or error "cannot unlink $deb: $!"; } } } @@ -1879,13 +2110,16 @@ sub setup { run_chroot { info "installing remaining packages inside the chroot..."; run_apt_progress({ - ARGV => [@chrootcmd, 'env', - '--unset=APT_CONFIG', - '--unset=TMPDIR', - 'apt-get', '--yes', 'install'], + ARGV => [ + @chrootcmd, 'env', + '--unset=APT_CONFIG', '--unset=TMPDIR', + 'apt-get', '--yes', + 'install' + ], PKGS => [@pkgs_to_install], }); - } $options; + } + $options; } } else { @@ -1898,31 +2132,41 @@ sub setup { run_hooks('customize', $options); # clean up temporary configuration file - unlink "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap" or error "failed to unlink /etc/apt/apt.conf.d/00mmdebstrap: $!"; + unlink "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap" + or error "failed to unlink /etc/apt/apt.conf.d/00mmdebstrap: $!"; info "cleaning package lists and apt cache..."; run_apt_progress({ - ARGV => ['apt-get', - '--option', 'Dir::Etc::SourceList=/dev/null', - '--option', 'Dir::Etc::SourceParts=/dev/null', - 'update'], + ARGV => [ + 'apt-get', '--option', + 'Dir::Etc::SourceList=/dev/null', '--option', + 'Dir::Etc::SourceParts=/dev/null', 'update' + ], CHDIR => $options->{root}, }); - run_apt_progress({ ARGV => ['apt-get', 'clean'], CHDIR => $options->{root} }); + run_apt_progress( + { ARGV => ['apt-get', 'clean'], CHDIR => $options->{root} }); # 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") { - rmdir "$options->{root}/var/lib/apt/lists/auxfiles" or die "cannot rmdir /var/lib/apt/lists/auxfiles: $!"; + rmdir "$options->{root}/var/lib/apt/lists/auxfiles" + or die "cannot rmdir /var/lib/apt/lists/auxfiles: $!"; } - if (defined $options->{qemu} and any { $_ eq $options->{mode} } ('root', 'unshare')) { - unlink "$options->{root}/usr/bin/qemu-$options->{qemu}-static" or error "cannot unlink /usr/bin/qemu-$options->{qemu}-static: $!"; + if (defined $options->{qemu} + and any { $_ eq $options->{mode} } ('root', 'unshare')) { + unlink "$options->{root}/usr/bin/qemu-$options->{qemu}-static" + or error "cannot unlink /usr/bin/qemu-$options->{qemu}-static: $!"; } # clean up certain files to make output reproducible - foreach my $fname ('/var/log/dpkg.log', '/var/log/apt/history.log', '/var/log/apt/term.log', '/var/log/alternatives.log', '/var/cache/ldconfig/aux-cache', '/var/log/apt/eipp.log.xz') { + foreach my $fname ( + '/var/log/dpkg.log', '/var/log/apt/history.log', + '/var/log/apt/term.log', '/var/log/alternatives.log', + '/var/cache/ldconfig/aux-cache', '/var/log/apt/eipp.log.xz' + ) { my $path = "$options->{root}$fname"; - if (! -e $path) { + if (!-e $path) { next; } unlink $path or error "cannot unlink $path: $!"; @@ -1930,13 +2174,14 @@ sub setup { # remove any possible leftovers in /tmp but warn about it if (-d "$options->{root}/tmp") { - opendir(my $dh, "$options->{root}/tmp") or error "Can't opendir($options->{root}/tmp): $!"; + opendir(my $dh, "$options->{root}/tmp") + or error "Can't opendir($options->{root}/tmp): $!"; while (my $entry = readdir $dh) { # skip the "." and ".." entries next if $entry eq "."; next if $entry eq ".."; warning "deleting files in /tmp: $entry"; - remove_tree("$options->{root}/tmp/$entry", {error => \my $err}); + remove_tree("$options->{root}/tmp/$entry", { error => \my $err }); if (@$err) { for my $diag (@$err) { my ($file, $message) = %$diag; @@ -1958,12 +2203,12 @@ sub setup { # messages from process outside unshared namespace to the inside # okthx -- success sub checkokthx { - my $fh = shift; - my $ret = read ($fh, my $buf, 2+5) // error "cannot read from socket: $!"; + my $fh = shift; + my $ret = read($fh, my $buf, 2 + 5) // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } my ($len, $msg) = unpack("nA5", $buf); if ($msg ne "okthx") { error "expected okthx but got: $msg"; } - if ($len != 0) { error "expected no payload but got $len bytes"; } + if ($len != 0) { error "expected no payload but got $len bytes"; } } sub main() { @@ -1987,7 +2232,7 @@ sub main() { # path that is valid on the outside -- fakechroot and proot have their # own reasons, see below my @cmdprefix = (); - my @tarcmd = ('tar'); + my @tarcmd = ('tar'); if ($hook eq 'setup') { if ($mode eq 'proot') { # since we cannot run tar inside the chroot under proot during @@ -1998,7 +2243,7 @@ sub main() { # owned by the root user. push @tarcmd, '--owner=root', '--group=root'; } - } elsif (any { $_ eq $hook} ('essential', 'customize')) { + } elsif (any { $_ eq $hook } ('essential', 'customize')) { if ($mode eq 'fakechroot') { # Fakechroot requires tar to run inside the chroot or # otherwise absolute symlinks will include the path to the @@ -2007,7 +2252,8 @@ sub main() { } elsif ($mode eq 'proot') { # proot requires tar to run inside proot or otherwise # permissions will be completely off - push @cmdprefix, 'proot', '--root-id', "--rootfs=$root", '--cwd=/', "--qemu=$qemu"; + push @cmdprefix, 'proot', '--root-id', "--rootfs=$root", + '--cwd=/', "--qemu=$qemu"; } elsif (any { $_ eq $mode } ('root', 'chrootless', 'unshare')) { push @cmdprefix, '/usr/sbin/chroot', $root; } else { @@ -2017,20 +2263,20 @@ sub main() { error "unknown hook: $hook"; } - if (any { $_ eq $command} ('copy-in', 'tar-in', 'upload')) { + if (any { $_ eq $command } ('copy-in', 'tar-in', 'upload')) { if (scalar @ARGV < 9) { error "copy-in and tar-in need at least one path on the" . " outside and the output path inside the chroot"; } my $outpath = $ARGV[-1]; - for (my $i = 7; $i < $#ARGV; $i++) { + for (my $i = 7 ; $i < $#ARGV ; $i++) { # the right argument for tar's --directory argument depends on # whether tar is called from inside the chroot or from the # outside my $directory; if ($hook eq 'setup') { $directory = "$root/$outpath"; - } elsif (any { $_ eq $hook} ('essential', 'customize')) { + } elsif (any { $_ eq $hook } ('essential', 'customize')) { $directory = $outpath; } else { error "unknown hook: $hook"; @@ -2045,24 +2291,29 @@ sub main() { my $fh; if ($command eq 'upload') { # open the requested file for writing - open $fh, '|-', @cmdprefix, 'sh', '-c', 'cat > "$1"', 'exec', $directory // error "failed to fork(): $!"; + open $fh, '|-', @cmdprefix, 'sh', '-c', 'cat > "$1"', + 'exec', $directory // error "failed to fork(): $!"; } else { # open a tar process that extracts the tarfile that we # supply it with on stdin to the output directory inside # the chroot - open $fh, '|-', @cmdprefix, @tarcmd, '--directory', $directory, '--extract', '--file', '-' // error "failed to fork(): $!"; + open $fh, '|-', @cmdprefix, @tarcmd, '--directory', + $directory, '--extract', '--file', + '-' // error "failed to fork(): $!"; } if ($command eq 'copy-in') { # instruct the parent process to create a tarball of the # requested path outside the chroot debug "sending mktar"; - print STDOUT (pack("n", length $ARGV[$i]) . "mktar" . $ARGV[$i]); + print STDOUT ( + pack("n", length $ARGV[$i]) . "mktar" . $ARGV[$i]); } else { # instruct parent process to open a tarball of the # requested path outside the chroot for reading debug "sending openr"; - print STDOUT (pack("n", length $ARGV[$i]) . "openr" . $ARGV[$i]); + print STDOUT ( + pack("n", length $ARGV[$i]) . "openr" . $ARGV[$i]); } STDOUT->flush(); debug "waiting for okthx"; @@ -2071,9 +2322,10 @@ sub main() { # handle "write" messages from the parent process and feed # their payload into the tar process until a "close" message # is encountered - while(1) { + while (1) { # receive the next message - my $ret = read (STDIN, my $buf, 2+5) // error "cannot read from socket: $!"; + my $ret = read(STDIN, my $buf, 2 + 5) + // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } @@ -2085,27 +2337,32 @@ sub main() { error "expected no payload but got $len bytes"; } debug "sending okthx"; - print STDOUT (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print STDOUT (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; STDOUT->flush(); last; } elsif ($msg ne "write") { # we should not receive this message at this point - print STDOUT (pack("n", 0) . "error") or error "cannot write to socket: $!"; + print STDOUT (pack("n", 0) . "error") + or error "cannot write to socket: $!"; STDOUT->flush(); error "expected write but got: $msg"; } # read the payload my $content; { - my $ret = read (STDIN, $content, $len) // error "error cannot read from socket: $!"; + my $ret = read(STDIN, $content, $len) + // error "error cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } } # write the payload to the tar process - print $fh $content or error "cannot write to tar process: $!"; + print $fh $content + or error "cannot write to tar process: $!"; debug "sending okthx"; - print STDOUT (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print STDOUT (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; STDOUT->flush(); } close $fh; @@ -2113,20 +2370,20 @@ sub main() { error "tar failed"; } } - } elsif (any { $_ eq $command} ('copy-out', 'tar-out', 'download')) { + } elsif (any { $_ eq $command } ('copy-out', 'tar-out', 'download')) { if (scalar @ARGV < 9) { error "copy-out needs at least one path inside the chroot and" . " the output path on the outside"; } my $outpath = $ARGV[-1]; - for (my $i = 7; $i < $#ARGV; $i++) { + for (my $i = 7 ; $i < $#ARGV ; $i++) { # the right argument for tar's --directory argument depends on # whether tar is called from inside the chroot or from the # outside my $directory; if ($hook eq 'setup') { $directory = "$root/$ARGV[$i]"; - } elsif (any { $_ eq $hook} ('essential', 'customize')) { + } elsif (any { $_ eq $hook } ('essential', 'customize')) { $directory = $ARGV[$i]; } else { error "unknown hook: $hook"; @@ -2141,25 +2398,30 @@ sub main() { my $fh; if ($command eq 'download') { # open the requested file for reading - open $fh, '-|', @cmdprefix, 'sh', '-c', 'cat "$1"', 'exec', $directory // error "failed to fork(): $!"; + open $fh, '-|', @cmdprefix, 'sh', '-c', 'cat "$1"', + 'exec', $directory // error "failed to fork(): $!"; } else { # Open a tar process that creates a tarfile of everything # in the requested directory inside the chroot and writes # it to stdout. To emulate the behaviour of cp, change to # the dirname of the requested path first. - open $fh, '-|', @cmdprefix, @tarcmd, '--directory', dirname($directory), '--create', '--file', '-', basename($directory) // error "failed to fork(): $!"; + open $fh, '-|', @cmdprefix, @tarcmd, '--directory', + dirname($directory), '--create', '--file', '-', + basename($directory) // error "failed to fork(): $!"; } if ($command eq 'copy-out') { # instruct the parent process to extract a tarball to a # certain path outside the chroot debug "sending untar"; - print STDOUT (pack("n", length $outpath) . "untar" . $outpath); + print STDOUT ( + pack("n", length $outpath) . "untar" . $outpath); } else { # instruct parent process to open a tarball of the # requested path outside the chroot for writing debug "sending openw"; - print STDOUT (pack("n", length $outpath) . "openw" . $outpath); + print STDOUT ( + pack("n", length $outpath) . "openw" . $outpath); } STDOUT->flush(); debug "waiting for okthx"; @@ -2169,7 +2431,8 @@ sub main() { # process while (1) { # read from tar - my $ret = read ($fh, my $cont, 4096) // error "cannot read from pipe: $!"; + my $ret = read($fh, my $cont, 4096) + // error "cannot read from pipe: $!"; if ($ret == 0) { last; } debug "sending write"; # send to parent @@ -2201,54 +2464,55 @@ sub main() { my $mtime = time; if (exists $ENV{SOURCE_DATE_EPOCH}) { - $mtime = $ENV{SOURCE_DATE_EPOCH}+0; + $mtime = $ENV{SOURCE_DATE_EPOCH} + 0; } - $ENV{DEBIAN_FRONTEND} = 'noninteractive'; + $ENV{DEBIAN_FRONTEND} = 'noninteractive'; $ENV{DEBCONF_NONINTERACTIVE_SEEN} = 'true'; - $ENV{LC_ALL} = 'C.UTF-8'; - $ENV{LANGUAGE} = 'C.UTF-8'; - $ENV{LANG} = 'C.UTF-8'; + $ENV{LC_ALL} = 'C.UTF-8'; + $ENV{LANGUAGE} = 'C.UTF-8'; + $ENV{LANG} = 'C.UTF-8'; # copy ARGV because getopt modifies it my @ARGVORIG = @ARGV; - chomp (my $hostarch = `dpkg --print-architecture`); + chomp(my $hostarch = `dpkg --print-architecture`); my $options = { - components => ["main"], - variant => "important", - include => [], - architectures => [$hostarch], - mode => 'auto', - dpkgopts => [], - aptopts => [], - apttrusted => "/etc/apt/trusted.gpg", + components => ["main"], + variant => "important", + include => [], + architectures => [$hostarch], + mode => 'auto', + dpkgopts => [], + aptopts => [], + apttrusted => "/etc/apt/trusted.gpg", apttrustedparts => "/etc/apt/trusted.gpg.d", - noop => [], - setup_hook => [], - essential_hook => [], - customize_hook => [], + noop => [], + setup_hook => [], + essential_hook => [], + customize_hook => [], }; my $logfile = undef; - Getopt::Long::Configure ('default', 'bundling', 'auto_abbrev', 'ignore_case_always'); + Getopt::Long::Configure('default', 'bundling', 'auto_abbrev', + 'ignore_case_always'); GetOptions( - 'h|help' => sub { pod2usage(-exitval => 0, -verbose => 1) }, - 'man' => sub { pod2usage(-exitval => 0, -verbose => 2) }, - 'version' => sub { print STDOUT "mmdebstrap $VERSION\n"; exit 0; }, - 'components=s@' => \$options->{components}, - 'variant=s' => \$options->{variant}, - 'include=s@' => \$options->{include}, + 'h|help' => sub { pod2usage(-exitval => 0, -verbose => 1) }, + 'man' => sub { pod2usage(-exitval => 0, -verbose => 2) }, + 'version' => sub { print STDOUT "mmdebstrap $VERSION\n"; exit 0; }, + 'components=s@' => \$options->{components}, + 'variant=s' => \$options->{variant}, + 'include=s@' => \$options->{include}, 'architectures=s@' => \$options->{architectures}, - 'mode=s' => \$options->{mode}, - 'dpkgopt=s@' => \$options->{dpkgopts}, - 'aptopt=s@' => \$options->{aptopts}, - 'keyring=s' => sub { + 'mode=s' => \$options->{mode}, + 'dpkgopt=s@' => \$options->{dpkgopts}, + 'aptopt=s@' => \$options->{aptopts}, + 'keyring=s' => sub { my ($opt_name, $opt_value) = @_; if ($opt_value =~ /"/) { error "--keyring: apt cannot handle paths with double quotes:" . " $opt_value"; } - if (! -e $opt_value) { + if (!-e $opt_value) { error "keyring \"$opt_value\" does not exist"; } my $abs_path = abs_path($opt_value); @@ -2263,19 +2527,21 @@ sub main() { $options->{apttrusted} = $opt_value; } }, - 's|silent' => sub { $verbosity_level = 0; }, - 'q|quiet' => sub { $verbosity_level = 0; }, + 's|silent' => sub { $verbosity_level = 0; }, + 'q|quiet' => sub { $verbosity_level = 0; }, 'v|verbose' => sub { $verbosity_level = 2; }, - 'd|debug' => sub { $verbosity_level = 3; }, + 'd|debug' => sub { $verbosity_level = 3; }, 'logfile=s' => \$logfile, # no-op options so that mmdebstrap can be used with # sbuild-createchroot --debootstrap=mmdebstrap - 'resolve-deps' => sub { push @{$options->{noop}}, 'resolve-deps'; }, - 'merged-usr' => sub { push @{$options->{noop}}, 'merged-usr'; }, - 'no-merged-usr' => sub { push @{$options->{noop}}, 'no-merged-usr'; }, - 'force-check-gpg' => sub { push @{$options->{noop}}, 'force-check-gpg'; }, + 'resolve-deps' => sub { push @{ $options->{noop} }, 'resolve-deps'; }, + 'merged-usr' => sub { push @{ $options->{noop} }, 'merged-usr'; }, + 'no-merged-usr' => + sub { push @{ $options->{noop} }, 'no-merged-usr'; }, + 'force-check-gpg' => + sub { push @{ $options->{noop} }, 'force-check-gpg'; }, # hook options are hidden until I'm happy with them - 'setup-hook=s@' => \$options->{setup_hook}, + 'setup-hook=s@' => \$options->{setup_hook}, 'essential-hook=s@' => \$options->{essential_hook}, 'customize-hook=s@' => \$options->{customize_hook}, ) or pod2usage(-exitval => 2, -verbose => 1); @@ -2284,14 +2550,17 @@ sub main() { open(STDERR, '>', $logfile) or error "cannot open $logfile: $!"; } - foreach my $arg (@{$options->{noop}}) { + foreach my $arg (@{ $options->{noop} }) { info "The option --$arg is a no-op. It only exists for compatibility" . " with some debootstrap wrappers."; } - my @valid_variants = ('extract', 'custom', 'essential', 'apt', 'required', - 'minbase', 'buildd', 'important', 'debootstrap', '-', 'standard'); - if (none { $_ eq $options->{variant}} @valid_variants) { + my @valid_variants = ( + 'extract', 'custom', 'essential', 'apt', + 'required', 'minbase', 'buildd', 'important', + 'debootstrap', '-', 'standard' + ); + if (none { $_ eq $options->{variant} } @valid_variants) { error "invalid variant. Choose from " . (join ', ', @valid_variants); } # debootstrap and - are an alias for important @@ -2299,7 +2568,8 @@ sub main() { $options->{variant} = 'important'; } - if ($options->{variant} eq 'essential' and scalar @{$options->{include}} > 0) { + if ($options->{variant} eq 'essential' + and scalar @{ $options->{include} } > 0) { warning "cannot install extra packages with variant essential because" . " apt is missing"; } @@ -2312,8 +2582,8 @@ sub main() { if ($options->{mode} eq 'sudo') { $options->{mode} = 'root'; } - my @valid_modes = ('auto', 'root', 'unshare', 'fakechroot', 'proot', - 'chrootless'); + my @valid_modes + = ('auto', 'root', 'unshare', 'fakechroot', 'proot', 'chrootless'); if (none { $_ eq $options->{mode} } @valid_modes) { error "invalid mode. Choose from " . (join ', ', @valid_modes); } @@ -2356,7 +2626,7 @@ sub main() { # the next fallback is fakechroot # exec ourselves again but within fakechroot my @prefix = (); - if($is_covering) { + if ($is_covering) { @prefix = ($EXECUTABLE_NAME, '-MDevel::Cover=-silent,-nogcov'); } exec 'fakechroot', 'fakeroot', @prefix, $PROGRAM_NAME, @ARGVORIG; @@ -2383,7 +2653,7 @@ sub main() { } else { # exec ourselves again but within fakechroot my @prefix = (); - if($is_covering) { + if ($is_covering) { @prefix = ($EXECUTABLE_NAME, '-MDevel::Cover=-silent,-nogcov'); } exec 'fakechroot', 'fakeroot', @prefix, $PROGRAM_NAME, @ARGVORIG; @@ -2391,8 +2661,11 @@ sub main() { } elsif ($options->{mode} eq 'unshare') { if (!test_unshare(1)) { my $procfile = '/proc/sys/kernel/unprivileged_userns_clone'; - open(my $fh, '<', $procfile) or error "failed to open $procfile: $!"; - chomp(my $content = do { local $/; <$fh> }); + open(my $fh, '<', $procfile) + or error "failed to open $procfile: $!"; + chomp( + my $content = do { local $/; <$fh> } + ); close($fh); if ($content ne "1") { info "/proc/sys/kernel/unprivileged_userns_clone is set to" @@ -2413,7 +2686,7 @@ sub main() { } my @architectures = (); - foreach my $archs (@{$options->{architectures}}) { + foreach my $archs (@{ $options->{architectures} }) { foreach my $arch (split /[,\s]+/, $archs) { # strip leading and trailing whitespace $arch =~ s/^\s+|\s+$//g; @@ -2422,14 +2695,14 @@ sub main() { next; } # do not append component if it's already in the list - if (any {$_ eq $arch} @architectures) { + if (any { $_ eq $arch } @architectures) { next; } push @architectures, $arch; } } - $options->{nativearch} = $hostarch; + $options->{nativearch} = $hostarch; $options->{foreignarchs} = []; if (scalar @architectures == 0) { warning "empty architecture list: falling back to native architecture" @@ -2438,46 +2711,49 @@ sub main() { $options->{nativearch} = $architectures[0]; } else { $options->{nativearch} = $architectures[0]; - push @{$options->{foreignarchs}}, @architectures[1..$#architectures]; + push @{ $options->{foreignarchs} }, + @architectures[1 .. $#architectures]; } debug "Native architecture (outside): $hostarch"; debug "Native architecture (inside): $options->{nativearch}"; - debug ("Foreign architectures (inside): " . (join ', ', @{$options->{foreignarchs}})); + debug("Foreign architectures (inside): " + . (join ', ', @{ $options->{foreignarchs} })); { # FIXME: autogenerate this list my $deb2qemu = { - alpha => 'alpha', - amd64 => 'x86_64', - arm => 'arm', - arm64 => 'aarch64', - armel => 'arm', - armhf => 'arm', - hppa => 'hppa', - i386 => 'i386', - m68k => 'm68k', - mips => 'mips', - mips64 => 'mips64', + alpha => 'alpha', + amd64 => 'x86_64', + arm => 'arm', + arm64 => 'aarch64', + armel => 'arm', + armhf => 'arm', + hppa => 'hppa', + i386 => 'i386', + m68k => 'm68k', + mips => 'mips', + mips64 => 'mips64', mips64el => 'mips64el', - mipsel => 'mipsel', - powerpc => 'ppc', - ppc64 => 'ppc64', - ppc64el => 'ppc64le', - riscv64 => 'riscv64', - s390x => 's390x', - sh4 => 'sh4', - sparc => 'sparc', - sparc64 => 'sparc64', + mipsel => 'mipsel', + powerpc => 'ppc', + ppc64 => 'ppc64', + ppc64el => 'ppc64le', + riscv64 => 'riscv64', + s390x => 's390x', + sh4 => 'sh4', + sparc => 'sparc', + sparc64 => 'sparc64', }; if ($hostarch ne $options->{nativearch}) { my $withemu = 0; - my $noemu = 0; + my $noemu = 0; { my $pid = open my $fh, '-|' // error "failed to fork(): $!"; if ($pid == 0) { { - no warnings; # don't print a warning if the following fails + no warnings + ; # don't print a warning if the following fails exec 'arch-test', $options->{nativearch}; } # if exec didn't work (for example because the arch-test @@ -2487,7 +2763,9 @@ sub main() { . " machine/kernel\n"; exit 1; } - chomp (my $content = do { local $/; <$fh> }); + chomp( + my $content = do { local $/; <$fh> } + ); close $fh; if ($? == 0 and $content eq "$options->{nativearch}: ok") { $withemu = 1; @@ -2497,7 +2775,8 @@ sub main() { my $pid = open my $fh, '-|' // error "failed to fork(): $!"; if ($pid == 0) { { - no warnings; # don't print a warning if the following fails + no warnings + ; # don't print a warning if the following fails exec 'arch-test', '-n', $options->{nativearch}; } # if exec didn't work (for example because the arch-test @@ -2507,7 +2786,9 @@ sub main() { . " machine/kernel\n"; exit 1; } - chomp (my $content = do { local $/; <$fh> }); + chomp( + my $content = do { local $/; <$fh> } + ); close $fh; if ($? == 0 and $content eq "$options->{nativearch}: ok") { $noemu = 1; @@ -2524,7 +2805,8 @@ sub main() { # 1 | 1 | don't use qemu emulation if ($withemu == 0 and $noemu == 0) { { - open my $fh, '<', '/proc/filesystems' or error "failed to open /proc/filesystems: $!"; + open my $fh, '<', '/proc/filesystems' + or error "failed to open /proc/filesystems: $!"; unless (grep /^nodev\tbinfmt_misc$/, (<$fh>)) { warning "binfmt_misc not found in /proc/filesystems --" . " is the module loaded?"; @@ -2532,21 +2814,31 @@ sub main() { close $fh; } { - open my $fh, '<', '/proc/mounts' or error "failed to open /proc/mounts: $!"; - unless (grep /^binfmt_misc \/proc\/sys\/fs\/binfmt_misc binfmt_misc/, (<$fh>)) { + open my $fh, '<', '/proc/mounts' + or error "failed to open /proc/mounts: $!"; + unless ( + grep +/^binfmt_misc \/proc\/sys\/fs\/binfmt_misc binfmt_misc/, + (<$fh>) + ) { warning "binfmt_misc not found in /proc/mounts -- not" . " mounted?"; } close $fh; } { - if (!exists $deb2qemu->{$options->{nativearch}}) { + if (!exists $deb2qemu->{ $options->{nativearch} }) { warning "no mapping from $options->{nativearch} to" . " qemu-user binary"; } else { - my $binfmt_identifier = 'qemu-' . $deb2qemu->{$options->{nativearch}}; - open my $fh, '-|', '/usr/sbin/update-binfmts', '--display', $binfmt_identifier // error "failed to fork(): $!"; - chomp (my $binfmts = do { local $/; <$fh> }); + my $binfmt_identifier + = 'qemu-' . $deb2qemu->{ $options->{nativearch} }; + open my $fh, '-|', '/usr/sbin/update-binfmts', + '--display', + $binfmt_identifier // error "failed to fork(): $!"; + chomp( + my $binfmts = do { local $/; <$fh> } + ); close $fh; if ($binfmts eq '') { warning "$binfmt_identifier is not a supported" @@ -2561,11 +2853,11 @@ sub main() { } elsif ($withemu == 1 and $noemu == 0) { info "$options->{nativearch} cannot be executed, falling back" . " to qemu-user"; - if (!exists $deb2qemu->{$options->{nativearch}}) { + if (!exists $deb2qemu->{ $options->{nativearch} }) { error "no mapping from $options->{nativearch} to qemu-user" . " binary"; } - $options->{qemu} = $deb2qemu->{$options->{nativearch}}; + $options->{qemu} = $deb2qemu->{ $options->{nativearch} }; } elsif ($withemu == 1 and $noemu == 1) { info "$options->{nativearch} is different from $hostarch but" . " can be executed natively"; @@ -2588,19 +2880,20 @@ sub main() { $options->{target} = '-'; } } else { - info "No SUITE specified, expecting sources.list on standard input"; + info + "No SUITE specified, expecting sources.list on standard input"; $options->{target} = '-'; } my $sourceslist = ''; - if (! defined $suite) { + if (!defined $suite) { # If no suite was specified, then the whole sources.list has to # come from standard input info "Reading sources.list from standard input..."; $sourceslist = do { local $/; }; } else { my @components = (); - foreach my $comp (@{$options->{components}}) { + foreach my $comp (@{ $options->{components} }) { my @comps = split /[,\s]+/, $comp; foreach my $c (@comps) { # strip leading and trailing whitespace @@ -2610,7 +2903,7 @@ sub main() { next; } # do not append component if it's already in the list - if (any {$_ eq $c} @components) { + if (any { $_ eq $c } @components) { next; } push @components, $c; @@ -2624,23 +2917,58 @@ sub main() { { # try to guess the right keyring path for the given suite my $keyring; - if (any {$_ eq $suite} ('potato', 'woody', 'sarge', 'etch', 'lenny', 'squeeze', 'wheezy')) { - $keyring = '/usr/share/keyrings/debian-archive-removed-keys.gpg'; - } elsif (any {$_ eq $suite} ('aequorea', 'bartholomea', 'chromodoris', 'dasyatis')) { - $keyring = '/usr/share/keyrings/tanglu-archive-keyring.gpg'; - } elsif (any {$_ eq $suite} ('kali-dev', 'kali-rolling', 'kali-bleeding-edge')) { + if ( + any { $_ eq $suite } ( + 'potato', 'woody', 'sarge', 'etch', + 'lenny', 'squeeze', 'wheezy' + ) + ) { + $keyring + = '/usr/share/keyrings/debian-archive-removed-keys.gpg'; + } elsif ( + any { $_ eq $suite } + ('aequorea', 'bartholomea', 'chromodoris', 'dasyatis') + ) { + $keyring + = '/usr/share/keyrings/tanglu-archive-keyring.gpg'; + } elsif ( + any { $_ eq $suite } + ('kali-dev', 'kali-rolling', 'kali-bleeding-edge') + ) { $keyring = '/usr/share/keyrings/kali-archive-keyring.gpg'; - } elsif (any {$_ eq $suite} ('trusty', 'xenial', 'zesty', 'artful', 'bionic', 'cosmic')) { - $keyring = '/usr/share/keyrings/ubuntu-archive-keyring.gpg'; - } elsif (any {$_ eq $suite} ('unstable', 'stable', 'oldstable', 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm')) { - $keyring = '/usr/share/keyrings/debian-archive-keyring.gpg'; + } elsif ( + any { $_ eq $suite } ( + 'trusty', 'xenial', 'zesty', 'artful', 'bionic', + 'cosmic' + ) + ) { + $keyring + = '/usr/share/keyrings/ubuntu-archive-keyring.gpg'; + } elsif ( + any { $_ eq $suite } ( + 'unstable', 'stable', 'oldstable', 'jessie', + 'stretch', 'buster', 'bullseye', 'bookworm' + ) + ) { + $keyring + = '/usr/share/keyrings/debian-archive-keyring.gpg'; } # we can only check if we need the signed-by entry if we u # automatically chosen keyring exists if (defined $keyring && -e $keyring) { # we can only check key material if gpg is installed - my $gpghome = tempdir("mmdebstrap.gpghome.XXXXXXXXXXXX", TMPDIR => 1, CLEANUP => 1); - my @gpgcmd = ('gpg', '--quiet', '--ignore-time-conflict', '--no-options', '--no-default-keyring', '--homedir', $gpghome, '--no-auto-check-trustdb', '--trust-model', 'always'); + my $gpghome = tempdir( + "mmdebstrap.gpghome.XXXXXXXXXXXX", + TMPDIR => 1, + CLEANUP => 1 + ); + my @gpgcmd = ( + 'gpg', '--quiet', + '--ignore-time-conflict', '--no-options', + '--no-default-keyring', '--homedir', + $gpghome, '--no-auto-check-trustdb', + '--trust-model', 'always' + ); my ($ret, $fh, $message); { # change warning handler to prevent message @@ -2653,10 +2981,12 @@ sub main() { if ($? == 0 && defined $ret && !defined $message) { # find all the fingerprints of the keys apt currently # knows about - my @aptfingerprints = (); + my @aptfingerprints = (); my $collect_fingerprints = sub { my $filename = shift; - open my $fh, '-|', @gpgcmd, '--keyring', $filename, '--with-colons', '--list-keys' // error "failed to fork(): $!"; + open my $fh, '-|', @gpgcmd, '--keyring', + $filename, '--with-colons', + '--list-keys' // error "failed to fork(): $!"; while (my $line = <$fh>) { if ($line !~ /^fpr:::::::::([^:]+):/) { next; @@ -2665,12 +2995,14 @@ sub main() { } close $fh; }; - opendir my $dh, "$options->{apttrustedparts}" or error "cannot read $options->{apttrustedparts}"; + opendir my $dh, "$options->{apttrustedparts}" + or error "cannot read $options->{apttrustedparts}"; while (my $filename = readdir $dh) { if ($filename !~ /\.(asc|gpg)$/) { next; } - $collect_fingerprints->("$options->{apttrustedparts}/$filename"); + $collect_fingerprints->( + "$options->{apttrustedparts}/$filename"); } if (-e $options->{apttrusted}) { $collect_fingerprints->($options->{apttrusted}); @@ -2679,7 +3011,9 @@ sub main() { # guessed are known by apt and only add signed-by # option if that's not the case my @suitefingerprints = (); - open my $suitefh, '-|', @gpgcmd, '--keyring', $keyring, '--with-colons', '--list-keys' // error "failed to fork(): $!"; + open my $suitefh, '-|', @gpgcmd, '--keyring', + $keyring, '--with-colons', + '--list-keys' // error "failed to fork(): $!"; while (my $line = <$suitefh>) { if ($line !~ /^fpr:::::::::([^:]+):/) { next; @@ -2697,14 +3031,17 @@ sub main() { } } else { info "gpg --version failed: cannot determine the right" - . " signed-by value" + . " signed-by value"; } - remove_tree($gpghome, {error => \my $err}); + remove_tree($gpghome, { 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"; } + if ($file eq '') { + warning "general error: $message"; + } else { + warning "problem unlinking $file: $message"; + } } } } @@ -2729,35 +3066,52 @@ sub main() { } } } else { - my @debstable = ('oldoldstable', 'oldstable', 'stable', 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm'); - my @ubuntustable = ('trusty', 'xenial', 'zesty', 'artful', 'bionic', 'cosmic'); - my @tanglustable = ('aequorea', 'bartholomea', 'chromodoris', 'dasyatis'); + my @debstable = ( + 'oldoldstable', 'oldstable', 'stable', 'jessie', + 'stretch', 'buster', 'bullseye', 'bookworm' + ); + my @ubuntustable + = ('trusty', 'xenial', 'zesty', 'artful', 'bionic', + 'cosmic'); + my @tanglustable + = ('aequorea', 'bartholomea', 'chromodoris', 'dasyatis'); my @kali = ('kali-dev', 'kali-rolling', 'kali-bleeding-edge'); - my $mirror = 'http://deb.debian.org/debian'; + my $mirror = 'http://deb.debian.org/debian'; my $secmirror = 'http://security.debian.org/debian-security'; - if (any {$_ eq $suite} @ubuntustable) { - if (any {$_ eq $options->{nativearch}} ('amd64', 'i386')) { - $mirror = 'http://archive.ubuntu.com/ubuntu'; + if (any { $_ eq $suite } @ubuntustable) { + if (any { $_ eq $options->{nativearch} } ('amd64', 'i386')) + { + $mirror = 'http://archive.ubuntu.com/ubuntu'; $secmirror = 'http://security.ubuntu.com/ubuntu'; } else { - $mirror = 'http://ports.ubuntu.com/ubuntu-ports'; + $mirror = 'http://ports.ubuntu.com/ubuntu-ports'; $secmirror = 'http://ports.ubuntu.com/ubuntu-ports'; } - } elsif (any {$_ eq $suite} @tanglustable) { - $mirror = 'http://archive.tanglu.org/tanglu' - } elsif (any {$_ eq $suite} @kali) { - $mirror = 'https://http.kali.org/kali' + } elsif (any { $_ eq $suite } @tanglustable) { + $mirror = 'http://archive.tanglu.org/tanglu'; + } elsif (any { $_ eq $suite } @kali) { + $mirror = 'https://http.kali.org/kali'; } $sourceslist .= "deb$signedby $mirror $suite $compstr\n"; - if (any {$_ eq $suite} @ubuntustable) { - $sourceslist .= "deb$signedby $mirror $suite-updates $compstr\n"; - $sourceslist .= "deb$signedby $secmirror $suite-security $compstr\n"; - } elsif (any {$_ eq $suite} @tanglustable) { - $sourceslist .= "deb$signedby $secmirror $suite-updates $compstr\n"; - } elsif (any {$_ eq $suite} @debstable) { - $sourceslist .= "deb$signedby $mirror $suite-updates $compstr\n"; - if (any {$_ eq $suite} ('oldoldstable', 'oldstable', 'stable', 'jessie', 'stretch', 'buster')) { + if (any { $_ eq $suite } @ubuntustable) { + $sourceslist + .= "deb$signedby $mirror $suite-updates $compstr\n"; + $sourceslist + .= "deb$signedby $secmirror $suite-security $compstr\n"; + } elsif (any { $_ eq $suite } @tanglustable) { + $sourceslist + .= "deb$signedby $secmirror $suite-updates $compstr\n"; + } elsif (any { $_ eq $suite } @debstable) { + $sourceslist + .= "deb$signedby $mirror $suite-updates $compstr\n"; + if ( + any { $_ eq $suite } ( + 'oldoldstable', 'oldstable', + 'stable', 'jessie', + 'stretch', 'buster' + ) + ) { $sourceslist .= "deb$signedby $secmirror $suite/updates" . " $compstr\n"; @@ -2793,21 +3147,27 @@ sub main() { my $tar_compressor = get_tar_compressor($options->{target}); # figure out whether a tarball has to be created in the end - $options->{maketar} = 0; + $options->{maketar} = 0; $options->{makesqfs} = 0; - if (defined $tar_compressor or $options->{target} =~ /\.tar$/ or $options->{target} eq '-') { + if ( defined $tar_compressor + or $options->{target} =~ /\.tar$/ + or $options->{target} eq '-') { $options->{maketar} = 1; # check if the compressor is installed if (defined $tar_compressor) { my $pid = fork() // error "fork() failed: $!"; if ($pid == 0) { - open(STDOUT, '>', '/dev/null') or error "cannot open /dev/null for writing: $!"; - open(STDIN, '<', '/dev/null') or error "cannot open /dev/null for reading: $!"; - exec { $tar_compressor->[0] } @{$tar_compressor} or error ("cannot exec " . (join " ", @{$tar_compressor}) . ": $!"); + open(STDOUT, '>', '/dev/null') + or error "cannot open /dev/null for writing: $!"; + open(STDIN, '<', '/dev/null') + or error "cannot open /dev/null for reading: $!"; + exec { $tar_compressor->[0] } @{$tar_compressor} + or error( + "cannot exec " . (join " ", @{$tar_compressor}) . ": $!"); } waitpid $pid, 0; if ($? != 0) { - error ("failed to start " . (join " ", @{$tar_compressor})); + error("failed to start " . (join " ", @{$tar_compressor})); } } } elsif ($options->{target} =~ /\.(squashfs|sqfs)$/) { @@ -2815,18 +3175,22 @@ sub main() { # check if tar2sqfs is installed my $pid = fork() // error "fork() failed: $!"; if ($pid == 0) { - open(STDOUT, '>', '/dev/null') or error "cannot open /dev/null for writing: $!"; - open(STDIN, '<', '/dev/null') or error "cannot open /dev/null for reading: $!"; - exec ('tar2sqfs', '--version') or error ("cannot exec tar2sqfs --version: $!"); + open(STDOUT, '>', '/dev/null') + or error "cannot open /dev/null for writing: $!"; + open(STDIN, '<', '/dev/null') + or error "cannot open /dev/null for reading: $!"; + exec('tar2sqfs', '--version') + or error("cannot exec tar2sqfs --version: $!"); } waitpid $pid, 0; if ($? != 0) { - error ("failed to start tar2sqfs --version"); + error("failed to start tar2sqfs --version"); } } if ($options->{maketar} or $options->{makesqfs}) { - if (any { $_ eq $options->{variant} } ('extract', 'custom') and any { $_ eq $options->{mode} } ('fakechroot', 'proot')) { + if ( any { $_ eq $options->{variant} } ('extract', 'custom') + and any { $_ eq $options->{mode} } ('fakechroot', 'proot')) { info "creating a tarball or squashfs image in fakechroot mode or" . " proot mode might fail in extract and custom variants because" . " there might be no tar inside the chroot"; @@ -2834,15 +3198,14 @@ sub main() { # try to fail early if target tarball or squashfs image cannot be # opened for writing if ($options->{target} ne '-') { - open my $fh, '>', $options->{target} or error "cannot open $options->{target} for writing: $!"; + open my $fh, '>', $options->{target} + or error "cannot open $options->{target} for writing: $!"; close $fh; } # since the output is a tarball, we create the rootfs in a temporary # directory - $options->{root} = tempdir( - 'mmdebstrap.XXXXXXXXXX', - DIR => File::Spec->tmpdir - ); + $options->{root} + = tempdir('mmdebstrap.XXXXXXXXXX', DIR => File::Spec->tmpdir); info "using $options->{root} as tempdir"; # in unshare and root mode, other users than the current user need to # access the rootfs, most prominently, the _apt user. Thus, make the @@ -2855,7 +3218,7 @@ sub main() { # directly in the supplied directory $options->{root} = $options->{target}; if (-e $options->{root}) { - if (! -d $options->{root}) { + if (!-d $options->{root}) { error "$options->{root} exists and is not a directory"; } # check if the directory is empty or contains nothing more than an @@ -2863,7 +3226,8 @@ sub main() { # ext3 and ext4 partitions. # rationale for requiring an empty directory: # https://bugs.debian.org/833525 - opendir(my $dh, $options->{root}) or error "Can't opendir($options->{root}): $!"; + opendir(my $dh, $options->{root}) + or error "Can't opendir($options->{root}): $!"; while (my $entry = readdir $dh) { # skip the "." and ".." entries next if $entry eq "."; @@ -2890,9 +3254,11 @@ sub main() { } closedir($dh); } else { - my $num_created = make_path "$options->{root}", {error => \my $err}; + my $num_created = make_path "$options->{root}", + { error => \my $err }; if ($err && @$err) { - error (join "; ", (map {"cannot create " . (join ": ", %{$_})} @$err)); + error(join "; ", + (map { "cannot create " . (join ": ", %{$_}) } @$err)); } elsif ($num_created == 0) { error "cannot create $options->{root}"; } @@ -2911,18 +3277,19 @@ sub main() { if ($options->{mode} eq 'unshare') { @idmap = read_subuid_subgid; # sanity check - if (scalar(@idmap) != 2 || $idmap[0][0] ne 'u' || $idmap[1][0] ne 'g') { + if (scalar(@idmap) != 2 || $idmap[0][0] ne 'u' || $idmap[1][0] ne 'g') + { error "invalid idmap"; } - my $outer_gid = $REAL_GROUP_ID+0; + my $outer_gid = $REAL_GROUP_ID + 0; my $pid = get_unshare_cmd { chown 1, 1, $options->{root} } [ ['u', '0', $REAL_USER_ID, '1'], - ['g', '0', $outer_gid, '1'], - ['u', '1', $idmap[0][2], '1'], - ['g', '1', $idmap[1][2], '1']]; + ['g', '0', $outer_gid, '1'], + ['u', '1', $idmap[0][2], '1'], + ['g', '1', $idmap[1][2], '1']]; waitpid $pid, 0; $? == 0 or error "chown failed"; } @@ -2932,10 +3299,14 @@ sub main() { if ($options->{mode} eq 'unshare') { my $pid = get_unshare_cmd { $options->{havemknod} = havemknod($options->{root}); - } \@idmap; + } + \@idmap; waitpid $pid, 0; $? == 0 or error "havemknod failed"; - } elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot', 'chrootless')) { + } elsif ( + any { $_ eq $options->{mode} } + ('root', 'fakechroot', 'proot', 'chrootless') + ) { $options->{havemknod} = havemknod($options->{root}); } else { error "unknown mode: $options->{mode}"; @@ -2945,32 +3316,40 @@ sub main() { # We always craft the /dev entries ourselves if a tarball is to be created if ($options->{maketar} or $options->{makesqfs}) { foreach my $file (@devfiles) { - my ($fname, $mode, $type, $linkname, $devmajor, $devminor) = @{$file}; - my $entry = pack('a100 a8 a8 a8 a12 a12 A8 a1 a100 a8 a32 a32 a8 a8 a155 x12', + my ($fname, $mode, $type, $linkname, $devmajor, $devminor) + = @{$file}; + my $entry = pack( + 'a100 a8 a8 a8 a12 a12 A8 a1 a100 a8 a32 a32 a8 a8 a155 x12', $fname, - sprintf('%07o', $mode), - sprintf('%07o', 0), # uid - sprintf('%07o', 0), # gid - sprintf('%011o', 0), # size + sprintf('%07o', $mode), + sprintf('%07o', 0), # uid + sprintf('%07o', 0), # gid + sprintf('%011o', 0), # size sprintf('%011o', $mtime), - '', # checksum + '', # checksum $type, $linkname, "ustar ", - '', # username - '', # groupname + '', # username + '', # groupname defined($devmajor) ? sprintf('%07o', $devmajor) : '', defined($devminor) ? sprintf('%07o', $devminor) : '', - '', # prefix + '', # prefix ); # compute and insert checksum - substr($entry,148,7) = sprintf("%06o\0", unpack("%16C*",$entry)); + substr($entry, 148, 7) + = sprintf("%06o\0", unpack("%16C*", $entry)); $devtar .= $entry; } } my $exitstatus = 0; - my @taropts = ('--sort=name', "--mtime=\@$mtime", '--clamp-mtime', '--numeric-owner', '--one-file-system', '--xattrs', '-c', '--exclude=./dev'); + my @taropts = ( + '--sort=name', "--mtime=\@$mtime", + '--clamp-mtime', '--numeric-owner', + '--one-file-system', '--xattrs', + '-c', '--exclude=./dev' + ); # disable signals so that we can fork and change behaviour of the signal # handler in the parent and child without getting interrupted @@ -2983,18 +3362,20 @@ sub main() { pipe my $rfh, my $wfh; # instead of two pipe calls, creating four file handles, we use socketpair - socketpair my $childsock, my $parentsock, AF_UNIX, SOCK_STREAM, PF_UNSPEC or error "socketpair failed: $!"; + socketpair my $childsock, my $parentsock, AF_UNIX, SOCK_STREAM, PF_UNSPEC + or error "socketpair failed: $!"; $options->{hooksock} = $childsock; if ($options->{mode} eq 'unshare') { $pid = get_unshare_cmd { # child - $SIG{'INT'} = 'DEFAULT'; - $SIG{'HUP'} = 'DEFAULT'; + $SIG{'INT'} = 'DEFAULT'; + $SIG{'HUP'} = 'DEFAULT'; $SIG{'PIPE'} = 'DEFAULT'; $SIG{'TERM'} = 'DEFAULT'; # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; close $rfh; close $parentsock; @@ -3020,23 +3401,29 @@ sub main() { print $devtar; # pack everything except ./dev - 0 == system('tar', @taropts, '-C', $options->{root}, '.') or error "tar failed: $?"; + 0 == system('tar', @taropts, '-C', $options->{root}, '.') + or error "tar failed: $?"; info "done"; } exit 0; - } \@idmap; - } elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot', 'chrootless')) { + } + \@idmap; + } elsif ( + any { $_ eq $options->{mode} } + ('root', 'fakechroot', 'proot', 'chrootless') + ) { $pid = fork() // error "fork() failed: $!"; if ($pid == 0) { - $SIG{'INT'} = 'DEFAULT'; - $SIG{'HUP'} = 'DEFAULT'; + $SIG{'INT'} = 'DEFAULT'; + $SIG{'HUP'} = 'DEFAULT'; $SIG{'PIPE'} = 'DEFAULT'; $SIG{'TERM'} = 'DEFAULT'; # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; close $rfh; close $parentsock; @@ -3065,26 +3452,35 @@ sub main() { # Fakechroot requires tar to run inside the chroot or # otherwise absolute symlinks will include the path to the # root directory - 0 == system('/usr/sbin/chroot', $options->{root}, 'tar', @taropts, '-C', '/', '.') or error "tar failed: $?"; + 0 == system('/usr/sbin/chroot', $options->{root}, 'tar', + @taropts, '-C', '/', '.') + or error "tar failed: $?"; } elsif ($options->{mode} eq 'proot') { # proot requires tar to run inside proot or otherwise # permissions will be completely off my @qemuopt = (); if (defined $options->{qemu}) { push @qemuopt, "--qemu=qemu-$options->{qemu}"; - push @taropts, "--exclude=./host-rootfs" + push @taropts, "--exclude=./host-rootfs"; } - 0 == system('proot', '--root-id', "--rootfs=$options->{root}", '--cwd=/', @qemuopt, 'tar', @taropts, '-C', '/', '.') or error "tar failed: $?"; - } elsif (any { $_ eq $options->{mode} } ('root', 'chrootless')) { + 0 == system('proot', '--root-id', + "--rootfs=$options->{root}", '--cwd=/', @qemuopt, + 'tar', @taropts, '-C', '/', '.') + or error "tar failed: $?"; + } elsif (any { $_ eq $options->{mode} } ('root', 'chrootless')) + { # If the chroot directory is not owned by the root user, # then we assume that no measure was taken to fake root # permissions. Since the final tarball should contain # entries with root ownership, we instruct tar to do so. my @owneropts = (); if ((stat $options->{root})[4] != 0) { - push @owneropts, '--owner=0', '--group=0', '--numeric-owner'; + push @owneropts, '--owner=0', '--group=0', + '--numeric-owner'; } - 0 == system('tar', @taropts, @owneropts, '-C', $options->{root}, '.') or error "tar failed: $?"; + 0 == system('tar', @taropts, @owneropts, '-C', + $options->{root}, '.') + or error "tar failed: $?"; } else { error "unknown mode: $options->{mode}"; } @@ -3100,20 +3496,21 @@ sub main() { # parent - my $got_signal = 0; + my $got_signal = 0; my $waiting_for = "setup"; - my $ignore = sub { + my $ignore = sub { $got_signal = shift; info "main() received signal $got_signal: waiting for $waiting_for..."; }; - $SIG{'INT'} = $ignore; - $SIG{'HUP'} = $ignore; + $SIG{'INT'} = $ignore; + $SIG{'HUP'} = $ignore; $SIG{'PIPE'} = $ignore; $SIG{'TERM'} = $ignore; # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; close $wfh; close $childsock; @@ -3129,7 +3526,8 @@ sub main() { my $len = -1; { debug "reading from parentsock"; - my $ret = read ($parentsock, my $buf, 2+5) // error "cannot read from socket: $!"; + my $ret = read($parentsock, my $buf, 2 + 5) + // error "cannot read from socket: $!"; debug "finished reading from parentsock"; if ($ret == 0) { error "received eof on socket"; @@ -3147,27 +3545,32 @@ sub main() { debug "received message: openr"; my $infile; { - my $ret = read ($parentsock, $infile, $len) // error "cannot read from socket: $!"; + my $ret = read($parentsock, $infile, $len) + // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } } # make sure that the requested path exists outside the chroot - if (! -e $infile) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + if (!-e $infile) { + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "$infile does not exist"; } debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); - open my $fh, '<', $infile or error "failed to open $infile for reading: $!"; + open my $fh, '<', $infile + or error "failed to open $infile for reading: $!"; # read from the file and send as payload to the child process while (1) { # read from file - my $ret = read ($fh, my $cont, 4096) // error "cannot read from pipe: $!"; + my $ret = read($fh, my $cont, 4096) + // error "cannot read from pipe: $!"; if ($ret == 0) { last; } debug "sending write"; # send to child @@ -3191,7 +3594,8 @@ sub main() { # payload is the output directory my $outfile; { - my $ret = read ($parentsock, $outfile, $len) // error "cannot read from socket: $!"; + my $ret = read($parentsock, $outfile, $len) + // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } @@ -3199,37 +3603,48 @@ sub main() { # make sure that the directory exists my $outdir = dirname($outfile); if (-e $outdir) { - if (! -d $outdir) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + if (!-d $outdir) { + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "$outdir already exists but is not a directory"; } } else { - my $num_created = make_path $outdir, {error => \my $err}; + my $num_created = make_path $outdir, { error => \my $err }; if ($err && @$err) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); - error (join "; ", (map {"cannot create " . (join ": ", %{$_})} @$err)); + error( + join "; ", + ( + map { "cannot create " . (join ": ", %{$_}) } + @$err + )); } elsif ($num_created == 0) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "cannot create $outdir"; } } debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); # now we expect one or more "write" messages containing the # tarball to write - open my $fh, '>', $outfile or error "failed to open $outfile for writing: $!"; + open my $fh, '>', $outfile + or error "failed to open $outfile for writing: $!"; # handle "write" messages from the child process and feed # their payload into the file handle until a "close" message # is encountered - while(1) { + while (1) { # receive the next message - my $ret = read ($parentsock, my $buf, 2+5) // error "cannot read from socket: $!"; + my $ret = read($parentsock, my $buf, 2 + 5) + // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } @@ -3241,27 +3656,32 @@ sub main() { error "expected no payload but got $len bytes"; } debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); last; } elsif ($msg ne "write") { # we should not receive this message at this point - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "expected write but got: $msg"; } # read the payload my $content; { - my $ret = read ($parentsock, $content, $len) // error "error cannot read from socket: $!"; + my $ret = read($parentsock, $content, $len) + // error "error cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } } # write the payload to the file handle - print $fh $content or error "cannot write to file handle: $!"; + print $fh $content + or error "cannot write to file handle: $!"; debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); } close $fh; @@ -3270,31 +3690,37 @@ sub main() { debug "received message: mktar"; my $indir; { - my $ret = read ($parentsock, $indir, $len) // error "cannot read from socket: $!"; + my $ret = read($parentsock, $indir, $len) + // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } } # make sure that the requested path exists outside the chroot - if (! -e $indir) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + if (!-e $indir) { + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "$indir does not exist"; } debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); # Open a tar process creating a tarfile of the instructed # path. To emulate the behaviour of cp, change to the # dirname of the requested path first. - open my $fh, '-|', 'tar', '--directory', dirname($indir), '--create', '--file', '-', basename($indir) // error "failed to fork(): $!"; + open my $fh, '-|', 'tar', '--directory', dirname($indir), + '--create', '--file', '-', + basename($indir) // error "failed to fork(): $!"; # read from the tar process and send as payload to the child # process while (1) { # read from tar - my $ret = read ($fh, my $cont, 4096) // error "cannot read from pipe: $!"; + my $ret = read($fh, my $cont, 4096) + // error "cannot read from pipe: $!"; if ($ret == 0) { last; } debug "sending write"; # send to child @@ -3321,44 +3747,56 @@ sub main() { # payload is the output directory my $outdir; { - my $ret = read ($parentsock, $outdir, $len) // error "cannot read from socket: $!"; + my $ret = read($parentsock, $outdir, $len) + // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } } # make sure that the directory exists if (-e $outdir) { - if (! -d $outdir) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + if (!-d $outdir) { + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "$outdir already exists but is not a directory"; } } else { - my $num_created = make_path $outdir, {error => \my $err}; + my $num_created = make_path $outdir, { error => \my $err }; if ($err && @$err) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); - error (join "; ", (map {"cannot create " . (join ": ", %{$_})} @$err)); + error( + join "; ", + ( + map { "cannot create " . (join ": ", %{$_}) } + @$err + )); } elsif ($num_created == 0) { - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "cannot create $outdir"; } } debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); # now we expect one or more "write" messages containing the # tarball to unpack - open my $fh, '|-', 'tar', '--directory', $outdir, '--extract', '--file', '-' // error "failed to fork(): $!"; + open my $fh, '|-', 'tar', '--directory', $outdir, '--extract', + '--file', '-' // error "failed to fork(): $!"; # handle "write" messages from the child process and feed # their payload into the tar process until a "close" message # is encountered - while(1) { + while (1) { # receive the next message - my $ret = read ($parentsock, my $buf, 2+5) // error "cannot read from socket: $!"; + my $ret = read($parentsock, my $buf, 2 + 5) + // error "cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } @@ -3370,27 +3808,32 @@ sub main() { error "expected no payload but got $len bytes"; } debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); last; } elsif ($msg ne "write") { # we should not receive this message at this point - print $parentsock (pack("n", 0) . "error") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "error") + or error "cannot write to socket: $!"; $parentsock->flush(); error "expected write but got: $msg"; } # read the payload my $content; { - my $ret = read ($parentsock, $content, $len) // error "error cannot read from socket: $!"; + my $ret = read($parentsock, $content, $len) + // error "error cannot read from socket: $!"; if ($ret == 0) { error "received eof on socket"; } } # write the payload to the tar process - print $fh $content or error "cannot write to tar process: $!"; + print $fh $content + or error "cannot write to tar process: $!"; debug "sending okthx"; - print $parentsock (pack("n", 0) . "okthx") or error "cannot write to socket: $!"; + print $parentsock (pack("n", 0) . "okthx") + or error "cannot write to socket: $!"; $parentsock->flush(); } close $fh; @@ -3425,42 +3868,52 @@ sub main() { my @argv = (); if ($options->{makesqfs}) { push @argv, 'tar2sqfs', - '--quiet', '--no-skip', '--force', '--exportable', - '--compressor', 'xz', - '--block-size', '1048576', - $options->{target}; + '--quiet', '--no-skip', '--force', '--exportable', + '--compressor', 'xz', + '--block-size', '1048576', + $options->{target}; } else { push @argv, @{$tar_compressor}; } - POSIX::sigprocmask(SIG_BLOCK, $sigset) or error "Can't block signals: $!"; + POSIX::sigprocmask(SIG_BLOCK, $sigset) + or error "Can't block signals: $!"; my $cpid = fork() // error "fork() failed: $!"; if ($cpid == 0) { # child: default signal handlers - $SIG{'INT'} = 'DEFAULT'; - $SIG{'HUP'} = 'DEFAULT'; + $SIG{'INT'} = 'DEFAULT'; + $SIG{'HUP'} = 'DEFAULT'; $SIG{'PIPE'} = 'DEFAULT'; $SIG{'TERM'} = 'DEFAULT'; # unblock all delayed signals (and possibly handle # them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; if ($options->{makesqfs}) { - open(STDOUT, '>', '/dev/null') or error "cannot open /dev/null for writing: $!"; + open(STDOUT, '>', '/dev/null') + or error "cannot open /dev/null for writing: $!"; } else { - open(STDOUT, '>', $options->{target}) or error "cannot open $options->{target} for writing: $!"; + open(STDOUT, '>', $options->{target}) + or error + "cannot open $options->{target} for writing: $!"; } - open(STDIN, '<&', $rfh) or error "cannot open file handle for reading: $!"; - eval 'Devel::Cover::set_coverage("none")' if $is_covering; - exec { $argv[0] } @argv or error ("cannot exec " . (join " ", @argv) . ": $!"); + open(STDIN, '<&', $rfh) + or error "cannot open file handle for reading: $!"; + eval 'Devel::Cover::set_coverage("none")' + if $is_covering; + exec { $argv[0] } @argv + or + error("cannot exec " . (join " ", @argv) . ": $!"); } - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or error "Can't unblock signals: $!"; + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; waitpid $cpid, 0; if ($? != 0) { - error ("failed to start " . (join " ", @argv)); + error("failed to start " . (join " ", @argv)); } } else { - if(!copy($rfh, $options->{target})) { + if (!copy($rfh, $options->{target})) { error "cannot copy to $options->{target}: $!"; } } @@ -3482,7 +3935,8 @@ sub main() { # change signal handler message $waiting_for = "cleanup"; - if (($options->{maketar} or $options->{makesqfs}) and -e $options->{root}) { + if (($options->{maketar} or $options->{makesqfs}) and -e $options->{root}) + { info "removing tempdir $options->{root}..."; if ($options->{mode} eq 'unshare') { # We don't have permissions to remove the directory outside @@ -3497,22 +3951,25 @@ sub main() { chdir "$options->{root}/.." or error "unable to chdir() to parent directory of" . " $options->{root}: $!"; - remove_tree($options->{root}, {error => \my $err}); + 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 { + } else { warning "problem unlinking $file: $message"; } } } - } \@idmap; + } + \@idmap; waitpid $pid, 0; $? == 0 or error "remove_tree failed"; - } elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot', 'chrootless')) { + } elsif ( + any { $_ eq $options->{mode} } + ('root', 'fakechroot', 'proot', 'chrootless') + ) { # without unshare, we use the system's rm to recursively remove the # temporary directory just to make sure that we do not accidentally # remove more than we should by using --one-file-system. @@ -3520,7 +3977,9 @@ sub main() { # --interactive=never is needed when in proot mode, the # write-protected file /apt/apt.conf.d/01autoremove-kernels is to # be removed. - 0 == system('rm', '--interactive=never', '--recursive', '--preserve-root', '--one-file-system', $options->{root}) or error "rm failed: $!"; + 0 == system('rm', '--interactive=never', '--recursive', + '--preserve-root', '--one-file-system', $options->{root}) + or error "rm failed: $!"; } else { error "unknown mode: $options->{mode}"; }