Compare commits

...

56 Commits
main ... main

Author SHA1 Message Date
Jochen Sprickerhof 4a294f05bd
Add test if dpkg-dev is installed
Needed for dpkg-architecture.
4 days ago
Jochen Sprickerhof 8c0ddc3266
mmdebstrap-autopkgtest-build-qemu: use mount --no-mtab
Otherwise it fails with:

umount: /tmp/mmdebstrap.Tw9G7ZLL4J/mnt: filesystem was unmounted, but failed to update userspace mount table.
E: setup failed: E: command failed: umount --lazy "$1/mnt"

Also umount mnt/dev.
4 days ago
Johannes Schauer Marin Rodrigues 1e68ffd2c4
tests: changelog.Debian.$foreign_arch.gz files are not always present 3 weeks ago
Johannes Schauer Marin Rodrigues 726fc38d1d
attempt diagnosing skip-tar-in-mknod failure 'file changed as we read it' using auditd 3 weeks ago
Johannes Schauer Marin Rodrigues ae09a50f9d
document unshare --map-auto --map-user=65536 --map-group=65536 --keep-caps trick 4 weeks ago
Johannes Schauer Marin Rodrigues 9726836ac4
mmdebstrap-autopkgtest-build-qemu: add documentation of some unshare magic 4 weeks ago
Johannes Schauer Marin Rodrigues cdf6959a41
make_mirror.sh: retry apt with verbose output 4 weeks ago
Johannes Schauer Marin Rodrigues 1cf0d87a60
hooks/file-mirror-automount/setup00.sh: prefix warning with W: 4 weeks ago
Johannes Schauer Marin Rodrigues 0973de1530
hooks/copy-host-apt-sources-and-preferences/setup00.sh: document with comment on top 4 weeks ago
Johannes Schauer Marin Rodrigues d883fa13bb
hooks/maybe-merged-usr: prepare for the time when usr-is-merged exists only as a virtual package 2 months ago
Max-Julian Pogner 286cecc21b
follow adduser's changes in example: --gecos => --comment
Considered References:
https://www.debian.org/releases/bookworm/amd64/release-notes/ch-information.en.html#adduser-changes
/usr/share/doc/adduser/NEWS.Debian.gz (from adduser v3.134)
https://manpages.debian.org/bookworm/adduser/adduser.8.en.html
2 months ago
Johannes Schauer Marin Rodrigues 113532b3e1
refactor worker function to remove code duplication
Thanks: Guillem Jover <guillem@debian.org>
3 months ago
Johannes Schauer Marin Rodrigues d244f4f1de
release 1.4.3 3 months ago
Johannes Schauer Marin Rodrigues 81589889f9
check for dpkg-dev being installed for dpkg-architecture when doing foreign fakechroot 3 months ago
Johannes Schauer Marin Rodrigues 35cd477fea
Take hard links into account when computing disk usage based on dpkg-gencontrol.pl
Thanks: Guillem Jover <guillem@debian.org>, Sven Joachim <svenjoac@gmx.de>
3 months ago
Johannes Schauer Marin Rodrigues a7586e55d1
coverage.txt: exclude create-foreign-tarball from arm64 in fakechroot mode because usrmerge postinst under fakechroot wants to copy /lib/ld-linux-x86-64.so.2 (which does not exist) instead of /lib64/ld-linux-x86-64.so.2 3 months ago
Johannes Schauer Marin Rodrigues 65c27a55b3
cleanup start-stop-daemon without root prefix when performing a pivot-root 3 months ago
Johannes Schauer Marin Rodrigues 59c9c399c6
remove leftover debugging output 3 months ago
Johannes Schauer Marin Rodrigues e661b79749
mmdebstrap-autopkgtest-build-qemu: add reasons for image being raw and not qcow2 3 months ago
Johannes Schauer Marin Rodrigues 4bcd6fa015
rename install-libmagic-mgc-on-arm64 test and allow running it on both amd64 and arm64 3 months ago
Johannes Schauer Marin Rodrigues b54564a84d
release 1.4.2 3 months ago
Johannes Schauer Marin Rodrigues ba6e9af9a2
README.md: update list of contributors 3 months ago
Francesco Poli 8410dc6636
mmdebstrap-autopkgtest-build-qemu: fix octal mode computation 3 months ago
Johannes Schauer Marin Rodrigues 3e233e10df
mmdebstrap-autopkgtest-build-qemu: make the hostname 'host' as it is done by autopkgtest-build-qemu as it is expected by sbuild-qemu-update and sbuild-qemu-boot 3 months ago
Johannes Schauer Marin Rodrigues 79ef2e3437
tests/remove-start-stop-daemon-and-policy-rc-d-in-hook: remove /usr/sbin/start-stop-daemon and not /sbin/start-stop-daemon 3 months ago
Johannes Schauer Marin Rodrigues 2e7a3ae8b7
remove docs concerning qemu-user-static 3 months ago
Johannes Schauer Marin Rodrigues cc831fc276
remove clean-up of qemu-user-static as it is not copied into the chroot anymore 3 months ago
Johannes Schauer Marin Rodrigues 366d2ffbec
rename create-arm64-tarball test and allow running it on both amd64 and arm64 3 months ago
Johannes Schauer Marin Rodrigues a8583eb39b
fix documentation of buildd variant to only include essential, apt and build-essential 3 months ago
Johannes Schauer Marin Rodrigues ac2aba5074
fix spelling enviroment -> environment 3 months ago
Johannes Schauer Marin Rodrigues 134fc15634
rename include-libmagic-mgc-arm64 tests and allow running them on both amd64 and arm64 3 months ago
Johannes Schauer Marin Rodrigues 4d72f617d9
dpkg 1.22.3 moved start-stop-daemon from /sbin to /usr/sbin, see #1059982 3 months ago
Johannes Schauer Marin Rodrigues ae5bddb2aa
coverage.sh: anticipate more variations for CMD 3 months ago
Johannes Schauer Marin Rodrigues 1c669e8f86
tests/chrootless-fakeroot: exclude /var/log/journal and /etc/credstore* from tarballs instead of trying to fix them up 3 months ago
Johannes Schauer Marin Rodrigues 4c87024356
release 1.4.1 3 months ago
Johannes Schauer Marin Rodrigues 2f768b07dc
coverage.sh: check for pod2man errors 3 months ago
Johannes Schauer Marin Rodrigues 4ca0556cd2
mmdebstrap-autopkgtest-build-qemu: usability and man page improvements
- explicitly instruct to add --boot=efi to autopkgtest-virt-qemu
 - add example how to run autopkgtest with --boot=efi
 - document image location requirements giving unshare restrictions
 - check if foreign arch is configured
 - instruct how to add a foreign architecture
 - check that the unshared user is able to access the image location
 - suggest to install qemu-system-* packages if they are missing
 - suggest to install packages containing EDK II OVMF UEFI firmware
