@ -23,7 +23,7 @@
use strict;
use warnings;
our $VERSION = '1.4.0 ';
our $VERSION = '1.4.3 ';
use English;
use Getopt::Long;
@ -1551,32 +1551,41 @@ sub setup_mounts {
if (any { $_ eq 'chroot/start-stop-daemon' } @{ $options->{skip} }) {
info "skipping chroot/start-stop-daemon as requested";
} else {
# $options->{root} must not be part of $ssdloc but must instead be
# evaluated at the time the cleanup is run or otherwise, when
# performing a pivot-root, the ssd location will still be prefixed
# with the chroot path even though we changed root
my $ssdloc;
if (-f "$options->{root}/sbin/start-stop-daemon") {
$ssdloc = "/sbin/start-stop-daemon";
} elsif (-f "$options->{root}/usr/sbin/start-stop-daemon") {
$ssdloc = "/usr/sbin/start-stop-daemon";
}
push @cleanup_tasks, sub {
if (-e "$options->{root}/sbin/start-stop-daemon.REAL") {
return unless length $ssdloc;
if (-e "$options->{root}/$ssdloc.REAL") {
move(
"$options->{root}/sbin/start-stop-daemon.REAL",
"$options->{root}/sbin/start-stop-daemon"
"$options->{root}/$ssdloc .REAL",
"$options->{root}/$ssdloc "
) 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
"$options->{root}/sbin/start-stop-daemon.REAL already"
. " exists";
if (length $ssdloc) {
if (-e "$options->{root}/$ssdloc.REAL") {
error "$options->{root}/$ssdloc.REAL already exists";
}
move(
"$options->{root}/sbin/start-stop-daemon ",
"$options->{root}/sbin/start-stop-daemon .REAL"
"$options->{root}/$ssdloc ",
"$options->{root}/$ssdloc .REAL"
) or error "cannot move start-stop-daemon: $!";
open my $fh, '>', "$options->{root}/sbin/start-stop-daemon "
open my $fh, '>', "$options->{root}/$ssdloc "
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 "
chmod 0755, "$options->{root}/$ssdloc "
or error "cannot chmod start-stop-daemon: $!";
}
}
@ -1953,7 +1962,7 @@ sub run_setup() {
# since we do not know the dpkg version inside the chroot at this
# point, we can only omit it in chrootless mode
if ($options->{mode} ne 'chrootless'
or scalar @{ $options->{dpkgopts} } > 0) {
or length $options->{dpkgopts } > 0) {
push @directories, '/etc/dpkg/dpkg.cfg.d/';
}
# if dpkg and apt operate from the outside we need some more
@ -2083,26 +2092,11 @@ sub run_setup() {
close $fh;
}
if (scalar @{ $options->{aptopts} } > 0
if (length $options->{aptopts } > 0
and (!-e "$options->{root}/etc/apt/apt.conf.d/99mmdebstrap")) {
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
$fh->flush();
copy $opt, $fh or error "cannot copy $opt: $!";
} else {
print $fh $opt;
if ($opt !~ /;$/) {
print $fh ';';
}
if ($opt !~ /\n$/) {
print $fh "\n";
}
}
}
print $fh $options->{aptopts};
close $fh;
if ($verbosity_level >= 3) {
debug "content of /etc/apt/apt.conf.d/99mmdebstrap:";
@ -2110,7 +2104,7 @@ sub run_setup() {
}
}
if (scalar @{ $options->{dpkgopts} } > 0
if (length $options->{dpkgopts } > 0
and (!-e "$options->{root}/etc/dpkg/dpkg.cfg.d/99mmdebstrap")) {
# FIXME: in chrootless mode, dpkg will only read the configuration
# from the host -- see #808203
@ -2120,19 +2114,7 @@ sub run_setup() {
}
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
$fh->flush();
copy $opt, $fh or error "cannot copy $opt: $!";
} else {
print $fh $opt;
if ($opt !~ /\n$/) {
print $fh "\n";
}
}
}
print $fh $options->{dpkgopts};
close $fh;
if ($verbosity_level >= 3) {
debug "content of /etc/dpkg/dpkg.cfg.d/99mmdebstrap:";
@ -2234,6 +2216,8 @@ sub run_setup() {
chmod $mode, "$options->{root}/$file"
or error "cannot chmod $file: $!";
}
} elsif (-e $file && -e "$options->{root}/$file") {
info "rootfs alreday contains $file";
} else {
warning("Host system does not have a $file to copy into the"
. " rootfs.");
@ -3137,22 +3121,12 @@ sub run_cleanup() {
} else {
# 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: $!";
or warning "failed to unlink /etc/apt/apt.conf.d/00mmdebstrap: $!";
if (defined $ENV{APT_CONFIG} && -e $ENV{APT_CONFIG}) {
unlink $ENV{APT_CONFIG}
or error "failed to unlink $ENV{APT_CONFIG}: $!";
}
if (any { $_ eq 'cleanup/mmdebstrap/qemu' } @{ $options->{skip} }) {
info "skipping cleanup/mmdebstrap/qemu as requested";
} elsif (defined $options->{qemu}
and any { $_ eq $options->{mode} } ('root', 'unshare')
and -e "$options->{root}/usr/bin/qemu-$options->{qemu}-static") {
unlink "$options->{root}/usr/bin/qemu-$options->{qemu}-static"
or error
"cannot unlink /usr/bin/qemu-$options->{qemu}-static: $!";
}
}
if (any { $_ eq 'cleanup/reproducible' } @{ $options->{skip} }) {
@ -4419,19 +4393,27 @@ sub approx_disk_usage {
# We ignore /dev because depending on the mode, the directory might be
# populated or not and we want consistent disk usage results independent
# of the mode.
my $installed_size = 0;
my $installed_size = 0;
my %hardlink;
my $scan_installed_size = sub {
if ($File::Find::name eq "$directory/dev") {
# add all entries of @devfiles once
$installed_size += scalar @devfiles;
return;
} elsif ($File::Find::name =~ /^$directory\/dev\//) {
# ignore everything below /dev
} elsif (-l $File::Find::name) {
# -f follows symlinks, so we first check if we have a symlink
$installed_size += 1;
} elsif (-f $File::Find::name) {
return;
}
lstat or error "cannot stat $File::Find::name";
if (-f _ or -l _) {
my ($dev, $ino, $nlink) = (lstat _)[0, 1, 3];
return if exists $hardlink{"$dev:$ino"};
# Track hardlinks to avoid repeated additions.
$hardlink{"$dev:$ino"} = 1 if $nlink > 1;
# add file size in 1024 byte blocks, rounded up
$installed_size += int(((-s $File::Find::name) + 1024) / 1024);
$installed_size += int(((-s _ ) + 1024) / 1024);
} else {
# all other entries are assumed to only take up one block
$installed_size += 1;
@ -4518,8 +4500,8 @@ sub main() {
architectures => [$hostarch],
mode => 'auto',
format => 'auto',
dpkgopts => [] ,
aptopts => [] ,
dpkgopts => '' ,
aptopts => '' ,
apttrusted => $apttrusted,
apttrustedparts => $apttrustedparts,
noop => [],
@ -4586,9 +4568,44 @@ sub main() {
},
'architectures=s@' => \$options->{architectures},
'mode=s' => \$options->{mode},
'dpkgopt=s@' => \$options->{dpkgopts},
'aptopt=s@' => \$options->{aptopts},
'keyring=s' => sub {
'dpkgopt=s' => sub {
my ($opt_name, $opt_value) = @_;
if (-r $opt_value) {
open my $fh, '<', $opt_value
or error "failed to open $opt_value: $!";
$options->{dpkgopts} .= do { local $/; <$fh> };
if ($options->{dpkgopts} !~ /\n$/) {
print $fh "\n";
}
close $fh;
} else {
$options->{dpkgopts} .= $opt_value;
if ($opt_value !~ /\n$/) {
$options->{dpkgopts} .= "\n";
}
}
},
'aptopt=s' => sub {
my ($opt_name, $opt_value) = @_;
if (-r $opt_value) {
open my $fh, '<', $opt_value
or error "failed to open $opt_value: $!";
$options->{aptopts} .= do { local $/; <$fh> };
if ($options->{aptopts} !~ /\n$/) {
print $fh "\n";
}
close $fh;
} else {
$options->{aptopts} .= $opt_value;
if ($opt_value !~ /;$/) {
$options->{aptopts} .= ';';
}
if ($opt_value !~ /\n$/) {
$options->{aptopts} .= "\n";
}
}
},
'keyring=s' => sub {
my ($opt_name, $opt_value) = @_;
if ($opt_value =~ /"/) {
error "--keyring: apt cannot handle paths with double quotes:"
@ -4722,7 +4739,7 @@ sub main() {
}
push @{ $options->{skip} }, $skip;
}
}) or pod2usage(-exitval => 2, -verbose => 1 );
}) or pod2usage(-exitval => 2, -verbose => 0 );
if (defined($logfile)) {
open(STDERR, '>', $logfile) or error "cannot open $logfile: $!";
@ -4939,9 +4956,17 @@ sub main() {
test_unshare_userns(1);
}
} elsif ($options->{mode} eq 'chrootless') {
if ($EFFECTIVE_USER_ID == 0) {
warning "running chrootless mode as root might damage the host "
. "system";
if (any { $_ eq 'check/chrootless' } @{ $options->{skip} }) {
info "skipping check/chrootless as requested";
} else {
my $ischroot = 0 == system 'ischroot';
if ( $EFFECTIVE_USER_ID == 0
&& !exists $ENV{FAKEROOTKEY}
&& !$ischroot) {
error
"running chrootless mode as root without fakeroot might "
. "damage the host system if not run inside a chroot";
}
}
} else {
error "unknown mode: $options->{mode}";
@ -5202,6 +5227,12 @@ sub main() {
}
}
if (defined $options->{qemu} && $options->{mode} eq 'fakechroot') {
if (!can_execute 'dpkg-architecture') {
error "cannot find dpkg-architecture";
}
}
{
$options->{suite} = undef;
if (scalar @ARGV > 0) {
@ -5932,8 +5963,6 @@ sub main() {
my $sigset = POSIX::SigSet->new(SIGINT, SIGHUP, SIGPIPE, SIGTERM);
POSIX::sigprocmask(SIG_BLOCK, $sigset) or error "Can't block signals: $!";
my $pid;
# a pipe to transfer the final tarball from the child to the parent
pipe my $rfh, my $wfh;
@ -5948,163 +5977,107 @@ sub main() {
# b) it puts code writing the protocol outside of the helper/listener
# c) the forked listener process cannot communicate to its parent
pipe my $nblkreader, my $nblkwriter or error "pipe failed: $!";
if ($options->{mode} eq 'unshare') {
$pid = get_unshare_cmd(
sub {
# child
local $SIG{'INT'} = 'DEFAULT';
local $SIG{'HUP'} = 'DEFAULT';
local $SIG{'PIPE'} = 'DEFAULT';
local $SIG{'TERM'} = 'DEFAULT';
# unblock all delayed signals (and possibly handle them)
POSIX::sigprocmask(SIG_UNBLOCK, $sigset)
or error "Can't unblock signals: $!";
close $rfh;
close $parentsock;
open(STDOUT, '>&', STDERR) or error "cannot open STDOUT: $!";
setup($options);
print $childsock (pack('n', 0) . 'adios');
$childsock->flush();
close $childsock;
close $nblkreader;
if (!$options->{dryrun} && $options->{format} eq 'ext2') {
my $numblocks = approx_disk_usage($options->{root});
print $nblkwriter "$numblocks\n";
$nblkwriter->flush();
}
close $nblkwriter;
if ($options->{dryrun}) {
info "simulate creating tarball...";
} elsif (any { $_ eq $options->{format} }
('tar', 'squashfs', 'ext2')) {
info "creating tarball...";
# redirect tar output to the writing end of the pipe so
# that the parent process can capture the output
open(STDOUT, '>&', $wfh) or error "cannot open STDOUT: $!";
# Add ./dev as the first entries of the tar file.
# We cannot add them after calling tar, because there is no
# way to prevent tar from writing NULL entries at the end.
if (any { $_ eq 'output/dev' } @{ $options->{skip} }) {
info "skipping output/dev as requested";
} else {
print $devtar;
}
my $worker = sub {
# child
local $SIG{'INT'} = 'DEFAULT';
local $SIG{'HUP'} = 'DEFAULT';
local $SIG{'PIPE'} = 'DEFAULT';
local $SIG{'TERM'} = 'DEFAULT';
# pack everything except ./dev
0 == system('tar', @taropts, '-C', $options->{root}, '.' )
or error "tar failed: $? ";
# unblock all delayed signals (and possibly handle them)
POSIX::sigprocmask(SIG_UNBLOCK, $sigset)
or error "Can't unblock signals: $!";
info "done";
} elsif (any { $_ eq $options->{format} }
('directory', 'null')) {
# nothing to do
} else {
error "unknown format: $options->{format}";
}
close $rfh;
close $parentsock;
open(STDOUT, '>&', STDERR) or error "cannot open STDOUT: $!";
exit 0;
},
\@idmap
);
} elsif (any { $_ eq $options->{mode} }
('root', 'fakechroot', 'chrootless')) {
$pid = fork() // error "fork() failed: $!";
if ($pid == 0) {
local $SIG{'INT'} = 'DEFAULT';
local $SIG{'HUP'} = 'DEFAULT';
local $SIG{'PIPE'} = 'DEFAULT';
local $SIG{'TERM'} = 'DEFAULT';
setup($options);
# unblock all delayed signals (and possibly handle them)
POSIX::sigprocmask(SIG_UNBLOCK, $sigset)
or error "Can't unblock signals: $!";
print $childsock (pack('n', 0) . 'adios');
$childsock->flush();
close $rfh;
close $parentsock;
open(STDOUT, '>&', STDERR) or error "cannot open STDOUT: $!";
close $childsock;
setup($options);
close $nblkreader;
if (!$options->{dryrun} && $options->{format} eq 'ext2') {
my $numblocks = approx_disk_usage($options->{root});
print $nblkwriter "$numblocks\n";
$nblkwriter->flush();
}
close $nblkwriter;
print $childsock (pack('n', 0) . 'adios');
$childsock->flush();
if ($options->{dryrun}) {
info "simulate creating tarball...";
} elsif (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2'))
{
info "creating tarball...";
close $childsock;
# redirect tar output to the writing end of the pipe so
# that the parent process can capture the output
open(STDOUT, '>&', $wfh) or error "cannot open STDOUT: $!";
close $nblkreader;
if (!$options->{dryrun} && $options->{format} eq 'ext2') {
my $numblocks = approx_disk_usage($options->{root});
print $nblkwriter $numblocks;
$nblkwriter->flush();
# Add ./dev as the first entries of the tar file.
# We cannot add them after calling tar, because there is no
# way to prevent tar from writing NULL entries at the end.
if (any { $_ eq 'output/dev' } @{ $options->{skip} }) {
info "skipping output/dev as requested";
} else {
print $devtar;
}
close $nblkwriter;
if ($options->{dryrun}) {
info "simulate creating tarball...";
} elsif (any { $_ eq $options->{format} }
('tar', 'squashfs', 'ext2')) {
info "creating tarball...";
# redirect tar output to the writing end of the pipe so that
# the parent process can capture the output
open(STDOUT, '>&', $wfh) or error "cannot open STDOUT: $!";
# Add ./dev as the first entries of the tar file.
# We cannot add them after calling tar, because there is no way
# to prevent tar from writing NULL entries at the end.
if (any { $_ eq 'output/dev' } @{ $options->{skip} }) {
info "skipping output/dev as requested";
} else {
print $devtar;
}
if ($options->{mode} eq 'fakechroot') {
# By default, FAKECHROOT_EXCLUDE_PATH includes /proc and
# /sys which means that the resulting tarball will contain
# the permission and ownership information of /proc and
# /sys from the outside, which we want to avoid.
## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{FAKECHROOT_EXCLUDE_PATH} = "/dev";
# Fakechroot requires tar to run inside the chroot or
# otherwise absolute symlinks will include the path to the
# root directory
0 == system('chroot', $options->{root}, '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';
}
0 == system('tar', @taropts, @owneropts, '-C',
$options->{root}, '.')
or error "tar failed: $?";
} else {
error "unknown mode: $options->{mode}";
if ($options->{mode} eq 'unshare') {
# pack everything except ./dev
0 == system('tar', @taropts, '-C', $options->{root}, '.')
or error "tar failed: $?";
} elsif ($options->{mode} eq 'fakechroot') {
# By default, FAKECHROOT_EXCLUDE_PATH includes /proc and /sys
# which means that the resulting tarball will contain the
# permission and ownership information of /proc and /sys from
# the outside, which we want to avoid.
## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{FAKECHROOT_EXCLUDE_PATH} = "/dev";
# Fakechroot requires tar to run inside the chroot or otherwise
# absolute symlinks will include the path to the root directory
0 == system('chroot', $options->{root}, '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';
}
info "done";
} elsif (any { $_ eq $options->{format} } ('directory', 'null')) {
# nothing to do
0 == system('tar', @taropts, @owneropts, '-C',
$options->{root}, '.')
or error "tar failed: $?";
} else {
error "unknown format: $options->{format }";
error "unknown mode: $options->{mode }";
}
exit 0;
info "done";
} elsif (any { $_ eq $options->{format} } ('directory', 'null')) {
# nothing to do
} else {
error "unknown format: $options->{format}";
}
exit 0;
};
my $pid;
if ($options->{mode} eq 'unshare') {
$pid = get_unshare_cmd($worker, \@idmap);
} elsif (any { $_ eq $options->{mode} }
('root', 'fakechroot', 'chrootless')) {
$pid = fork() // error "fork() failed: $!";
if ($pid == 0) {
$worker->();
}
} else {
error "unknown mode: $options->{mode}";
@ -6392,45 +6365,52 @@ B<mmdebstrap> creates a Debian chroot of I<SUITE> into I<TARGET> from one or
more I<MIRROR>s. It is meant as an alternative to the debootstrap tool (see
section B<DEBOOTSTRAP>). In contrast to debootstrap it uses apt to resolve
dependencies and is thus able to use more than one mirror and resolve more
complex dependencies. See section B<OPERATION> for an overview of how
B<mmdebstrap> works internally.
If no I<MIRROR> option is provided and I<SUITE> is not a stable release name,
L<http://deb.debian.org/debian> is used. If I<SUITE> is a stable release name
and no I<MIRROR> is specified, then mirrors for updates and security are
automatically added. If a I<MIRROR> option starts with "deb " or "deb-src "
then it is used as a one-line-style format entry for apt's sources.list inside
the chroot. If a I<MIRROR> option contains a "://" then it is interpreted as a
mirror URI and the apt line inside the chroot is assembled as "deb [arch=A] B C
D" where A is the host's native architecture, B is the I<MIRROR>, C is the
given I<SUITE> and D is the components given via B<--components> (defaults to
"main"). If a I<MIRROR> option happens to be an existing file, then its
contents are pasted into the chroot's sources.list. This can be used to supply
a deb822 style sources.list. If I<MIRROR> is C<-> then standard input is pasted
into the chroot's sources.list. More than one mirror can be specified and are
appended to the chroot's sources.list in the given order. If you specify a
https or tor I<MIRROR> and you want the chroot to be able to update itself,
don't forget to also install the ca-certificates package, the
apt-transport-https package for apt versions less than 1.5 and/or the
apt-transport-tor package using the B<--include> option, as necessary.
The optional I<TARGET> argument can either be the path to a directory, the path
to a tarball filename, the path to a squashfs image, the path to an ext2 image,
a FIFO, a character special device, or C<->. Without the B<--format> option,
I<TARGET> will be used to choose the format. See the section B<FORMATS> for
more information. If no I<TARGET> was specified or if I<TARGET> is C<->, an
uncompressed tarball will be sent to standard output.
The I<SUITE> may be a valid release code name (eg, sid, stretch, jessie) or a
symbolic name (eg, unstable, testing, stable, oldstable). Any suite name that
works with apt on the given mirror will work. If no I<SUITE> was specified,
then a single I<MIRROR> C<-> is added and thus the information of the desired
suite has to come from standard input as part of a valid apt sources.list file.
complex dependency relationships. See section B<OPERATION> for an overview of
how B<mmdebstrap> works internally.
The I<SUITE> option may either be a valid release code name (eg, sid, bookworm,
trixie) or a symbolic name (eg, unstable, testing, stable, oldstable). Any
suite name that works with apt on the given mirror will work. The I<SUITE>
option is optional if no I<TARGET> and no I<MIRROR> option is provided. If
I<SUITE> is missing, then the information of the desired suite has to come from
standard input as part of a valid apt sources.list file or be set up via hooks.
The value of the I<SUITE> argument will be used to determine which apt index to
use for finding out the set of C<Essential:yes> packages and/or the set of
packages with the right priority for the selected variant. See the section
packages with the right priority for the selected variant. This functionality
can be disabled by choosing the empty string for I<SUITE>. See the section
B<VARIANTS> for more information.
The I<TARGET> option may either be the path to a directory, the path to a
tarball filename, the path to a squashfs image, the path to an ext2 image, a
FIFO, a character special device, or C<->. The I<TARGET> option is optional if
no I<MIRROR> option is provided. If I<TARGET> is missing or if I<TARGET> is
C<->, an uncompressed tarball will be sent to standard output. Without the
B<--format> option, I<TARGET> will be used to choose the format. See the
section B<FORMATS> for more information.
The I<MIRROR> option may either be provided as a URI, in apt one-line format,
as a path to a file in apt's one-line or deb822-format, or C<->. If no
I<MIRROR> option is provided, then L<http://deb.debian.org/debian> is used as
the default. If I<SUITE> does not refer to "unstable" or "testing", then
I<SUITE>-updates and I<SUITE>-security mirrors are automatically added. If a
I<MIRROR> option starts with "deb " or "deb-src " then it is used as a one-line
format entry for apt's sources.list inside the chroot. If a I<MIRROR> option
contains a "://" then it is interpreted as a mirror URI and the apt line inside
the chroot is assembled as "deb [arch=A] B C D" where A is the host's native
architecture, B is the I<MIRROR>, C is the given I<SUITE> and D is the
components given via B<--components> (defaults to "main"). If a I<MIRROR>
option happens to be an existing file, then its contents are written into the
chroot's sources.list (if the first I<MIRROR> is a file in one-line format) or
into the chroot's sources.list.d directory, named with the extension .list or
.sources, depending on whether the file is in one-line or deb822 format,
respectively. If I<MIRROR> is C<-> then standard input is pasted into the
chroot's sources.list. More than one mirror can be specified and are appended
to the chroot's sources.list in the given order. If you specify a https or tor
I<MIRROR> and you want the chroot to be able to update itself, don't forget to
also install the ca-certificates package, the apt-transport-https package for
apt versions less than 1.5 and/or the apt-transport-tor package using the
B<--include> option, as necessary.
All status output is printed to standard error unless B<--logfile> is used to
redirect it to a file or B<--quiet> or B<--silent> is used to suppress any
output on standard error. Help and version information will be printed to
@ -6897,12 +6877,39 @@ Or without LXC:
$ mmdebstrap --unshare-helper /usr/sbin/chroot ./debian-rootfs /bin/bash
Or, if you don't mind using superuser privileges and have systemd-nspawn
Or without mmdebstrap:
$ unshare --map-auto --map-user=65536 --map-group=65536 --keep-caps -- \
> /usr/sbin/chroot ./debian-rootfs /bin/bash
The above uses C<--map-auto> to map the block of user/group ids for the
effective user/group to a block starting at user/group ID 0. We also want to
map the current effective user/group ID into the subuid/subgid range using
C<--map-user> and C<--map-group>, respectively. But if that uid/gid overlaps
with the respective range, a "hole" will be removed from the mapping and the
remaining uid/gid values will get shifted. Thus, we map the current effective
user/group ID to the highest possible uid/gid, putting them at the end. Since
that means that the user/group will be "nobody" and not "root" inside the
namespace, C<--keep-caps> propagate permitted capabilities into the ambient set
and thus give the user C<CAP_DAC_OVERRIDE> and other capabilities that it
would've had.
Lastly, if you don't mind using superuser privileges and have systemd-nspawn
available and you know your subuid/subgid offset (100000 in this example):
$ sudo systemd-nspawn --private-users=100000 \
> --directory=./debian-rootfs /bin/bash
A directory created in B<unshare> mode cannot be removed the normal way.
Instead, use something like this:
$ unshare --map-root-user --map-auto rm -rf ./debian-rootfs
The above L<unshare(1)> command will map user and group ids into different
ranges compared to the mapping used by B<mmdebstrap> (effectively shifting them
one up) but it will provide the required capabilities for the removal
operation.
If this mode is used as the root user, the user namespace is not unshared (but
the mount namespace and other still are) and created directories will have
correct ownership information. This is also useful in cases where the root user
@ -6934,10 +6941,13 @@ dpkg-root-support usertag of debian-dpkg@lists.debian.org in the Debian bug
tracking system. B<WARNING>: if this option is used carelessly with packages
that do not support C<DPKG_ROOT>, this mode can result in undesired changes to
the system running B<mmdebstrap> because maintainer-scripts will be run without
L<chroot(1)>.
L<chroot(1)>. Make sure to run this mode without superuser privileges and/or
inside a throw-away chroot environment like so:
=for TODO
=item B<qemu>
mmdebstrap --variant=apt --include=mmdebstrap \
--customize-hook='chroot "$1" mmdebstrap --mode=chrootless
--variant=apt unstable chrootless.tar' \
--customize-hook='copy-out chrootless.tar .' unstable /dev/null
=back
@ -6968,7 +6978,9 @@ then this variant will fail because it cannot configure the packages.
=item B<essential>
C<Essential:yes> packages.
C<Essential:yes> packages. If I<SUITE> is a non-empty string, then only
packages from the archive with suite or codename matching I<SUITE> will be
considered for selection of C<Essential:yes> packages.
=item B<apt>
@ -6981,19 +6993,19 @@ get installed at once. The downside of this variant is, that if it should
happen that an B<essential> package is not installable, then it will just get
ignored without throwing an error.
=item B<required>, B<minbase >
=item B<buildd >
The B<essential> set plus all packages with Priority:required and apt .
The B<essential> set plus apt and build-essential .
It is roughly equivalent to running mmdebstrap with
--variant=essential --include="?priority(required) "
--variant=essential --include="apt,build-essential "
=item B<buildd >
=item B<required>, B<minbase >
The B<minbase> set plus build-essential .
The B<essential> set plus all packages with Priority:required .
It is roughly equivalent to running mmdebstrap with
--variant=essential --include="?priority(required),build-essential "
--variant=essential --include="?priority(required)"
=item B<important>, B<debootstrap>, B<->
@ -7240,6 +7252,8 @@ Upon startup, several checks are carried out, like:
=item * which mode to use and whether prerequisites are met
=item * do not allow chrootless mode as root (without fakeroot) unless inside a chroot. This check can be disabled using B<--skip=check/chrootless>
=item * whether the requested architecture can be executed (requires arch-test) using qemu binfmt_misc support. This requires arch-test and can be disabled using B<--skip=check/qemu>
=item * how the apt sources can be assembled from I<SUITE>, I<MIRROR> and B<--components> and/or from standard input as deb822 or one-line format and whether the required GPG keys exist.
@ -7301,7 +7315,7 @@ 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
F</usr/ 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.
@ -7312,8 +7326,7 @@ Extract the downloaded packages into the rootfs.
=item B<prepare>
In B<fakechroot> mode, environment variables C<LD_LIBRARY_PATH> will be set up
correctly. If the chroot requires the qemu-user-static binary it will be copied
in. For foreign B<fakechroot> environments, C<LD_LIBRARY_PATH> and
correctly. For foreign B<fakechroot> environments, C<LD_LIBRARY_PATH> and
C<QEMU_LD_PREFIX> are set up accordingly. This step is not carried out in
B<extract> mode and neither for the B<chrootless> variant.
@ -7348,7 +7361,7 @@ This step is not carried out in B<extract> mode.
=item B<unmount>
Unmount everything that was mounted during the B<mount> stage and restores
F</usr/sbin/policy-rc.d> and F</sbin/start-stop-daemon> if necessary.
F</usr/sbin/policy-rc.d> and F</usr/ sbin/start-stop-daemon> if necessary.
=item B<cleanup>
@ -7358,7 +7371,7 @@ Performs cleanup tasks, unless B<--skip=cleanup> is used:
=item * Removes the package lists (unless B<--skip=cleanup/apt/lists>) and apt cache (unless B<--skip=cleanup/apt/cache>). Both removals can be disabled by using B<--skip=cleanup/apt>.
=item * Remove all files that were put into the chroot for setup purposes, like F</etc/apt/apt.conf.d/00mmdebstrap>, the temporary apt config and the qemu-user-static binary . This can be disabled using B<--skip=cleanup/mmdebstrap>.
=item * Remove all files that were put into the chroot for setup purposes, like F</etc/apt/apt.conf.d/00mmdebstrap> and the temporary apt config . This can be disabled using B<--skip=cleanup/mmdebstrap>.
=item * Remove files that make the result unreproducible and write the empty string to /etc/machine-id if it exists. This can be disabled using B<--skip=cleanup/reproducible>. Note that this will not remove files that make the result unreproducible on machines with differing F</etc/resolv.conf> or F</etc/hostname>. Use a B<--customize-hook> to make those two files reproducible across multiple hosts. See section C<SOURCE_DATE_EPOCH> for more information. The following files will be removed:
@ -7476,7 +7489,7 @@ translated manual packages (but not the untranslated ones), and documentation
Create a bootable USB Stick that boots into a full Debian desktop:
$ mmdebstrap --aptopt='Apt::Install-Recommends "true"' --customize-hook \
'chroot "$1" adduser --gecos user --disabled-password user' \
'chroot "$1" adduser --comment user --disabled-password user' \
--customize-hook='echo 'user:live' | chroot "$1" chpasswd' \
--customize-hook='echo host > "$1/etc/hostname"' \
--customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \