Instead of mounting and unmounting for each run_chroot() call, do it once before the extract hook and unmount after the customize hooks

This commit is contained in:
Johannes Schauer Marin Rodrigues 2022-11-10 14:53:36 +01:00
parent eb54f6a23a
commit 449fb248e2
Signed by untrusted user: josch
GPG key ID: F2CBA5C78FBD83E1

View file

@ -1109,28 +1109,11 @@ sub run_apt_download_progress {
return @listofdebs; return @listofdebs;
} }
sub run_chroot { sub setup_mounts {
my $cmd = shift;
my $options = shift; my $options = shift;
my @cleanup_tasks = (); 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 { eval {
if (any { $_ eq $options->{mode} } ('root', 'unshare')) { if (any { $_ eq $options->{mode} } ('root', 'unshare')) {
# if more than essential should be installed, make the system look # 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} }) { if (any { $_ eq 'chroot/policy-rc.d' } @{ $options->{skip} }) {
info "skipping chroot/policy-rc.d as requested"; info "skipping chroot/policy-rc.d as requested";
} else { } 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/") { if (-d "$options->{root}/usr/sbin/") {
open my $fh, '>', "$options->{root}/usr/sbin/policy-rc.d" open my $fh, '>', "$options->{root}/usr/sbin/policy-rc.d"
or error "cannot open 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} }) { if (any { $_ eq 'chroot/start-stop-daemon' } @{ $options->{skip} }) {
info "skipping chroot/start-stop-daemon as requested"; info "skipping chroot/start-stop-daemon as requested";
} else { } 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 (-f "$options->{root}/sbin/start-stop-daemon") {
if (-e "$options->{root}/sbin/start-stop-daemon.REAL") { if (-e "$options->{root}/sbin/start-stop-daemon.REAL") {
error error
@ -1532,40 +1529,12 @@ sub run_chroot {
or error "cannot chmod start-stop-daemon: $!"; 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 = $@; if ($@) {
error "setup_mounts failed: $@";
# we use the cleanup function to do the unmounting
$cleanup->(0);
if ($error) {
error "run_chroot failed: $error";
} }
return; return @cleanup_tasks;
} }
sub run_hooks { sub run_hooks {
@ -1630,7 +1599,14 @@ sub run_hooks {
("MMDEBSTRAP_INCLUDE=" . (join ",", @escaped_includes)); ("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"} }) { foreach my $script (@{ $options->{"${name}_hook"} }) {
if ( if (
$script =~ /^( $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 # Restore flags
fcntl($options->{hooksock}, F_SETFD, $flags) or error "fcntl F_SETFD: $!"; fcntl($options->{hooksock}, F_SETFD, $flags) or error "fcntl F_SETFD: $!";
return; return;
@ -1755,21 +1718,57 @@ sub setup {
# FIXME: dpkg could be changed to produce the same results # FIXME: dpkg could be changed to produce the same results
run_extract($options, $essential_pkgs); run_extract($options, $essential_pkgs);
run_hooks('extract', $options); # setup mounts
my @cleanup_tasks = ();
if ($options->{variant} ne 'extract') { my $cleanup = sub {
my $chrootcmd = []; my $signal = $_[0];
if ($options->{mode} ne 'chrootless') { while (my $task = pop @cleanup_tasks) {
$chrootcmd = run_prepare($options); $task->();
} }
if ($signal) {
warning "pid $PID cought signal: $signal";
exit 1;
}
};
run_essential($options, $essential_pkgs, $chrootcmd, $cached_debs); # 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;
run_hooks('essential', $options); @cleanup_tasks = setup_mounts($options);
}
run_install($options, $chrootcmd); eval {
run_hooks('extract', $options);
run_hooks('customize', $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_hooks('essential', $options);
run_install($options);
run_hooks('customize', $options);
}
};
my $msg = $@;
$cleanup->(0);
if ($msg) {
error "setup failed: $msg";
} }
if (any { $_ eq 'cleanup' } @{ $options->{skip} }) { if (any { $_ eq 'cleanup' } @{ $options->{skip} }) {
@ -2769,18 +2768,11 @@ sub run_essential() {
info "simulate installing essential packages..."; info "simulate installing essential packages...";
} else { } else {
info "installing essential packages..."; info "installing essential packages...";
run_chroot( run_dpkg_progress({
sub { ARGV =>
run_dpkg_progress({ [@{$chrootcmd}, 'dpkg', '--install', '--force-depends'],
ARGV => [ PKGS => $essential_pkgs,
@{$chrootcmd}, 'dpkg', });
'--install', '--force-depends'
],
PKGS => $essential_pkgs,
});
},
$options
);
} }
} else { } else {
error "unknown mode: $options->{mode}"; error "unknown mode: $options->{mode}";
@ -2807,8 +2799,7 @@ sub run_essential() {
} }
sub run_install() { sub run_install() {
my $options = shift; my $options = shift;
my $chrootcmd = shift;
my %pkgs_to_install; my %pkgs_to_install;
for my $incl (@{ $options->{include} }) { for my $incl (@{ $options->{include} }) {
@ -2901,35 +2892,26 @@ sub run_install() {
# --root but this would only make sense in situations where there # --root but this would only make sense in situations where there
# is no dpkg inside the chroot. # is no dpkg inside the chroot.
if (!$options->{dryrun}) { if (!$options->{dryrun}) {
run_chroot( info "installing remaining packages inside the chroot...";
sub { run_apt_progress({
info "installing remaining packages inside the" ARGV => [
. " chroot..."; 'apt-get',
run_apt_progress({ '-o',
ARGV => [ 'Dir::Bin::dpkg=env',
'apt-get', '-o',
'-o', 'DPkg::Options::=--unset=TMPDIR',
'Dir::Bin::dpkg=env', '-o',
'-o', 'DPkg::Options::=dpkg',
'DPkg::Options::=--unset=TMPDIR', $options->{mode} eq 'fakechroot'
'-o', ? ('-o', 'DPkg::Install::Recursive::force=true')
'DPkg::Options::=dpkg', : (),
$options->{mode} eq 'fakechroot' '-o',
? ( "DPkg::Chroot-Directory=$options->{root}",
'-o', '--yes',
'DPkg::Install::Recursive::force=true' 'install'
) ],
: (), PKGS => [@pkgs_to_install],
'-o', });
"DPkg::Chroot-Directory=$options->{root}",
'--yes',
'install'
],
PKGS => [@pkgs_to_install],
});
},
$options
);
} else { } else {
info "simulate installing remaining packages inside the" info "simulate installing remaining packages inside the"
. " chroot..."; . " chroot...";
@ -6822,6 +6804,17 @@ C<apt-get dist-upgrade>. In the remaining variants, all Packages files
downloaded by the B<update> step are inspected to find the C<Essential:yes> downloaded by the B<update> step are inspected to find the C<Essential:yes>
package set as well as all packages of the required priority. package set as well as all packages of the required priority.
=item B<mount>
Mount relevant device nodes, F</proc> and F</sys> 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<mmdebstrap> will disable running
services by temporarily moving F</usr/sbin/policy-rc.d> and
F</sbin/start-stop-daemon> 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> =item B<extract>
Extract the downloaded packages into the rootfs. Extract the downloaded packages into the rootfs.
@ -6862,17 +6855,10 @@ out in B<extract> mode.
Run B<--customize-hook> options and all F<customize*> scripts in B<--hook-dir>. Run B<--customize-hook> options and all F<customize*> scripts in B<--hook-dir>.
This step is not carried out in B<extract> mode. This step is not carried out in B<extract> mode.
Whenever B<mmdebstrap> does a chroot call in B<root> or B<unshare> modes, it =item B<unmount>
will mount relevant device nodes, F</proc> and F</sys> 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.
For each command that is run inside the chroot, B<mmdebstrap> will disable Unmount everything that was mounted during the B<mount> stage and restores
running services by temporarily moving F</usr/sbin/policy-rc.d> and F</usr/sbin/policy-rc.d> and F</sbin/start-stop-daemon> if necessary.
F</sbin/start-stop-daemon> if they exist. This can be disabled with
B<--skip=chroot/policy-rc.d> and B<--skip=chroot/start-stop-daemon>,
respectively.
=item B<cleanup> =item B<cleanup>