3 months ago
Johannes Schauer Marin Rodrigues d9f9c64ac2
do not fail during cleanup if /etc/apt/apt.conf.d/00mmdebstrap got removed, only warn 3 months ago
Johannes Schauer Marin Rodrigues dd94ee3b84
read files passed as --aptopt and --dpkgopt outside the unshared namespace to avoid permission issues 3 months ago
Johannes Schauer Marin Rodrigues 99d2579e0b
document that the required and minbase variants do not explicitly install apt 3 months ago
Johannes Schauer Marin Rodrigues 610058d105
document how SUITE influences the selection of essential packages 3 months ago
Johannes Schauer Marin Rodrigues 2ff8f6142d
document how to run chrootless mode wrapped inside mmdebstrap 3 months ago
Johannes Schauer Marin Rodrigues 417d958a14
document how to remove a directory created with unshare mode 3 months ago
Johannes Schauer Marin Rodrigues 8674e11c71
allow for /etc/resolv.conf and /etc/hostname to already exist inside the chroot without warning about it 3 months ago
Johannes Schauer Marin Rodrigues daa886264b
reword the first few paragraphs
Thanks: Raphaƫl Hertzog
3 months ago
Johannes Schauer Marin Rodrigues d157ba2b9a
only print short --help output if wrong args are passed 3 months ago
Johannes Schauer Marin Rodrigues 884a04b18a
make_mirror.sh: disable networking 3 months ago
Johannes Schauer Marin Rodrigues 014a9c30a5
tests/check-against-debootstrap-dist: systemd 255 dropped split-/usr support 3 months ago
Johannes Schauer Marin Rodrigues 90fe7941bb
tests/check-against-debootstrap-dist: debootstrap installs prio:required packages in oldstable and stable for the buildd profile 3 months ago
Johannes Schauer Marin Rodrigues 428ee78121
disallow running chrootless as root without fakeroot unless --skip=check/chrootless is used 3 months ago
Johannes Schauer Marin Rodrigues ae6dcc001d
tests/install-busybox-based-sub-essential-system: busybox 1:1.36.1-6 moved to /usr 3 months ago
Johannes Schauer Marin Rodrigues b4ba78897b
make_mirror.sh: add newline at the end of /etc/hosts so that appending writes to the next line and not the current 3 months ago
Johannes Schauer Marin Rodrigues 69954515e7
tests/chrootless-fakeroot: also fix permissions and extended attributes for /etc/credstore 3 months ago
Johannes Schauer Marin Rodrigues 136cbdf0f1
run_qemu.sh: replace storing the pid and kill it a trap by using 'setpriv --pdeathsig TERM' 3 months ago
Johannes Schauer Marin Rodrigues 87edb1c2d1
coverage.sh: also run shellcheck on mmdebstrap-autopkgtest-build-qemu 3 months ago
Johannes Schauer Marin Rodrigues 0de9e19ca4
make_mirror.sh: explicitly install passwd since systemd 254.4-1 doesn't pull it in anymore 3 months ago

