From 449fb248e23e7a420deb3be6db989907097dcb26 Mon Sep 17 00:00:00 2001 From: Johannes Schauer Marin Rodrigues Date: Thu, 10 Nov 2022 14:53:36 +0100 Subject: [PATCH] Instead of mounting and unmounting for each run_chroot() call, do it once before the extract hook and unmount after the customize hooks --- mmdebstrap | 238 +++++++++++++++++++++++++---------------------------- 1 file changed, 112 insertions(+), 126 deletions(-) diff --git a/mmdebstrap b/mmdebstrap index ed6c0be..25efe7c 100755 --- a/mmdebstrap +++ b/mmdebstrap @@ -1109,28 +1109,11 @@ sub run_apt_download_progress { return @listofdebs; } -sub run_chroot { - my $cmd = shift; +sub setup_mounts { my $options = shift; my @cleanup_tasks = (); - my $cleanup = sub { - my $signal = $_[0]; - while (my $task = pop @cleanup_tasks) { - $task->(); - } - if ($signal) { - warning "pid $PID cought signal: $signal"; - exit 1; - } - }; - - local $SIG{INT} = $cleanup; - local $SIG{HUP} = $cleanup; - local $SIG{PIPE} = $cleanup; - local $SIG{TERM} = $cleanup; - eval { if (any { $_ eq $options->{mode} } ('root', 'unshare')) { # if more than essential should be installed, make the system look @@ -1496,6 +1479,12 @@ sub run_chroot { if (any { $_ eq 'chroot/policy-rc.d' } @{ $options->{skip} }) { info "skipping chroot/policy-rc.d as requested"; } else { + push @cleanup_tasks, sub { + if (-f "$options->{root}/usr/sbin/policy-rc.d") { + unlink "$options->{root}/usr/sbin/policy-rc.d" + or error "cannot unlink policy-rc.d: $!"; + } + }; if (-d "$options->{root}/usr/sbin/") { open my $fh, '>', "$options->{root}/usr/sbin/policy-rc.d" or error "cannot open policy-rc.d: $!"; @@ -1511,6 +1500,14 @@ sub run_chroot { if (any { $_ eq 'chroot/start-stop-daemon' } @{ $options->{skip} }) { info "skipping chroot/start-stop-daemon as requested"; } else { + push @cleanup_tasks, sub { + 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: $!"; + } + }; if (-f "$options->{root}/sbin/start-stop-daemon") { if (-e "$options->{root}/sbin/start-stop-daemon.REAL") { error @@ -1532,40 +1529,12 @@ sub run_chroot { or error "cannot chmod start-stop-daemon: $!"; } } - - &{$cmd}(); - - # cleanup - if (any { $_ eq 'chroot/start-stop-daemon' } @{ $options->{skip} }) { - info "skipping chroot/start-stop-daemon as requested"; - } else { - 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: $!"; - } - } - if (any { $_ eq 'chroot/policy-rc.d' } @{ $options->{skip} }) { - info "skipping chroot/policy-rc.d as requested"; - } else { - if (-f "$options->{root}/usr/sbin/policy-rc.d") { - unlink "$options->{root}/usr/sbin/policy-rc.d" - or error "cannot unlink policy-rc.d: $!"; - } - } - }; - my $error = $@; - - # we use the cleanup function to do the unmounting - $cleanup->(0); - - if ($error) { - error "run_chroot failed: $error"; + if ($@) { + error "setup_mounts failed: $@"; } - return; + return @cleanup_tasks; } sub run_hooks { @@ -1630,7 +1599,14 @@ sub run_hooks { ("MMDEBSTRAP_INCLUDE=" . (join ",", @escaped_includes)); } - my $runner = sub { + # Unset the close-on-exec flag, so that the file descriptor does not + # get closed when we exec + my $flags = fcntl($options->{hooksock}, F_GETFD, 0) + or error "fcntl F_GETFD: $!"; + fcntl($options->{hooksock}, F_SETFD, $flags & ~FD_CLOEXEC) + or error "fcntl F_SETFD: $!"; + + { foreach my $script (@{ $options->{"${name}_hook"} }) { if ( $script =~ /^( @@ -1687,19 +1663,6 @@ sub run_hooks { } }; - # Unset the close-on-exec flag, so that the file descriptor does not - # get closed when we exec - my $flags = fcntl($options->{hooksock}, F_GETFD, 0) - or error "fcntl F_GETFD: $!"; - fcntl($options->{hooksock}, F_SETFD, $flags & ~FD_CLOEXEC) - or error "fcntl F_SETFD: $!"; - if ($name eq 'setup') { - # execute directly without mounting anything (the mount points do not - # exist yet) - &{$runner}(); - } else { - run_chroot(\&$runner, $options); - } # Restore flags fcntl($options->{hooksock}, F_SETFD, $flags) or error "fcntl F_SETFD: $!"; return; @@ -1755,21 +1718,57 @@ sub setup { # FIXME: dpkg could be changed to produce the same results run_extract($options, $essential_pkgs); - run_hooks('extract', $options); - - if ($options->{variant} ne 'extract') { - my $chrootcmd = []; - if ($options->{mode} ne 'chrootless') { - $chrootcmd = run_prepare($options); + # setup mounts + my @cleanup_tasks = (); + my $cleanup = sub { + my $signal = $_[0]; + while (my $task = pop @cleanup_tasks) { + $task->(); } + if ($signal) { + warning "pid $PID cought signal: $signal"; + exit 1; + } + }; + + # we only need to setup the mounts if there is anything to do + if ( $options->{variant} ne 'custom' + or scalar @{ $options->{include} } > 0 + or scalar @{ $options->{"extract_hook"} } > 0 + or scalar @{ $options->{"essential_hook"} } > 0 + or scalar @{ $options->{"customize_hook"} } > 0) { + local $SIG{INT} = $cleanup; + local $SIG{HUP} = $cleanup; + local $SIG{PIPE} = $cleanup; + local $SIG{TERM} = $cleanup; + + @cleanup_tasks = setup_mounts($options); + } + + eval { + run_hooks('extract', $options); + + if ($options->{variant} ne 'extract') { + my $chrootcmd = []; + if ($options->{mode} ne 'chrootless') { + $chrootcmd = run_prepare($options); + } - run_essential($options, $essential_pkgs, $chrootcmd, $cached_debs); + run_essential($options, $essential_pkgs, $chrootcmd, $cached_debs); - run_hooks('essential', $options); + run_hooks('essential', $options); - run_install($options, $chrootcmd); + run_install($options); - run_hooks('customize', $options); + run_hooks('customize', $options); + } + }; + + my $msg = $@; + + $cleanup->(0); + if ($msg) { + error "setup failed: $msg"; } if (any { $_ eq 'cleanup' } @{ $options->{skip} }) { @@ -2769,18 +2768,11 @@ sub run_essential() { info "simulate installing essential packages..."; } else { info "installing essential packages..."; - run_chroot( - sub { - run_dpkg_progress({ - ARGV => [ - @{$chrootcmd}, 'dpkg', - '--install', '--force-depends' - ], - PKGS => $essential_pkgs, - }); - }, - $options - ); + run_dpkg_progress({ + ARGV => + [@{$chrootcmd}, 'dpkg', '--install', '--force-depends'], + PKGS => $essential_pkgs, + }); } } else { error "unknown mode: $options->{mode}"; @@ -2807,8 +2799,7 @@ sub run_essential() { } sub run_install() { - my $options = shift; - my $chrootcmd = shift; + my $options = shift; my %pkgs_to_install; for my $incl (@{ $options->{include} }) { @@ -2901,35 +2892,26 @@ sub run_install() { # --root but this would only make sense in situations where there # is no dpkg inside the chroot. if (!$options->{dryrun}) { - run_chroot( - sub { - info "installing remaining packages inside the" - . " chroot..."; - run_apt_progress({ - ARGV => [ - 'apt-get', - '-o', - 'Dir::Bin::dpkg=env', - '-o', - 'DPkg::Options::=--unset=TMPDIR', - '-o', - 'DPkg::Options::=dpkg', - $options->{mode} eq 'fakechroot' - ? ( - '-o', - 'DPkg::Install::Recursive::force=true' - ) - : (), - '-o', - "DPkg::Chroot-Directory=$options->{root}", - '--yes', - 'install' - ], - PKGS => [@pkgs_to_install], - }); - }, - $options - ); + info "installing remaining packages inside the chroot..."; + run_apt_progress({ + ARGV => [ + 'apt-get', + '-o', + 'Dir::Bin::dpkg=env', + '-o', + 'DPkg::Options::=--unset=TMPDIR', + '-o', + 'DPkg::Options::=dpkg', + $options->{mode} eq 'fakechroot' + ? ('-o', 'DPkg::Install::Recursive::force=true') + : (), + '-o', + "DPkg::Chroot-Directory=$options->{root}", + '--yes', + 'install' + ], + PKGS => [@pkgs_to_install], + }); } else { info "simulate installing remaining packages inside the" . " chroot..."; @@ -6822,6 +6804,17 @@ C. In the remaining variants, all Packages files downloaded by the B step are inspected to find the C package set as well as all packages of the required priority. +=item B + +Mount relevant device nodes, F and F into the chroot and unmount +them afterwards. This can be disabled using B<--skip=chroot/mount> or +specifically by B<--skip=chroot/mount/dev>, B<--skip=chroot/mount/proc> and +B<--skip=chroot/mount/sys>, respectively. B will disable running +services by temporarily moving F and +F if they exist. This can be disabled with +B<--skip=chroot/policy-rc.d> and B<--skip=chroot/start-stop-daemon>, +respectively. + =item B Extract the downloaded packages into the rootfs. @@ -6862,17 +6855,10 @@ out in B mode. Run B<--customize-hook> options and all F scripts in B<--hook-dir>. This step is not carried out in B mode. -Whenever B does a chroot call in B or B modes, it -will mount relevant device nodes, F and F into the chroot and -unmount them afterwards. This can be disabled using B<--skip=chroot/mount> or -specifically by B<--skip=chroot/mount/dev>, B<--skip=chroot/mount/proc> and -B<--skip=chroot/mount/sys>, respectively. +=item B -For each command that is run inside the chroot, B will disable -running services by temporarily moving F and -F if they exist. This can be disabled with -B<--skip=chroot/policy-rc.d> and B<--skip=chroot/start-stop-daemon>, -respectively. +Unmount everything that was mounted during the B stage and restores +F and F if necessary. =item B