@ -1,3 +1,23 @@
1.4.3 (2024-02-01)
------------------
- take hard links into account when computing disk usage
1.4.2 (2024-01-29)
------------------
- allow for start-stop-daemon to be in either /sbin or /usr/sbin
- mmdebstrap-autopkgtest-build-qemu: fix octal mode computation and hostname
1.4.1 (2024-01-09)
------------------
- set DPkg::Chroot-Directory in APT_CONFIG to simplify calling apt in hooks
- disallow running chrootless as root without fakeroot unless
--skip=check/chrootless is used
- only print short --help output if wrong args are passed
- read files passed as --aptopt and --dpkgopt outside the unshared namespace
1.4.0 (2023-10-24)
------------------

@ -165,6 +165,7 @@ Contributors
- Konstantin Demin
- David Kalnischkies
- Emilio Pozuelo Monfort
- Francesco Poli
- Jakub Wilk
- Joe Groocock
- Nicolas Vigier

@ -2,11 +2,21 @@
set -eu
if [ -e ./mmdebstrap ]; then
# by default, use the mmdebstrap executable in the current directory together
# with perl Devel::Cover but allow to overwrite this
: "${CMD:=perl -MDevel::Cover=-silent,-nogcov ./mmdebstrap}"
case "$CMD" in
"mmdebstrap "*|mmdebstrap|*" mmdebstrap"|*" mmdebstrap "*)
MMSCRIPT="$(command -v mmdebstrap 2>/dev/null)";;
*) MMSCRIPT=./mmdebstrap;;
esac
if [ -e "$MMSCRIPT" ]; then
TMPFILE=$(mktemp)
perltidy < ./mmdebstrap > "$TMPFILE"
perltidy < "$MMSCRIPT" > "$TMPFILE"
ret=0
diff -u ./mmdebstrap "$TMPFILE" || ret=$?
diff -u "$MMSCRIPT" "$TMPFILE" || ret=$?
if [ "$ret" -ne 0 ]; then
echo "perltidy failed" >&2
rm "$TMPFILE"
@ -14,12 +24,14 @@ if [ -e ./mmdebstrap ]; then
fi
rm "$TMPFILE"
if [ "$(sed -e '/^__END__$/,$d' ./mmdebstrap | wc --max-line-length)" -gt 79 ]; then
if [ "$(sed -e '/^__END__$/,$d' "$MMSCRIPT" | wc --max-line-length)" -gt 79 ]; then
echo "exceeded maximum line length of 79 characters" >&2
exit 1
fi
perlcritic --severity 4 --verbose 8 ./mmdebstrap
perlcritic --severity 4 --verbose 8 "$MMSCRIPT"
pod2man "$MMSCRIPT" >/dev/null
fi
for f in tarfilter coverage.py caching_proxy.py; do
@ -27,7 +39,7 @@ for f in tarfilter coverage.py caching_proxy.py; do
black --check "./$f"
done
shellcheck --exclude=SC2016 coverage.sh make_mirror.sh run_null.sh run_qemu.sh gpgvnoexpkeysig hooks/*/*.sh
shellcheck --exclude=SC2016 coverage.sh make_mirror.sh run_null.sh run_qemu.sh gpgvnoexpkeysig mmdebstrap-autopkgtest-build-qemu hooks/*/*.sh
mirrordir="./shared/cache/debian"
@ -63,9 +75,6 @@ export LC_ALL=C.UTF-8
: "${HAVE_BINFMT:=yes}"
# by default, use the mmdebstrap executable in the current directory together
# with perl Devel::Cover but allow to overwrite this
: "${CMD:=perl -MDevel::Cover=-silent,-nogcov ./mmdebstrap}"
mirror="http://127.0.0.1/debian"
export HAVE_QEMU HAVE_BINFMT RUN_MA_SAME_TESTS DEFAULT_DIST SOURCE_DATE_EPOCH CMD mirror

@ -194,18 +194,18 @@ Skip-If:
hostarch != "amd64"
not run_ma_same_tests
Test: include-libmagic-mgc-arm64
Test: include-foreign-libmagic-mgc
Needs-Root: true
Needs-APT-Config: true
Skip-If:
hostarch != "amd64"
hostarch not in ["amd64", "arm64"]
not run_ma_same_tests
Test: include-libmagic-mgc-arm64-with-multiple-arch-options
Test: include-foreign-libmagic-mgc-with-multiple-arch-options
Needs-Root: true
Needs-APT-Config: true
Skip-If:
hostarch != "amd64"
hostarch not in ["amd64", "arm64"]
not run_ma_same_tests
Test: aptopt
@ -365,21 +365,22 @@ Variants: custom
Modes: chrootless
Needs-APT-Config: true
Test: install-libmagic-mgc-on-arm64
Test: install-libmagic-mgc-on-foreign
Variants: custom
Modes: chrootless
Skip-If:
hostarch != "amd64"
hostarch not in ["amd64", "arm64"]
not have_binfmt
Test: install-busybox-based-sub-essential-system
Needs-Root: true
Test: create-arm64-tarball
Test: create-foreign-tarball
Modes: root unshare fakechroot
Skip-If:
hostarch != "amd64"
hostarch not in ["amd64", "arm64"]
mode == "fakechroot" and not run_ma_same_tests
mode == "fakechroot" and hostarch == "arm64" # usrmerge postinst under fakechroot wants to copy /lib/ld-linux-x86-64.so.2 (which does not exist) instead of /lib64/ld-linux-x86-64.so.2
not have_binfmt
Test: no-sbin-in-path
@ -432,4 +433,4 @@ Test: skip-output-mknod
Modes: root unshare
Test: skip-tar-in-mknod
Modes: unshare
Modes: root

@ -1,4 +1,13 @@
#!/bin/sh
#
# This script makes sure that the apt sources.list and preferences from outside
# the chroot also exist inside the chroot by *appending* them to any existing
# files. If you do not want to keep the original content, add another setup
# hook before this one which cleans up the files you don't want to keep.
#
# If instead of copying sources.list verbatim you want to mangle its contents,
# consider using python-apt for that. An example can be found in the Debian
# packaging of mmdebstrap in ./debian/tests/sourcesfilter
set -eu

@ -15,7 +15,7 @@ env APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get indextargets --no-release-info -
| while read -r path; do
mkdir -p "$rootdir/run/mmdebstrap"
if [ ! -d "/$path" ]; then
echo "/$path is not an existing directory" >&2
echo "W: /$path is not an existing directory" >&2
continue
fi
case $MMDEBSTRAP_MODE in

@ -15,6 +15,10 @@ case "$ver" in
echo "usr-is-merged package from src:usrmerge installed -- not running merged-usr essential hook" >&2
exit 0
;;
'not-installed ')
echo "usr-is-merged was not installed in a previous hook -- not running merged-usr essential hook" >&2
exit 0
;;
*)
echo "unexpected situation for package usr-is-merged: $ver" >&2
exit 1

@ -4,12 +4,22 @@ set -eu
env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get update --error-on=any
# if the usr-is-merged package cannot be installed with apt, do nothing
if ! env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then
echo "no package called usr-is-merged found -- not running merged-usr extract hook" >&2
exit 0
if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then
# if apt-cache exited successfully, then usr-is-merged exists either as
# a real or virtual package
if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged 2>/dev/null | grep -q "Package: usr-is-merged"; then
echo "usr-is-merged found -- running merged-usr extract hook" >&2
else
# The usr-is-merged must be virtual, so assume that nothing
# has to be done. This is the case with Debian Trixie or later
# or with Ubuntu Lunar or later
echo "usr-is-merged found but not real -- not running merged-usr extract hook" >&2
exit 0
fi
else
echo "package usr-is-merged found -- running merged-usr extract hook" >&2
# if the usr-is-merged package cannot be installed with apt, do nothing
echo "no package providing usr-is-merged found -- not running merged-usr extract hook" >&2
exit 0
fi
# resolve the script path using several methods in order:

@ -4,12 +4,22 @@ set -eu
env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get update --error-on=any
# if the usr-is-merged package cannot be installed with apt, do nothing
if ! env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then
echo "no package called usr-is-merged found -- not running merged-usr setup hook" >&2
exit 0
if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then
# if apt-cache exited successfully, then usr-is-merged exists either as
# a real or virtual package
if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged 2>/dev/null | grep -q "Package: usr-is-merged"; then
echo "usr-is-merged found -- running merged-usr setup hook" >&2
else
# The usr-is-merged must be virtual, so assume that nothing
# has to be done. This is the case with Debian Trixie or later
# or with Ubuntu Lunar or later
echo "usr-is-merged found but not real -- not running merged-usr setup hook" >&2
exit 0
fi
else
echo "package usr-is-merged found -- running merged-usr setup hook" >&2
# if the usr-is-merged package cannot be installed with apt, do nothing
echo "no package providing usr-is-merged found -- not running merged-usr setup hook" >&2
exit 0
fi
# resolve the script path using several methods in order:

@ -236,7 +236,11 @@ END
esac
# shellcheck disable=SC2086
APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install $pkgs
APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install $pkgs \
|| APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install \
-oDebug::pkgProblemResolver=true -oDebug::pkgDepCache::Marker=1 \
-oDebug::pkgDepCache::AutoInstall=1 \
$pkgs
rm "$rootdir/var/cache/apt/archives/lock"
rmdir "$rootdir/var/cache/apt/archives/partial"
@ -449,10 +453,11 @@ if [ "$HAVE_QEMU" = "yes" ]; then
tmpdir="$(mktemp -d)"
trap 'kill "$PROXYPID" || :;cleanuptmpdir; cleanup_newcachedir' EXIT INT TERM
pkgs=perl-doc,systemd-sysv,perl,arch-test,fakechroot,fakeroot,mount,uidmap,qemu-user-static,qemu-user,dpkg-dev,mini-httpd,libdevel-cover-perl,libtemplate-perl,debootstrap,procps,apt-cudf,aspcud,python3,libcap2-bin,gpg,debootstrap,distro-info-data,iproute2,ubuntu-keyring,apt-utils,squashfs-tools-ng,genext2fs,linux-image-generic
pkgs=perl-doc,systemd-sysv,perl,arch-test,fakechroot,fakeroot,mount,uidmap,qemu-user-static,qemu-user,dpkg-dev,mini-httpd,libdevel-cover-perl,libtemplate-perl,debootstrap,procps,apt-cudf,aspcud,python3,libcap2-bin,gpg,debootstrap,distro-info-data,iproute2,ubuntu-keyring,apt-utils,squashfs-tools-ng,genext2fs,linux-image-generic,passwd
if [ ! -e ./mmdebstrap ]; then
pkgs="$pkgs,mmdebstrap"
fi
pkgs="$pkgs,auditd"
arches=$HOSTARCH
if [ "$RUN_MA_SAME_TESTS" = "yes" ]; then
case "$HOSTARCH" in
@ -493,6 +498,8 @@ mount -t 9p -o trans=virtio,access=any,msize=128k mmdebstrap /mnt
# need to restart mini-httpd because we mounted different content into www-root
systemctl restart mini-httpd
ip link set enp0s1 down || :
handler () {
while IFS= read -r line || [ -n "$line" ]; do
printf "%s %s: %s\n" "$(date -u -d "0 $(date +%s.%3N) seconds - $2 seconds" +"%T.%3N")" "$1" "$line"
@ -530,7 +537,8 @@ END
fi
# set PATH to pick up the correct mmdebstrap variant
env PATH="$(dirname "$(realpath --canonicalize-existing "$CMD")"):$PATH" \
debvm-create --skip=usrmerge --size="$DISK_SIZE" --release="$DEFAULT_DIST" \
debvm-create --skip=usrmerge,systemdnetwork \
--size="$DISK_SIZE" --release="$DEFAULT_DIST" \
--output="$newcachedir/debian-$DEFAULT_DIST.ext4" -- \
--architectures="$arches" --include="$pkgs" \
--setup-hook='echo "Acquire::http::Proxy \"http://127.0.0.1:8080/\";" > "$1/etc/apt/apt.conf.d/00proxy"' \
@ -541,7 +549,7 @@ END
--customize-hook='touch "$1/mmdebstrap-testenv"' \
--customize-hook='copy-in "'"$tmpdir"'/mmdebstrap.service" /etc/systemd/system/' \
--customize-hook='copy-in "'"$tmpdir"'/worker.sh" /' \
--customize-hook='printf 127.0.0.1 localhost > "$1/etc/hosts"' \
--customize-hook='echo 127.0.0.1 localhost > "$1/etc/hosts"' \
--customize-hook='printf "START=1\nDAEMON_OPTS=\"-h 127.0.0.1 -p 80 -u nobody -dd /mnt/cache -i /var/run/mini-httpd.pid -T UTF-8\"\n" > "$1/etc/default/mini-httpd"' \
"$mirror"

@ -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"' \

@ -30,7 +30,9 @@ B<mmdebstrap-autopkgtest-build-qemu> is a mostly compatible drop-in replacement
for L<autopkgtest-build-qemu(1)> with two main differences: Firstly, it uses
L<mmdebstrap(1)> instead of L<vmdb2(1)> and thus is able to create QEMU disk
images without requiring superuser privileges. Secondly, it uses
L<systemd-boot(7)> and thus only supports booting via EFI.
L<systemd-boot(7)> and thus only supports booting via EFI. For architectures
for which L<autopkgtest-virt-qemu(1)> does not default to EFI booting you must
pass B<--boot=efi> when invoking the autopkgtest virt backend.
=head1 POSITIONAL PARAMETERS
@ -96,9 +98,17 @@ Passes an additional B<--keyring> parameter to B<mmdebstrap>.
=head1 EXAMPLES
$ mmdebstrap-autopkgtest-build-qemu --boot=efi stable /path/to/debian-stable-i386.img i386
Make sure, that F</path/to/debian-unstable.img> is a path that the unshared
user has access to. This can be done by ensuring world-execute permissions on
all path components or by creating the image in a world-readable directory like
/tmp before copying it into its final location.
$ mmdebstrap-autopkgtest-build-qemu --boot=efi unstable /path/to/debian-unstable.img
$ mmdebstrap-autopkgtest-build-qemu --boot=efi --arch=amd64 unstable /path/to/debian-unstable.img
[...]
$ autopkgtest mypackage -- qemu --boot=efi --dpkg-architecture=amd64 /path/to/debian-unstable.img
Make sure to add B<--boot=efi> to both the B<mmdebstrap-autopkgtest-build-qemu>
as well as the B<autopkgtest-virt-qemu> invocation.
=head1 SEE ALSO
@ -223,37 +233,63 @@ test "$BOOT" = efi ||
case "$ARCHITECTURE" in
amd64)
EFIIMG=bootx64.efi
QEMUARCH=x86_64
VMFPKG=ovmf
;;
arm64)
EFIIMG=bootaa64.efi
QEMUARCH=aarch64
VMFPKG=qemu-efi-aarch64
;;
armhf)
EFIIMG=bootarm.efi
QEMUARCH=arm
VMFPKG=qemu-efi-arm
;;
i386)
EFIIMG=bootia32.efi
QEMUARCH=i386
VMFPKG=ovmf-ia32
;;
riscv64)
EFIIMG=bootriscv64.efi
QEMUARCH=riscv64
VMFPKG=
;;
*)
die "unsupported architecture"
die "unsupported architecture: $ARCHITECTURE"
;;
esac
test_installed() {
pkg="$1"
if [ "$(dpkg-query -f '${db:Status-Status}' -W "$pkg")" != installed ]; then
die "please install $pkg"
fi
}
for pkg in autopkgtest dosfstools e2fsprogs fdisk mount mtools passwd uidmap; do
test_installed "$pkg"
done
if test "$(dpkg-query -f '${db:Status-Status}' -W binutils-multiarch)" = installed; then
GNU_PREFIX=
else
test_installed dpkg-dev
GNU_ARCHITECTURE="$(dpkg-architecture "-a$ARCHITECTURE" -qDEB_HOST_GNU_TYPE)"
GNU_PREFIX="$GNU_ARCHITECTURE-"
GNU_SUFFIX="-$(echo "$GNU_ARCHITECTURE" | tr _ -)"
test "$(dpkg-query -f '${db:Status-Status}' -W "binutils$GNU_SUFFIX")" = installed ||
die "please install binutils$GNU_SUFFIX or binutils-multiarch"
fi
for pkg in autopkgtest dosfstools e2fsprogs fdisk mount mtools passwd "systemd-boot-efi:$ARCHITECTURE" uidmap; do
test "$(dpkg-query -f '${db:Status-Status}' -W "$pkg")" = installed ||
die "please install $pkg"
done
arches=" $(dpkg --print-architecture) $(dpkg --print-foreign-architectures | tr '\n' ' ') "
case $arches in
*" $ARCHITECTURE "*) : ;; # nothing to do
*) die "enable $ARCHITECTURE by running: sudo dpkg --add-architecture $ARCHITECTURE && sudo apt update" ;;
esac
test_installed "systemd-boot-efi:$ARCHITECTURE"
BOOTSTUB="/usr/lib/systemd/boot/efi/linux${EFIIMG#boot}.stub"
@ -270,14 +306,35 @@ WORKDIR=$(mktemp -d)
FAT_OFFSET_SECTORS=$((1024*2))
FAT_SIZE_SECTORS=$((1024*254))
# The image is raw and not in qcow2 format because:
# - faster run-time as the "qemu-image convert" step is not needed
# - image can be used independent of qemu tooling
# - modifying the image just with "mount" instead of requiring qemu-nbd
# - sparse images make the file just as small as with qcow2
# - trim support is more difficult on qcow2
# - snapshots and overlays work just as well with raw images
# - users who prefer qcow2 get to choose to run it themselves with their own
# custom options like compression
#
# --map-users=auto --map-user=0 => 0:$UID:1 + 1:$SUBUIDBASE:65535
# --map-users=auto --map-user=65536 => 0:$SUBUIDBASE:65536 + 65536:$UID:1
#
# Make the image writeable to the first subgid. mmdebstrap will map this gid to
# the root group. unshare instead will map the current gid to 0 and the first
# subgid to 1. Therefore mmdebstrap will be able to write to the image.
rm -f "$IMAGE"
: >"$IMAGE"
unshare -U -r --map-groups=auto chown 0:1 "$IMAGE"
unshare --map-user=0 --map-group=0 --map-groups=auto chown 0:1 "$IMAGE"
chmod 0660 "$IMAGE"
# Make sure that the unshared user is able to access the file.
# Alternatively to using /sbin/mkfs.ext4 could use --format=ext2 which would
# add an extra copy operation and come with the limitations of ext2.
# Another solution: https://github.com/tytso/e2fsprogs/pull/118
if ! mmdebstrap --unshare-helper touch "$IMAGE"; then
die "$IMAGE cannot be accessed by the unshared user -- either make all path components up to the image itself world-executable or place the image into a world-readable path like /tmp"
fi
set -- \
--mode=unshare \
--variant=important \
@ -288,8 +345,8 @@ test "$RELEASE" = jessie &&
set -- "$@" \
"--include=init,linux-image-$ARCHITECTURE,python3" \
'--customize-hook=echo autopkgtestvm >"$1/etc/hostname"' \
'--customize-hook=echo 127.0.0.1 localhost autopkgtestvm >"$1/etc/hosts"' \
'--customize-hook=echo host >"$1/etc/hostname"' \
'--customize-hook=echo 127.0.0.1 localhost host >"$1/etc/hosts"' \
'--customize-hook=passwd --root "$1" --delete root' \
'--customize-hook=useradd --root "$1" --home-dir /home/user --create-home user' \
'--customize-hook=passwd --root "$1" --delete user' \
@ -307,10 +364,11 @@ EXT4_OPTIONS="offset=$EXT4_OFFSET_BYTES,assume_storage_prezeroed=1"
set -- "$@" \
"--customize-hook=download vmlinuz '$WORKDIR/kernel'" \
"--customize-hook=download initrd.img '$WORKDIR/initrd'" \
'--customize-hook=mount --bind "$1" "$1/mnt"' \
'--customize-hook=mount --bind "$1/mnt/mnt" "$1/mnt/dev"' \
'--customize-hook=mount --no-mtab --bind "$1" "$1/mnt"' \
'--customize-hook=mount --no-mtab --bind "$1/mnt/mnt" "$1/mnt/dev"' \
'--customize-hook=/sbin/mkfs.ext4 -d "$1/mnt" -L autopkgtestvm -E '"'$EXT4_OPTIONS' '$IMAGE' '$SIZE'" \
'--customize-hook=umount --lazy "$1/mnt"' \
'--customize-hook=umount --lazy --no-mtab "$1/mnt/dev"' \
'--customize-hook=umount --lazy --no-mtab "$1/mnt"' \
"$RELEASE" \
/dev/null
@ -321,7 +379,7 @@ echo "mmdebstrap $*"
mmdebstrap "$@" || die "mmdebstrap failed"