Compare commits

..

443 commits
main ... main

Author SHA1 Message Date
6617436d70
release 1.5.4 2024-10-28 09:16:58 +01:00
e4777d8fdf
document how to build a jessie chroot tarball 2024-10-28 09:15:18 +01:00
14e2f9ea11
tests/check-against-debootstrap-dist: require iputils-ping >= 3:20240905-1 2024-10-28 09:15:00 +01:00
99f82456f3
do not generate apt sources.list entry if SUITE is empty 2024-10-28 09:13:48 +01:00
5055b1b44c
tests/check-against-debootstrap-dist: since iputils-ping 20240905 there are no extended attributes anymore 2024-10-11 18:38:04 +02:00
c82fc7e261
format shell scripts with shfmt --binary-next-line --case-indent --indent 2 --simplify 2024-10-11 18:32:57 +02:00
eca6cb314c
tests/tarfilter-idshift: use a fabricated tarball instead of real chroot
iputils-ping does not ship /bin/ping with xattrs anymore.
2024-10-11 11:16:14 +02:00
395ee60a7f
release 1.5.3 2024-09-13 07:35:10 +02:00
1a8f2537ac
explain --pax-option with comment 2024-09-13 01:19:17 +02:00
0f5d935941
more sanity checks of target directory 2024-09-13 01:19:02 +02:00
dbce1ee27b
fix typo 2024-09-13 01:18:20 +02:00
6e4ed4a049
add --skip=cleanup/reproducible/machine-id 2024-09-13 01:17:52 +02:00
87b9b385b3
tarfilter: do not rely on paths being absolute (starting with a single slash) 2024-09-13 01:16:31 +02:00
acf036fa79
qemu-user now includes the static binary and qemu-user-static will be dropped in the long-term 2024-09-13 01:13:04 +02:00
d9c04338f8
support chrootless hurd-i386
$ mmdebstrap --variant=apt --include=passwd,debian-ports-archive-keyring,mmdebstrap,sysvinit-core,sysv-rc \
    --customize-hook='chroot "$1" mmdebstrap --mode=chrootless --arch=hurd-i386 --include=sysvinit-core,sysv-rc,debian-ports-archive-keyring,gnumach-image-1-486 --customize-hook="passwd --root=\"\$1\" --delete root" --variant=apt unstable /tmp/chroot.tar "deb http://ftp.ports.debian.org/debian-ports/ unstable main" "deb http://ftp.ports.debian.org/debian-ports/ unreleased main"' \
    --customize-hook='copy-out /tmp/chroot.tar .' unstable /dev/null
$ /sbin/mkfs.ext2 -q -F -o hurd -I 128 -b 4096 -d chroot.tar hurd.ext2 204800
$ qemu-system-i386 -nographic -net user,hostfwd=tcp:127.0.0.1:2222-:22 \
    -net nic,model=e1000 -m 1G -kernel gnumach-1.8-486-up \
    -append 'root=device:hd0 console=com0' --initrd './ext2fs.static --multiboot-command-line=${kernel-command-line} --host-priv-port=${host-port} --device-master-port=${device-port} --exec-server-task=${exec-task} -T typed ${root} $(task-create) $(task-resume),./exec.static $(exec-task=task-create)' \
    -drive file=hurd.ext2,format=raw
2024-08-27 01:15:00 +02:00
d0568a2b9e
Wait for (reap) potential zombies and otherwise long-running background processes
Otherwise they might hog resources like /dev/null which can then not be
unmounted resulting in their mountpoints (the regular files) not being
removable and then the removal of device nodes in run_cleanup (if
mmdebstrap is run with --skip=output/dev) will fail.

Another potential solution would be to run each hook and apt invocation
in its own process namespace but this would require to remount /proc and
this in turn would require a new mount namespace as well but we'd like
to keep the mount namespace across multiple hooks...
2024-08-18 22:07:52 +02:00
Jochen Sprickerhof
98b3c7f2cd
m-a-b-q: replace test_installed by dpkg-checkbuilddeps 2024-07-12 09:49:39 +02:00
8e2f62d08c
release 1.5.2 2024-06-26 12:52:56 +02:00
8130f1cef0
README.md: add new authors 2024-06-26 07:36:40 +02:00
6262c1921c
tests/check-against-debootstrap-dist: allow for different installation order of passwd and systemd 2024-06-26 07:35:26 +02:00
500b0d2512
mmdebstrap: fix perltidy formatting 2024-06-26 07:35:26 +02:00
Chris Hofstaedtler
37678c4fb5
tests/check-against-debootstrap-dist: allow /var/log/{faillog,lastlog} to be absent 2024-06-26 07:35:19 +02:00
134330d786
mmdebstrap-autopkgtest-build-qemu: pipe tarball to mke2fs and make bit-by-bit reproducible 2024-06-26 07:35:19 +02:00
Colin Watson
bda207af63
mmdebstrap-autopkgtest-build-qemu: selects wrong kernel package for armhf
Package: mmdebstrap
Version: 1.5.0-2
Severity: normal
Tags: patch

With --architecture=armhf, mmdebstrap-autopkgtest-build-qemu fails with
"E: Unable to locate package linux-image-armhf".  The following patch
fixes that.

i386 has a similar problem, and I included a fix for that in this patch
too, though I haven't tested that.

After this, armhf still fails with:

  arm-linux-gnueabihf-objdump: /usr/lib/systemd/boot/efi/linuxarm.efi.stub: file format not recognized
  failed to discover the alignment of the efi stub

... but that's a separate problem.
2024-06-17 13:57:21 +02:00
d0c30c70bd
tests/debootstrap: dump $tmpdir/debootstrap/debootstrap.log on failure 2024-06-12 11:41:17 +02:00
1a4bb39aad
check if libarchive is available for ext4 format 2024-06-12 11:40:39 +02:00
dfeb21cfe5
tests/empty-sources.list: only write out /etc/apt/sources.list if it doesn't exist yet
In the mmdebstrap autpkgtest, debian/tests/sourcesfilter will write out
/etc/apt/sources.list before the test's setup-hook is run. Thus, the
test's setup-hook will overwrite the contents of the sources.list that
debian/tests/sourcesfilter set up, which will end up pulling in the
wrong packages.

Thus, only write out our own /etc/apt/sources.list if it does not yet
exist. If it does exist, nothing needs to be done.
2024-06-11 15:49:48 +02:00
Jochen Sprickerhof
d0add325d3
Mark oldstable as bullseye_or_later 2024-06-11 15:49:48 +02:00
d4149bb4db
coverage.sh: add missing newline at end of curl output 2024-06-11 15:49:48 +02:00
61509691a8
tests: base-files now ships merged-/usr symlinks 2024-06-11 15:49:48 +02:00
3969727cc0
tests: bug #1031276 got fixed 2024-06-11 15:49:48 +02:00
6818d2ed40
release 1.5.1 2024-06-03 10:46:29 +02:00
9f8172bbc0
Switch variant apt from 'apt-get dist-upgrade' to apt patterns
The dist-upgrade method will fail to install essential packages but
still exit successfully without reporting an error, see #1072218
2024-06-03 08:04:03 +02:00
767e8a8bfb
run_qemu.sh: allow setting MMDEBSTRAP_TESTS_DEBUG and add auto-detection for amlogic a311d bananapi 2024-06-03 08:04:03 +02:00
abc66c5dc7
increase additional number of blocks from 1.1 to 1.2 times the computed number 2024-06-03 08:04:02 +02:00
f3ea5f2676
add flock on temporary directory in /tmp
Since systemd 256~rc3-3, /tmp is regularly cleaned up, removing files
older than 10 days. Since a rootfs contains files with timestamps
potentially much older than that, we exclude our temporary directory by
adding an exclusive lock on it which will stop systemd-tmpfiles from
cleaning up anything in it.

Thanks: Peter Pentchev <roam@ringlet.net>
2024-06-03 08:03:04 +02:00
5f491f2955
make_mirror.sh: force systemd to not mount /tmp as tmpfs
Since systemd 256~rc3-3, /tmp is mounted as tmpfs by default. This
breaks our tests because /tmp is mounted with nodev which makes it
impossible for debootstrap to mknod.
2024-06-02 08:44:28 +02:00
821c2e1328
In unshare mode, make all mounts private recursively
This emulates what unshare(1) does by default or by passing
--propagation=private explicitly. Mounting and unmounting filesystems
will affect mounts outside the namespace which are marked as shared (see
last column of `findmnt -o+PROPAGATION`). Since mmdebstrap's goal is to
isolate the mounts in the new namespace, we perform the equivalent of

    mount(NULL, "/", MS_REC | MS_PRIVATE, NULL);

from util-linux/sys-utils/unshare.c:set_propagation() which is in shell:

    mount --make-rprivate /

See mount_namespaces(7) for details. Without setting this, unmounting
/sys (and its sub-mounts) in unshare mode as root user will also unmount
the sub-mounts of /sys on the outside of the namespace. This breaks
tests/unshare-as-root-user which will fail to shut down with the following
errors in the log:

[FAILED] Failed unmounting mnt.mount - /mnt.
[FAILED] Failed unmounting run-lock.mount - Legacy Locks Directory /run/lock.
[...]
[  OK  ] Reached target poweroff.target - System Power Off.

Afterwards it will stall indefinitely. Stopping mmdebstrap from messing
with the /sys mounts on the outside stops this behaviour and allows to
cleanly shut down the virtual machine.

Thanks: Helmut Grohne
2024-06-02 08:11:51 +02:00
84f80673f4
Revert "attempt diagnosing skip-tar-in-mknod failure 'file changed as we read it' using auditd"
This reverts commit 726fc38d1d.
2024-05-15 00:23:56 +02:00
e3eafd0009
release 1.5.0 2024-05-14 07:39:00 +02:00
eed6a86480
mmdebstrap-autopkgtest-build-qemu: document https://github.com/util-linux/util-linux/issues/2981 2024-05-14 07:37:19 +02:00
5a06c67aaa
document that --cache-dir is also not a supported debootstrap option 2024-05-14 07:32:28 +02:00
d26afd110f
add more docs to the tar-out special hook 2024-05-14 07:32:09 +02:00
4ad8245a14
tests/missing-dev-sys-proc-inside-the-chroot: work around for bug #1071078 2024-05-14 07:31:27 +02:00
409686048b
add --format=ext4 2024-05-12 18:38:47 +02:00
Jochen Sprickerhof
4a294f05bd
Add test if dpkg-dev is installed
Needed for dpkg-architecture.
2024-04-15 14:50:54 +02:00
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.
2024-04-15 14:18:11 +02:00
1e68ffd2c4
tests: changelog.Debian.$foreign_arch.gz files are not always present 2024-03-27 05:50:51 +01:00
726fc38d1d
attempt diagnosing skip-tar-in-mknod failure 'file changed as we read it' using auditd 2024-03-27 05:49:46 +01:00
ae09a50f9d
document unshare --map-auto --map-user=65536 --map-group=65536 --keep-caps trick 2024-03-23 22:50:34 +01:00
9726836ac4
mmdebstrap-autopkgtest-build-qemu: add documentation of some unshare magic 2024-03-23 22:49:27 +01:00
cdf6959a41
make_mirror.sh: retry apt with verbose output 2024-03-23 22:38:30 +01:00
1cf0d87a60
hooks/file-mirror-automount/setup00.sh: prefix warning with W: 2024-03-23 22:37:26 +01:00
0973de1530
hooks/copy-host-apt-sources-and-preferences/setup00.sh: document with comment on top 2024-03-23 22:36:48 +01:00
d883fa13bb
hooks/maybe-merged-usr: prepare for the time when usr-is-merged exists only as a virtual package 2024-02-27 00:32:45 +01:00
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
2024-02-08 12:30:17 +01:00
113532b3e1
refactor worker function to remove code duplication
Thanks: Guillem Jover <guillem@debian.org>
2024-02-02 23:13:13 +01:00
d244f4f1de
release 1.4.3 2024-02-01 06:00:58 +01:00
81589889f9
check for dpkg-dev being installed for dpkg-architecture when doing foreign fakechroot 2024-02-01 05:58:44 +01:00
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>
2024-02-01 05:53:57 +01:00
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 2024-02-01 05:52:46 +01:00
65c27a55b3
cleanup start-stop-daemon without root prefix when performing a pivot-root 2024-01-30 07:41:08 +01:00
59c9c399c6
remove leftover debugging output 2024-01-30 07:09:19 +01:00
e661b79749
mmdebstrap-autopkgtest-build-qemu: add reasons for image being raw and not qcow2 2024-01-30 07:08:56 +01:00
4bcd6fa015
rename install-libmagic-mgc-on-arm64 test and allow running it on both amd64 and arm64 2024-01-30 07:08:22 +01:00
b54564a84d
release 1.4.2 2024-01-29 11:44:16 +01:00
ba6e9af9a2
README.md: update list of contributors 2024-01-29 11:39:53 +01:00
Francesco Poli
8410dc6636
mmdebstrap-autopkgtest-build-qemu: fix octal mode computation 2024-01-29 08:28:53 +01:00
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 2024-01-28 17:21:40 +01:00
79ef2e3437
tests/remove-start-stop-daemon-and-policy-rc-d-in-hook: remove /usr/sbin/start-stop-daemon and not /sbin/start-stop-daemon 2024-01-26 09:30:28 +01:00
2e7a3ae8b7
remove docs concerning qemu-user-static 2024-01-26 09:29:35 +01:00
cc831fc276
remove clean-up of qemu-user-static as it is not copied into the chroot anymore 2024-01-26 09:28:53 +01:00
366d2ffbec
rename create-arm64-tarball test and allow running it on both amd64 and arm64 2024-01-25 10:52:52 +01:00
a8583eb39b
fix documentation of buildd variant to only include essential, apt and build-essential 2024-01-25 09:50:13 +01:00
ac2aba5074
fix spelling enviroment -> environment 2024-01-25 09:49:51 +01:00
134fc15634
rename include-libmagic-mgc-arm64 tests and allow running them on both amd64 and arm64 2024-01-25 09:49:03 +01:00
4d72f617d9
dpkg 1.22.3 moved start-stop-daemon from /sbin to /usr/sbin, see #1059982 2024-01-25 09:30:46 +01:00
ae5bddb2aa
coverage.sh: anticipate more variations for CMD 2024-01-24 00:29:15 +01:00
1c669e8f86
tests/chrootless-fakeroot: exclude /var/log/journal and /etc/credstore* from tarballs instead of trying to fix them up 2024-01-24 00:27:51 +01:00
4c87024356
release 1.4.1 2024-01-23 17:38:55 +01:00
2f768b07dc
coverage.sh: check for pod2man errors 2024-01-23 16:32:50 +01:00
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
2024-01-23 16:28:58 +01:00
d9f9c64ac2
do not fail during cleanup if /etc/apt/apt.conf.d/00mmdebstrap got removed, only warn 2024-01-23 16:28:58 +01:00
dd94ee3b84
read files passed as --aptopt and --dpkgopt outside the unshared namespace to avoid permission issues 2024-01-23 16:28:58 +01:00
99d2579e0b
document that the required and minbase variants do not explicitly install apt 2024-01-23 16:28:58 +01:00
610058d105
document how SUITE influences the selection of essential packages 2024-01-23 16:28:57 +01:00
2ff8f6142d
document how to run chrootless mode wrapped inside mmdebstrap 2024-01-23 16:28:57 +01:00
417d958a14
document how to remove a directory created with unshare mode 2024-01-09 10:29:53 +01:00
8674e11c71
allow for /etc/resolv.conf and /etc/hostname to already exist inside the chroot without warning about it 2024-01-09 09:47:35 +01:00
daa886264b
reword the first few paragraphs
Thanks: Raphaël Hertzog
2024-01-08 23:02:55 +01:00
d157ba2b9a
only print short --help output if wrong args are passed 2024-01-08 23:01:53 +01:00
884a04b18a
make_mirror.sh: disable networking 2024-01-08 22:56:55 +01:00
014a9c30a5
tests/check-against-debootstrap-dist: systemd 255 dropped split-/usr support 2024-01-08 22:54:43 +01:00
90fe7941bb
tests/check-against-debootstrap-dist: debootstrap installs prio:required packages in oldstable and stable for the buildd profile 2024-01-08 22:53:36 +01:00
428ee78121
disallow running chrootless as root without fakeroot unless --skip=check/chrootless is used 2024-01-08 22:37:13 +01:00
ae6dcc001d
tests/install-busybox-based-sub-essential-system: busybox 1:1.36.1-6 moved to /usr 2024-01-08 22:36:07 +01:00
b4ba78897b
make_mirror.sh: add newline at the end of /etc/hosts so that appending writes to the next line and not the current 2024-01-08 22:30:33 +01:00
69954515e7
tests/chrootless-fakeroot: also fix permissions and extended attributes for /etc/credstore 2024-01-08 22:27:35 +01:00
136cbdf0f1
run_qemu.sh: replace storing the pid and kill it a trap by using 'setpriv --pdeathsig TERM' 2024-01-08 22:25:07 +01:00
87edb1c2d1
coverage.sh: also run shellcheck on mmdebstrap-autopkgtest-build-qemu 2024-01-08 16:50:08 +01:00
0de9e19ca4
make_mirror.sh: explicitly install passwd since systemd 254.4-1 doesn't pull it in anymore 2024-01-08 14:37:00 +01:00
f2020cf3ed
coverage.txt: disable merged-fakechroot-inside-unmerged-chroot on testing and unstable 2023-10-29 23:14:29 +01:00
7e5ffbeb93
use L<> in POD formatting 2023-10-28 15:26:25 +02:00
2856fbdda3
hooks/copy-host-apt-sources-and-preferences/setup00.sh: mkdir parent directories if necessary 2023-10-28 15:26:25 +02:00
fd33bd2a40 set DPkg::Chroot-Directory in APT_CONFIG
This is so that users calling apt-get install from a hook only need to
have APT_CONFIG=$MMDEBSTRAP_APT_CONFIG set and do not also have to pass
-oDPkg::Chroot-Directory="$1".

This breaks users running apt-get with
DPkg::Options::=--force-script-chrootless or with Dpkg::Pre-Install-Pkgs
from within a hook with APT_CONFIG=$MMDEBSTRAP_APT_CONFIG.

In those situations, DPkg::Chroot-Directory has to be set to the empty
string explicitly with -o to overwrite the APT_CONFIG setting.

Thanks: Helmut Grohne
2023-10-28 13:26:04 +00:00
1ffa88b182
release 1.4.0 2023-10-24 00:05:23 +02:00
abcfda0442
add example for mmdebstrap-autopkgtest-build-qemu 2023-10-23 11:56:15 +02:00
63d5ffb2a6
reflow paragraph 2023-10-23 11:55:53 +02:00
a1e5043676
add example on how to remove /etc/resolv.conf and /etc/hostname to make the chroot reproducible 2023-10-23 11:55:35 +02:00
ecc167e87e
add example for using debvm-create and debvm-run 2023-10-23 11:54:12 +02:00
2d758ba576
add example on how to add additional apt sources 2023-10-23 11:52:28 +02:00
16c7276921
be more verbose when sending SIGHUP to the process group 2023-10-23 11:51:41 +02:00
1a62ccec46
do not run genext2fs if $numblocks value is invalid 2023-10-23 11:51:14 +02:00
d02ea1c7f1
hooklistener: print errors as warning and not just as debug messages 2023-10-23 11:50:09 +02:00
ce12fbdd41
hooklistener: process error messages 2023-10-23 11:49:14 +02:00
aef8fcfb75
add comment explaining why dpkg-deb --extract still cannot be used 2023-10-23 11:48:32 +02:00
cb500ef6ba
add --skip=tar-in/mknod,copy-in/mknod,sync-in/mknod 2023-10-23 11:47:27 +02:00
c33f719278
coverage.py: print total runtime 2023-10-23 11:43:32 +02:00
84c53fc120
hook-helper: repurpose third arg for skip options now that qemu-user info does not need to be propagated anymore (was only needed for proot) 2023-10-23 10:41:46 +02:00
27d1bad2a5
since debootstrap 1.0.133, the buildd variant only installs apt and not priority:required anymore 2023-10-23 10:38:10 +02:00
261cfda58f
make_mirror.sh: drop disorderfs 2023-10-23 10:35:20 +02:00
629187cd68
set MMDEBSTRAP_FORMAT in hooks 2023-10-23 10:35:07 +02:00
e77e194ebd
only warn if symlink cannot be created, don't error out 2023-10-23 10:33:54 +02:00
bc7ce4affc
skip symlink creation if it already exists 2023-10-23 10:33:37 +02:00
199e577757
tarfilter: add --type-exclude option 2023-10-23 10:26:47 +02:00
21366f76b7
Stop copying qemu-$arch-static binary into the chroot
- since qemu-user binfmt has support for pre-opening the interpreter
   since stretch, there is no reason to copy it into the chroot anymore
2023-09-28 07:53:48 +02:00
bf41b91e6f
Properly implement --skip=output/dev and add --skip=output/mknod
- the first implementation of --skip=output/dev was broken in root mode
 - add tests
 - add documentation
2023-09-28 07:53:43 +02:00
cee8b67045
clarify, that deb.d.o is only used if SUITE is not a stable release 2023-09-27 08:03:23 +02:00
669c404938
typos: qume->qemu, SYS_CAP_ADMIN->CAP_SYS_ADMIN 2023-09-27 08:00:15 +02:00
767fa11571
mmdebstrap-autopkgtest-build-qemu: add reference to old grub+guestfish implementation 2023-09-27 07:58:53 +02:00
c23727e136
export container=mmdebstrap-unshare environment variable in unshare-mode hooks 2023-09-27 07:56:49 +02:00
eaa67aea9c
fixup comment indentation 2023-09-27 07:52:35 +02:00
6068e7d22e
tarfilter: fixup comment 2023-09-27 07:49:04 +02:00
8339721fca
make_mirror.sh: also use bash and wget to check for the proxy running 2023-09-27 07:47:08 +02:00
e5bcc1827e
make_mirror.sh: print success message at the end 2023-09-27 07:44:56 +02:00
806ea4b35d
make_mirror.sh: support FORCE_UPDATE=yes 2023-09-27 07:42:12 +02:00
8bf8da5e8e
coverage.txt: verbose and debug tests rely on variant:standard chroots 2023-09-27 07:41:26 +02:00
2c5e6db317
mmdebstrap-autopkgtest-build-qemu: add POD manual 2023-09-19 12:56:08 +02:00
c741711938
mmdebstrap-autopkgtest-build-qemu: make executable 2023-09-19 12:55:56 +02:00
1f606f913d
mmdebstrap-autopkgtest-build-qemu: allow binutils-multiarch and fix --script 2023-09-19 11:18:22 +02:00
be3cd00243
mmdebstrap-autopkgtest-build-qemu: add --arch option 2023-09-19 11:17:50 +02:00
Helmut Grohne
e07818d2d6
add mmdebstrap-autopkgtest-build-qemu 2023-09-17 07:48:39 +02:00
6cc9d1b99b
release 1.3.8 2023-08-20 13:38:13 +02:00
c7559e305e
coverage.py: move print of failed tests to the end 2023-08-20 08:03:48 +02:00
9c970c0326
coverage.sh: check whether wiki is up-to-date if run on developer machine 2023-08-20 08:03:30 +02:00
8eb09569bb
make_mirror.sh: remove duplicate --skip=usrmerge 2023-08-20 08:02:35 +02:00
44cf2f94a6
tests/ascii-armored-keys: convert key to armored using gpg instead of sed 2023-08-20 08:02:11 +02:00
1c67ac111a
exclude ./lost+found from tarball 2023-08-20 08:01:37 +02:00
8d91ca7b24
hooks/merged-usr: implement post-merging as debootstrap does (see #1049898) 2023-08-20 08:01:04 +02:00
4d76d04cfe
release 1.3.7 2023-06-21 07:56:05 +02:00
73f06a6356
set MMDEBSTRAP_SUITE when running hooks 2023-06-21 07:54:07 +02:00
956dcb42e1
switch from guestfish to debvm 2023-06-21 07:54:07 +02:00
e523741610
add hooks/copy-host-apt-sources-and-preferences 2023-06-21 07:54:02 +02:00
5a6883970a
release 1.3.6 2023-06-16 07:02:13 +02:00
5a9123aa11
testsuite changes for bookworm release 2023-06-16 07:01:12 +02:00
04950847d3
add debvm and bdebstrap to docs 2023-06-16 07:01:12 +02:00
f660f0095a
make_mirror.sh: kill proxypid more reliably upon failure 2023-06-16 07:01:12 +02:00
2b327ac0fd
caching_proxy.py: add comment about not using shutil.copyfileobj() 2023-06-14 07:34:51 +02:00
910427b598
make_mirror.sh: run apt-get update with --error-on=any 2023-06-11 15:42:31 +02:00
ebf7a67e37
README.md: link to HTML version of the man page 2023-06-02 05:07:04 +02:00
14f13c6309
tests/{eatmydata-via-hook-dir,jessie-or-older}: dash 0.5.12-3 dropped diversions 2023-05-11 14:49:25 +02:00
Jochen Sprickerhof
c66b41eb7e
Use dpkg-query --show to not get stuck in a pager 2023-05-07 08:46:07 +02:00
e27a8d3472
tests: doc-debian 11.0 changed the doc-base paths 2023-05-06 08:33:15 +02:00
Emilio Pozuelo Monfort
6dd3e557db
Reword gpgvnoexpkeysig public domain claim
cherry-picked from
e2cd601b6f
2023-03-23 17:45:41 +01:00
81d155cac8
release 1.3.5 2023-03-20 06:26:51 +01:00
b3338bd33b
refine warnings and add documentation for using --include with .deb files in unshare mode 2023-03-19 09:04:06 +01:00
71a9a2e7a9
tests/include-deb-file: run in all modes 2023-03-19 09:03:19 +01:00
19a2ec044a
fix undefined variable in error message 2023-03-19 09:02:43 +01:00
af95b4d778
hooks/file-mirror-automount/setup00.sh: use mmdebstrap --hook-helper upload and sync-in instead of cp 2023-03-19 09:01:37 +01:00
4d44b9dbbe
export MMDEBSTRAP_ARGV0 for hooks 2023-03-19 09:00:46 +01:00
846662276f
tests/multiple-include: be more liberal with packages adding or dropping maintainer scripts 2023-03-18 15:10:28 +01:00
9a19801095
debian-archive-keyring now puts *.asc keys into /etc/apt/trusted.gpg.d 2023-03-18 04:53:28 +01:00
9710ee16a0
release 1.3.4 2023-03-17 22:54:06 +01:00
8db443a6c4
README.md: update list of contributors 2023-03-17 08:03:19 +01:00
ff9b76ed19
improve debug and error message wording 2023-03-16 22:18:49 +01:00
Jakub Wilk
a719ffd20a
Fix apt.conf permissions
Having world-writable apt.conf may be exploited by locals users to
execute arbitrary code in the context of the user running mmdebstrap.
2023-03-16 21:56:16 +01:00
55cae49ec7
let test_unshare_userns error out itself if necessary 2023-03-16 08:14:39 +01:00
055e1719b9
try unsharing before automatically choosing unshare mode 2023-03-15 17:08:12 +01:00
2614924925
make_mirror.sh: clean up more things left over by USE_HOST_APT_CONFIG=yes 2023-03-13 13:22:22 +01:00
901d017099
tests: do not run debug and verbose tests with variant standards for arches affected by #1031276 2023-03-13 13:21:18 +01:00
6b49a2dbbf
coverage.sh: pass any cli args on to coverage.py 2023-03-13 13:20:05 +01:00
034698f8d3
tests/check-for-bit-by-bit-identical-format-output: use the default user check 2023-03-05 19:26:13 +01:00
a184cc003c
tests: run verbose and debug tests with --variant=standard for maximum output 2023-03-05 19:25:13 +01:00
8253daf403
make_mirror.sh: also delete mmdebstrap-*-*.* 2023-03-05 17:43:54 +01:00
dd774b4f20
tests: skip debootstrap tests variant - on oldstable because of #917773 2023-03-05 15:26:37 +01:00
1ffa32f590
caching_proxy.py: only take .deb and by-hash artifacts from the old path 2023-03-05 15:25:42 +01:00
db2be70f88
tests: split out creation of mmdebstrap chroot into its own test to avoid running the same thing multiple times and speed up tests 2023-03-05 15:23:26 +01:00
b214d74129
make_mirror.sh: output contents of sources.list and preferences.d for debugging 2023-03-05 11:07:09 +01:00
c3bcc7b04a
make_mirror.sh: filter out file:// mirrors when USE_HOST_APT_CONFIG=yes 2023-03-05 10:54:51 +01:00
5471b372e2
coverage.txt: mark two more tests Needs-APT-Config: true 2023-03-05 10:09:58 +01:00
cc8dab5be8
add non-free-firmware to docs 2023-03-05 10:02:23 +01:00
Jochen Sprickerhof
84ea1e042b
Fail in --mode=unshare when newuidmap is not available 2023-03-03 23:59:19 +01:00
5885291213
Use an caching apt proxy instead of copying /var/cache/apt/archives/*.deb
- only download Release files once and not by apt as well as with curl
   and thus avoid a mirror push happening between both downloads
 - no heuristic needed to place the file in their correct mirror
   location
 - no manual checksum checking
 - only throttle download speed when actually downloading and not when
   retrieving files from the cache
 - no translation of filenames between how the epoch colon is stored in
   files in /var/cache/apt/archives versus how it is stored in files on
   the mirrors
 - no special handling of stable update and security mirrors
 - implemented in Python instead of shell and thus an order of magnitude
   faster
2023-03-03 08:34:39 +01:00
70092c49e8
coverage.txt: #1030638 got fixed 2023-03-02 11:54:03 +01:00
158607b3af
mmdebstrap: improve docs for --keyring 2023-03-02 11:53:43 +01:00
e7f21ce04c
Do not die if reading the number of ext2 blocks failed as that would skip the cleanup action
Reported-by: Helmut Grohne <helmut@subdivi.de>
2023-02-23 21:50:55 +01:00
8bdd04fce1
release 1.3.3 2023-02-19 09:36:57 +01:00
aea6fc70d1
make_mirror.sh: add ONLY_DEFAULT_DIST and ONLY_HOSTARCH 2023-02-19 09:19:42 +01:00
b90f1196e5
tests/root-mode-inside-chroot: run test script with -x 2023-02-19 09:17:57 +01:00
2f337767ea
tests/multiple-include: tzdata is producing /etc/timezone again 2023-02-18 23:18:01 +01:00
3c0b992d94
coverage.txt: fakechroot bug #1023286 is not fixed in testing yet 2023-02-18 23:17:35 +01:00
80f8978ecc
coverage.sh: do not fail if files to be cleaned do not exist 2023-02-18 23:17:12 +01:00
2837f5b5d3
coverage.py: support USE_HOST_APT_CONFIG and new Needs-APT-Config field 2023-02-18 23:16:48 +01:00
55f376d04a
tests: more cleanup traps 2023-02-18 23:06:13 +01:00
f3ab0a3d2d
release 1.3.2 2023-02-16 07:33:11 +01:00
fdbb66f75a
coverage.txt: skip check-for-bit-by-bit-identical-format-output in variant standard on armel, armhf and mipsel because of #1031276 2023-02-14 22:19:42 +01:00
46fc269b54
improve documentation of unshare mode 2023-02-14 22:00:19 +01:00
02769190ad
tests/as-debootstrap-unshare-wrapper: bind-mount /proc to work around #1031222 2023-02-13 16:17:21 +01:00
b3810b0fcd
tests/as-debootstrap-unshare-wrapper: run in variants minbase and important 2023-02-13 14:48:01 +01:00
4c5097f59b
skip check-for-bit-by-bit-identical-format-output on 32bit arches because of #1030638 2023-02-13 14:45:28 +01:00
5e07567d5a
move running debootstrap from make_mirror.sh to a test case 2023-02-12 14:05:09 +01:00
3c0990d050
tarfilter, coverage.py: changes for black 23.1.0 2023-02-12 14:05:09 +01:00
8d9a94fca5
if /proc is bind-mounted, make it a (recursive) slave mount so that changes to it (like unmounting) do not propagate to the outside
Thanks: Helmut Grohne
2023-02-12 14:05:09 +01:00
b18849caac
Assume that we can always run unshare
With mount --rbind we can bind-mount /proc in a privileged docker
container as it is used by salsaci.
2023-02-12 14:05:08 +01:00
ba76c1d3d9
coverage.py: format skipped tests with format_test as well instead of printing a tuple 2023-02-10 13:00:17 +01:00
b474150f27
tests: fall back to diffoscope if cmp failed 2023-02-10 13:00:13 +01:00
a23dd36bb6
fix warning to not talk about bind-mounting 2023-02-10 04:00:11 +01:00
4c64adf6ee
add tests/auto-mode-as-normal-user 2023-02-10 04:00:11 +01:00
b648db0afd
tests: tzdata dropped /etc/timezone 2023-02-10 04:00:10 +01:00
8f8f5bd706
relax apt version regex even further to be able to cope with versions like 2.5.3ubuntu0.1 2023-02-10 04:00:10 +01:00
9ebb3d07ac
unify /proc mounting between root and unshare mode and fall back to rbind-mounting
This makes unshare mode work on salsaci and debci.
2023-02-10 03:59:33 +01:00
d9e6d62328
tests: redirect all id output to /dev/null 2023-02-09 10:53:09 +01:00
a2d5573749
tests: drop qemu requirements for tests that only use it to create a user by defaulting to SUDO_USER 2023-02-09 10:53:08 +01:00
98aef0d023
tests: do not run sysctl -w kernel.unprivileged_userns_clone=1 as its the default value since linux 5.10.1 (Dec 2020) 2023-02-01 18:39:27 +01:00
4a77fc76a8
.mailmap: add botched email due to https://salsa.debian.org/debian/devscripts/-/merge_requests/323 2023-02-01 18:39:26 +01:00
b990a3aa09
run apt with -oDebug:: options for all calls and not only in run_download() 2023-02-01 18:39:26 +01:00
090ce862c7
tests: replace adduser with useradd 2023-02-01 18:03:38 +01:00
a0133a6393
document maybe-merged-usr hook 2023-01-26 09:28:50 +01:00
7910ca79ac
redirect apt-helper drop-privs output to /dev/null as we are only interested in the exit status and want to avoid spurious error messages from apt 2023-01-26 09:28:35 +01:00
06f84b84ae
coverage.py: print accumulated time per test type 2023-01-26 09:27:51 +01:00
f737cce3f1
Support creating a fakechroot with merged-/usr on an unmerged-/usr system
Thanks: Helmut Grohne for finding this issue and help interating this
2023-01-24 09:58:42 +01:00
d554c0b469
de-duplicate FAKECHROOT_CMD_SUBST variable 2023-01-23 15:01:21 +01:00
Helmut Grohne
4974f59248
ldconfig.fakechroot: do not ignore it, if ldconfig was already called with -r 2023-01-23 07:20:13 +01:00
9cc494f245
release 1.3.1 2023-01-20 07:11:03 +01:00
6d220e9a8d
run script with -e to catch exit code 2023-01-20 07:09:17 +01:00
5ea299f3d2
document the apt variant better 2023-01-20 07:08:55 +01:00
fb1e5c32e6
improve maybe-* hook conditions 2023-01-20 07:07:58 +01:00
104fba0256
run_null.sh: use file descriptors instead of temporary files to get the exit status of the first part of a pipeline 2023-01-16 23:16:11 +01:00
736cb493ea
release 1.3.0 2023-01-16 19:30:13 +01:00
c738e96752
allow empty sources.list entries 2023-01-16 15:19:50 +01:00
860a9048d5
make info message lowercase like the rest 2023-01-16 15:01:30 +01:00
327b75846f
add --skip=check/signed-by 2023-01-16 14:58:23 +01:00
42386c90c8
coverage.py: add timing for tests 2023-01-16 14:34:25 +01:00
ec58228f71
add more docs that non-empty SUITE will be used to select Essential:yes set 2023-01-16 14:32:42 +01:00
f27ed490d6
Do not split --include values again in run_download and run_install
Closes: #1028977
2023-01-16 12:13:21 +01:00
3db3779b6a
add hooks/maybe-jessie-or-older and hooks/maybe-merged-usr 2023-01-16 12:06:22 +01:00
cc5ea8c0c7
tests/chrootless: now that all of essential is supported, test everything 2023-01-16 12:06:22 +01:00
52d1531c0d
tests/multiple-include: tzdata stopped shipping /usr/sbin/tzconfig 2023-01-16 12:06:21 +01:00
4925587b34
tests/as-debootstrap-unshare-wrapper: run diff with -u 2023-01-16 12:06:21 +01:00
ebd0f282fd
run_qemu.sh: output log while test is running with tail -f 2023-01-16 12:06:20 +01:00
36f691f22b
document that positional arguments can be mixed with non-positional ones and that a double-dash has the expected effect 2023-01-16 12:06:20 +01:00
b0a5c30fb1
to find signed-by value, run gpg on the individual keys to print better error messages in case it fails (gpg doesn't give an indication which file it was unable to read) and print progress bar 2023-01-16 12:06:20 +01:00
ea2b57870b
warn if a hook is named like one but not executable and if a hook is executable but not named like one 2023-01-16 07:55:27 +01:00
0b7188ce32
be more verbose when 'apt-get update' failed 2023-01-16 07:54:27 +01:00
de8e31193b
make_mirror.sh: output log and exit status to two individual files 2023-01-16 07:54:03 +01:00
e93c145822
make_mirror.sh: also install foreign amd64 on arm64 2023-01-16 07:44:38 +01:00
3b953d4398
make_mirror.sh: test if debian-*.qcow exists before removing it 2023-01-16 07:44:13 +01:00
9945e65701
skip running apt-get update if we are very sure that it was already run 2023-01-16 07:43:09 +01:00
644ac62ecd
tests/as-debootstrap-unshare-wrapper: isc-dhcp-client postinst doesn't create /etc/apparmor.d/local/sbin.dhclient 2023-01-16 07:43:09 +01:00
eaf96dc7f6
make_mirror.sh: remove the old cache if the last run failed 2023-01-16 07:43:09 +01:00
65ea2edfab
hooks/no-merged-usr/essential00.sh: symlink to ../merged-usr/essential00.sh
Both hooks need to install the real usr-is-merged package after having
installed a dummy in the setup hook even if it's for different reasons.
2023-01-16 07:43:08 +01:00
3ba97580ec
hooks/jessie-or-older: split into two individual hook files 2023-01-16 07:43:08 +01:00
5fd96553f5
release 1.2.5 2023-01-04 07:24:50 +01:00
b67d30cb86
mmdebstrap: bump copyright year 2023-01-04 07:24:14 +01:00
d4eb268795
warn if hook directory contains no executable scripts 2023-01-04 07:23:56 +01:00
60186650cd
tests: tzdata gained lintian overrides and preinst prerm scripts 2023-01-03 22:24:22 +01:00
59ac33cebe
make shellcheck 0.9.0-1 more happy 2022-12-25 08:00:15 +01:00
847d8fe95b
make shellcheck 0.9.0-1 happy 2022-12-25 01:06:00 +01:00
4595d3df44
release 1.2.4 2022-12-23 10:57:11 +01:00
577f690540
README.md: update list of contributors 2022-12-23 10:55:20 +01:00
2b832e0128
add jessie-or-older extract hook 2022-12-23 10:06:28 +01:00
a7b7e16033
move extract hook execution after run_prepare so that fakechroot works in it 2022-12-23 10:06:28 +01:00
eb98dfbaee
apt also needs /var/lib to exist 2022-12-23 10:06:28 +01:00
6c5210a94f
error out early if setup fails and thus the ext2 block reader returns EOF 2022-12-23 10:06:28 +01:00
a6a31e60eb
make sure that the unshared user has read access to the included package files 2022-12-23 10:06:28 +01:00
0dfd9adf2b
make sure absolute package paths start with a slash and are readable files 2022-12-23 10:06:28 +01:00
2fd3d768e8
avoid division by zero in progress computation 2022-12-23 10:06:27 +01:00
ccd8919e67
add tests/unshare-include-deb 2022-12-23 10:06:27 +01:00
b39713def5
remove last traces of proot 2022-12-23 10:06:27 +01:00
348c582866
coverage.py: error out if tests are missing from ./tests or from coverage.txt 2022-12-23 10:06:27 +01:00
67fbe118f3
tests/i386-which-can-be-executed-without-qemu: fixup spurious merged-/usr problem 2022-12-23 10:06:27 +01:00
5a263b5532
tests/file-mirror: wrap lines 2022-12-23 10:06:27 +01:00
830270840b
make sure genext2fs and genext2fs exist for the respective formats 2022-12-21 20:02:56 +01:00
Jochen Sprickerhof
374ae3dc99
use $username for subgid check
/etc/subgid contains a mapping of user names (not group names) to group
ids as defined in man subgid.
2022-11-18 21:39:15 +01:00
1f15194a6e
tests/pivot_root: use the mmdebstrap binary we copied into the chroot 2022-11-18 09:49:52 +01:00
117e4251a1
.mailmap: add Helmut Grohne and Benjamin Drung 2022-11-18 09:49:27 +01:00
adf8f9399d
coverage.py: check that all files in ./tests appear in coverage.txt 2022-11-18 09:48:26 +01:00
543093eddc
README.md: update list of contributors 2022-11-18 09:48:07 +01:00
4dc1375840
run_qemu.sh: remove accidental qemu option from aavmf code filename 2022-11-17 11:38:17 +01:00
c3aa679fec
run_qemu.sh: short-form boolean option 'read-only' deprecated, using read-only=on instead 2022-11-17 11:35:43 +01:00
7a057e37dd
release 1.2.3 2022-11-16 14:06:50 +01:00
5bd3da0aef
tests/create-tarball-dry-run: fix MODE->VARIANT 2022-11-16 14:02:58 +01:00
d442f436de
shellcheck everything 2022-11-16 13:59:38 +01:00
889c02419e
update for perltidy 20220613 2022-11-15 14:48:01 +01:00
a156d93314
run_qemu.sh: bump timeout to 40 minutes so that it works on an ARM Cortex-A53 at 1.5 GHz 2022-11-15 14:47:59 +01:00
4ccd799b50
tests/include-deb-file: create a dummy binary package to make sure apt doesn't download the package from the mirror 2022-11-15 14:47:42 +01:00
24c5a45202
make_mirror.sh: switch from extlinux to grub-efi to support arm64 2022-11-15 14:47:38 +01:00
2e8eaeb18b
mmdebstrap-autopkgtest-build-qemu: fix i386 grub target i386-efi -> i386-pc 2022-11-14 14:35:12 +01:00
420080648e
Revert "add another --dpkgopt example"
This reverts commit 40b6155967.

dpkg does not support the {foo,bar,baz} type of glob

Closes: #28
2022-11-14 14:35:12 +01:00
be156e7a14
tests/chrootless: skip if libpam-runtime (<= 1.5.2-5) 2022-11-14 14:35:12 +01:00
ea146ad108
add undocumented --chrooted-*-hook calling pivot_root in unshare mode 2022-11-14 14:35:12 +01:00
449fb248e2
Instead of mounting and unmounting for each run_chroot() call, do it once before the extract hook and unmount after the customize hooks 2022-11-14 14:31:06 +01:00
eb54f6a23a
Instead of re-execing mmdebstrap under /bin/sh, use Text::ParseWords::shellwords
- saves a few PIDs
 - saves a bit of time because useless exec and fork is avoided
 - allows to run in pivoted chroots without mmdebstrap
2022-11-14 14:31:05 +01:00
d2238c891b
coverage.py: add --skip option 2022-11-14 14:31:05 +01:00
bf33a614c3
add mini-mmdebstrap in shell to the man page 2022-11-14 14:31:05 +01:00
d15be6abbf
tests/check-against-debootstrap-dist: add more restrictions for remaining hacks 2022-11-14 14:31:05 +01:00
67902e06e9
tests/dev-ptmx: needs adduser inside the chroot 2022-11-07 16:11:47 +01:00
d9ca7c21ff
make failure to remove /dev/ptmx a warning and not an error 2022-11-07 16:10:55 +01:00
d29f9195d7
coverage.txt: add more requirements found by running tests on salsa ci and debci 2022-11-07 16:10:13 +01:00
b454892ddd
release 1.2.2 2022-10-27 16:11:30 +02:00
c2cd4a2a77
tests/check-against-debootstrap-dist: sort /etc/group for variant important 2022-10-27 14:53:04 +02:00
5ec6256461
tests/create-arm64-tarball: use regex instead of hardcoding the perl version 2022-10-27 14:52:21 +02:00
46f5889b54
make_mirror.sh: add console=tty0 to linux cmdline 2022-10-27 14:27:01 +02:00
822f8eafec
tests: test chrootless essential and systemd-sysv with fakeroot and foreign 2022-10-27 14:22:53 +02:00
b5f6c7f08f
coverage.py: print failed options if more than one choices 2022-10-27 11:37:39 +02:00
43ba07e790
tests/check-against-debootstrap-dist: avoid code duplication by using a loop 2022-10-18 10:51:22 +02:00
eb7cf54155
base-passwd now creates the _apt user 2022-10-18 10:42:09 +02:00
80ade97458
tests: put temporary files in /tmp and not into CWD 2022-10-18 10:35:23 +02:00
e887a329ab
more changes for merged-/usr which is now default in testing and unstable 2022-10-18 10:32:03 +02:00
fce852770b
tests/check-for-bit-by-bit-identical-format-output: add comment explaining why we cannot test chrootless mode here 2022-10-18 10:22:19 +02:00
07e3673161
tests/as-debootstrap-unshare-wrapper: systemd-sysusers before systemd 252 doesn't respect SOURCE_DATE_EPOCH when adding users to /etc/shadow 2022-10-18 10:20:02 +02:00
10c3d3e5f4
tests: output to stderr to prevent interleaving with set -x output 2022-10-16 22:32:46 +02:00
4048293be5
only print progress bars on interactive terminals that are wide enough 2022-10-16 22:03:06 +02:00
0903b3f6a7
tests/create-tarball-with-tmp-mounted-nodev: increase tmpfs size 2022-10-16 21:58:41 +02:00
aac7157820
remove workarounds for #1010957 2022-10-16 18:07:42 +02:00
bcb3fcdaf1
run_qemu.sh: add another example for how to connect to qemu via serial 2022-10-16 15:31:10 +02:00
f0f211f383
coverage.py: only print dist, mode, variant and format for failed jobs if necessary 2022-10-16 15:30:17 +02:00
3a17a91b3c
hooks: do not require MMDEBSTRAP_VERBOSITY to be set
This allows running the hook from older mmdebstrap versions.
2022-10-16 15:21:04 +02:00
fc5b60e038
adjust tests as /var/lib/dpkg/arch is now created unconditionally 2022-10-16 15:20:02 +02:00
a207ac020b
remove test merged-usr-via-setup-hook as merged-/usr is now the default 2022-10-16 14:45:29 +02:00
a16937e3e4
coverage.py: factor out coverage.txt parsing 2022-10-15 10:22:16 +02:00
8cb5b6e0ef
coverage.py: add --format option 2022-10-15 07:37:13 +02:00
91ca37706f
coverage.py: print default values in --help text 2022-10-15 07:36:36 +02:00
d84a65b07f
coverage.py: do not allow 'default' in coverage.txt 2022-10-14 07:17:38 +02:00
fbc3e5549d
coverage.py: improve output format of failed tests 2022-10-10 23:11:35 +02:00
d4cb065639
Write an empty /etc/machine-id instead of writing 'uninitialized'.
Writing "uninitialized" instructs systemd to run units with
ConditionFirstBoot=yes which should only be done by tools that know how
to correctly set up such units.

Debian-Bug: #1021478
2022-10-10 15:01:03 +02:00
Jochen Sprickerhof
adf62afcea
guestfish: move set-label after mkfs
Fixes:

libguestfs: error: set_label: don't know how to set the label for '' filesystems
2022-09-23 09:23:38 +02:00
3999212c48
always create /var/lib/dpkg/arch to make foreign architecture chrootless tarballs bit-by-bit identical 2022-09-22 14:22:37 +02:00
c6c2baee6a
hooks/merged-usr/essential00.sh: avoid chroot in chrootless mode 2022-09-19 20:41:45 +02:00
6c68ab2e5e
also provide the info() function to debootstrap 2022-09-12 11:56:26 +02:00
2f27eccad4
allow /etc/apt/trusted.gpg.d/ not to exist 2022-09-11 21:12:14 +02:00
045b56bb4e
release 1.2.1 2022-09-08 04:43:37 +02:00
7123808b6c
do not clean up /run/lock as /var/lock is a symlink to it according to Debian policy §9.1.4 2022-09-06 21:30:48 +02:00
6416ce96c9
hooks/file-mirror-automount/setup00.sh: also parse MMDEBSTRAP_INCLUDE and make it available 2022-09-06 14:22:03 +02:00
410c5fcb24
fix --include option for files and add test case 2022-09-06 13:06:40 +02:00
9682e74385
release 1.2.0 2022-09-05 06:26:40 +02:00
b0caeeef54
bump dates to 2022 2022-09-05 06:25:24 +02:00
d209fb0c11
reformat with perltidy 2022-09-05 06:21:17 +02:00
f4a3865c00
Remove support for proot.
The proot mode was broken from the start because in contrast to
fakechroot, no ownership information can be retained across multiple
invocations of proot. Since mmdebstrap started using apt from the
outside by setting DPkg::Chroot-Directory in mmdebstrap 0.8.0 proot mode
was finally completely broken because proot cannot wrap the chroot call
done by apt. Users of proot are recommended to run mmdebstrap in
fakechroot mode and then use proot with the resulting directory.
2022-09-05 06:21:17 +02:00
892e568496
coverage.sh: split up linting conditional 2022-09-05 06:21:17 +02:00
b85df6b8f2
coverage.sh: idshift 2022-09-05 06:21:17 +02:00
7e8931578b
Store --include option values in MMDEBSTRAP_INCLUDE for hooks 2022-09-05 06:21:07 +02:00
e1f0b0fa40
ensure operator precedence using more parenthesis 2022-09-02 23:38:53 +02:00
0ff2bef84c
tests/as-debootstrap-unshare-wrapper: redirect output of cmp and diff to stderr to preserve output order 2022-09-02 23:36:44 +02:00
e875bca7fb
support apt patterns and paths with commas and whitespace for the --include option 2022-09-02 23:35:56 +02:00
0af22912f7
also delete everything in /run and add --skip=cleanup/run 2022-09-02 23:29:52 +02:00
add9412a47
add --skip=chroot/mount and --skip=chroot/mount/dev, --skip=chroot/mount/proc, --skip=chroot/mount/sys 2022-09-02 23:27:27 +02:00
e61e352f67
add --skip=chroot/start-stop-daemon and --skip=chroot/policy-rc.d 2022-09-02 23:25:48 +02:00
18c1e9bbc5
multiple skip options can be passed by separating them by comma or whitespace 2022-09-02 23:23:53 +02:00
7ce6db0ca7
mmdebstrap: Show APT's dependency trace when in debug mode
A dependency trace is a powerful tool to debug issues related to
APT's selection of packages, especially in custom mode.

Thus it makes sense to ask APT to output a dependency trace when
`mmdebstrap` is run with the `--debug` flag.
2022-09-02 10:46:51 +02:00
7d7d757f00
tarfilter: add --transform option 2022-08-31 05:52:28 +02:00
902bc55c4d
tarfilter --idshift now provides taridshift 2022-08-31 05:35:40 +02:00
226f86fea9
fix mmdebstrap hanging if apt in download step failed (closes: #1017795) 2022-08-30 21:55:57 +02:00
df2226fb25
tests/check-against-debootstrap-dist: cmp outputs errors to stdout -- redirect to stderr to presereve output order with set +x 2022-08-28 08:22:00 +02:00
3fb97753ea
tests/check-against-debootstrap-dist: systemd started using systemd-sysusers instead of adduser 2022-08-28 08:18:47 +02:00
89a7e4c6ee
tests/custom-tmpdir: chown /home/user to 711 so that this test still works with adduser DIR_MODE=700 2022-08-28 08:16:28 +02:00
f1d847e4ae
tests/dev-ptmx: we expect the grep calls to fail -- make sure they don't fail because /tmp/log doesn't exist 2022-08-28 08:15:08 +02:00
c95632f963
coverage.py: add --variant option 2022-08-28 08:13:33 +02:00
5533b25255
tests/chrootless-essential: enable again now that glibc is fixed 2022-08-11 12:45:00 +02:00
34a9de929d
use standard character classes instead of bracketed character classes 2022-07-28 17:22:47 +02:00
b385eb548a
only check first argument if we have one 2022-07-28 17:21:27 +02:00
d82afec5de
error out if stdout is a tty 2022-07-28 17:20:57 +02:00
117a1591c5
coverage.py: also output failed to stderr 2022-07-28 16:58:29 +02:00
3fcb125e3c
release 1.1.0 2022-07-26 22:29:09 +02:00
35dc676394
relax apt version regex to allow devuan apt versions like 2.5.0devuan1 2022-07-26 22:29:09 +02:00
0ae0adde26
document mmdebstrap hanging forever instead of ENOSPC in qemu as a comment 2022-07-26 22:29:09 +02:00
5e22e0bfc8
adjust message about file-mirror-automount hook 2022-07-26 22:29:09 +02:00
2021f6f7cd
README.md: add an example for running coverage.py 2022-07-26 22:29:09 +02:00
432170c68e
tests/check-against-debootstrap-dist: account for ordering differences in /var/lib/dpkg/triggers/File 2022-07-26 22:29:09 +02:00
15c7de4a3b
tests/check-against-debootstrap-dist: static group ids for crontab, systemd-journal, systemd-network and systemd-resolve 2022-07-26 22:29:09 +02:00
bf379f7e50
make_mirror.sh: bump DISK_SIZE to 10G because of gcc-defaults changing to gcc-12 2022-07-26 22:29:09 +02:00
d91a18a350
Adjust merged-/usr as it's done by debootstrap
- implements the same as debootstrap in
   https://salsa.debian.org/installer-team/debootstrap/-/merge_requests/71
 - builds a temporary usr-is-merged package and upgrades to the real one
 - create merged-/usr chroots for unstable and testing (which will
   become Debian 12 Bookworm)
 - add a dedicated merged-/usr section to the manual page
2022-07-26 22:29:08 +02:00
7cec147b9e
tests/arm64-without-qemu-support: removing qemu-user disables binfmt support again since src:systemd 251.2-4
Closes: #1012163
2022-07-13 11:01:44 +02:00
009089ee8a
Mount a new instance of /dev/pts in the chroot
Before, we bind-mounted /dev/ptmx and /dev/pts from the host into the
chroot. This will make posix_openpt() fail with 'No such file or
directory'.  The ability to create pseudo terminals is important for apt
(which will throw a warning otherwise) or running script(1) or source
package testsuites like for src:util-linux. This functionality is
restored by mounting a new devpts instance to /dev/pts and making
/dev/ptmx a symlink to /dev/pts/ptmx. Mounting with ptmxmode=666 is
required such that also non-root users in unshare mode are able to
create pseudo terminals. See also:

https://www.kernel.org/doc/Documentation/filesystems/devpts.txt
https://salsa.debian.org/debian/schroot/-/merge_requests/2
https://bugs.debian.org/856877
https://bugs.debian.org/817236
2022-06-14 08:26:48 +02:00
679f6cb2fc
coverage.py: allow passing tests by number 2022-06-13 16:01:39 +02:00
e9e9cec884
coverage.py: remove unused variable 2022-06-13 14:22:45 +02:00
b707676432
coverage.py: add --dist argument 2022-06-13 14:22:20 +02:00
b51b5b9e2a
coverage.py: set sensible defaults for SOURCE_DATE_EPOCH and CMD 2022-06-13 14:15:36 +02:00
793d8bb561
add forgotten test create-directory-dry-run 2022-06-13 14:00:44 +02:00
9ca613da0a
coverage.py: instead of printing the length of the skipped dictionary, print the sum of the length of lists of tests it references 2022-06-04 08:50:53 +02:00
51ad1426c3
hooks/file-mirror-automount/setup00.sh: instead of grepping for Repo-URI, only output REPO_URI 2022-06-04 08:49:44 +02:00
153d1fa969
tests/arm64-without-qemu-support: disable binfmt not by uninstalling but by writing to /proc/sys/fs/binfmt_misc/qemu-aarch64
Since 1:7.0+dfsg-3, binfmt.d from systemd is used as preferred
alternative to binfmt-support. And systemd does not provide an official
way to trigger binfmt (de)registration besides a reboot.

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1012163

Since we also have binfmt-support installed, systemd and binfmt-support
work in parallel so this test becomes flaky and sometimes removing the
qemu packages would have the desired effect and sometimes not.

To make the test deterministic again, we explicitly disable emulation by
writing a 0 to /proc/sys/fs/binfmt_misc/qemu-aarch64
2022-06-04 08:43:38 +02:00
c4962f9ab0
print value of SOURCE_DATE_EPOCH when creating and comparing debootstrap chroot to find bug only occurring when running autopkgtest around midnight 2022-06-04 08:42:23 +02:00
c37e5e6059
tests/custom-tmpdir: try running mmdebstrap in a TMPDIR with special shell characters in its path 2022-06-04 08:30:53 +02:00
28122a8b5c
coverage.py: instead of killing (and leaving temporary files undeleted) just send SIGTERM and wait 2022-06-04 08:26:39 +02:00
bf31355c62
run_qemu.sh: run timeout with --foreground so that qemu can receive sigint and quit immediately 2022-06-04 08:25:32 +02:00
b46149b851
release 1.0.1 2022-05-29 09:45:03 +02:00
7198ad50f6
coverage.py: actually count number of successes to print the correct number when ctrl+c was pressed 2022-05-29 09:43:41 +02:00
5ea760920d
coverage.py: fix typo successully -> successfully 2022-05-29 09:33:18 +02:00
86f616996d
coverage.py: strip off fractional seconds of time left 2022-05-29 09:32:58 +02:00
1b5d87c7cf
coverage.py: add --mode to only run tests with given mode 2022-05-29 09:32:02 +02:00
d96e85fddd
file-mirror-automount hook now supports modes that cannot mount (like fakechroot) by copying the repo into the chroot 2022-05-29 09:31:12 +02:00
c1c6297db7
move copying files into shared directory from coverage.sh to coverage.py 2022-05-29 09:28:34 +02:00
e4ef326b59
Only set up FAKECHROOT_CMD_SUBST for paths in PATH containing the original binary
If FAKECHROOT_CMD_SUBST sets up wrong substitutions, then binaries
cannot be found. For example if /usr/bin/chroot is listed in
FAKECHROOT_CMD_SUBST but /usr/sbin (the actual location of the chroot
binary) is not in PATH, the command fails
2022-05-29 08:11:43 +02:00
158956e213
release 1.0.0 2022-05-28 17:51:14 +02:00
4c3fddcd54
rewrite coverage.sh
- multiple individual shell scripts instead of one 3.5k line monster
 - tests driven by Python script allowing:
    * declarative test description in coverage.txt
    * collecting errors instead of aborting on first error
    * skipping tests
    * running specific tests
2022-05-28 17:49:05 +02:00
e71676e15c
use warning() instead of warn() when unmounting /sys and /proc fails 2022-05-28 17:47:51 +02:00
a238d90774
hooks/merged-usr: workaround not necessary anymore since debootstrap 1.0.125 2022-05-26 07:36:22 +02:00
790294ddca
hooks/merged-usr: acquire native architecture from apt-config
`dpkg --print-architecture` will be wrong when creating a foreign
architecture chroot and we cannot chroot() as the chroot directory is
still empty.
2022-05-26 07:36:22 +02:00
cffd47e087
drop /usr/sbin prefixes from executables 2022-05-26 07:36:22 +02:00
c6c9c27969
use DPkg::Path as default value for PATH 2022-05-26 07:36:22 +02:00
27926c75f9
unify checking if tools exist by running them with --version 2022-05-26 07:36:22 +02:00
0f9c6543c4
improve qemu-user
- rephrase info message to be less misleading
 - do not require qemu-$arch-static binary
 - check if /proc/sys/fs/binfmt_misc/qemu-$arch exists before reading it
2022-05-26 07:36:22 +02:00
b99f1d53d5
add file-mirror-automount hook-dir 2022-05-26 07:36:21 +02:00
cc3150ef04
Rework download stage to allow file:// mirrors
- factor out package downloading function
 - replace -oApt::Get::Download-Only=true by -oDebug::pkgDpkgPm=1
 - remove guessing of package names in /var/cache/apt/archives/
 - drop edsp parsing with proxysolver/mmdebstrap-dump-solution to obtain
   downloaded filenames in favour of -oDpkg::Pre-Install-Pkgs::=cat
 - /var/cache/apt/archives/ is now allowed to contain packages
 - drop --skip=download/empty
 - file:// mirrors are now supported if their path is available inside
   the chroot
2022-05-26 07:36:21 +02:00
c8835a6149
coverage.sh: make sure archives we copied into /var/cache/apt/archives are not deleted 2022-05-24 04:16:11 +02:00
dc8b09ed50
fix pod formatting typo 2022-05-24 04:15:25 +02:00
21b23ebb9f
set MMDEBSTRAP_VERBOSITY in hooks 2022-05-24 04:14:08 +02:00
0664792cd5
manually push option arguments to array instead of using s@
By mixing s@ for --$foo-hook options and manual pushing in --hook-dir,
it can happen that options get lost. Consider the following test:

use Getopt::Long;
my $arr = [];
GetOptions(
    'A=s@' => \$arr,
    'B=s' => sub { push @{$arr}, $_[1]; }
);
foreach my $hook (@{$arr}) { print "hook: $hook\n"; }

This works fine:

    perl test.pl --A=a1 --B=b1 --A=a2 --B=b2
    hook: a1
    hook: b1
    hook: a2
    hook: b2

This misses b1:

    perl test.pl --B=b1 --A=a2 --B=b2
    hook: a2
    hook: b2
2022-05-24 04:09:41 +02:00
26af846d0a
fix that cached debs were not returned if there was nothing to download 2022-05-23 23:30:29 +02:00
df6900ec4a
hooks/eatmydata/extract.sh: fix regex to also work on s390x 2022-05-22 07:52:06 +02:00
5c5f7de898
more documentation for TMPDIR 2022-05-22 02:57:42 +02:00
29b23bbcbc
document how to build on top of an existing tarball 2022-05-22 02:57:01 +02:00
d10f320f5d
document how to build an sbuild unshare chroot mode tarball 2022-05-22 02:56:21 +02:00
ce23e702e2
fixup comparison with debootstrap 2022-05-22 02:54:41 +02:00
2c155f7cc9
coverage.sh: only skip foreign arch if RUN_MA_SAME_TESTS==no and mode==fakechroot 2022-05-22 02:54:01 +02:00
d7b39b6c97
coverage.sh: enable building variant=standard 2022-05-22 02:53:17 +02:00
6ec09c27ca
coverage.sh: mount tmpfs as workaround for #1010957 2022-05-22 02:51:58 +02:00
454121acb1
run_qemu.sh: use -cpu host as a workaround for #1011003 and because it's faster 2022-05-22 02:51:10 +02:00
09f1dd2ee6
Improve documentation of reproducibility of /etc/resolv.conf and /etc/hostname
Closes: #26
2022-05-11 10:47:25 +02:00
57e0ecb20f
release 0.8.6 2022-03-25 14:27:25 +01:00
70b081d299
allow running root mode inside unshare mode 2022-03-25 14:25:54 +01:00
a6186e8485
add .mailmap 2022-03-23 13:17:26 +01:00
64ba5f8229
release 0.8.5 2022-03-07 23:44:45 +01:00
409ce1cfee
improve man page further 2022-03-07 23:41:58 +01:00
4ce5a92123
coverage.sh: now that the _apt user is created as a system user by adduser, we also need to change /etc/shadow 2022-03-07 23:41:12 +01:00
3a9ba24d12
coverage.sh: output diff to stderr prevent interleaving with sh -x output 2022-03-07 11:30:45 +01:00
7044baf6b1
run busybox from an absolute path to allow running it even when /proc is not mounted as busybox uses /proc/self/exe to figure out its own path 2022-03-07 11:27:10 +01:00
3a90ce96ad
make_mirror.sh: use extlinux explicitly instead of syslinux 2022-03-07 11:23:02 +01:00
01bbdb9d2c
finalize mmdebstrap-autopkgtest-build-qemu 2022-03-06 10:16:11 +01:00
4b7669be43
examples/mmdebstrap-autopkgtest-qemu: add support for arm64, armhf and ppc64el via efi and ieee1275 boot 2022-03-05 20:58:46 +01:00
c7e8e28af9
examples/mmdebstrap-autopkgtest-qemu: use grub to support more architectures 2022-03-05 08:03:13 +01:00
f612826fdf
Add examples/mmdebstrap-autopkgtest-qemu
Thanks to Francesco Poli for providing ideas and testing this.
2022-03-04 12:30:18 +01:00
155 changed files with 10258 additions and 6000 deletions

6
.mailmap Normal file
View file

@ -0,0 +1,6 @@
Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Johannes Schauer Marin Rodrigues <josch@mister-muffin.de> <j.schauer@email.de>
Johannes Schauer Marin Rodrigues <josch@mister-muffin.de> <josch@debian.org>
Johannes Schauer Marin Rodrigues <josch@mister-muffin.de> <Johannes Schauer Marin Rodrigues josch@debian.org>
Helmut Grohne <helmut@subdivi.de> <helmut.grohne@intenta.de>
Benjamin Drung <benjamin.drung@ionos.com> <benjamin.drung@cloud.ionos.com>

View file

@ -1,3 +1,198 @@
1.5.4 (2024-10-28)
------------------
- do not generate apt sources.list entry if SUITE is empty
1.5.3 (2024-09-13)
------------------
- tidy up any zombie processes
- chrootless hurd-i386
- add --skip=cleanup/reproducible/machine-id
- m-a-b-q: replace test_installed by dpkg-checkbuilddeps
1.5.2 (2024-06-26)
------------------
- mmdebstrap-autopkgtest-build-qemu produces bit-by-bit reproducible output
1.5.1 (2024-06-03)
------------------
- in root and unshare mode, run 'mount --make-rprivate /' before bind-mounting
- switch apt variant from using 'apt-get dist-upgrade' to apt patterns
1.5.0 (2024-05-14)
------------------
- add --format=ext4
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)
------------------
- add mmdebstrap-autopkgtest-build-qemu
- export container=mmdebstrap-unshare env variable in unshare-mode hooks
- add new skip options: output/dev, output/mknod, tar-in/mknod,
copy-in/mknod, sync-in/mknod
- stop copying qemu-$arch-static binary into the chroot
- tarfilter: add --type-exclude option
- set MMDEBSTRAP_FORMAT in hooks
- do not install priority:required in buildd variant following debootstrap
1.3.8 (2023-08-20)
------------------
- hooks/merged-usr: implement post-merging as debootstrap does
- exclude ./lost+found from tarball
1.3.7 (2023-06-21)
------------------
- add hooks/copy-host-apt-sources-and-preferences
1.3.6 (2023-06-16)
------------------
- bugfix release
1.3.5 (2023-03-20)
------------------
- bugfix release
1.3.4 (2023-03-16)
------------------
- more safeguards before automatically choosing unshare mode
1.3.3 (2023-02-19)
------------------
- testsuite improvements
1.3.2 (2023-02-16)
------------------
- unshare mode works in privileged docker containers
1.3.1 (2023-01-20)
------------------
- bugfix release
1.3.0 (2023-01-16)
------------------
- add hooks/maybe-jessie-or-older and hooks/maybe-merged-usr
- add --skip=check/signed-by
- hooks/jessie-or-older: split into two individual hook files
- skip running apt-get update if we are very sure that it was already run
- be more verbose when 'apt-get update' failed
- warn if a hook is named like one but not executable and if a hook is
executable but not named like one
- to find signed-by value, run gpg on the individual keys to print better
error messages in case it fails (gpg doesn't give an indication which file
it was unable to read) and print progress bar
- allow empty sources.list entries
1.2.5 (2023-01-04)
------------------
- bugfix release
1.2.4 (2022-12-23)
------------------
- bugfix release
- add jessie-or-older extract hook
1.2.3 (2022-11-16)
------------------
- use Text::ParseWords::shellwords instead of spawning a new shell
- mount and unmount once, instead for each run_chroot() call
1.2.2 (2022-10-27)
------------------
- allow /etc/apt/trusted.gpg.d/ not to exist
- always create /var/lib/dpkg/arch to make foreign architecture chrootless
tarballs bit-by-bit identical
- write an empty /etc/machine-id instead of writing 'uninitialized'
- only print progress bars on interactive terminals that are wide enough
1.2.1 (2022-09-08)
------------------
- bugfix release
1.2.0 (2022-09-05)
------------------
- remove proot mode
- error out if stdout is an interactive terminal
- replace taridshift by tarfilter --idshift
- tarfilter: add --transform option
- multiple --skip options can be separated by comma or whitespace
- also cleanup the contents of /run
- support apt patterns and paths with commas and whitespace in --include
- hooks: store the values of the --include option in MMDEBSTRAP_INCLUDE
- add new --skip options: chroot/start-stop-daemon, chroot/policy-rc.d
chroot/mount, chroot/mount/dev, chroot/mount/proc, chroot/mount/sys,
cleanup/run
1.1.0 (2022-07-26)
----------------
- mount a new /dev/pts instance into the chroot to make posix_openpt work
- adjust merged-/usr hook to work the same way as debootstrap
- add no-merged-usr hook
1.0.1 (2022-05-29)
------------------
- bugfix release
1.0.0 (2022-05-28)
------------------
- all documented interfaces are now considered stable
- allow file:// mirrors
- /var/cache/apt/archives/ is now allowed to contain *.deb packages
- add file-mirror-automount hook-dir
- set $MMDEBSTRAP_VERBOSITY in hooks
- rewrite coverage with multiple individual and skippable shell scripts
0.8.6 (2022-03-25)
------------------
- allow running root mode inside unshare mode
0.8.5 (2022-03-07)
------------------
- improve documentation
0.8.4 (2022-02-11) 0.8.4 (2022-02-11)
------------------ ------------------

View file

@ -23,6 +23,11 @@ For the full documentation use:
pod2man ./mmdebstrap | man -l - pod2man ./mmdebstrap | man -l -
Or read a HTML version of the man page in either of these locations:
- https://gitlab.mister-muffin.de/josch/mmdebstrap/wiki
- https://manpages.debian.org/unstable/mmdebstrap/mmdebstrap.1.en.html
The sales pitch in comparison to debootstrap The sales pitch in comparison to debootstrap
-------------------------------------------- --------------------------------------------
@ -34,7 +39,7 @@ Summary:
- chroot with apt in 11 seconds - chroot with apt in 11 seconds
- gzipped tarball with apt is 27M small - gzipped tarball with apt is 27M small
- bit-by-bit reproducible output - bit-by-bit reproducible output
- unprivileged operation using Linux user namespaces, fakechroot or proot - unprivileged operation using Linux user namespaces or fakechroot
- can operate on filesystems mounted with nodev - can operate on filesystems mounted with nodev
- foreign architecture chroots with qemu-user - foreign architecture chroots with qemu-user
- variant installing only Essential:yes packages and dependencies - variant installing only Essential:yes packages and dependencies
@ -78,9 +83,9 @@ privileges to create a file (the chroot tarball) in one's home directory.
Thus, mmdebstrap provides multiple options to create a chroot tarball with the Thus, mmdebstrap provides multiple options to create a chroot tarball with the
right permissions **without superuser privileges**. This avoids a whole class right permissions **without superuser privileges**. This avoids a whole class
of bugs like #921815. Depending on what is available, it uses either Linux user of bugs like #921815. Depending on what is available, it uses either Linux user
namespaces, fakechroot or proot. Debootstrap supports fakechroot but will not namespaces or fakechroot. Debootstrap supports fakechroot but will not
create a tarball with the right permissions by itself. Support for Linux user create a tarball with the right permissions by itself. Support for Linux user
namespaces and proot is missing (see bugs #829134 and #698347, respectively). namespaces is missing (see #829134).
When creating a chroot tarball with debootstrap, the temporary chroot directory When creating a chroot tarball with debootstrap, the temporary chroot directory
cannot be on a filesystem that has been mounted with nodev. In unprivileged cannot be on a filesystem that has been mounted with nodev. In unprivileged
@ -137,6 +142,11 @@ By default, `coverage.sh` will skip running a single test which tries creating
a Ubuntu Focal chroot. To not skip that test, run `coverage.sh` with the a Ubuntu Focal chroot. To not skip that test, run `coverage.sh` with the
environment variable `ONLINE=yes`. environment variable `ONLINE=yes`.
If a test fails you can run individual tests by executing `coverage.py` with
the test name and optionally limit it to a specific distribution like so:
CMD=./mmdebstrap ./coverage.py --dist unstable check-against-debootstrap-dist
Bugs Bugs
==== ====
@ -148,10 +158,21 @@ Contributors
- Johannes Schauer Marin Rodrigues (main author) - Johannes Schauer Marin Rodrigues (main author)
- Helmut Grohne - Helmut Grohne
- Jochen Sprickerhof
- Gioele Barabucci
- Benjamin Drung - Benjamin Drung
- Steve Dodd
- Josh Triplett - Josh Triplett
- Konstantin Demin - Konstantin Demin
- Chris Hofstaedtler
- Colin Watson
- David Kalnischkies
- Emilio Pozuelo Monfort
- Francesco Poli
- Jakub Wilk
- Joe Groocock
- Max-Julian Pogner
- Nicolas Vigier
- Raul Tambre
- Steve Dodd
- Trent W. Buck - Trent W. Buck
- Vagrant Cascadian - Vagrant Cascadian
- Gioele Barabucci

118
caching_proxy.py Executable file
View file

@ -0,0 +1,118 @@
#!/usr/bin/env python3
import sys
import os
import time
import http.client
import http.server
from io import StringIO
import pathlib
import urllib.parse
oldcachedir = None
newcachedir = None
readonly = False
class ProxyRequestHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
assert int(self.headers.get("Content-Length", 0)) == 0
assert self.headers["Host"]
pathprefix = "http://" + self.headers["Host"] + "/"
assert self.path.startswith(pathprefix)
sanitizedpath = urllib.parse.unquote(self.path.removeprefix(pathprefix))
oldpath = oldcachedir / sanitizedpath
newpath = newcachedir / sanitizedpath
if not readonly:
newpath.parent.mkdir(parents=True, exist_ok=True)
# just send back to client
if newpath.exists():
print(f"proxy cached: {self.path}", file=sys.stderr)
self.wfile.write(b"HTTP/1.1 200 OK\r\n")
self.send_header("Content-Length", newpath.stat().st_size)
self.end_headers()
with newpath.open(mode="rb") as new:
while True:
buf = new.read(64 * 1024) # same as shutil uses
if not buf:
break
self.wfile.write(buf)
self.wfile.flush()
return
if readonly:
newpath = pathlib.Path("/dev/null")
# copy from oldpath to newpath and send back to client
# Only take files from the old cache if they are .deb files or Packages
# files in the by-hash directory as only those are unique by their path
# name. Other files like InRelease files have to be downloaded afresh.
if oldpath.exists() and (
oldpath.suffix == ".deb" or "by-hash" in oldpath.parts
):
print(f"proxy cached: {self.path}", file=sys.stderr)
self.wfile.write(b"HTTP/1.1 200 OK\r\n")
self.send_header("Content-Length", oldpath.stat().st_size)
self.end_headers()
with oldpath.open(mode="rb") as old, newpath.open(mode="wb") as new:
# we are not using shutil.copyfileobj() because we want to
# write to two file objects simultaneously
while True:
buf = old.read(64 * 1024) # same as shutil uses
if not buf:
break
self.wfile.write(buf)
new.write(buf)
self.wfile.flush()
return
# download fresh copy
try:
print(f"\rproxy download: {self.path}", file=sys.stderr)
conn = http.client.HTTPConnection(self.headers["Host"], timeout=5)
conn.request("GET", self.path, None, dict(self.headers))
res = conn.getresponse()
assert (res.status, res.reason) == (200, "OK"), (res.status, res.reason)
self.wfile.write(b"HTTP/1.1 200 OK\r\n")
for k, v in res.getheaders():
# do not allow a persistent connection
if k == "connection":
continue
self.send_header(k, v)
self.end_headers()
with newpath.open(mode="wb") as f:
# we are not using shutil.copyfileobj() because we want to
# write to two file objects simultaneously and throttle the
# writing speed to 1024 kB/s
while True:
buf = res.read(64 * 1024) # same as shutil uses
if not buf:
break
self.wfile.write(buf)
f.write(buf)
time.sleep(64 / 1024) # 1024 kB/s
self.wfile.flush()
except Exception as e:
self.send_error(502)
def main():
global oldcachedir, newcachedir, readonly
if sys.argv[1] == "--readonly":
readonly = True
oldcachedir = pathlib.Path(sys.argv[2])
newcachedir = pathlib.Path(sys.argv[3])
else:
oldcachedir = pathlib.Path(sys.argv[1])
newcachedir = pathlib.Path(sys.argv[2])
print(f"starting caching proxy for {newcachedir}", file=sys.stderr)
httpd = http.server.ThreadingHTTPServer(
server_address=("", 8080), RequestHandlerClass=ProxyRequestHandler
)
httpd.serve_forever()
if __name__ == "__main__":
main()

474
coverage.py Executable file
View file

@ -0,0 +1,474 @@
#!/usr/bin/env python3
from debian.deb822 import Deb822, Release
import email.utils
import os
import sys
import shutil
import subprocess
import argparse
import time
from datetime import timedelta
from collections import defaultdict
from itertools import product
have_qemu = os.getenv("HAVE_QEMU", "yes") == "yes"
have_binfmt = os.getenv("HAVE_BINFMT", "yes") == "yes"
run_ma_same_tests = os.getenv("RUN_MA_SAME_TESTS", "yes") == "yes"
use_host_apt_config = os.getenv("USE_HOST_APT_CONFIG", "no") == "yes"
cmd = os.getenv("CMD", "./mmdebstrap")
default_dist = os.getenv("DEFAULT_DIST", "unstable")
all_dists = ["oldstable", "stable", "testing", "unstable"]
default_mode = "auto"
all_modes = ["auto", "root", "unshare", "fakechroot", "chrootless"]
default_variant = "apt"
all_variants = [
"extract",
"custom",
"essential",
"apt",
"minbase",
"buildd",
"-",
"standard",
]
default_format = "auto"
all_formats = ["auto", "directory", "tar", "squashfs", "ext2", "ext4", "null"]
mirror = os.getenv("mirror", "http://127.0.0.1/debian")
hostarch = subprocess.check_output(["dpkg", "--print-architecture"]).decode().strip()
release_path = f"./shared/cache/debian/dists/{default_dist}/InRelease"
if not os.path.exists(release_path):
print("path doesn't exist:", release_path, file=sys.stderr)
print("run ./make_mirror.sh first", file=sys.stderr)
exit(1)
if os.getenv("SOURCE_DATE_EPOCH") is not None:
s_d_e = os.getenv("SOURCE_DATE_EPOCH")
else:
with open(release_path) as f:
rel = Release(f)
s_d_e = str(email.utils.mktime_tz(email.utils.parsedate_tz(rel["Date"])))
separator = (
"------------------------------------------------------------------------------"
)
def skip(condition, dist, mode, variant, fmt):
if not condition:
return ""
for line in condition.splitlines():
if not line:
continue
if eval(line):
return line.strip()
return ""
def parse_config(confname):
config_dict = defaultdict(dict)
config_order = list()
all_vals = {
"Dists": all_dists,
"Modes": all_modes,
"Variants": all_variants,
"Formats": all_formats,
}
with open(confname) as f:
for test in Deb822.iter_paragraphs(f):
if "Test" not in test.keys():
print("Test without name", file=sys.stderr)
exit(1)
name = test["Test"]
config_order.append(name)
for k in test.keys():
v = test[k]
if k not in [
"Test",
"Dists",
"Modes",
"Variants",
"Formats",
"Skip-If",
"Needs-QEMU",
"Needs-Root",
"Needs-APT-Config",
]:
print(f"Unknown field name {k} in test {name}")
exit(1)
if k in all_vals.keys():
if v == "default":
print(
f"Setting {k} to default in Test {name} is redundant",
file=sys.stderr,
)
exit(1)
if v == "any":
v = all_vals[k]
else:
# else, split the value by whitespace
v = v.split()
for i in v:
if i not in all_vals[k]:
print(
f"{i} is not a valid value for {k}", file=sys.stderr
)
exit(1)
config_dict[name][k] = v
return config_order, config_dict
def format_test(num, total, name, dist, mode, variant, fmt, config_dict):
ret = f"({num}/{total}) {name}"
if len(config_dict[name].get("Dists", [])) > 1:
ret += f" --dist={dist}"
if len(config_dict[name].get("Modes", [])) > 1:
ret += f" --mode={mode}"
if len(config_dict[name].get("Variants", [])) > 1:
ret += f" --variant={variant}"
if len(config_dict[name].get("Formats", [])) > 1:
ret += f" --format={fmt}"
return ret
def print_time_per_test(time_per_test, name="test"):
print(
f"average time per {name}:",
sum(time_per_test.values(), start=timedelta()) / len(time_per_test),
file=sys.stderr,
)
print(
f"median time per {name}:",
sorted(time_per_test.values())[len(time_per_test) // 2],
file=sys.stderr,
)
head_tail_num = 10
print(f"{head_tail_num} fastests {name}s:", file=sys.stderr)
for k, v in sorted(time_per_test.items(), key=lambda i: i[1])[
: min(head_tail_num, len(time_per_test))
]:
print(f" {k}: {v}", file=sys.stderr)
print(f"{head_tail_num} slowest {name}s:", file=sys.stderr)
for k, v in sorted(time_per_test.items(), key=lambda i: i[1], reverse=True)[
: min(head_tail_num, len(time_per_test))
]:
print(f" {k}: {v}", file=sys.stderr)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("test", nargs="*", help="only run these tests")
parser.add_argument(
"-x",
"--exitfirst",
action="store_const",
dest="maxfail",
const=1,
help="exit instantly on first error or failed test.",
)
parser.add_argument(
"--maxfail",
metavar="num",
action="store",
type=int,
dest="maxfail",
default=0,
help="exit after first num failures or errors.",
)
parser.add_argument(
"--mode",
metavar="mode",
help=f"only run tests with this mode (Default = {default_mode})",
)
parser.add_argument(
"--dist",
metavar="dist",
help=f"only run tests with this dist (Default = {default_dist})",
)
parser.add_argument(
"--variant",
metavar="variant",
help=f"only run tests with this variant (Default = {default_variant})",
)
parser.add_argument(
"--format",
metavar="format",
help=f"only run tests with this format (Default = {default_format})",
)
parser.add_argument(
"--skip", metavar="test", action="append", help="skip this test"
)
args = parser.parse_args()
# copy over files from git or as distributed
for git, dist, target in [
("./mmdebstrap", "/usr/bin/mmdebstrap", "mmdebstrap"),
("./tarfilter", "/usr/bin/mmtarfilter", "tarfilter"),
(
"./proxysolver",
"/usr/lib/apt/solvers/mmdebstrap-dump-solution",
"proxysolver",
),
(
"./ldconfig.fakechroot",
"/usr/libexec/mmdebstrap/ldconfig.fakechroot",
"ldconfig.fakechroot",
),
]:
if os.path.exists(git):
shutil.copy(git, f"shared/{target}")
else:
shutil.copy(dist, f"shared/{target}")
# copy over hooks from git or as distributed
if os.path.exists("hooks"):
shutil.copytree("hooks", "shared/hooks", dirs_exist_ok=True)
else:
shutil.copytree(
"/usr/share/mmdebstrap/hooks", "shared/hooks", dirs_exist_ok=True
)
# parse coverage.txt
config_order, config_dict = parse_config("coverage.txt")
indirbutnotcovered = set(
[d for d in os.listdir("tests") if not d.startswith(".")]
) - set(config_order)
if indirbutnotcovered:
print(
"test(s) missing from coverage.txt: %s"
% (", ".join(sorted(indirbutnotcovered))),
file=sys.stderr,
)
exit(1)
coveredbutnotindir = set(config_order) - set(
[d for d in os.listdir("tests") if not d.startswith(".")]
)
if coveredbutnotindir:
print(
"test(s) missing from ./tests: %s"
% (", ".join(sorted(coveredbutnotindir))),
file=sys.stderr,
)
exit(1)
# produce the list of tests using the cartesian product of all allowed
# dists, modes, variants and formats of a given test
tests = []
for name in config_order:
test = config_dict[name]
for dist, mode, variant, fmt in product(
test.get("Dists", [default_dist]),
test.get("Modes", [default_mode]),
test.get("Variants", [default_variant]),
test.get("Formats", [default_format]),
):
skipreason = skip(test.get("Skip-If"), dist, mode, variant, fmt)
if skipreason:
tt = ("skip", skipreason)
elif (
test.get("Needs-APT-Config", "false") == "true" and use_host_apt_config
):
tt = ("skip", "test cannot use host apt config")
elif have_qemu:
tt = "qemu"
elif test.get("Needs-QEMU", "false") == "true":
tt = ("skip", "test needs QEMU")
elif test.get("Needs-Root", "false") == "true":
tt = "sudo"
elif mode == "root":
tt = "sudo"
else:
tt = "null"
tests.append((tt, name, dist, mode, variant, fmt))
torun = []
num_tests = len(tests)
if args.test:
# check if all given tests are either a valid name or a valid number
for test in args.test:
if test in [name for (_, name, _, _, _, _) in tests]:
continue
if not test.isdigit():
print(f"cannot find test named {test}", file=sys.stderr)
exit(1)
if int(test) >= len(tests) or int(test) <= 0 or str(int(test)) != test:
print(f"test number {test} doesn't exist", file=sys.stderr)
exit(1)
for i, (_, name, _, _, _, _) in enumerate(tests):
# if either the number or test name matches, then we use this test,
# otherwise we skip it
if name in args.test:
torun.append(i)
if str(i + 1) in args.test:
torun.append(i)
num_tests = len(torun)
starttime = time.time()
skipped = defaultdict(list)
failed = []
num_success = 0
num_finished = 0
time_per_test = {}
acc_time_per_test = defaultdict(list)
for i, (test, name, dist, mode, variant, fmt) in enumerate(tests):
if torun and i not in torun:
continue
print(separator, file=sys.stderr)
print("(%d/%d) %s" % (i + 1, len(tests), name), file=sys.stderr)
print("dist: %s" % dist, file=sys.stderr)
print("mode: %s" % mode, file=sys.stderr)
print("variant: %s" % variant, file=sys.stderr)
print("format: %s" % fmt, file=sys.stderr)
if num_finished > 0:
currenttime = time.time()
timeleft = timedelta(
seconds=int(
(num_tests - num_finished)
* (currenttime - starttime)
/ num_finished
)
)
print("time left: %s" % timeleft, file=sys.stderr)
if failed:
print("failed: %d" % len(failed), file=sys.stderr)
num_finished += 1
with open("tests/" + name) as fin, open("shared/test.sh", "w") as fout:
for line in fin:
line = line.replace("{{ CMD }}", cmd)
line = line.replace("{{ SOURCE_DATE_EPOCH }}", s_d_e)
line = line.replace("{{ DIST }}", dist)
line = line.replace("{{ MIRROR }}", mirror)
line = line.replace("{{ MODE }}", mode)
line = line.replace("{{ VARIANT }}", variant)
line = line.replace("{{ FORMAT }}", fmt)
line = line.replace("{{ HOSTARCH }}", hostarch)
fout.write(line)
# ignore:
# SC2016 Expressions don't expand in single quotes, use double quotes for that.
# SC2050 This expression is constant. Did you forget the $ on a variable?
# SC2194 This word is constant. Did you forget the $ on a variable?
shellcheck = subprocess.run(
[
"shellcheck",
"--exclude=SC2050,SC2194,SC2016",
"-f",
"gcc",
"shared/test.sh",
],
check=False,
stdout=subprocess.PIPE,
).stdout.decode()
shfmt = subprocess.run(
[
"shfmt",
"--posix",
"--binary-next-line",
"--case-indent",
"--indent",
"2",
"--simplify",
"-d",
"shared/test.sh",
],
check=False,
stdout=subprocess.PIPE,
).stdout.decode()
argv = None
match test:
case "qemu":
argv = ["./run_qemu.sh"]
case "sudo":
argv = ["./run_null.sh", "SUDO"]
case "null":
argv = ["./run_null.sh"]
case ("skip", reason):
skipped[reason].append(
format_test(
i + 1, len(tests), name, dist, mode, variant, fmt, config_dict
)
)
print(f"skipped because of {reason}", file=sys.stderr)
continue
print(separator, file=sys.stderr)
if args.skip and name in args.skip:
print(f"skipping because of --skip={name}", file=sys.stderr)
continue
if args.dist and args.dist != dist:
print(f"skipping because of --dist={args.dist}", file=sys.stderr)
continue
if args.mode and args.mode != mode:
print(f"skipping because of --mode={args.mode}", file=sys.stderr)
continue
if args.variant and args.variant != variant:
print(f"skipping because of --variant={args.variant}", file=sys.stderr)
continue
if args.format and args.format != fmt:
print(f"skipping because of --format={args.format}", file=sys.stderr)
continue
before = time.time()
proc = subprocess.Popen(argv)
try:
proc.wait()
except KeyboardInterrupt:
proc.terminate()
proc.wait()
break
after = time.time()
walltime = timedelta(seconds=int(after - before))
formated_test_name = format_test(
i + 1, len(tests), name, dist, mode, variant, fmt, config_dict
)
time_per_test[formated_test_name] = walltime
acc_time_per_test[name].append(walltime)
print(separator, file=sys.stderr)
print(f"duration: {walltime}", file=sys.stderr)
if proc.returncode != 0 or shellcheck != "" or shfmt != "":
if shellcheck != "":
print(shellcheck)
if shfmt != "":
print(shfmt)
failed.append(formated_test_name)
print("result: FAILURE", file=sys.stderr)
else:
print("result: SUCCESS", file=sys.stderr)
num_success += 1
if args.maxfail and len(failed) >= args.maxfail:
break
print(separator, file=sys.stderr)
print(
"successfully ran %d tests" % num_success,
file=sys.stderr,
)
if skipped:
print("skipped %d:" % sum([len(v) for v in skipped.values()]), file=sys.stderr)
for reason, l in skipped.items():
print(f"skipped because of {reason}:", file=sys.stderr)
for t in l:
print(f" {t}", file=sys.stderr)
if len(time_per_test) > 1:
print_time_per_test(time_per_test)
if len(acc_time_per_test) > 1:
print_time_per_test(
{
f"{len(v)}x {k}": sum(v, start=timedelta())
for k, v in acc_time_per_test.items()
},
"accumulated test",
)
if failed:
print("failed %d:" % len(failed), file=sys.stderr)
for f in failed:
print(f, file=sys.stderr)
currenttime = time.time()
walltime = timedelta(seconds=int(currenttime - starttime))
print(f"total runtime: {walltime}", file=sys.stderr)
if failed:
exit(1)
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

437
coverage.txt Normal file
View file

@ -0,0 +1,437 @@
Test: debootstrap
Dists: any
Variants: minbase buildd -
Needs-Root: true
Needs-APT-Config: true
Test: check-against-debootstrap-dist
Dists: any
Variants: minbase buildd -
Needs-Root: true
Needs-APT-Config: true
Test: as-debootstrap-unshare-wrapper
Modes: unshare
Needs-Root: true
Variants: minbase -
Needs-APT-Config: true
Test: help
Test: man
Test: version
Test: create-directory
Needs-Root: true
Test: unshare-as-root-user
Needs-Root: true
Test: dist-using-codename
Dists: any
Needs-APT-Config: true
Test: fail-without-etc-subuid
Needs-QEMU: true
Test: fail-without-username-in-etc-subuid
Needs-QEMU: true
Test: unshare-as-root-user-inside-chroot
Needs-Root: true
Needs-APT-Config: true
Test: root-mode-inside-chroot
Needs-Root: true
Needs-APT-Config: true
Test: root-mode-inside-unshare-chroot
Modes: unshare
Needs-APT-Config: true
Test: root-without-cap-sys-admin
Needs-Root: true
Test: mount-is-missing
Needs-QEMU: true
Test: mmdebstrap
Needs-Root: true
Modes: root
Formats: tar squashfs ext2 ext4
Variants: essential apt minbase buildd - standard
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
mode == "fakechroot" and variant in ["-", "standard"] # no extended attributes
variant == "standard" and dist in ["oldstable", "stable"] and hostarch in ["armel", "armhf", "mipsel"] # #1031276
Test: check-for-bit-by-bit-identical-format-output
Modes: unshare fakechroot
Formats: tar squashfs ext2 ext4
Variants: essential apt minbase buildd - standard
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
mode == "fakechroot" and variant in ["-", "standard"] # no extended attributes
variant == "standard" and dist in ["oldstable", "stable"] and hostarch in ["armel", "armhf", "mipsel"] # #1031276
Test: tarfilter-idshift
Needs-QEMU: true
Test: progress-bars-on-fake-tty
Test: debug-output-on-fake-tty
Test: existing-empty-directory
Needs-Root: true
Test: existing-directory-with-lost-found
Needs-Root: true
Test: fail-installing-to-non-empty-lost-found
Test: fail-installing-to-non-empty-target-directory
Test: missing-device-nodes-outside-the-chroot
Needs-QEMU: true
Test: missing-dev-sys-proc-inside-the-chroot
Modes: unshare
Variants: custom
Test: chroot-directory-not-accessible-by-apt-user
Needs-Root: true
Test: cwd-directory-not-accessible-by-unshared-user
Needs-Root: true
Modes: unshare
Test: create-gzip-compressed-tarball
Test: custom-tmpdir
Needs-Root: true
Modes: unshare
Test: xz-compressed-tarball
Test: directory-ending-in-tar
Modes: root
Needs-Root: true
Test: auto-mode-without-unshare-capabilities
Needs-QEMU: true
Test: fail-with-missing-lz4
Test: fail-with-path-with-quotes
Test: create-tarball-with-tmp-mounted-nodev
Needs-QEMU: true
Test: read-from-stdin-write-to-stdout
Test: supply-components-manually
Modes: root
Needs-Root: true
Needs-APT-Config: true
Test: stable-default-mirror
Needs-QEMU: true
Test: pass-distribution-but-implicitly-write-to-stdout
Needs-QEMU: true
Test: aspcud-apt-solver
Test: mirror-is-stdin
Test: copy-mirror
Needs-QEMU: true
Test: file-mirror
Needs-QEMU: true
Test: file-mirror-automount-hook
Modes: root unshare fakechroot
Needs-QEMU: true
Test: mirror-is-deb
Test: mirror-is-real-file
Needs-APT-Config: true
Test: deb822-1-2
Modes: root
Needs-Root: true
Needs-APT-Config: true
Test: deb822-2-2
Modes: root
Needs-Root: true
Needs-APT-Config: true
Test: automatic-mirror-from-suite
Needs-QEMU: true
Test: invalid-mirror
Needs-APT-Config: true
Test: fail-installing-to-root
Modes: root
Needs-Root: true
Test: fail-installing-to-existing-file
Modes: root
Needs-Root: true
Test: arm64-without-qemu-support
Needs-QEMU: true
Skip-If: hostarch != "amd64"
Test: i386-which-can-be-executed-without-qemu
Needs-QEMU: true
Skip-If:
hostarch != "amd64"
not run_ma_same_tests
Test: include-foreign-libmagic-mgc
Needs-Root: true
Needs-APT-Config: true
Skip-If:
hostarch not in ["amd64", "arm64"]
not run_ma_same_tests
Test: include-foreign-libmagic-mgc-with-multiple-arch-options
Needs-Root: true
Needs-APT-Config: true
Skip-If:
hostarch not in ["amd64", "arm64"]
not run_ma_same_tests
Test: aptopt
Needs-Root: true
Test: keyring
Needs-QEMU: true
Test: keyring-overwrites
Needs-Root: true
Needs-APT-Config: true
Test: signed-by-without-host-keys
Needs-QEMU: true
Test: ascii-armored-keys
Needs-QEMU: true
Test: signed-by-with-host-keys
Needs-Root: true
Needs-APT-Config: true
Test: dpkgopt
Needs-Root: true
Test: include
Needs-Root: true
Test: multiple-include
Needs-Root: true
Test: include-with-multiple-apt-sources
Needs-Root: true
Test: essential-hook
Needs-Root: true
Test: customize-hook
Needs-Root: true
Test: failing-customize-hook
Needs-Root: true
Test: sigint-during-customize-hook
Needs-Root: true
Test: hook-directory
Needs-Root: true
Test: eatmydata-via-hook-dir
Needs-Root: true
Test: special-hooks-using-helpers
Needs-Root: true
Needs-APT-Config: true
Test: special-hooks-using-helpers-and-env-vars
Needs-Root: true
Needs-APT-Config: true
Test: special-hooks-with-mode-mode
Modes: root unshare fakechroot
Test: debootstrap-no-op-options
Needs-Root: true
Test: verbose
Variants: standard
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
Test: debug
Variants: standard
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
Test: quiet
Needs-Root: true
Test: logfile
Needs-Root: true
Needs-APT-Config: true
Test: without-etc-resolv-conf-and-etc-hostname
Needs-QEMU: true
Test: preserve-mode-of-etc-resolv-conf-and-etc-hostname
Modes: root
Needs-QEMU: true
Test: not-having-to-install-apt-in-include-because-a-hook-did-it-before
Test: remove-start-stop-daemon-and-policy-rc-d-in-hook
Test: skip-start-stop-daemon-policy-rc
Test: skip-mount
Modes: unshare
Test: compare-output-with-pre-seeded-var-cache-apt-archives
Needs-QEMU: true
Variants: any
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
Test: create-directory-dry-run
Modes: root
Test: create-tarball-dry-run
Variants: any
Modes: any
Test: unpack-doc-debian
Modes: root fakechroot
Variants: extract
Needs-APT-Config: true
Test: install-doc-debian
Modes: chrootless
Variants: custom
Needs-APT-Config: true
Test: chrootless
Variants: essential
Modes: chrootless
Needs-Root: true
Skip-If:
dist == "oldstable"
Test: chrootless-fakeroot
Variants: essential
Modes: chrootless
Skip-If:
dist == "oldstable"
hostarch in ["i386", "armel", "armhf", "mipsel"] # #1023286
Test: chrootless-foreign
Variants: essential
Modes: chrootless
Skip-If:
dist == "oldstable"
hostarch not in ["amd64", "arm64"]
not run_ma_same_tests
Needs-QEMU: true
Test: install-doc-debian-and-output-tarball
Variants: custom
Modes: chrootless
Needs-APT-Config: true
Test: install-doc-debian-and-test-hooks
Variants: custom
Modes: chrootless
Needs-APT-Config: true
Test: install-libmagic-mgc-on-foreign
Variants: custom
Modes: chrootless
Skip-If:
hostarch not in ["amd64", "arm64"]
not have_binfmt
Test: install-busybox-based-sub-essential-system
Needs-Root: true
Test: create-foreign-tarball
Modes: root unshare fakechroot
Skip-If:
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
Modes: fakechroot
Test: dev-ptmx
Modes: root unshare
Test: error-if-stdout-is-tty
Test: variant-custom-timeout
Test: include-deb-file
Modes: root unshare fakechroot
Needs-APT-Config: true
Test: unshare-include-deb
Modes: unshare
Test: pivot_root
Modes: root unshare
Needs-APT-Config: true
Test: jessie-or-older
Needs-Root: true
Modes: root unshare fakechroot
Variants: essential apt minbase
Skip-If: mode == "fakechroot" and hostarch in ["i386", "armel", "armhf", "mipsel"] # #1023286
Test: apt-patterns
Test: apt-patterns-custom
Test: empty-sources.list
Test: merged-fakechroot-inside-unmerged-chroot
Needs-Root: true
Needs-APT-Config: true
Skip-If:
hostarch in ["i386", "armel", "armhf", "mipsel"] # #1023286
dist in ["testing", "unstable"] # #1053671
Test: auto-mode-as-normal-user
Modes: auto
Test: skip-output-dev
Modes: root unshare
Test: skip-output-mknod
Modes: root unshare
Test: skip-tar-in-mknod
Modes: unshare
Test: zombie-reaping
Modes: unshare
Test: empty-suite

View file

@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# This script is in the public domain # No copyright is claimed. This code is in the public domain; do with
# it what you wish.
# #
# Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de> # Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
# #
@ -40,7 +41,7 @@ find_gpgv_status_fd() {
GPGSTATUSFD="$(find_gpgv_status_fd "$@")" GPGSTATUSFD="$(find_gpgv_status_fd "$@")"
case $GPGSTATUSFD in case $GPGSTATUSFD in
''|*[!0-9]*) '' | *[!0-9]*)
echo "invalid --status-fd argument" >&2 echo "invalid --status-fd argument" >&2
exit 1 exit 1
;; ;;

View file

@ -1,6 +1,10 @@
#!/bin/sh #!/bin/sh
set -exu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
rootdir="$1" rootdir="$1"

View file

@ -1,6 +1,10 @@
#!/bin/sh #!/bin/sh
set -exu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
rootdir="$1" rootdir="$1"

View file

@ -0,0 +1,44 @@
#!/usr/bin/perl
#
# This script makes sure that all packages that are installed both locally as
# well as inside the chroot have the same version.
#
# It is implemented in Perl because there are no associative arrays in POSIX
# shell.
use strict;
use warnings;
sub get_pkgs {
my $root = shift;
my %pkgs = ();
open(my $fh, '-|', 'dpkg-query', "--root=$root", '--showformat',
'${binary:Package}=${Version}\n', '--show')
// die "cannot exec dpkg-query";
while (my $line = <$fh>) {
my ($pkg, $ver) = split(/=/, $line, 2);
$pkgs{$pkg} = $ver;
}
close $fh;
if ($? != 0) { die "failed to run dpkg-query" }
return %pkgs;
}
my %pkgs_local = get_pkgs('/');
my %pkgs_chroot = get_pkgs($ARGV[0]);
my @diff = ();
foreach my $pkg (keys %pkgs_chroot) {
next unless exists $pkgs_local{$pkg};
if ($pkgs_local{$pkg} ne $pkgs_chroot{$pkg}) {
push @diff, $pkg;
}
}
if (scalar @diff > 0) {
print STDERR "E: packages from the host and the chroot differ:\n";
foreach my $pkg (@diff) {
print STDERR "E: $pkg $pkgs_local{$pkg} $pkgs_chroot{$pkg}\n";
}
exit 1;
}

View file

@ -0,0 +1,55 @@
#!/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
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
if [ -n "${MMDEBSTRAP_SUITE:-}" ]; then
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 1 ]; then
echo "W: using a non-empty suite name $MMDEBSTRAP_SUITE does not make sense with this hook and might select the wrong Essential:yes package set" >&2
fi
fi
rootdir="$1"
SOURCELIST="/etc/apt/sources.list"
eval "$(apt-config shell SOURCELIST Dir::Etc::SourceList/f)"
SOURCEPARTS="/etc/apt/sources.d/"
eval "$(apt-config shell SOURCEPARTS Dir::Etc::SourceParts/d)"
PREFERENCES="/etc/apt/preferences"
eval "$(apt-config shell PREFERENCES Dir::Etc::Preferences/f)"
PREFERENCESPARTS="/etc/apt/preferences.d/"
eval "$(apt-config shell PREFERENCESPARTS Dir::Etc::PreferencesParts/d)"
for f in "$SOURCELIST" \
"$SOURCEPARTS"/*.list \
"$SOURCEPARTS"/*.sources \
"$PREFERENCES" \
"$PREFERENCESPARTS"/*; do
[ -e "$f" ] || continue
mkdir --parents "$(dirname "$rootdir/$f")"
if [ -e "$rootdir/$f" ]; then
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 2 ]; then
echo "I: $f already exists in chroot, appending..." >&2
fi
# Add extra newline between old content and new content.
# This is required in case of deb822 files.
echo >> "$rootdir/$f"
fi
cat "$f" >> "$rootdir/$f"
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
echo "D: contents of $f inside the chroot:" >&2
cat "$rootdir/$f" >&2
fi
done

View file

@ -1,6 +1,10 @@
#!/bin/sh #!/bin/sh
set -exu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
rootdir="$1" rootdir="$1"
@ -9,14 +13,14 @@ if [ -e "$rootdir/var/lib/dpkg/arch" ]; then
else else
chrootarch=$(dpkg --print-architecture) chrootarch=$(dpkg --print-architecture)
fi fi
libdir="/usr/lib/$(dpkg-architecture -a $chrootarch -q DEB_HOST_MULTIARCH)" libdir="/usr/lib/$(dpkg-architecture -a "$chrootarch" -q DEB_HOST_MULTIARCH)"
# if eatmydata was actually installed properly, then we are not removing # if eatmydata was actually installed properly, then we are not removing
# anything here # anything here
if ! chroot "$rootdir" dpkg-query --list eatmydata; then if ! chroot "$rootdir" dpkg-query --show eatmydata; then
rm "$rootdir/usr/bin/eatmydata" rm "$rootdir/usr/bin/eatmydata"
fi fi
if ! chroot "$rootdir" dpkg-query --list libeatmydata1; then if ! chroot "$rootdir" dpkg-query --show libeatmydata1; then
rm "$rootdir$libdir"/libeatmydata.so* rm "$rootdir$libdir"/libeatmydata.so*
fi fi

View file

@ -1,6 +1,10 @@
#!/bin/sh #!/bin/sh
set -exu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
rootdir="$1" rootdir="$1"
@ -10,8 +14,10 @@ else
chrootarch=$(dpkg --print-architecture) chrootarch=$(dpkg --print-architecture)
fi fi
eval $(apt-config shell trusted Dir::Etc::trusted/f) trusted=
eval $(apt-config shell trustedparts Dir::Etc::trustedparts/d) eval "$(apt-config shell trusted Dir::Etc::trusted/f)"
trustedparts=
eval "$(apt-config shell trustedparts Dir::Etc::trustedparts/d)"
tmpfile=$(mktemp --tmpdir="$rootdir/tmp") tmpfile=$(mktemp --tmpdir="$rootdir/tmp")
cat << END > "$tmpfile" cat << END > "$tmpfile"
Apt::Architecture "$chrootarch"; Apt::Architecture "$chrootarch";
@ -25,8 +31,8 @@ END
# nothing will be printed for them # nothing will be printed for them
tmpdir=$(mktemp --directory --tmpdir="$rootdir/tmp") tmpdir=$(mktemp --directory --tmpdir="$rootdir/tmp")
env --chdir="$tmpdir" APT_CONFIG="$tmpfile" apt-get download --print-uris eatmydata libeatmydata1 \ env --chdir="$tmpdir" APT_CONFIG="$tmpfile" apt-get download --print-uris eatmydata libeatmydata1 \
| sed -ne "s/^'\([^']\+\)'\s\+\([^\s]\+\)\s\+\([0-9]\+\)\s\+\(SHA256:[a-f0-9]\+\)$/\1 \2 \3 \4/p" \ | sed -ne "s/^'\([^']\+\)'\s\+\(\S\+\)\s\+\([0-9]\+\)\s\+\(SHA256:[a-f0-9]\+\)$/\1 \2 \3 \4/p" \
| while read uri fname size hash; do | while read -r uri fname size hash; do
echo "processing $fname" >&2 echo "processing $fname" >&2
if [ -e "$tmpdir/$fname" ]; then if [ -e "$tmpdir/$fname" ]; then
echo "$tmpdir/$fname already exists" >&2 echo "$tmpdir/$fname already exists" >&2
@ -41,7 +47,7 @@ env --chdir="$tmpdir" APT_CONFIG="$tmpfile" apt-get download --print-uris eatmyd
| tar --directory="$rootdir/usr/bin" --strip-components=3 --extract --verbose ./usr/bin/eatmydata | tar --directory="$rootdir/usr/bin" --strip-components=3 --extract --verbose ./usr/bin/eatmydata
;; ;;
libeatmydata1_*_$chrootarch.deb) libeatmydata1_*_$chrootarch.deb)
libdir="/usr/lib/$(dpkg-architecture -a $chrootarch -q DEB_HOST_MULTIARCH)" libdir="/usr/lib/$(dpkg-architecture -a "$chrootarch" -q DEB_HOST_MULTIARCH)"
mkdir -p "$rootdir$libdir" mkdir -p "$rootdir$libdir"
dpkg-deb --fsys-tarfile "$tmpdir/$fname" \ dpkg-deb --fsys-tarfile "$tmpdir/$fname" \
| tar --directory="$rootdir$libdir" --strip-components=4 --extract --verbose --wildcards ".$libdir/libeatmydata.so*" | tar --directory="$rootdir$libdir" --strip-components=4 --extract --verbose --wildcards ".$libdir/libeatmydata.so*"

View file

@ -0,0 +1,41 @@
#!/bin/sh
#
# shellcheck disable=SC2086
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
rootdir="$1"
if [ ! -e "$rootdir/run/mmdebstrap/file-mirror-automount" ]; then
exit 0
fi
xargsopts="--null --no-run-if-empty -I {} --max-args=1"
case $MMDEBSTRAP_MODE in
root|unshare)
echo "unmounting the following mountpoints:" >&2 ;;
*)
echo "removing the following directories:" >&2 ;;
esac
< "$rootdir/run/mmdebstrap/file-mirror-automount" \
xargs $xargsopts echo " $rootdir/{}"
case $MMDEBSTRAP_MODE in
root|unshare)
< "$rootdir/run/mmdebstrap/file-mirror-automount" \
xargs $xargsopts umount "$rootdir/{}"
;;
*)
< "$rootdir/run/mmdebstrap/file-mirror-automount" \
xargs $xargsopts rm -r "$rootdir/{}"
;;
esac
rm "$rootdir/run/mmdebstrap/file-mirror-automount"
rmdir --ignore-fail-on-non-empty "$rootdir/run/mmdebstrap"

View file

@ -0,0 +1,73 @@
#!/bin/sh
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
rootdir="$1"
# process all configured apt repositories
env APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get indextargets --no-release-info --format '$(REPO_URI)' \
| sed -ne 's/^file:\/\+//p' \
| sort -u \
| while read -r path; do
mkdir -p "$rootdir/run/mmdebstrap"
if [ ! -d "/$path" ]; then
echo "W: /$path is not an existing directory" >&2
continue
fi
case $MMDEBSTRAP_MODE in
root|unshare)
echo "bind-mounting /$path into the chroot" >&2
mkdir -p "$rootdir/$path"
mount -o ro,bind "/$path" "$rootdir/$path"
;;
*)
echo "copying /$path into the chroot" >&2
mkdir -p "$rootdir/$path"
"$MMDEBSTRAP_ARGV0" --hook-helper "$rootdir" "$MMDEBSTRAP_MODE" "$MMDEBSTRAP_HOOK" env "$MMDEBSTRAP_VERBOSITY" sync-in "/$path" "/$path" <&"$MMDEBSTRAP_HOOKSOCK" >&"$MMDEBSTRAP_HOOKSOCK"
;;
esac
printf '/%s\0' "$path" >> "$rootdir/run/mmdebstrap/file-mirror-automount"
done
# process all files given via --include
set -f # turn off pathname expansion
IFS=',' # split by comma
for pkg in $MMDEBSTRAP_INCLUDE; do
set +f; unset IFS
case $pkg in
./*|../*|/*) : ;; # we are interested in this case
*) continue ;; # not a file
esac
# undo escaping
pkg="$(printf '%s' "$pkg" | sed 's/%2C/,/g; s/%25/%/g')"
# check for existance
if [ ! -f "$pkg" ]; then
echo "$pkg does not exist" >&2
continue
fi
# make path absolute
pkg="$(realpath "$pkg")"
case "$pkg" in
/*) : ;;
*) echo "path for $pkg is not absolute" >&2; continue;;
esac
mkdir -p "$rootdir/run/mmdebstrap"
mkdir -p "$rootdir/$(dirname "$pkg")"
case $MMDEBSTRAP_MODE in
root|unshare)
echo "bind-mounting $pkg into the chroot" >&2
touch "$rootdir/$pkg"
mount -o bind "$pkg" "$rootdir/$pkg"
;;
*)
echo "copying $pkg into the chroot" >&2
"$MMDEBSTRAP_ARGV0" --hook-helper "$rootdir" "$MMDEBSTRAP_MODE" "$MMDEBSTRAP_HOOK" env "$MMDEBSTRAP_VERBOSITY" upload "$pkg" "$pkg" <&"$MMDEBSTRAP_HOOKSOCK" >&"$MMDEBSTRAP_HOOKSOCK"
;;
esac
printf '/%s\0' "$pkg" >> "$rootdir/run/mmdebstrap/file-mirror-automount"
done
set +f; unset IFS

View file

@ -0,0 +1,16 @@
#!/bin/sh
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
TARGET="$1"
# not needed since dpkg 1.17.11
for f in available diversions cmethopt; do
if [ ! -e "$TARGET/var/lib/dpkg/$f" ]; then
touch "$TARGET/var/lib/dpkg/$f"
fi
done

View file

@ -0,0 +1,47 @@
#!/bin/sh
#
# needed until init 1.33 which pre-depends on systemd-sysv
# starting with init 1.34, init is not Essential:yes anymore
#
# jessie has init 1.22
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
TARGET="$1"
if [ -z "${MMDEBSTRAP_ESSENTIAL+x}" ]; then
MMDEBSTRAP_ESSENTIAL=
for f in "$TARGET/var/cache/apt/archives/"*.deb; do
[ -f "$f" ] || continue
f="${f#"$TARGET"}"
MMDEBSTRAP_ESSENTIAL="$MMDEBSTRAP_ESSENTIAL $f"
done
fi
fname_base_passwd=
fname_base_files=
fname_dpkg=
for pkg in $MMDEBSTRAP_ESSENTIAL; do
pkgname=$(dpkg-deb --show --showformat='${Package}' "$TARGET/$pkg")
# shellcheck disable=SC2034
case $pkgname in
base-passwd) fname_base_passwd=$pkg;;
base-files) fname_base_files=$pkg;;
dpkg) fname_dpkg=$pkg;;
esac
done
for var in base_passwd base_files dpkg; do
eval 'val=$fname_'"$var"
[ -z "$val" ] && continue
chroot "$TARGET" dpkg --install --force-depends "$val"
done
# shellcheck disable=SC2086
chroot "$TARGET" dpkg --unpack --force-depends $MMDEBSTRAP_ESSENTIAL
chroot "$TARGET" dpkg --configure --pending

View file

@ -0,0 +1,37 @@
#!/bin/sh
set -eu
# we need to check the version of dpkg
# since at this point packages are just extracted but not installed, we cannot use dpkg-query
# since we want to support chrootless, we cannot run dpkg --version inside the chroot
# to avoid this hook depending on dpkg-dev being installed, we do not parse the extracted changelog with dpkg-parsechangelog
# we also want to avoid parsing the changelog because /usr/share/doc might've been added to dpkg --path-exclude
# instead, we just ask apt about the latest version of dpkg it knows of
# this should only fail in situations where there are multiple versions of dpkg in different suites
ver=$(env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions dpkg 2>/dev/null | sed -ne 's/^Version: \(.*\)$/\1/p' || printf '')
if [ -z "$ver" ]; then
echo "no package called dpkg can be installed -- not running jessie-or-older extract00 hook" >&2
exit 0
fi
if dpkg --compare-versions "$ver" ge 1.17.11; then
echo "dpkg version $ver is >= 1.17.11 -- not running jessie-or-older extract00 hook" >&2
exit 0
else
echo "dpkg version $ver is << 1.17.11 -- running jessie-or-older extract00 hook" >&2
fi
# resolve the script path using several methods in order:
# 1. using dirname -- "$0"
# 2. using ./hooks
# 3. using /usr/share/mmdebstrap/hooks/
for p in "$(dirname -- "$0")/.." ./hooks /usr/share/mmdebstrap/hooks; do
if [ -x "$p/jessie-or-older/extract00.sh" ] && [ -x "$p/jessie-or-older/extract01.sh" ]; then
"$p/jessie-or-older/extract00.sh" "$1"
exit 0
fi
done
echo "cannot find jessie-or-older hook anywhere" >&2
exit 1

View file

@ -0,0 +1,57 @@
#!/bin/sh
set -eu
# The jessie-or-older extract01 hook has to be run up to the point where the
# Essential:yes field was removed from the init package (with
# init-system-helpers 1.34). Since the essential packages have only been
# extracted but not installed, we cannot use dpkg-query to find out its
# version. Since /usr/share/doc might be missing due to dpkg --path-exclude, we
# also cannot check whether /usr/share/doc/init/copyright exists. There also
# was a time (before init-system-helpers 1.20) where there was no init package
# at all where we also want to apply this hook. So we just ask apt about the
# candidate version for init-system-helpers. This should only fail in
# situations where there are multiple versions of init-system-helpers in
# different suites.
ver=$(env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions init-system-helpers 2>/dev/null | sed -ne 's/^Version: \(.*\)$/\1/p' || printf '')
if [ -z "$ver" ]; then
# there is no package called init-system-helpers, so either:
# - this is so old that init-system-helpers didn't exist yet
# - we are in a future where init-system-helpers doesn't exist anymore
# - something strange is going on
# we should only call the hook in the first case
ver=$(env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions base-files 2>/dev/null | sed -ne 's/^Version: \(.*\)$/\1/p' || printf '')
if [ -z "$ver" ]; then
echo "neither init-system-helpers nor base-files can be installed -- not running jessie-or-older extract01 hook" >&2
exit 0
fi
# Jessie is Debian 8
if dpkg --compare-versions "$ver" ge 8; then
echo "there is no init-system-helpers but base-files version $ver is >= 8 -- not running jessie-or-older extract01 hook" >&2
exit 0
else
echo "there is no init-system-helpers but base-files version $ver is << 8 -- running jessie-or-older extract01 hook" >&2
fi
else
if dpkg --compare-versions "$ver" ge 1.34; then
echo "init-system-helpers version $ver is >= 1.34 -- not running jessie-or-older extract01 hook" >&2
exit 0
else
echo "init-system-helpers version $ver is << 1.34 -- running jessie-or-older extract01 hook" >&2
fi
fi
# resolve the script path using several methods in order:
# 1. using dirname -- "$0"
# 2. using ./hooks
# 3. using /usr/share/mmdebstrap/hooks/
for p in "$(dirname -- "$0")/.." ./hooks /usr/share/mmdebstrap/hooks; do
if [ -x "$p/jessie-or-older/extract00.sh" ] && [ -x "$p/jessie-or-older/extract01.sh" ]; then
"$p/jessie-or-older/extract01.sh" "$1"
exit 0
fi
done
echo "cannot find jessie-or-older hook anywhere" >&2
exit 1

View file

@ -0,0 +1,40 @@
#!/bin/sh
set -eu
ver=$(dpkg-query --root="$1" -f '${db:Status-Status} ${Source} ${Version}' --show usr-is-merged 2>/dev/null || printf '')
case "$ver" in
'')
echo "no package called usr-is-merged is installed -- not running merged-usr essential hook" >&2
exit 0
;;
'installed mmdebstrap-dummy-usr-is-merged 1')
echo "dummy usr-is-merged package installed -- running merged-usr essential hook" >&2
;;
'installed usrmerge '*)
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
;;
esac
# resolve the script path using several methods in order:
# 1. using dirname -- "$0"
# 2. using ./hooks
# 3. using /usr/share/mmdebstrap/hooks/
for p in "$(dirname -- "$0")/.." ./hooks /usr/share/mmdebstrap/hooks; do
if [ -x "$p/merged-usr/setup00.sh" ] && [ -x "$p/merged-usr/extract00.sh" ] && [ -x "$p/merged-usr/essential00.sh" ]; then
"$p/merged-usr/essential00.sh" "$1"
exit 0
fi
done
echo "cannot find merged-usr hook anywhere" >&2
exit 1

View file

@ -0,0 +1,37 @@
#!/bin/sh
set -eu
env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get update --error-on=any
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
# 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:
# 1. using dirname -- "$0"
# 2. using ./hooks
# 3. using /usr/share/mmdebstrap/hooks/
for p in "$(dirname -- "$0")/.." ./hooks /usr/share/mmdebstrap/hooks; do
if [ -x "$p/merged-usr/setup00.sh" ] && [ -x "$p/merged-usr/extract00.sh" ] && [ -x "$p/merged-usr/essential00.sh" ]; then
"$p/merged-usr/extract00.sh" "$1"
exit 0
fi
done
echo "cannot find merged-usr hook anywhere" >&2
exit 1

View file

@ -0,0 +1,37 @@
#!/bin/sh
set -eu
env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get update --error-on=any
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
# 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:
# 1. using dirname -- "$0"
# 2. using ./hooks
# 3. using /usr/share/mmdebstrap/hooks/
for p in "$(dirname -- "$0")/.." ./hooks /usr/share/mmdebstrap/hooks; do
if [ -x "$p/merged-usr/setup00.sh" ] && [ -x "$p/merged-usr/extract00.sh" ] && [ -x "$p/merged-usr/essential00.sh" ]; then
"$p/merged-usr/setup00.sh" "$1"
exit 0
fi
done
echo "cannot find merged-usr hook anywhere" >&2
exit 1

28
hooks/merged-usr/essential00.sh Executable file
View file

@ -0,0 +1,28 @@
#!/bin/sh
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
TARGET="$1"
if [ "${MMDEBSTRAP_MODE:-}" = "chrootless" ]; then
APT_CONFIG=$MMDEBSTRAP_APT_CONFIG apt-get --yes install \
-oDPkg::Chroot-Directory= \
-oDPkg::Options::=--force-not-root \
-oDPkg::Options::=--force-script-chrootless \
-oDPkg::Options::=--root="$TARGET" \
-oDPkg::Options::=--log="$TARGET/var/log/dpkg.log" \
usr-is-merged
export DPKG_ROOT="$TARGET"
dpkg-query --showformat '${db:Status-Status}\n' --show usr-is-merged | grep -q '^installed$'
dpkg-query --showformat '${Source}\n' --show usr-is-merged | grep -q '^usrmerge$'
dpkg --compare-versions "1" "lt" "$(dpkg-query --showformat '${Version}\n' --show usr-is-merged)"
else
APT_CONFIG=$MMDEBSTRAP_APT_CONFIG apt-get --yes install usr-is-merged
chroot "$TARGET" dpkg-query --showformat '${db:Status-Status}\n' --show usr-is-merged | grep -q '^installed$'
chroot "$TARGET" dpkg-query --showformat '${Source}\n' --show usr-is-merged | grep -q '^usrmerge$'
dpkg --compare-versions "1" "lt" "$(chroot "$TARGET" dpkg-query --showformat '${Version}\n' --show usr-is-merged)"
fi

85
hooks/merged-usr/extract00.sh Executable file
View file

@ -0,0 +1,85 @@
#!/bin/sh
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
TARGET="$1"
# can_usrmerge_symlink() and can_usrmerge_symlink() are
# Copyright 2023 Helmut Grohne <helmut@subdivi.de>
# and part of the debootstrap source in /usr/share/debootstrap/functions
# https://salsa.debian.org/installer-team/debootstrap/-/merge_requests/96
# https://bugs.debian.org/104989
can_usrmerge_symlink() {
# Absolute symlinks can be relocated without problems.
test "${2#/}" = "$2" || return 0
while :; do
if test "${2#/}" != "$2"; then
# Handle double-slashes.
set -- "$1" "${2#/}"
elif test "${2#./}" != "$2"; then
# Handle ./ inside a link target.
set -- "$1" "${2#./}"
elif test "$2" = ..; then
# A parent directory symlink is ok if it does not
# cross the top level directory.
test "${1%/*/*}" != "$1" -a -n "${1%/*/*}"
return $?
elif test "${2#../}" != "$2"; then
# Symbolic link crossing / cannot be moved safely.
# This is prohibited by Debian Policy 10.5.
test "${1%/*/*}" = "$1" -o -z "${1%/*/*}" && return 1
set -- "${1%/*}" "${2#../}"
else
# Consider the symlink ok if its target does not
# contain a parent directory. When we fail here,
# the link target is non-minimal and doesn't happen
# in the archive.
test "${2#*/../}" = "$2"
return $?
fi
done
}
merge_usr_entry() {
# shellcheck disable=SC3043
local entry canon
canon="$TARGET/usr/${1#"$TARGET/"}"
test -h "$canon" &&
error 1 USRMERGEFAIL "cannot move %s as its destination exists as a symlink" "${1#"$TARGET"}"
if ! test -e "$canon"; then
mv "$1" "$canon"
return 0
fi
test -d "$1" ||
error 1 USRMERGEFAIL "cannot move non-directory %s as its destination exists" "${1#"$TARGET"}"
test -d "$canon" ||
error 1 USRMERGEFAIL "cannot move directory %s as its destination is not a directory" "${1#"$TARGET"}"
for entry in "$1/"* "$1/."*; do
# Some shells return . and .. on dot globs.
test "${entry%/.}" != "${entry%/..}" && continue
if test -h "$entry" && ! can_usrmerge_symlink "${entry#"$TARGET"}" "$(readlink "$entry")"; then
error 1 USRMERGEFAIL "cannot move relative symlink crossing top-level directory" "${entry#"$TARGET"}"
fi
# Ignore glob match failures
if test "${entry%'/*'}" != "${entry%'/.*'}" && ! test -e "$entry"; then
continue
fi
merge_usr_entry "$entry"
done
rmdir "$1"
}
# This is list includes all possible multilib directories. It must be
# updated when new multilib directories are being added. Hopefully,
# all new architectures use multiarch instead, so we never get to
# update this.
for dir in bin lib lib32 lib64 libo32 libx32 sbin; do
test -h "$TARGET/$dir" && continue
test -e "$TARGET/$dir" || continue
merge_usr_entry "$TARGET/$dir"
ln -s "usr/$dir" "$TARGET/$dir"
done

View file

@ -39,43 +39,41 @@
# out merged-/usr is bad from the dpkg point-of-view and completely opposite of # out merged-/usr is bad from the dpkg point-of-view and completely opposite of
# the vision with which in mind I wrote mmdebstrap. # the vision with which in mind I wrote mmdebstrap.
set -exu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
TARGET="$1" TARGET="$1"
if [ -e "$TARGET/var/lib/dpkg/arch" ]; then # now install an empty "usr-is-merged" package to avoid installing the
ARCH=$(head -1 "$TARGET/var/lib/dpkg/arch") # usrmerge package on this system even after init-system-helpers starts
else # depending on "usrmerge | usr-is-merged".
ARCH=$(dpkg --print-architecture) #
fi # This package will not end up in the final chroot because the essential
# hook replaces it with the actual usr-is-merged package from src:usrmerge.
if [ -e /usr/share/debootstrap/functions ]; then tmpdir=$(mktemp --directory --tmpdir="$TARGET/tmp")
. /usr/share/debootstrap/functions mkdir -p "$tmpdir/usr-is-merged/DEBIAN"
doing_variant () { [ $1 != "buildd" ]; }
MERGED_USR="yes"
# until https://salsa.debian.org/installer-team/debootstrap/-/merge_requests/48 gets merged
link_dir=""
setup_merged_usr
else
link_dir=""
case $ARCH in
hurd-*) exit 0;;
amd64) link_dir="lib32 lib64 libx32" ;;
i386) link_dir="lib64 libx32" ;;
mips|mipsel) link_dir="lib32 lib64" ;;
mips64*|mipsn32*) link_dir="lib32 lib64 libo32" ;;
powerpc) link_dir="lib64" ;;
ppc64) link_dir="lib32 lib64" ;;
ppc64el) link_dir="lib64" ;;
s390x) link_dir="lib32" ;;
sparc) link_dir="lib64" ;;
sparc64) link_dir="lib32 lib64" ;;
x32) link_dir="lib32 lib64 libx32" ;;
esac
link_dir="bin sbin lib $link_dir"
for dir in $link_dir; do cat << END > "$tmpdir/usr-is-merged/DEBIAN/control"
ln -s usr/"$dir" "$TARGET/$dir" Package: usr-is-merged
mkdir -p "$TARGET/usr/$dir" Priority: optional
done Section: oldlibs
fi Maintainer: Johannes Schauer Marin Rodrigues <josch@debian.org>
Architecture: all
Multi-Arch: foreign
Source: mmdebstrap-dummy-usr-is-merged
Version: 1
Description: dummy package created by mmdebstrap merged-usr setup hook
This package was generated and installed by the mmdebstrap merged-usr
setup hook at /usr/share/mmdebstrap/hooks/merged-usr.
.
If this package is installed in the final chroot, then this is a bug
in mmdebstrap. Please report: https://gitlab.mister-muffin.de/josch/mmdebstrap
END
dpkg-deb --build "$tmpdir/usr-is-merged" "$tmpdir/usr-is-merged.deb"
dpkg --root="$TARGET" --log="$TARGET/var/log/dpkg.log" --install "$tmpdir/usr-is-merged.deb"
rm "$tmpdir/usr-is-merged.deb" "$tmpdir/usr-is-merged/DEBIAN/control"
rmdir "$tmpdir/usr-is-merged/DEBIAN" "$tmpdir/usr-is-merged" "$tmpdir"

View file

@ -0,0 +1 @@
../merged-usr/essential00.sh

54
hooks/no-merged-usr/setup00.sh Executable file
View file

@ -0,0 +1,54 @@
#!/bin/sh
#
# mmdebstrap does have a --no-merged-usr option but only as a no-op for
# debootstrap compatibility
#
# Using this hook script, you can emulate what debootstrap does to set up
# a system without merged-/usr even after the essential init-system-helpers
# package added a dependency on "usrmerge | usr-is-merged". By installing
# a dummy usr-is-merged package, it avoids pulling in the dependencies of
# the usrmerge package.
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
TARGET="$1"
echo "Warning: starting with Debian 12 (Bookworm), systems without merged-/usr are not supported anymore" >&2
echo "Warning: starting with Debian 13 (Trixie), merged-/usr symlinks are shipped by packages in the essential-set making this hook ineffective" >&2
echo "this system will not be supported in the future" > "$TARGET/etc/unsupported-skip-usrmerge-conversion"
# now install an empty "usr-is-merged" package to avoid installing the
# usrmerge package on this system even after init-system-helpers starts
# depending on "usrmerge | usr-is-merged".
#
# This package will not end up in the final chroot because the essential
# hook replaces it with the actual usr-is-merged package from src:usrmerge.
tmpdir=$(mktemp --directory --tmpdir="$TARGET/tmp")
mkdir -p "$tmpdir/usr-is-merged/DEBIAN"
cat << END > "$tmpdir/usr-is-merged/DEBIAN/control"
Package: usr-is-merged
Priority: optional
Section: oldlibs
Maintainer: Johannes Schauer Marin Rodrigues <josch@debian.org>
Architecture: all
Multi-Arch: foreign
Source: mmdebstrap-dummy-usr-is-merged
Version: 1
Description: dummy package created by mmdebstrap no-merged-usr setup hook
This package was generated and installed by the mmdebstrap no-merged-usr
setup hook at /usr/share/mmdebstrap/hooks/no-merged-usr.
.
If this package is installed in the final chroot, then this is a bug
in mmdebstrap. Please report: https://gitlab.mister-muffin.de/josch/mmdebstrap
END
dpkg-deb --build "$tmpdir/usr-is-merged" "$tmpdir/usr-is-merged.deb"
dpkg --root="$TARGET" --log="$TARGET/var/log/dpkg.log" --install "$tmpdir/usr-is-merged.deb"
rm "$tmpdir/usr-is-merged.deb" "$tmpdir/usr-is-merged/DEBIAN/control"
rmdir "$tmpdir/usr-is-merged/DEBIAN" "$tmpdir/usr-is-merged" "$tmpdir"

View file

@ -106,10 +106,24 @@ def main():
for d in get_libdirs(chroot, [chroot / "etc" / "ld.so.conf"]): for d in get_libdirs(chroot, [chroot / "etc" / "ld.so.conf"]):
make_relative(d) make_relative(d)
rootarg = chroot
argv = sys.argv[1:]
for arg in sys.argv[1:]:
if arg == "-r":
rootarg = None
elif rootarg is None:
argpath = Path(arg)
if argpath.is_absolute():
rootarg = chroot / argpath.relative_to("/")
else:
rootarg = Path.cwd() / argpath
if rootarg is None:
rootarg = chroot
# we add any additional arguments before "-r" such that any other "-r" # we add any additional arguments before "-r" such that any other "-r"
# option will be overwritten by the one we set # option will be overwritten by the one we set
subprocess.check_call( subprocess.check_call(
[chroot / "sbin" / "ldconfig"] + sys.argv[1:] + ["-r", chroot] [chroot / "sbin" / "ldconfig"] + sys.argv[1:] + ["-r", rootarg]
) )

View file

@ -11,7 +11,7 @@ set -eu
deletecache() { deletecache() {
dir="$1" dir="$1"
echo "running deletecache $dir">&2 echo "running deletecache $dir" >&2
if [ ! -e "$dir" ]; then if [ ! -e "$dir" ]; then
return return
fi fi
@ -20,7 +20,10 @@ deletecache() {
return 1 return 1
fi fi
# be very careful with removing the old directory # be very careful with removing the old directory
for dist in oldstable stable testing unstable; do # experimental is pulled in with USE_HOST_APT_CONFIG=yes on debci
# when testing a package from experimental
for dist in oldstable stable testing unstable experimental; do
# deleting artifacts from test "debootstrap"
for variant in minbase buildd -; do for variant in minbase buildd -; do
if [ -e "$dir/debian-$dist-$variant.tar" ]; then if [ -e "$dir/debian-$dist-$variant.tar" ]; then
rm "$dir/debian-$dist-$variant.tar" rm "$dir/debian-$dist-$variant.tar"
@ -28,12 +31,24 @@ deletecache() {
echo "does not exist: $dir/debian-$dist-$variant.tar" >&2 echo "does not exist: $dir/debian-$dist-$variant.tar" >&2
fi fi
done done
# deleting artifacts from test "mmdebstrap"
for variant in essential apt minbase buildd - standard; do
for format in tar ext2 ext4 squashfs; do
if [ -e "$dir/mmdebstrap-$dist-$variant.$format" ]; then
# attempt to delete for all dists because DEFAULT_DIST might've been different the last time
rm "$dir/mmdebstrap-$dist-$variant.$format"
elif [ "$dist" = "$DEFAULT_DIST" ]; then
# only warn about non-existance when it's expected to exist
echo "does not exist: $dir/mmdebstrap-$dist-$variant.$format" >&2
fi
done
done
if [ -e "$dir/debian/dists/$dist" ]; then if [ -e "$dir/debian/dists/$dist" ]; then
rm --one-file-system --recursive "$dir/debian/dists/$dist" rm --one-file-system --recursive "$dir/debian/dists/$dist"
else else
echo "does not exist: $dir/debian/dists/$dist" >&2 echo "does not exist: $dir/debian/dists/$dist" >&2
fi fi
case "$dist" in oldstable|stable) case "$dist" in oldstable | stable)
if [ -e "$dir/debian/dists/$dist-updates" ]; then if [ -e "$dir/debian/dists/$dist-updates" ]; then
rm --one-file-system --recursive "$dir/debian/dists/$dist-updates" rm --one-file-system --recursive "$dir/debian/dists/$dist-updates"
else else
@ -41,15 +56,7 @@ deletecache() {
fi fi
;; ;;
esac esac
case "$dist" in case "$dist" in oldstable | stable)
oldstable)
if [ -e "$dir/debian-security/dists/$dist/updates" ]; then
rm --one-file-system --recursive "$dir/debian-security/dists/$dist/updates"
else
echo "does not exist: $dir/debian-security/dists/$dist/updates" >&2
fi
;;
stable)
if [ -e "$dir/debian-security/dists/$dist-security" ]; then if [ -e "$dir/debian-security/dists/$dist-security" ]; then
rm --one-file-system --recursive "$dir/debian-security/dists/$dist-security" rm --one-file-system --recursive "$dir/debian-security/dists/$dist-security"
else else
@ -58,16 +65,21 @@ deletecache() {
;; ;;
esac esac
done done
if [ -e $dir/debian-*.qcow ]; then for f in "$dir/debian-"*.ext4; do
rm --one-file-system "$dir"/debian-*.qcow if [ -e "$f" ]; then
else rm --one-file-system "$f"
echo "does not exist: $dir/debian-*.qcow" >&2
fi fi
if [ -e "$dir/debian/pool/main" ]; then done
rm --one-file-system --recursive "$dir/debian/pool/main" # on i386 and amd64, the intel-microcode and amd64-microcode packages
# from non-free-firwame get pulled in because they are
# priority:standard with USE_HOST_APT_CONFIG=yes
for c in main non-free-firmware; do
if [ -e "$dir/debian/pool/$c" ]; then
rm --one-file-system --recursive "$dir/debian/pool/$c"
else else
echo "does not exist: $dir/debian/pool/main" >&2 echo "does not exist: $dir/debian/pool/$c" >&2
fi fi
done
if [ -e "$dir/debian-security/pool/updates/main" ]; then if [ -e "$dir/debian-security/pool/updates/main" ]; then
rm --one-file-system --recursive "$dir/debian-security/pool/updates/main" rm --one-file-system --recursive "$dir/debian-security/pool/updates/main"
else else
@ -96,72 +108,6 @@ cleanup_newcachedir() {
deletecache "$newcachedir" deletecache "$newcachedir"
} }
get_oldaptnames() {
if [ ! -e "$1/$2" ]; then
return
fi
xz -dc "$1/$2" \
| grep-dctrl --no-field-names --show-field=Package,Version,Architecture,Filename '' \
| paste -sd " \n" \
| while read name ver arch fname; do
if [ ! -e "$1/$fname" ]; then
continue
fi
# apt stores deb files with the colon encoded as %3a while
# mirrors do not contain the epoch at all #645895
case "$ver" in *:*) ver="${ver%%:*}%3a${ver#*:}";; esac
aptname="$rootdir/var/cache/apt/archives/${name}_${ver}_${arch}.deb"
# we have to cp and not mv because other
# distributions might still need this file
# we have to cp and not symlink because apt
# doesn't recognize symlinks
cp --link "$1/$fname" "$aptname"
echo "$aptname"
done
}
get_newaptnames() {
if [ ! -e "$1/$2" ]; then
return
fi
# skip empty files by trying to uncompress the first byte of the payload
if [ "$(xz -dc "$1/$2" | head -c1 | wc -c)" -eq 0 ]; then
return
fi
xz -dc "$1/$2" \
| grep-dctrl --no-field-names --show-field=Package,Version,Architecture,Filename,SHA256 '' \
| paste -sd " \n" \
| while read name ver arch fname hash; do
# sanity check for the hash because sometimes the
# archive switches the hash algorithm
if [ "${#hash}" -ne 64 ]; then
echo "expected hash length of 64 but got ${#hash} for: $hash" >&2
exit 1
fi
dir="${fname%/*}"
# apt stores deb files with the colon encoded as %3a while
# mirrors do not contain the epoch at all #645895
case "$ver" in *:*) ver="${ver%%:*}%3a${ver#*:}";; esac
aptname="$rootdir/var/cache/apt/archives/${name}_${ver}_${arch}.deb"
if [ -e "$aptname" ]; then
# make sure that we found the right file by checking its hash
echo "$hash $aptname" | sha256sum --check >&2
mkdir -p "$1/$dir"
# since we move hardlinks around, the same hardlink might've been
# moved already into the same place by another distribution.
# mv(1) refuses to copy A to B if both are hardlinks of each other.
if [ "$aptname" -ef "$1/$fname" ]; then
# both files are already the same so we just need to
# delete the source
rm "$aptname"
else
mv "$aptname" "$1/$fname"
fi
echo "$aptname"
fi
done
}
cleanupapt() { cleanupapt() {
echo "running cleanupapt" >&2 echo "running cleanupapt" >&2
if [ ! -e "$rootdir" ]; then if [ ! -e "$rootdir" ]; then
@ -175,10 +121,11 @@ cleanupapt() {
"$rootdir/var/lib/dpkg/status" \ "$rootdir/var/lib/dpkg/status" \
"$rootdir/var/lib/dpkg/lock-frontend" \ "$rootdir/var/lib/dpkg/lock-frontend" \
"$rootdir/var/lib/dpkg/lock" \ "$rootdir/var/lib/dpkg/lock" \
"$rootdir/var/lib/apt/lists/lock" \
"$rootdir/etc/apt/apt.conf" \ "$rootdir/etc/apt/apt.conf" \
"$rootdir/etc/apt/sources.list.d/"* \
"$rootdir/etc/apt/preferences.d/"* \
"$rootdir/etc/apt/sources.list" \ "$rootdir/etc/apt/sources.list" \
"$rootdir/oldaptnames" \
"$rootdir/newaptnames" \
"$rootdir/var/cache/apt/archives/lock"; do "$rootdir/var/cache/apt/archives/lock"; do
if [ ! -e "$f" ]; then if [ ! -e "$f" ]; then
echo "does not exist: $f" >&2 echo "does not exist: $f" >&2
@ -207,16 +154,16 @@ update_cache() (
# we only set this trap here and overwrite the previous trap, because # we only set this trap here and overwrite the previous trap, because
# the update_cache function is run as part of a pipe and thus in its # the update_cache function is run as part of a pipe and thus in its
# own process which will EXIT after it finished # own process which will EXIT after it finished
trap "cleanupapt" EXIT INT TERM trap 'kill "$PROXYPID" || :;cleanupapt' EXIT INT TERM
for p in /etc/apt/apt.conf.d /etc/apt/sources.list.d /etc/apt/preferences.d /var/cache/apt/archives /var/lib/apt/lists/partial /var/lib/dpkg; do for p in /etc/apt/apt.conf.d /etc/apt/sources.list.d /etc/apt/preferences.d /var/cache/apt/archives /var/lib/apt/lists/partial /var/lib/dpkg; do
mkdir -p "$rootdir/$p" mkdir -p "$rootdir/$p"
done done
# read sources.list content from stdin # read sources.list content from stdin
cat > "$rootdir/etc/apt/sources.list" cat >"$rootdir/etc/apt/sources.list"
cat << END > "$rootdir/etc/apt/apt.conf" cat <<END >"$rootdir/etc/apt/apt.conf"
Apt::Architecture "$nativearch"; Apt::Architecture "$nativearch";
Apt::Architectures "$nativearch"; Apt::Architectures "$nativearch";
Dir::Etc "$rootdir/etc/apt"; Dir::Etc "$rootdir/etc/apt";
@ -227,34 +174,49 @@ Apt::Get::Download-Only true;
Acquire::Languages "none"; Acquire::Languages "none";
Dir::Etc::Trusted "/etc/apt/trusted.gpg"; Dir::Etc::Trusted "/etc/apt/trusted.gpg";
Dir::Etc::TrustedParts "/etc/apt/trusted.gpg.d"; Dir::Etc::TrustedParts "/etc/apt/trusted.gpg.d";
Acquire::http::Dl-Limit "1000"; Acquire::http::Proxy "http://127.0.0.1:8080/";
Acquire::https::Dl-Limit "1000";
Acquire::Retries "5";
END END
> "$rootdir/var/lib/dpkg/status" : >"$rootdir/var/lib/dpkg/status"
APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get update if [ "$dist" = "$DEFAULT_DIST" ] && [ "$nativearch" = "$HOSTARCH" ] && [ "$USE_HOST_APT_CONFIG" = "yes" ]; then
# we append sources and settings instead of overwriting after
# an empty line
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -e "$f" ] || continue
[ -e "$rootdir/$f" ] && echo >>"$rootdir/$f"
# Filter out file:// repositories as they are added
# to each mmdebstrap call verbatim by
# debian/tests/copy_host_apt_config
# Also filter out all mirrors that are not of suite
# $DEFAULT_DIST, except experimental if the suite
# is unstable. This prevents packages from
# unstable entering a testing mirror.
if [ "$dist" = unstable ]; then
grep -v ' file://' "$f" \
| grep -E " (unstable|experimental) " \
>>"$rootdir/$f" || :
else
grep -v ' file://' "$f" \
| grep " $DEFAULT_DIST " \
>>"$rootdir/$f" || :
fi
done
for f in /etc/apt/preferences.d/*; do
[ -e "$f" ] || continue
[ -e "$rootdir/$f" ] && echo >>"$rootdir/$f"
cat "$f" >>"$rootdir/$f"
done
fi
# before downloading packages and before replacing the old Packages echo "creating mirror for $dist" >&2
# file, copy all old *.deb packages from the mirror to for f in /etc/apt/sources.list /etc/apt/sources.list.d/* /etc/apt/preferences.d/*; do
# /var/cache/apt/archives so that apt will not re-download *.deb [ -e "$rootdir/$f" ] || continue
# packages that we already have echo "contents of $f:" >&2
{ cat "$rootdir/$f" >&2
get_oldaptnames "$oldmirrordir" "dists/$dist/main/binary-$nativearch/Packages.xz" done
case "$dist" in oldstable|stable)
get_oldaptnames "$oldmirrordir" "dists/$dist-updates/main/binary-$nativearch/Packages.xz" APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get update --error-on=any
;;
esac
case "$dist" in
oldstable)
get_oldaptnames "$oldcachedir/debian-security" "dists/$dist/updates/main/binary-$nativearch/Packages.xz"
;;
stable)
get_oldaptnames "$oldcachedir/debian-security" "dists/$dist-security/main/binary-$nativearch/Packages.xz"
;;
esac
} | sort -u > "$rootdir/oldaptnames"
pkgs=$(APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get indextargets \ pkgs=$(APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get indextargets \
--format '$(FILENAME)' 'Created-By: Packages' "Architecture: $nativearch" \ --format '$(FILENAME)' 'Created-By: Packages' "Architecture: $nativearch" \
@ -264,77 +226,24 @@ END
--or --field=Priority important --or --field=Priority standard \ --or --field=Priority important --or --field=Priority standard \
\)) \))
pkgs="$(echo $pkgs) build-essential busybox gpg eatmydata" pkgs="$pkgs build-essential busybox gpg eatmydata fakechroot fakeroot"
APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install $pkgs # we need usr-is-merged to simulate debootstrap behaviour for all dists
# starting from Debian 12 (Bullseye)
# to be able to also test gpg verification, we need to create a mirror
mkdir -p "$newmirrordir/dists/$dist/main/binary-$nativearch/"
curl --location "$mirror/dists/$dist/Release" > "$newmirrordir/dists/$dist/Release"
curl --location "$mirror/dists/$dist/Release.gpg" > "$newmirrordir/dists/$dist/Release.gpg"
curl --location "$mirror/dists/$dist/main/binary-$nativearch/Packages.xz" > "$newmirrordir/dists/$dist/main/binary-$nativearch/Packages.xz"
codename=$(awk '/^Codename: / { print $2; }' < "$newmirrordir/dists/$dist/Release")
[ -L "$newmirrordir/dists/$codename" ] || ln -s "$dist" "$newmirrordir/dists/$codename"
case "$dist" in oldstable|stable)
mkdir -p "$newmirrordir/dists/$dist-updates/main/binary-$nativearch/"
curl --location "$mirror/dists/$dist-updates/Release" > "$newmirrordir/dists/$dist-updates/Release"
curl --location "$mirror/dists/$dist-updates/Release.gpg" > "$newmirrordir/dists/$dist-updates/Release.gpg"
curl --location "$mirror/dists/$dist-updates/main/binary-$nativearch/Packages.xz" > "$newmirrordir/dists/$dist-updates/main/binary-$nativearch/Packages.xz"
[ -L "$newmirrordir/dists/$codename-updates" ] || ln -s "$dist-updates" "$newmirrordir/dists/$codename-updates"
;;
esac
case "$dist" in case "$dist" in
oldstable) oldstable) : ;;
mkdir -p "$newcachedir/debian-security/dists/$dist/updates/main/binary-$nativearch/" *) pkgs="$pkgs usr-is-merged usrmerge" ;;
curl --location "$security_mirror/dists/$dist/updates/Release" > "$newcachedir/debian-security/dists/$dist/updates/Release"
curl --location "$security_mirror/dists/$dist/updates/Release.gpg" > "$newcachedir/debian-security/dists/$dist/updates/Release.gpg"
curl --location "$security_mirror/dists/$dist/updates/main/binary-$nativearch/Packages.xz" > "$newcachedir/debian-security/dists/$dist/updates/main/binary-$nativearch/Packages.xz"
;;
stable)
mkdir -p "$newcachedir/debian-security/dists/$dist-security/main/binary-$nativearch/"
curl --location "$security_mirror/dists/$dist-security/Release" > "$newcachedir/debian-security/dists/$dist-security/Release"
curl --location "$security_mirror/dists/$dist-security/Release.gpg" > "$newcachedir/debian-security/dists/$dist-security/Release.gpg"
curl --location "$security_mirror/dists/$dist-security/main/binary-$nativearch/Packages.xz" > "$newcachedir/debian-security/dists/$dist-security/main/binary-$nativearch/Packages.xz"
[ -L "$newcachedir/debian-security/dists/$codename-security" ] || ln -s "$dist-security" "$newcachedir/debian-security/dists/$codename-security"
;;
esac esac
# the deb files downloaded by apt must be moved to their right locations in the # shellcheck disable=SC2086
# pool directory APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install $pkgs \
# || APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install \
# Instead of parsing the Packages file, we could also attempt to move the deb -oDebug::pkgProblemResolver=true -oDebug::pkgDepCache::Marker=1 \
# files ourselves to the appropriate pool directories. But that approach -oDebug::pkgDepCache::AutoInstall=1 \
# requires re-creating the heuristic by which the directory is chosen, requires $pkgs
# stripping the epoch from the filename and will break once mirrors change.
# This way, it doesn't matter where the mirror ends up storing the package.
{
get_newaptnames "$newmirrordir" "dists/$dist/main/binary-$nativearch/Packages.xz";
case "$dist" in oldstable|stable)
get_newaptnames "$newmirrordir" "dists/$dist-updates/main/binary-$nativearch/Packages.xz"
;;
esac
case "$dist" in
oldstable)
get_newaptnames "$newcachedir/debian-security" "dists/$dist/updates/main/binary-$nativearch/Packages.xz"
;;
stable)
get_newaptnames "$newcachedir/debian-security" "dists/$dist-security/main/binary-$nativearch/Packages.xz"
;;
esac
} | sort -u > "$rootdir/newaptnames"
rm "$rootdir/var/cache/apt/archives/lock" rm "$rootdir/var/cache/apt/archives/lock"
rmdir "$rootdir/var/cache/apt/archives/partial" rmdir "$rootdir/var/cache/apt/archives/partial"
# remove all packages that were in the old Packages file but not in the
# new one anymore
comm -23 "$rootdir/oldaptnames" "$rootdir/newaptnames" | xargs --delimiter="\n" --no-run-if-empty rm
# now the apt cache should be empty
if [ ! -z "$(ls -1qA "$rootdir/var/cache/apt/archives/")" ]; then
echo "$rootdir/var/cache/apt/archives not empty:"
ls -la "$rootdir/var/cache/apt/archives/"
exit 1
fi
APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --option Dir::Etc::SourceList=/dev/null update APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --option Dir::Etc::SourceList=/dev/null update
APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get clean APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get clean
@ -345,6 +254,17 @@ END
trap "-" EXIT INT TERM trap "-" EXIT INT TERM
) )
check_proxy_running() {
if timeout 1 bash -c 'exec 3<>/dev/tcp/127.0.0.1/8080 && printf "GET http://deb.debian.org/debian/dists/'"$DEFAULT_DIST"'/InRelease HTTP/1.1\nHost: deb.debian.org\n\n" >&3 && grep "Suite: '"$DEFAULT_DIST"'" <&3 >/dev/null' 2>/dev/null; then
return 0
elif timeout 1 env http_proxy="http://127.0.0.1:8080/" wget --quiet -O - "http://deb.debian.org/debian/dists/$DEFAULT_DIST/InRelease" | grep "Suite: $DEFAULT_DIST" >/dev/null; then
return 0
elif timeout 1 curl --proxy "http://127.0.0.1:8080/" --silent "http://deb.debian.org/debian/dists/$DEFAULT_DIST/InRelease" | grep "Suite: $DEFAULT_DIST" >/dev/null; then
return 0
fi
return 1
}
if [ -e "./shared/cache.A" ] && [ -e "./shared/cache.B" ]; then if [ -e "./shared/cache.A" ] && [ -e "./shared/cache.B" ]; then
echo "both ./shared/cache.A and ./shared/cache.B exist" >&2 echo "both ./shared/cache.A and ./shared/cache.B exist" >&2
echo "was a former run of the script aborted?" >&2 echo "was a former run of the script aborted?" >&2
@ -352,16 +272,22 @@ if [ -e "./shared/cache.A" ] && [ -e "./shared/cache.B" ]; then
echo "cache symlink points to $(readlink ./shared/cache)" >&2 echo "cache symlink points to $(readlink ./shared/cache)" >&2
case "$(readlink ./shared/cache)" in case "$(readlink ./shared/cache)" in
cache.A) cache.A)
echo "maybe rm -r ./shared/cache.B" >&2 echo "removing ./shared/cache.B" >&2
rm -r ./shared/cache.B
;; ;;
cache.B) cache.B)
echo "maybe rm -r ./shared/cache.A" >&2 echo "removing ./shared/cache.A" >&2
rm -r ./shared/cache.A
;; ;;
*) *)
echo "unexpected" >&2 echo "unexpected" >&2
esac
fi
exit 1 exit 1
;;
esac
else
echo "./shared/cache doesn't exist" >&2
exit 1
fi
fi fi
if [ -e "./shared/cache.A" ]; then if [ -e "./shared/cache.A" ]; then
@ -383,37 +309,72 @@ security_mirror="http://security.debian.org/debian-security"
components=main components=main
: "${DEFAULT_DIST:=unstable}" : "${DEFAULT_DIST:=unstable}"
: "${ONLY_DEFAULT_DIST:=no}"
: "${ONLY_HOSTARCH:=no}"
: "${HAVE_QEMU:=yes}" : "${HAVE_QEMU:=yes}"
: "${RUN_MA_SAME_TESTS:=yes}" : "${RUN_MA_SAME_TESTS:=yes}"
: "${HAVE_PROOT:=yes}"
# by default, use the mmdebstrap executable in the current directory # by default, use the mmdebstrap executable in the current directory
: "${CMD:=./mmdebstrap}" : "${CMD:=./mmdebstrap}"
: "${USE_HOST_APT_CONFIG:=no}"
: "${FORCE_UPDATE:=no}"
if [ -e "$oldmirrordir/dists/$DEFAULT_DIST/Release" ]; then if [ "$FORCE_UPDATE" != "yes" ] && [ -e "$oldmirrordir/dists/$DEFAULT_DIST/InRelease" ]; then
http_code=$(curl --output /dev/null --silent --location --head --time-cond "$oldmirrordir/dists/$DEFAULT_DIST/Release" --write-out '%{http_code}' "$mirror/dists/$DEFAULT_DIST/Release") http_code=$(curl --output /dev/null --silent --location --head --time-cond "$oldmirrordir/dists/$DEFAULT_DIST/InRelease" --write-out '%{http_code}' "$mirror/dists/$DEFAULT_DIST/InRelease")
case "$http_code" in case "$http_code" in
200) ;; # need update 200) ;; # need update
304) echo up-to-date; exit 0;; 304)
*) echo "unexpected status: $http_code"; exit 1;; echo up-to-date
exit 0
;;
*)
echo "unexpected status: $http_code"
exit 1
;;
esac esac
fi fi
trap "cleanup_newcachedir" EXIT INT TERM ./caching_proxy.py "$oldcachedir" "$newcachedir" &
PROXYPID=$!
trap 'kill "$PROXYPID" || :' EXIT INT TERM
for i in $(seq 10); do
check_proxy_running && break
sleep 1
done
if [ ! -s "$newmirrordir/dists/$DEFAULT_DIST/InRelease" ]; then
echo "failed to start proxy" >&2
kill $PROXYPID
exit 1
fi
trap 'kill "$PROXYPID" || :;cleanup_newcachedir' EXIT INT TERM
mkdir -p "$newcachedir" mkdir -p "$newcachedir"
touch "$newcachedir/mmdebstrapcache" touch "$newcachedir/mmdebstrapcache"
HOSTARCH=$(dpkg --print-architecture) HOSTARCH=$(dpkg --print-architecture)
arches="$HOSTARCH"
if [ "$HOSTARCH" = amd64 ]; then if [ "$HOSTARCH" = amd64 ]; then
arches="amd64 arm64 i386" arches="$arches arm64 i386"
else elif [ "$HOSTARCH" = arm64 ]; then
arches="$HOSTARCH" arches="$arches amd64 armhf"
fi fi
for nativearch in $arches; do # we need the split_inline_sig() function
for dist in oldstable stable testing unstable; do # shellcheck disable=SC1091
. /usr/share/debootstrap/functions
for dist in oldstable stable testing unstable; do
for nativearch in $arches; do
# non-host architectures are only downloaded for $DEFAULT_DIST # non-host architectures are only downloaded for $DEFAULT_DIST
if [ $nativearch != $HOSTARCH ] && [ $DEFAULT_DIST != $dist ]; then if [ "$nativearch" != "$HOSTARCH" ] && [ "$DEFAULT_DIST" != "$dist" ]; then
continue
fi
# if ONLY_DEFAULT_DIST is set, only download DEFAULT_DIST
if [ "$ONLY_DEFAULT_DIST" = "yes" ] && [ "$DEFAULT_DIST" != "$dist" ]; then
continue
fi
if [ "$ONLY_HOSTARCH" = "yes" ] && [ "$nativearch" != "$HOSTARCH" ]; then
continue continue
fi fi
# we need a first pass without updates and security patches # we need a first pass without updates and security patches
@ -422,16 +383,8 @@ for nativearch in $arches; do
echo "deb [arch=$nativearch] $mirror $dist $components" | update_cache "$dist" "$nativearch" echo "deb [arch=$nativearch] $mirror $dist $components" | update_cache "$dist" "$nativearch"
# we need to include the base mirror again or otherwise # we need to include the base mirror again or otherwise
# packages like build-essential will be missing # packages like build-essential will be missing
case "$dist" in case "$dist" in oldstable | stable)
oldstable) cat <<END | update_cache "$dist" "$nativearch"
cat << END | update_cache "$dist" "$nativearch"
deb [arch=$nativearch] $mirror $dist $components
deb [arch=$nativearch] $mirror $dist-updates main
deb [arch=$nativearch] $security_mirror $dist/updates main
END
;;
stable)
cat << END | update_cache "$dist" "$nativearch"
deb [arch=$nativearch] $mirror $dist $components deb [arch=$nativearch] $mirror $dist $components
deb [arch=$nativearch] $mirror $dist-updates main deb [arch=$nativearch] $mirror $dist-updates main
deb [arch=$nativearch] $security_mirror $dist-security main deb [arch=$nativearch] $security_mirror $dist-security main
@ -439,8 +392,20 @@ END
;; ;;
esac esac
done done
codename=$(awk '/^Codename: / { print $2; }' <"$newmirrordir/dists/$dist/InRelease")
ln -s "$dist" "$newmirrordir/dists/$codename"
# split the InRelease file into Release and Release.gpg not because apt
# or debootstrap need it that way but because grep-dctrl does
split_inline_sig \
"$newmirrordir/dists/$dist/InRelease" \
"$newmirrordir/dists/$dist/Release" \
"$newmirrordir/dists/$dist/Release.gpg"
touch --reference="$newmirrordir/dists/$dist/InRelease" "$newmirrordir/dists/$dist/Release" "$newmirrordir/dists/$dist/Release.gpg"
done done
kill $PROXYPID
# Create some symlinks so that we can trick apt into accepting multiple apt # Create some symlinks so that we can trick apt into accepting multiple apt
# lines that point to the same repository but look different. This is to # lines that point to the same repository but look different. This is to
# avoid the warning: # avoid the warning:
@ -458,12 +423,7 @@ cleanuptmpdir() {
if [ ! -e "$tmpdir" ]; then if [ ! -e "$tmpdir" ]; then
return return
fi fi
for f in "$tmpdir/extlinux.conf" \ for f in "$tmpdir/worker.sh" "$tmpdir/mmdebstrap.service"; do
"$tmpdir/worker.sh" \
"$tmpdir/mini-httpd" "$tmpdir/hosts" \
"$tmpdir/debian-chroot.tar" \
"$tmpdir/mmdebstrap.service" \
"$tmpdir/debian-$DEFAULT_DIST.img"; do
if [ ! -e "$f" ]; then if [ ! -e "$f" ]; then
echo "does not exist: $f" >&2 echo "does not exist: $f" >&2
continue continue
@ -473,76 +433,51 @@ cleanuptmpdir() {
rmdir "$tmpdir" rmdir "$tmpdir"
} }
export SOURCE_DATE_EPOCH=$(date --date="$(grep-dctrl -s Date -n '' "$newmirrordir/dists/$DEFAULT_DIST/Release")" +%s) SOURCE_DATE_EPOCH="$(date --date="$(grep-dctrl -s Date -n '' "$newmirrordir/dists/$DEFAULT_DIST/Release")" +%s)"
export SOURCE_DATE_EPOCH
if [ "$HAVE_QEMU" = "yes" ]; then if [ "$HAVE_QEMU" = "yes" ]; then
case "$HOSTARCH" in # we use the caching proxy again when building the qemu image
amd64|i386) # - we can re-use the packages that were already downloaded earlier
# okay # - we make sure that the qemu image uses the same Release file even
;; # if a mirror push happened between now and earlier
*) # - we avoid polluting the mirror with the additional packages by
echo "qemu support is only available on amd64 and i386" >&2 # using --readonly
echo "because syslinux is only available on those arches" >&2 ./caching_proxy.py --readonly "$oldcachedir" "$newcachedir" &
PROXYPID=$!
for i in $(seq 10); do
check_proxy_running && break
sleep 1
done
if [ ! -s "$newmirrordir/dists/$DEFAULT_DIST/InRelease" ]; then
echo "failed to start proxy" >&2
kill $PROXYPID
exit 1 exit 1
;; fi
esac
# We must not use any --dpkgopt here because any dpkg options still
# leak into the chroot with chrootless mode.
# We do not use our own package cache here because
# - it doesn't (and shouldn't) contain the extra packages
# - it doesn't matter if the base system is from a different mirror timestamp
# procps is needed for /sbin/sysctl
tmpdir="$(mktemp -d)" tmpdir="$(mktemp -d)"
trap "cleanuptmpdir; cleanup_newcachedir" EXIT INT TERM trap 'kill "$PROXYPID" || :;cleanuptmpdir; cleanup_newcachedir' EXIT INT TERM
pkgs=perl-doc,systemd-sysv,perl,arch-test,fakechroot,fakeroot,mount,uidmap,qemu-user-static,binfmt-support,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 pkgs=perl-doc,systemd-sysv,perl,arch-test,fakechroot,fakeroot,mount,uidmap,qemu-user-binfmt,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,e2fsprogs,uuid-runtime
if [ "$DEFAULT_DIST" != "oldstable" ]; then
pkgs="$pkgs,squashfs-tools-ng,genext2fs"
fi
if [ "$HAVE_PROOT" = "yes" ]; then
pkgs="$pkgs,proot"
fi
if [ ! -e ./mmdebstrap ]; then if [ ! -e ./mmdebstrap ]; then
pkgs="$pkgs,mmdebstrap" pkgs="$pkgs,mmdebstrap"
fi fi
arches=$HOSTARCH
if [ "$RUN_MA_SAME_TESTS" = "yes" ]; then
case "$HOSTARCH" in case "$HOSTARCH" in
amd64|arm64) amd64)
pkgs="$pkgs,linux-image-$HOSTARCH"
;;
i386)
pkgs="$pkgs,linux-image-686"
;;
ppc64el)
pkgs="$pkgs,linux-image-powerpc64le"
;;
*)
echo "no kernel image for $HOSTARCH" >&2
exit 1
;;
esac
if [ "$HOSTARCH" = amd64 ] && [ "$RUN_MA_SAME_TESTS" = "yes" ]; then
arches=amd64,arm64 arches=amd64,arm64
pkgs="$pkgs,libfakechroot:arm64,libfakeroot:arm64" pkgs="$pkgs,libfakechroot:arm64,libfakeroot:arm64"
else ;;
arches=$HOSTARCH arm64)
arches=arm64,amd64
pkgs="$pkgs,libfakechroot:amd64,libfakeroot:amd64"
;;
esac
fi fi
$CMD --variant=apt --architectures=$arches --include="$pkgs" \
--aptopt='Acquire::http::Dl-Limit "1000"' \
--aptopt='Acquire::https::Dl-Limit "1000"' \
--aptopt='Acquire::Retries "5"' \
$DEFAULT_DIST - "$mirror" > "$tmpdir/debian-chroot.tar"
cat << END > "$tmpdir/extlinux.conf" cat <<END >"$tmpdir/mmdebstrap.service"
default linux
timeout 0
label linux
kernel /vmlinuz
append initrd=/initrd.img root=/dev/vda1 rw console=ttyS0,115200
serial 0 115200
END
cat << END > "$tmpdir/mmdebstrap.service"
[Unit] [Unit]
Description=mmdebstrap worker script Description=mmdebstrap worker script
@ -561,13 +496,15 @@ END
# filesystem that doesn't support ownership information at all and a umask that # filesystem that doesn't support ownership information at all and a umask that
# gives read/write access to everybody. # gives read/write access to everybody.
# https://github.com/pjcj/Devel--Cover/issues/223 # https://github.com/pjcj/Devel--Cover/issues/223
cat << 'END' > "$tmpdir/worker.sh" cat <<'END' >"$tmpdir/worker.sh"
#!/bin/sh #!/bin/sh
echo 'root:root' | chpasswd echo 'root:root' | chpasswd
mount -t 9p -o trans=virtio,access=any,msize=128k mmdebstrap /mnt 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 # need to restart mini-httpd because we mounted different content into www-root
systemctl restart mini-httpd systemctl restart mini-httpd
ip link set enp0s1 down || :
handler () { handler () {
while IFS= read -r line || [ -n "$line" ]; do 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" printf "%s %s: %s\n" "$(date -u -d "0 $(date +%s.%3N) seconds - $2 seconds" +"%T.%3N")" "$1" "$line"
@ -590,100 +527,43 @@ handler () {
} 2>&1; } 2>&1;
} | { read xs; exit $xs; }; } | { read xs; exit $xs; };
} 3>&1 || ret=$? } 3>&1 || ret=$?
echo $ret > /mnt/exitstatus.txt
if [ -e cover_db.img ]; then if [ -e cover_db.img ]; then
df -h cover_db df -h cover_db
umount cover_db umount cover_db
fi fi
echo $ret ) > /mnt/output.txt 2>&1
) > /mnt/result.txt 2>&1
umount /mnt umount /mnt
systemctl poweroff systemctl poweroff
END END
chmod +x "$tmpdir/worker.sh" chmod +x "$tmpdir/worker.sh"
# initially we serve from the new cache so that debootstrap can grab
# the new package repository and not the old
cat << END > "$tmpdir/mini-httpd"
START=1
DAEMON_OPTS="-h 127.0.0.1 -p 80 -u nobody -dd /mnt/$newcache -i /var/run/mini-httpd.pid -T UTF-8"
END
cat << 'END' > "$tmpdir/hosts"
127.0.0.1 localhost
END
#libguestfs-test-tool
#export LIBGUESTFS_DEBUG=1 LIBGUESTFS_TRACE=1
#
# In case the rootfs was prepared in fakechroot mode, ldconfig has to
# run to populate /etc/ld.so.cache or otherwise fakechroot tests will
# fail to run.
#
# The disk size is sufficient in most cases. Sometimes, gcc will do
# an upload with unstripped executables to make tracking down ICEs much
# easier (see #872672, #894014). During times with unstripped gcc, the
# buildd variant will not be 400MB but 1.3GB large and needs a 10G
# disk.
if [ -z ${DISK_SIZE+x} ]; then if [ -z ${DISK_SIZE+x} ]; then
DISK_SIZE=3G DISK_SIZE=10G
fi fi
guestfish -N "$tmpdir/debian-$DEFAULT_DIST.img"=disk:$DISK_SIZE -- \ # set PATH to pick up the correct mmdebstrap variant
part-disk /dev/sda mbr : \ env PATH="$(dirname "$(realpath --canonicalize-existing "$CMD")"):$PATH" \
mkfs ext2 /dev/sda1 : \ debvm-create --skip=usrmerge,systemdnetwork \
mount /dev/sda1 / : \ --size="$DISK_SIZE" --release="$DEFAULT_DIST" \
tar-in "$tmpdir/debian-chroot.tar" / : \ --output="$newcachedir/debian-$DEFAULT_DIST.ext4" -- \
command /sbin/ldconfig : \ --architectures="$arches" --include="$pkgs" \
copy-in "$tmpdir/extlinux.conf" / : \ --setup-hook='echo "Acquire::http::Proxy \"http://127.0.0.1:8080/\";" > "$1/etc/apt/apt.conf.d/00proxy"' \
mkdir-p /etc/systemd/system/multi-user.target.wants : \ --hook-dir=/usr/share/mmdebstrap/hooks/maybe-merged-usr \
ln-s ../mmdebstrap.service /etc/systemd/system/multi-user.target.wants/mmdebstrap.service : \ --customize-hook='rm "$1/etc/apt/apt.conf.d/00proxy"' \
copy-in "$tmpdir/mmdebstrap.service" /etc/systemd/system/ : \ --customize-hook='mkdir -p "$1/etc/systemd/system/multi-user.target.wants"' \
copy-in "$tmpdir/worker.sh" / : \ --customize-hook='ln -s ../mmdebstrap.service "$1/etc/systemd/system/multi-user.target.wants/mmdebstrap.service"' \
copy-in "$tmpdir/mini-httpd" /etc/default : \ --customize-hook='touch "$1/mmdebstrap-testenv"' \
copy-in "$tmpdir/hosts" /etc/ : \ --customize-hook='copy-in "'"$tmpdir"'/mmdebstrap.service" /etc/systemd/system/' \
touch /mmdebstrap-testenv : \ --customize-hook='copy-in "'"$tmpdir"'/worker.sh" /' \
upload /usr/lib/SYSLINUX/mbr.bin /mbr.bin : \ --customize-hook='echo 127.0.0.1 localhost > "$1/etc/hosts"' \
copy-file-to-device /mbr.bin /dev/sda size:440 : \ --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"' \
rm /mbr.bin : \ --customize-hook='touch "$1/etc/systemd/system/tmp.mount"' \
extlinux / : \ "$mirror"
sync : \
umount / : \ kill $PROXYPID
part-set-bootable /dev/sda 1 true : \
shutdown
qemu-img convert -O qcow2 "$tmpdir/debian-$DEFAULT_DIST.img" "$newcachedir/debian-$DEFAULT_DIST.qcow"
cleanuptmpdir cleanuptmpdir
trap "cleanup_newcachedir" EXIT INT TERM trap "cleanup_newcachedir" EXIT INT TERM
fi fi
mirror="http://127.0.0.1/debian"
for dist in oldstable stable testing unstable; do
for variant in minbase buildd -; do
echo "running debootstrap --no-merged-usr --variant=$variant $dist \${TEMPDIR} $mirror"
cat << END > shared/test.sh
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH
tmpdir="\$(mktemp -d)"
chmod 755 "\$tmpdir"
debootstrap --no-merged-usr --variant=$variant $dist "\$tmpdir" $mirror
tar --sort=name --mtime=@$SOURCE_DATE_EPOCH --clamp-mtime --numeric-owner --one-file-system --xattrs -C "\$tmpdir" -c . > "$newcache/debian-$dist-$variant.tar"
rm -r "\$tmpdir"
END
if [ "$HAVE_QEMU" = "yes" ]; then
cachedir=$newcachedir ./run_qemu.sh
else
./run_null.sh SUDO
fi
done
done
if [ "$HAVE_QEMU" = "yes" ]; then
# now replace the minihttpd config with one that serves the new repository
guestfish -a "$newcachedir/debian-$DEFAULT_DIST.qcow" -i <<EOF
upload -<<END /etc/default/mini-httpd
START=1
DAEMON_OPTS="-h 127.0.0.1 -p 80 -u nobody -dd /mnt/cache -i /var/run/mini-httpd.pid -T UTF-8"
END
EOF
fi
# delete possibly leftover symlink # delete possibly leftover symlink
if [ -e ./shared/cache.tmp ]; then if [ -e ./shared/cache.tmp ]; then
rm ./shared/cache.tmp rm ./shared/cache.tmp
@ -695,3 +575,5 @@ mv --no-target-directory ./shared/cache.tmp ./shared/cache
deletecache "$oldcachedir" deletecache "$oldcachedir"
trap - EXIT INT TERM trap - EXIT INT TERM
echo "$0 finished successfully" >&2

4680
mmdebstrap

File diff suppressed because it is too large Load diff

460
mmdebstrap-autopkgtest-build-qemu Executable file
View file

@ -0,0 +1,460 @@
#!/bin/sh
# Copyright 2023 Johannes Schauer Marin Rodrigues <josch@debian.org>
# Copyright 2023 Helmut Grohne <helmut@subdivi.de>
# SPDX-License-Identifier: MIT
# We generally use single quotes to avoid variable expansion:
# shellcheck disable=SC2016
# Replacement for autopkgtest-build-qemu and vmdb2 for all architectures
# supporting EFI booting (amd64, arm64, armhf, i386, riscv64).
# For use as replacement for autopkgtest-build-qemu and vmdb2 on ppc64el which
# neither supports extlinux nor efi booting there is an unmaintained script
# which uses grub instead to boot:
#
# https://gitlab.mister-muffin.de/josch/mmdebstrap/src/commit/
# e523741610a4ed8579642bfc755956f64c847ef3/mmdebstrap-autopkgtest-build-qemu
: <<'POD2MAN'
=head1 NAME
mmdebstrap-autopkgtest-build-qemu - autopkgtest-build-qemu without vmdb2 but mmdebstrap and EFI boot
=head1 SYNOPSIS
B<mmdebstrap-autopkgtest-build-qemu> [I<OPTIONS>] B<--boot>=B<efi> I<RELEASE> I<IMAGE>
=head1 DESCRIPTION
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 and with bit-by-bit reproducible
output. Secondly, it uses 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
=over 8
=item I<RELEASE>
The release to download from the I<MIRROR>. This parameter is required.
=item I<IMAGE>
The file to write, in raw format. This parameter is required.
=back
=head1 OPTIONS
=over 8
=item B<--mirror>=I<MIRROR>
Specify which distribution to install. It defaults to
http://deb.debian.org/debian (i.e. Debian), but you can pass a mirror of any
Debian derivative.
=item B<--architecture>=I<ARCHITECTURE>
Set the architecture for the virtual machine image, specified as a L<dpkg(1)>
architecture. If omitted, the host architecture is assumed.
B<--arch>=I<ARCH> is an alias for this option.
=item B<--script>=I<SCRIPT>
Specifies a user script that will be called with the root filesystem of the
image as its first parameter. This script can them make any necesssary
modifications to the root filesystem.
The script must be a POSIX shell script, and should not depend on bash-specific
features. This script will be executed inside a L<chroot(1)> call in the
virtual machine root filesystem.
=item B<--size>=I<SIZE>
Specifies the image size for the virtual machine, defaulting to 25G.
=item B<--apt-proxy>=I<PROXY>
Specify an apt proxy to use in the virtual machine. By default, if you have
an apt proxy configured on the host, the virtual machine will automatically use
this, otherwise there is no default.
=item B<--boot>=B<efi>, B<--efi>
Select the way the generated image will expect to be booted. Unless you
explicitly select --boot=efi, operation will fail.
=item B<--keyring>=I<KEYRING>
Passes an additional B<--keyring> parameter to B<mmdebstrap>.
=back
=head1 EXAMPLES
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 --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.
Create bit-by-bit reproducible images from a given snapshot.d.o timestamp.
SOURCE_DATE_EPOCH=1612543740 mmdebstrap-autopkgtest-build-qemu --boot=efi \
--mirror=http://snapshot.debian.org/archive/debian/20210205T164900Z/ \
unstable /path/to/debian-unstable.img
=head1 SEE ALSO
L<autopkgtest-build-qemu(1)>, L<autopkgtest-virt-qemu(1)>, L<mmdebstrap(1)>, L<autopkgtest(1)>
=cut
POD2MAN
set -eu
die() {
echo "$*" 1>&2
exit 1
}
usage() {
die "usage: $0 [--architecture=|--apt-proxy=|--keyring=|--mirror=|--script=|--size=] --boot=efi <RELEASE> <IMAGE>"
}
usage_error() {
echo "error: $*" 1>&2
usage
}
BOOT=auto
ARCHITECTURE=$(dpkg --print-architecture)
IMAGE=
MIRROR=
KEYRING=
RELEASE=
SIZE=25G
SCRIPT=
# consumed by setup-testbed
export AUTOPKGTEST_BUILD_QEMU=1
opt_boot() {
BOOT="$1"
}
opt_architecture() {
ARCHITECTURE="$1"
}
opt_arch() {
ARCHITECTURE="$1"
}
opt_apt_proxy() {
# consumed by setup-testbed
export AUTOPKGTEST_APT_PROXY="$1"
# consumed by mmdebstrap
if test "$1" = DIRECT; then
unset http_proxy
else
export http_proxy="$1"
fi
}
opt_keyring() {
KEYRING="$1"
}
opt_mirror() {
# consumed by setup-testbed
export MIRROR="$1"
}
opt_script() {
test -f "$1" || die "passed script '$1' does not refer to a file"
SCRIPT="$1"
}
opt_size() {
SIZE="$1"
}
positional=1
positional_1() {
# consumed by setup-testbed
export RELEASE="$1"
}
positional_2() {
IMAGE="$1"
}
positional_3() { opt_mirror "$@"; }
positional_4() { opt_architecture "$@"; }
positional_5() { opt_script "$@"; }
positional_6() { opt_size "$@"; }
positional_7() {
die "too many positional options"
}
while test "$#" -gt 0; do
case "$1" in
--architecture=* | --arch=* | --boot=* | --keyring=* | --mirror=* | --script=* | --size=*)
optname="${1%%=*}"
"opt_${optname#--}" "${1#*=}"
;;
--apt-proxy=*)
opt_apt_proxy "${1#*=}"
;;
--architecture | --arch | --boot | --keyring | --mirror | --script | --size)
test "$#" -ge 2 || usage_error "missing argument for $1"
"opt_${1#--}" "$2"
shift
;;
--apt-proxy)
test "$#" -ge 2 || usage_error "missing argument for $1"
opt_apt_proxy "$2"
shift
;;
--efi)
opt_boot efi
;;
--*)
usage_error "unrecognized argument $1"
;;
*)
"positional_$positional" "$1"
positional=$((positional + 1))
;;
esac
shift
done
test -z "$RELEASE" -o -z "$IMAGE" && usage_error "missing positional arguments"
test "$BOOT" = efi \
|| die "this tool does not support boot modes other than efi"
case "$ARCHITECTURE" in
amd64)
EFIIMG=bootx64.efi
QEMUARCH=x86_64
VMFPKG=ovmf
LINUXIMAGE=linux-image-amd64
;;
arm64)
EFIIMG=bootaa64.efi
QEMUARCH=aarch64
VMFPKG=qemu-efi-aarch64
LINUXIMAGE=linux-image-arm64
;;
armhf)
EFIIMG=bootarm.efi
QEMUARCH=arm
VMFPKG=qemu-efi-arm
LINUXIMAGE=linux-image-armmp
;;
i386)
EFIIMG=bootia32.efi
QEMUARCH=i386
VMFPKG=ovmf-ia32
LINUXIMAGE=linux-image-686-pae
;;
riscv64)
EFIIMG=bootriscv64.efi
QEMUARCH=riscv64
VMFPKG=
LINUXIMAGE=linux-image-riscv64
;;
*)
die "unsupported architecture: $ARCHITECTURE"
;;
esac
if test "$(dpkg-query -f '${db:Status-Status}' -W binutils-multiarch)" = installed; then
GNU_PREFIX=
BINUTILS=
else
GNU_ARCHITECTURE="$(dpkg-architecture "-a$ARCHITECTURE" -qDEB_HOST_GNU_TYPE)"
GNU_PREFIX="$GNU_ARCHITECTURE-"
GNU_SUFFIX="-$(echo "$GNU_ARCHITECTURE" | tr _ -)"
BINUTILS=", binutils$GNU_SUFFIX | binutils-multiarch"
fi
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 "$(dpkg-query -f '${db:Status-Status}' -W "dpkg-dev")" = installed \
|| die "please install dpkg-dev"
dpkg-checkbuilddeps -d "autopkgtest, dosfstools, e2fsprogs, fdisk, mount, mtools, passwd, uidmap, libarchive13, systemd-boot-efi:$ARCHITECTURE $BINUTILS" /dev/null \
|| die "please install the required packages listed above"
BOOTSTUB="/usr/lib/systemd/boot/efi/linux${EFIIMG#boot}.stub"
WORKDIR=
cleanup() {
test -n "$WORKDIR" && rm -Rf "$WORKDIR"
}
trap cleanup EXIT INT TERM QUIT
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
set -- \
--mode=unshare \
--format=tar \
--variant=important \
--architecture="$ARCHITECTURE"
case $MIRROR in http://snapshot.debian.org/archive/* | https://snapshot.debian.org/archive/*)
set -- "$@" --aptopt='Acquire::Check-Valid-Until "false"'
;;
esac
EXT_FEATURES=
if test "$RELEASE" = jessie; then
set -- "$@" --keyring=/usr/share/keyrings/debian-archive-removed-keys.gpg
set -- "$@" --aptopt='Apt::Key::gpgvcommand "/usr/libexec/mmdebstrap/gpgvnoexpkeysig"'
set -- "$@" --hook-dir=/usr/share/mmdebstrap/hooks/jessie-or-older
EXT_FEATURES="^metadata_csum,^metadata_csum_seed,^orphan_file"
fi
set -- "$@" \
"--include=init,$LINUXIMAGE,python3" \
'--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' \
'--customize-hook=/usr/share/autopkgtest/setup-commands/setup-testbed'
if test -n "$SCRIPT"; then
set -- "$@" \
"--customize-hook=upload '$SCRIPT' /userscript" \
"--chrooted-customize-hook=sh /userscript" \
'--customize-hook=rm -f "$1/userscript"'
fi
set -- "$@" \
"--customize-hook=download vmlinuz '$WORKDIR/kernel'" \
"--customize-hook=download initrd.img '$WORKDIR/initrd'" \
"$RELEASE" \
-
test -n "$MIRROR" && set -- "$@" "$MIRROR"
test -n "$KEYRING" && set -- "$@" "--keyring=$KEYRING"
echo "+ mmdebstrap $*" >&2
# https://github.com/koalaman/shellcheck/issues/2555
# shellcheck disable=SC3040
set -o pipefail
mmdebstrap "$@" | {
set -- -t ext4 -L autopkgtestvm -d -
if test -n "$EXT_FEATURES"; then
set -- "$@" -O "$EXT_FEATURES"
fi
EXTOPTS="offset=$(((FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS) * 512))"
if test -n "${SOURCE_DATE_EPOCH-}"; then
uuid="$(uuidgen --sha1 --namespace="$(uuidgen --sha1 --namespace='@dns' --name mister-muffin.de)" --name "$SOURCE_DATE_EPOCH")"
set -- "$@" -U "$uuid"
EXTOPTS="$EXTOPTS,hash_seed=$uuid"
fi
set -- "$@" -E "$EXTOPTS" "$IMAGE" "$SIZE"
echo "+ mke2fs $*" >&2
/sbin/mke2fs "$@"
}
echo "root=LABEL=autopkgtestvm rw console=ttyS0" >"$WORKDIR/cmdline"
align_size() {
echo "$((($1) + ($2) - 1 - (($1) + ($2) - 1) % ($2)))"
}
alignment=$("${GNU_PREFIX}objdump" -p "$BOOTSTUB" | sed 's/^SectionAlignment\s\+\([0-9]\)/0x/;t;d')
test -z "$alignment" && die "failed to discover the alignment of the efi stub"
echo "determined efi vma alignment as $alignment"
test "$RELEASE" = jessie -a "$((alignment))" -lt "$((1024 * 1024))" && {
echo "increasing efi vma alignment for jessie"
alignment=$((1024 * 1024))
}
lastoffset=0
# shellcheck disable=SC2034 # unused variables serve documentation
lastoffset="$("${GNU_PREFIX}objdump" -h "$BOOTSTUB" \
| while read -r idx name size vma lma fileoff algn behind; do
test -z "$behind" -a "${algn#"2**"}" != "$algn" || continue
offset=$((0x$vma + 0x$size))
test "$offset" -gt "$lastoffset" || continue
lastoffset="$offset"
echo "$lastoffset"
done | tail -n1)"
lastoffset=$(align_size "$lastoffset" "$alignment")
echo "determined minimum efi vma offset as $lastoffset"
cmdline_size="$(stat -Lc%s "$WORKDIR/cmdline")"
cmdline_size="$(align_size "$cmdline_size" "$alignment")"
linux_size="$(stat -Lc%s "$WORKDIR/kernel")"
linux_size="$(align_size "$linux_size" "$alignment")"
cmdline_offset="$lastoffset"
linux_offset=$((cmdline_offset + cmdline_size))
initrd_offset=$((linux_offset + linux_size))
SOURCE_DATE_EPOCH=0 \
"${GNU_PREFIX}objcopy" \
--enable-deterministic-archives \
--add-section .cmdline="$WORKDIR/cmdline" \
--change-section-vma .cmdline="$(printf 0x%x "$cmdline_offset")" \
--add-section .linux="$WORKDIR/kernel" \
--change-section-vma .linux="$(printf 0x%x "$linux_offset")" \
--add-section .initrd="$WORKDIR/initrd" \
--change-section-vma .initrd="$(printf 0x%x "$initrd_offset")" \
"$BOOTSTUB" "$WORKDIR/efiimg"
rm -f "$WORKDIR/kernel" "$WORKDIR/initrd"
truncate -s "$((FAT_SIZE_SECTORS * 512))" "$WORKDIR/fat"
/sbin/mkfs.fat -F 32 --invariant "$WORKDIR/fat"
mmd -i "$WORKDIR/fat" EFI EFI/BOOT
mcopy -i "$WORKDIR/fat" "$WORKDIR/efiimg" "::EFI/BOOT/$EFIIMG"
rm -f "$WORKDIR/efiimg"
truncate --size="+$((34 * 512))" "$IMAGE"
/sbin/sfdisk "$IMAGE" <<EOF
label: gpt
unit: sectors
start=$FAT_OFFSET_SECTORS, size=$FAT_SIZE_SECTORS, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
start=$((FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS)), type=0FC63DAF-8483-4772-8E79-3D69D8477DE4
EOF
dd if="$WORKDIR/fat" of="$IMAGE" conv=notrunc,sparse bs=512 "seek=$FAT_OFFSET_SECTORS" status=none
if test "$(dpkg --print-architecture)" != "$ARCHITECTURE" && test "$(dpkg-query -f '${db:Status-Status}' -W "qemu-system-$QEMUARCH")" != installed; then
echo "I: you might need to install a package providing qemu-system-$QEMUARCH to use this image with autopkgtest-virt-qemu" >&2
fi
if test -n "$VMFPKG" && test "$(dpkg-query -f '${db:Status-Status}' -W "$VMFPKG")" != installed; then
echo "I: you might need to install $VMFPKG to use this image with autopkgtest-virt-qemu" >&2
fi
echo "I: SUCCESS! Your new image can be found here: $IMAGE" >&2
echo "I: Don't forget to pass --boot=efi when running autopkgtest-virt-qemu with this image" >&2

View file

@ -17,14 +17,33 @@ while [ "$#" -gt 0 ]; do
shift shift
done done
# subshell so that we can cd without effecting the rest # - Run command with fds 3 and 4 closed so that whatever test.sh does it
( # cannot interfere with these.
set +e # - Both stdin and stderr of test.sh are written to stdout
cd ./shared; # - Write exit status of test.sh to fd 3
$SUDO sh -x ./test.sh; # - Write stdout to shared/output.txt as well as to fd 4
echo $?; # - Redirect fd 3 to stdout
) 2>&1 | tee shared/result.txt | head --lines=-1 # - Read fd 3 and let the group exit with that value
if [ "$(tail --lines=1 shared/result.txt)" -ne 0 ]; then # - Redirect fd 4 to stdout
ret=0
{
{
{
{
ret=0
(
exec 3>&- 4>&-
env --chdir=./shared $SUDO sh -x ./test.sh 2>&1
) || ret=$?
echo $ret >&3
} | tee shared/output.txt >&4
} 3>&1
} | {
read -r xs
exit "$xs"
}
} 4>&1 || ret=$?
if [ "$ret" -ne 0 ]; then
echo "test.sh failed" echo "test.sh failed"
exit 1 exit 1
fi fi

View file

@ -4,17 +4,15 @@ set -eu
: "${DEFAULT_DIST:=unstable}" : "${DEFAULT_DIST:=unstable}"
: "${cachedir:=./shared/cache}" : "${cachedir:=./shared/cache}"
: "${MMDEBSTRAP_TESTS_DEBUG:=no}"
tmpdir="$(mktemp -d)" tmpdir="$(mktemp -d)"
cleanup() { cleanup() {
rv=$? rv=$?
rm -f "$tmpdir/debian-$DEFAULT_DIST-overlay.qcow"
rm -f "$tmpdir/log" rm -f "$tmpdir/log"
[ -e "$tmpdir" ] && rmdir "$tmpdir" [ -e "$tmpdir" ] && rmdir "$tmpdir"
if [ -e shared/result.txt ]; then if [ -e shared/output.txt ]; then
head --lines=-1 shared/result.txt res="$(cat shared/exitstatus.txt)"
res="$(tail --lines=1 shared/result.txt)"
rm shared/result.txt
if [ "$res" != "0" ]; then if [ "$res" != "0" ]; then
# this might possibly overwrite another non-zero rv # this might possibly overwrite another non-zero rv
rv=1 rv=1
@ -25,23 +23,49 @@ cleanup() {
trap cleanup INT TERM EXIT trap cleanup INT TERM EXIT
# the path to debian-$DEFAULT_DIST.qcow must be absolute or otherwise qemu will echo 1 >shared/exitstatus.txt
# look for the path relative to debian-$DEFAULT_DIST-overlay.qcow if [ -e shared/output.txt ]; then
qemu-img create -f qcow2 -b "$(realpath $cachedir)/debian-$DEFAULT_DIST.qcow" -F qcow2 "$tmpdir/debian-$DEFAULT_DIST-overlay.qcow" rm shared/output.txt
# to connect to serial use: fi
# minicom -D 'unix#/tmp/ttyS0' touch shared/output.txt
ret=0 setpriv --pdeathsig TERM tail -f shared/output.txt &
timeout 20m qemu-system-x86_64 \
-no-user-config \ set -- timeout --foreground 40m \
-M accel=kvm:tcg -m 1G -nographic \ debvm-run --image="$(realpath "$cachedir")/debian-$DEFAULT_DIST.ext4" \
-object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0 \ --
cpuname=$(lscpu | awk '/Model name:/ {print $3}' | tr '\n' '+')
ncpu=$(lscpu | awk '/Core\(s\) per socket:/ {print $4}' | tr '\n' '+')
if [ "$cpuname" = "Cortex-A53+Cortex-A73+" ] && [ "$ncpu" = "2+4+" ]; then
# crude detection of the big.LITTLE heterogeneous setup of cores on the
# amlogic a311d bananapi
#
# https://lists.nongnu.org/archive/html/qemu-devel/2020-10/msg08494.html
# https://gitlab.com/qemu-project/qemu/-/issues/239
# https://segments.zhan.science/posts/kvm_on_pinehone_pro/#trouble-with-heterogeneous-architecture
set -- taskset --cpu-list 2,3,4,5 "$@" -smp 4
fi
set -- "$@" -nic none -m 4G -snapshot
if [ "$MMDEBSTRAP_TESTS_DEBUG" = "no" ]; then
# to connect to serial use:
# minicom -D 'unix#/tmp/ttyS0'
# or this (quit with ctrl+q):
# socat stdin,raw,echo=0,escape=0x11 unix-connect:/tmp/ttyS0
set -- "$@" \
-monitor unix:/tmp/monitor,server,nowait \ -monitor unix:/tmp/monitor,server,nowait \
-serial unix:/tmp/ttyS0,server,nowait \ -serial unix:/tmp/ttyS0,server,nowait \
-serial unix:/tmp/ttyS1,server,nowait \ -serial unix:/tmp/ttyS1,server,nowait
-net nic,model=virtio -net user \ fi
-virtfs local,id=mmdebstrap,path="$(pwd)/shared",security_model=none,mount_tag=mmdebstrap \
-drive file="$tmpdir/debian-$DEFAULT_DIST-overlay.qcow",cache=unsafe,index=0,if=virtio \ set -- "$@" -virtfs local,id=mmdebstrap,path="$(pwd)/shared",security_model=none,mount_tag=mmdebstrap
>"$tmpdir/log" 2>&1 || ret=$?
ret=0
if [ "$MMDEBSTRAP_TESTS_DEBUG" = "no" ]; then
"$@" >"$tmpdir/log" 2>&1 || ret=$?
else
"$@" 2>&1 | tee "$tmpdir/log" || ret=$?
fi
if [ "$ret" -ne 0 ]; then if [ "$ret" -ne 0 ]; then
cat "$tmpdir/log" cat "$tmpdir/log"
exit $ret exit $ret

171
tarfilter
View file

@ -43,20 +43,80 @@ class PaxFilterAction(argparse.Action):
setattr(namespace, "paxfilter", items) setattr(namespace, "paxfilter", items)
class TypeFilterAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
items = getattr(namespace, "typefilter", [])
match values:
case "REGTYPE" | "0":
items.append(tarfile.REGTYPE)
case "LNKTYPE" | "1":
items.append(tarfile.LNKTYPE)
case "SYMTYPE" | "2":
items.append(tarfile.SYMTYPE)
case "CHRTYPE" | "3":
items.append(tarfile.CHRTYPE)
case "BLKTYPE" | "4":
items.append(tarfile.BLKTYPE)
case "DIRTYPE" | "5":
items.append(tarfile.DIRTYPE)
case "FIFOTYPE" | "6":
items.append(tarfile.FIFOTYPE)
case _:
raise ValueError("invalid type: %s" % values)
setattr(namespace, "typefilter", items)
class TransformAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
items = getattr(namespace, "trans", [])
# This function mimics what src/transform.c from tar does
if not values.startswith("s"):
raise ValueError("regex must start with an 's'")
if len(values) <= 4:
# minimum regex: s/x//
raise ValueError("invalid regex (too short)")
d = values[1]
if values.startswith(f"s{d}{d}"):
raise ValueError("empty regex")
values = values.removeprefix(f"s{d}")
flags = 0
if values.endswith(f"{d}i"):
# trailing flags
flags = re.IGNORECASE
values = values.removesuffix(f"{d}i")
# This regex only finds non-empty tokens.
# Finding empty tokens would require a variable length look-behind
# or \K in order to find escaped delimiters which is not supported by
# the python re module.
tokens = re.findall(rf"(?:\\[\\{d}]|[^{d}])+", values)
match len(tokens):
case 0:
raise ValueError("invalid regex: not enough terms")
case 1:
repl = ""
case 2:
repl = tokens[1]
case _:
raise ValueError("invalid regex: too many terms: %s" % tokens)
items.append((re.compile(tokens[0], flags), repl))
setattr(namespace, "trans", items)
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\ description="""\
Filters a tarball on standard input by the same rules as the dpkg --path-exclude Filters a tarball on standard input by the same rules as the dpkg --path-exclude
and --path-include options and writes resulting tarball to standard output. See and --path-include options and writes resulting tarball to standard output. See
dpkg(1) for information on how these two options work in detail. Since this is dpkg(1) for information on how these two options work in detail. To reuse the
meant for filtering tarballs storing a rootfs, notice that paths must be given exact same semantics as used by dpkg, paths must be given as /path and not as
as /path and not as ./path even though they might be stored as such in the ./path even though they might be stored as such in the tarball.
tarball.
Similarly, filter out unwanted pax extended headers. This is useful in cases Secondly, filter out unwanted pax extended headers using --pax-exclude and
where a tool only accepts certain xattr prefixes. For example tar2sqfs only --pax-include. This is useful in cases where a tool only accepts certain xattr
supports SCHILY.xattr.user.*, SCHILY.xattr.trusted.* and prefixes. For example tar2sqfs only supports SCHILY.xattr.user.*,
SCHILY.xattr.security.* but not SCHILY.xattr.system.posix_acl_default.*. SCHILY.xattr.trusted.* and SCHILY.xattr.security.* but not
SCHILY.xattr.system.posix_acl_default.*.
Both types of options use Unix shell-style wildcards: Both types of options use Unix shell-style wildcards:
@ -65,45 +125,85 @@ Both types of options use Unix shell-style wildcards:
[seq] matches any character in seq [seq] matches any character in seq
[!seq] matches any character not in seq [!seq] matches any character not in seq
Thirdly, strip leading directory components off of tar members. Just as with Thirdly, filter out files matching a specific tar archive member type using
--type-exclude. Valid type names are REGTYPE (regular file), LNKTYPE
(hardlink), SYMTYPE (symlink), CHRTYPE (character special), BLKTYPE (block
special), DIRTYPE (directory), FIFOTYPE (fifo) or their tar format flag value
(0-6, respectively).
Fourthly, transform the path of tar members using a sed expression just as with
GNU tar --transform.
Fifthly, strip leading directory components off of tar members. Just as with
GNU tar --strip-components, tar members that have less or equal components in GNU tar --strip-components, tar members that have less or equal components in
their path are not passed through. their path are not passed through.
"""
Lastly, shift user id and group id of each entry by the value given by the
--idshift argument. The resulting uid or gid must not be negative.
""",
) )
parser.add_argument( parser.add_argument(
"--path-exclude", "--path-exclude",
metavar="pattern", metavar="pattern",
action=PathFilterAction, action=PathFilterAction,
help="Exclude path matching the given shell pattern.", help="Exclude path matching the given shell pattern. "
"This option can be specified multiple times.",
) )
parser.add_argument( parser.add_argument(
"--path-include", "--path-include",
metavar="pattern", metavar="pattern",
action=PathFilterAction, action=PathFilterAction,
help="Re-include a pattern after a previous exclusion.", help="Re-include a pattern after a previous exclusion. "
"This option can be specified multiple times.",
) )
parser.add_argument( parser.add_argument(
"--pax-exclude", "--pax-exclude",
metavar="pattern", metavar="pattern",
action=PaxFilterAction, action=PaxFilterAction,
help="Exclude pax header matching the given globbing pattern.", help="Exclude pax header matching the given globbing pattern. "
"This option can be specified multiple times.",
) )
parser.add_argument( parser.add_argument(
"--pax-include", "--pax-include",
metavar="pattern", metavar="pattern",
action=PaxFilterAction, action=PaxFilterAction,
help="Re-include a pax header after a previous exclusion.", help="Re-include a pax header after a previous exclusion. "
"This option can be specified multiple times.",
)
parser.add_argument(
"--type-exclude",
metavar="type",
action=TypeFilterAction,
help="Exclude certain member types by their type. Choose types either "
"by their name (REGTYPE, LNKTYPE, SYMTYPE, CHRTYPE, BLKTYPE, DIRTYPE, "
"FIFOTYPE) or by their tar format flag values (0-6, respectively). "
"This option can be specified multiple times.",
)
parser.add_argument(
"--transform",
"--xform",
metavar="EXPRESSION",
action=TransformAction,
help="Use sed replace EXPRESSION to transform file names. "
"This option can be specified multiple times.",
) )
parser.add_argument( parser.add_argument(
"--strip-components", "--strip-components",
metavar="number", metavar="NUMBER",
type=int, type=int,
help="Strip NUMBER leading components from file names", help="Strip NUMBER leading components from file names",
) )
parser.add_argument(
"--idshift",
metavar="NUM",
type=int,
help="Integer value by which to shift the uid and gid of each entry",
)
args = parser.parse_args() args = parser.parse_args()
if ( if (
not hasattr(args, "pathfilter") not hasattr(args, "pathfilter")
and not hasattr(args, "paxfilter") and not hasattr(args, "paxfilter")
and not hasattr(args, "typefilter")
and not hasattr(args, "strip_components") and not hasattr(args, "strip_components")
): ):
from shutil import copyfileobj from shutil import copyfileobj
@ -118,19 +218,22 @@ their path are not passed through.
skip = False skip = False
if not hasattr(args, "pathfilter"): if not hasattr(args, "pathfilter"):
return False return False
for (t, r) in args.pathfilter: # normalize path and make it absolute by stripping off all leading
if r.match(member.name[1:]) is not None: # dots and slashes and then prepending a slash
name = "/" + member.name.lstrip("./")
for t, r in args.pathfilter:
if r.match(name) is not None:
if t == "path_include": if t == "path_include":
skip = False skip = False
else: else:
skip = True skip = True
if skip and (member.isdir() or member.issym()): if skip and (member.isdir() or member.issym()):
for (t, r) in args.pathfilter: for t, r in args.pathfilter:
if t != "path_include": if t != "path_include":
continue continue
prefix = prefix_prog.sub(r"\1", r.pattern) prefix = prefix_prog.sub(r"\1", r.pattern)
prefix = prefix.rstrip("/") prefix = prefix.rstrip("/")
if member.name[1:].startswith(prefix): if name.startswith(prefix):
return False return False
return skip return skip
@ -138,7 +241,7 @@ their path are not passed through.
if not hasattr(args, "paxfilter"): if not hasattr(args, "paxfilter"):
return False return False
skip = False skip = False
for (t, r) in args.paxfilter: for t, r in args.paxfilter:
if r.match(header) is None: if r.match(header) is None:
continue continue
if t == "pax_include": if t == "pax_include":
@ -147,16 +250,28 @@ their path are not passed through.
skip = True skip = True
return skip return skip
# starting with Python 3.8, the default format became PAX_FORMAT, so this def type_filter_should_skip(member):
# is only for compatibility with older versions of Python 3 if not hasattr(args, "typefilter"):
return False
for t in args.typefilter:
if member.type == t:
return True
return False
# starting with Python 3.8, the default format became PAX_FORMAT but we
# are still explicit here in case of future changes.
with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as in_tar, tarfile.open( with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as in_tar, tarfile.open(
fileobj=sys.stdout.buffer, mode="w|", format=tarfile.PAX_FORMAT fileobj=sys.stdout.buffer, mode="w|", format=tarfile.PAX_FORMAT
) as out_tar: ) as out_tar:
for member in in_tar: for member in in_tar:
if path_filter_should_skip(member): if path_filter_should_skip(member):
continue continue
if type_filter_should_skip(member):
continue
if args.strip_components: if args.strip_components:
comps = member.name.split("/") comps = member.name.split("/")
# just as with GNU tar, archive members with less or equal
# number of components are not passed through at all
if len(comps) <= args.strip_components: if len(comps) <= args.strip_components:
continue continue
member.name = "/".join(comps[args.strip_components :]) member.name = "/".join(comps[args.strip_components :])
@ -165,6 +280,18 @@ their path are not passed through.
for k, v in member.pax_headers.items() for k, v in member.pax_headers.items()
if not pax_filter_should_skip(k) if not pax_filter_should_skip(k)
} }
if args.idshift:
if args.idshift < 0 and -args.idshift > member.uid:
print("uid cannot be negative", file=sys.stderr)
exit(1)
if args.idshift < 0 and -args.idshift > member.gid:
print("gid cannot be negative", file=sys.stderr)
exit(1)
member.uid += args.idshift
member.gid += args.idshift
if hasattr(args, "trans"):
for r, s in args.trans:
member.name = r.sub(s, member.name)
if member.isfile(): if member.isfile():
with in_tar.extractfile(member) as file: with in_tar.extractfile(member) as file:
out_tar.addfile(member, file) out_tar.addfile(member, file)

View file

@ -1,67 +0,0 @@
#!/usr/bin/env python3
#
# This script is in the public domain
#
# Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
#
# This script accepts a tarball on standard input and prints a tarball on
# standard output with the same contents but all uid and gid ownership
# information shifted by the value given as first command line argument.
#
# A tool like this should be written in C but libarchive has issues:
# https://github.com/libarchive/libarchive/issues/587
# https://github.com/libarchive/libarchive/pull/1288/ (needs 3.4.1)
# Should these issues get fixed, then a good template is tarfilter.c in the
# examples directory of libarchive.
#
# We are not using Perl either, because Archive::Tar slurps the whole tarball
# into memory.
#
# We could also use Go but meh...
# https://stackoverflow.com/a/59542307/784669
import tarfile
import sys
import argparse
def main():
parser = argparse.ArgumentParser(
description="""\
Accepts a tarball on standard input and prints a tarball on standard output
with the same contents but all uid and gid ownership information shifted by the
value given as first command line argument.
"""
)
parser.add_argument(
"idshift",
metavar="NUM",
type=int,
help="Integer value by which to shift the uid and gid of each entry",
)
args = parser.parse_args()
# starting with Python 3.8, the default format became PAX_FORMAT, so this
# is only for compatibility with older versions of Python 3
with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as in_tar, tarfile.open(
fileobj=sys.stdout.buffer, mode="w|", format=tarfile.PAX_FORMAT
) as out_tar:
for member in in_tar:
if args.idshift < 0 and -args.idshift > member.uid:
print("uid cannot be negative", file=sys.stderr)
exit(1)
if args.idshift < 0 and -args.idshift > member.gid:
print("gid cannot be negative", file=sys.stderr)
exit(1)
member.uid += args.idshift
member.gid += args.idshift
if member.isfile():
with in_tar.extractfile(member) as file:
out_tar.addfile(member, file)
else:
out_tar.addfile(member)
if __name__ == "__main__":
main()

8
tests/apt-patterns Normal file
View file

@ -0,0 +1,8 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
{{ CMD }} --mode={{ MODE }} --variant=essential \
--include '?or(?exact-name(dummy-does-not-exist),?exact-name(apt))' \
{{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -

12
tests/apt-patterns-custom Normal file
View file

@ -0,0 +1,12 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
{{ CMD }} --mode={{ MODE }} --variant=custom \
--include '?narrow(?archive(^{{ DIST }}$),?essential)' \
--include apt \
{{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
{
tar -tf /tmp/debian-chroot.tar
echo ./var/lib/apt/extended_states
} | sort | diff -u tar1.txt -

9
tests/aptopt Normal file
View file

@ -0,0 +1,9 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot; rm -f /tmp/config" EXIT INT TERM
echo 'Acquire::Languages "none";' >/tmp/config
{{ CMD }} --mode=root --variant=apt --aptopt='Acquire::Check-Valid-Until "false"' --aptopt=/tmp/config {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
printf 'Acquire::Check-Valid-Until "false";\nAcquire::Languages "none";\n' | cmp /tmp/debian-chroot/etc/apt/apt.conf.d/99mmdebstrap -
rm /tmp/debian-chroot/etc/apt/apt.conf.d/99mmdebstrap
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

View file

@ -0,0 +1,18 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
apt-get remove --yes qemu-user-binfmt binfmt-support qemu-user
# the following is not necessary anymore since systemd-binfmt
# successfully disables support upon removal of qemu-user with
# the upload of src:systemd 251.2-4: https://bugs.debian.org/1012163
#echo 0 > /proc/sys/fs/binfmt_misc/qemu-aarch64
ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt --architectures=arm64 {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

View file

@ -0,0 +1,133 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
# debootstrap uses apt-config to figure out whether the system running it has
# any proxies configured and then runs the binary to set the http_proxy
# environment variable. This will fail if debootstrap is run in a linux user
# namespace because auto-apt-proxy will see /tmp/.auto-apt-proxy-0 as being
# owned by the user "nobody" and group "nogroup" and fail with:
# insecure cache dir /tmp/.auto-apt-proxy-0. Must be owned by UID 0 and have permissions 700
# We cannot overwrite a configuration item using the APT_CONFIG environment
# variable, so instead we use it to set the Dir configuration option
# to /dev/null to force all apt settings to their defaults.
# There is currently no better way to disable this behavior. See also:
# https://bugs.debian.org/1031105
# https://salsa.debian.org/installer-team/debootstrap/-/merge_requests/90
AUTOPROXY=
eval "$(apt-config shell AUTOPROXY Acquire::http::Proxy-Auto-Detect)"
if [ -n "$AUTOPROXY" ] && [ -x "$AUTOPROXY" ] && [ -e /tmp/.auto-apt-proxy-0 ]; then
TMP_APT_CONFIG=$(mktemp)
echo 'Dir "/dev/null";' >"$TMP_APT_CONFIG"
chmod 644 "$TMP_APT_CONFIG"
fi
$prefix {{ CMD }} --variant=custom --mode={{ MODE }} \
--setup-hook='env '"${AUTOPROXY:+APT_CONFIG='$TMP_APT_CONFIG'}"' debootstrap --variant={{ VARIANT }} unstable "$1" {{ MIRROR }}' \
- /tmp/debian-mm.tar {{ MIRROR }}
if [ -n "$AUTOPROXY" ] && [ -x "$AUTOPROXY" ] && [ -e /tmp/.auto-apt-proxy-0 ]; then
rm "$TMP_APT_CONFIG"
fi
mkdir /tmp/debian-mm
tar --xattrs --xattrs-include='*' -C /tmp/debian-mm -xf /tmp/debian-mm.tar
mkdir /tmp/debian-debootstrap
tar --xattrs --xattrs-include='*' -C /tmp/debian-debootstrap -xf "cache/debian-unstable-{{ VARIANT }}.tar"
# diff cannot compare device nodes, so we use tar to do that for us and then
# delete the directory
tar -C /tmp/debian-debootstrap -cf dev1.tar ./dev
tar -C /tmp/debian-mm -cf dev2.tar ./dev
cmp dev1.tar dev2.tar >&2
rm dev1.tar dev2.tar
rm -r /tmp/debian-debootstrap/dev /tmp/debian-mm/dev
# remove downloaded deb packages
rm /tmp/debian-debootstrap/var/cache/apt/archives/*.deb
# remove aux-cache
rm /tmp/debian-debootstrap/var/cache/ldconfig/aux-cache
# remove logs
rm /tmp/debian-debootstrap/var/log/dpkg.log \
/tmp/debian-debootstrap/var/log/bootstrap.log \
/tmp/debian-debootstrap/var/log/alternatives.log \
/tmp/debian-mm/var/log/bootstrap.log
# clear out /run except for /run/lock
find /tmp/debian-debootstrap/run/ -mindepth 1 -maxdepth 1 ! -name lock -print0 | xargs --no-run-if-empty -0 rm -r
# debootstrap doesn't clean apt
rm /tmp/debian-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_unstable_main_binary-{{ HOSTARCH }}_Packages \
/tmp/debian-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_unstable_InRelease \
/tmp/debian-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_unstable_Release \
/tmp/debian-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_unstable_Release.gpg
if [ -e /tmp/debian-debootstrap/etc/machine-id ]; then
rm /tmp/debian-debootstrap/etc/machine-id /tmp/debian-mm/etc/machine-id
fi
rm /tmp/debian-mm/var/cache/apt/archives/lock
rm /tmp/debian-mm/var/lib/apt/lists/lock
rm /tmp/debian-mm/var/lib/dpkg/arch
# also needed for users that are created by systemd-sysusers before systemd 252
# https://github.com/systemd/systemd/pull/24534
for f in shadow shadow-; do
if [ ! -e /tmp/debian-debootstrap/etc/$f ]; then
continue
fi
if ! cmp /tmp/debian-debootstrap/etc/$f /tmp/debian-mm/etc/$f >&2; then
echo patching /etc/$f >&2
awk -v FS=: -v OFS=: -v SDE={{ SOURCE_DATE_EPOCH }} '{ print $1,$2,int(SDE/60/60/24),$4,$5,$6,$7,$8,$9 }' </tmp/debian-mm/etc/$f >/tmp/debian-mm/etc/$f.bak
cat /tmp/debian-mm/etc/$f.bak >/tmp/debian-mm/etc/$f
rm /tmp/debian-mm/etc/$f.bak
else
echo no difference for /etc/$f >&2
fi
done
# isc-dhcp-client postinst doesn't create this file in debootstrap run with
# unshared wrapper. The responsible postinst snippet was automatically added
# by dh_apparmor since isc-dhcp-client 4.4.3-P1-1.1
if [ -e /tmp/debian-debootstrap/etc/apparmor.d/local/sbin.dhclient ] && [ ! -s /tmp/debian-debootstrap/etc/apparmor.d/local/sbin.dhclient ]; then
echo /sbin/setcap >/tmp/debian-debootstrap/etc/apparmor.d/local/sbin.dhclient
fi
# check if the file content differs
diff --unified --no-dereference --recursive /tmp/debian-debootstrap /tmp/debian-mm >&2
# check permissions, ownership, symlink targets, modification times using tar
# mtimes of directories created by mmdebstrap will differ, thus we equalize them first
for d in etc/apt/preferences.d/ etc/apt/sources.list.d/ etc/dpkg/dpkg.cfg.d/ var/log/apt/; do
touch --date="@{{ SOURCE_DATE_EPOCH }}" /tmp/debian-debootstrap/$d /tmp/debian-mm/$d
done
# debootstrap never ran apt -- fixing permissions
for d in ./var/lib/apt/lists/partial ./var/cache/apt/archives/partial; do
chroot /tmp/debian-debootstrap chmod 0700 $d
chroot /tmp/debian-debootstrap chown _apt:root $d
done
tar -C /tmp/debian-debootstrap --numeric-owner --xattrs --xattrs-include='*' --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/root1.tar .
tar -C /tmp/debian-mm --numeric-owner --xattrs --xattrs-include='*' --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/root2.tar .
tar --full-time --verbose -tf /tmp/root1.tar >/tmp/root1.tar.list
tar --full-time --verbose -tf /tmp/root2.tar >/tmp/root2.tar.list
# despite SOURCE_DATE_EPOCH and --clamp-mtime, the timestamps in the tarball
# will slightly differ from each other in the sub-second precision (last
# decimals) so the tarballs will not be identical, so we use diff to compare
# content and tar to compare attributes
diff -u /tmp/root1.tar.list /tmp/root2.tar.list >&2
rm /tmp/root1.tar /tmp/root2.tar /tmp/root1.tar.list /tmp/root2.tar.list
rm /tmp/debian-mm.tar
rm -r /tmp/debian-debootstrap /tmp/debian-mm

21
tests/ascii-armored-keys Normal file
View file

@ -0,0 +1,21 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
for f in /etc/apt/trusted.gpg.d/*.gpg /etc/apt/trusted.gpg.d/*.asc; do
[ -e "$f" ] || continue
rm "$f"
done
rmdir /etc/apt/trusted.gpg.d
mkdir /etc/apt/trusted.gpg.d
for f in /usr/share/keyrings/*.gpg; do
name=$(basename "$f" .gpg)
gpg --no-default-keyring --keyring="/usr/share/keyrings/$name.gpg" --armor --output="/etc/apt/trusted.gpg.d/$name.asc" --export
rm "/usr/share/keyrings/$name.gpg"
done
{{ CMD }} --mode=root --variant=apt {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm -r /tmp/debian-chroot.tar

14
tests/aspcud-apt-solver Normal file
View file

@ -0,0 +1,14 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
{{ CMD }} --mode={{ MODE }} --variant=custom \
--include "$(tr '\n' ',' <pkglist.txt)" \
--aptopt='APT::Solver "aspcud"' \
{{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
{
tar -tf /tmp/debian-chroot.tar
echo ./var/lib/apt/extended_states
} | sort \
| grep -v '^./etc/apt/apt.conf.d/99mmdebstrap$' \
| diff -u tar1.txt -

View file

@ -0,0 +1,22 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -f /tmp/debian-chroot.tar.gz" EXIT INT TERM
[ {{ MODE }} = "auto" ]
prefix=
if [ "$(id -u)" -eq 0 ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
$prefix {{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot.tar.gz {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar.gz | sort | diff -u tar1.txt -

View file

@ -0,0 +1,14 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir /home/user --create-home user
if [ -e /proc/sys/kernel/unprivileged_userns_clone ] && [ "$(sysctl -n kernel.unprivileged_userns_clone)" = "1" ]; then
sysctl -w kernel.unprivileged_userns_clone=0
fi
runuser -u user -- {{ CMD }} --mode=auto --variant=apt {{ DIST }} /tmp/debian-chroot.tar.gz {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar.gz | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar.gz

View file

@ -0,0 +1,14 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
cat <<HOSTS >>/etc/hosts
127.0.0.1 deb.debian.org
127.0.0.1 security.debian.org
HOSTS
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot.tar
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar

View file

@ -0,0 +1,257 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH"
# we create the apt user ourselves or otherwise its uid/gid will differ
# compared to the one chosen in debootstrap because of different installation
# order in comparison to the systemd users
# https://bugs.debian.org/969631
# we cannot use useradd because passwd is not Essential:yes
{{ CMD }} --variant={{ VARIANT }} --mode={{ MODE }} \
--essential-hook='[ {{ DIST }} = oldstable ] && [ {{ VARIANT }} = - ] && echo _apt:*:100:65534::/nonexistent:/usr/sbin/nologin >> "$1"/etc/passwd || :' \
"$(if [ {{ DIST }} = oldstable ]; then echo --merged-usr; else echo --hook-dir=./hooks/merged-usr; fi)" \
"$(case {{ DIST }} in oldstable) echo --include=e2fsprogs,mount,tzdata,gcc-9-base ;; stable) echo --include=e2fsprogs,mount,tzdata ;; *) echo --include=base-files ;; esac)" \
{{ DIST }} /tmp/debian-{{ DIST }}-mm.tar {{ MIRROR }}
mkdir /tmp/debian-{{ DIST }}-mm
tar --xattrs --xattrs-include='*' -C /tmp/debian-{{ DIST }}-mm -xf /tmp/debian-{{ DIST }}-mm.tar
rm /tmp/debian-{{ DIST }}-mm.tar
mkdir /tmp/debian-{{ DIST }}-debootstrap
tar --xattrs --xattrs-include='*' -C /tmp/debian-{{ DIST }}-debootstrap -xf "cache/debian-{{ DIST }}-{{ VARIANT }}.tar"
# diff cannot compare device nodes, so we use tar to do that for us and then
# delete the directory
tar -C /tmp/debian-{{ DIST }}-debootstrap -cf /tmp/dev1.tar ./dev
tar -C /tmp/debian-{{ DIST }}-mm -cf /tmp/dev2.tar ./dev
ret=0
cmp /tmp/dev1.tar /tmp/dev2.tar >&2 || ret=$?
if [ "$ret" -ne 0 ]; then
if type diffoscope >/dev/null; then
diffoscope /tmp/dev1.tar /tmp/dev2.tar
exit 1
else
echo "no diffoscope installed" >&2
fi
if type base64 >/dev/null; then
base64 /tmp/dev1.tar
base64 /tmp/dev2.tar
exit 1
else
echo "no base64 installed" >&2
fi
if type xxd >/dev/null; then
xxd /tmp/dev1.tar
xxd /tmp/dev2.tar
exit 1
else
echo "no xxd installed" >&2
fi
exit 1
fi
rm /tmp/dev1.tar /tmp/dev2.tar
rm -r /tmp/debian-{{ DIST }}-debootstrap/dev /tmp/debian-{{ DIST }}-mm/dev
# remove downloaded deb packages
rm /tmp/debian-{{ DIST }}-debootstrap/var/cache/apt/archives/*.deb
# remove aux-cache
rm /tmp/debian-{{ DIST }}-debootstrap/var/cache/ldconfig/aux-cache
# remove logs
rm /tmp/debian-{{ DIST }}-debootstrap/var/log/dpkg.log \
/tmp/debian-{{ DIST }}-debootstrap/var/log/bootstrap.log \
/tmp/debian-{{ DIST }}-debootstrap/var/log/alternatives.log
# remove *-old files
rm /tmp/debian-{{ DIST }}-debootstrap/var/cache/debconf/config.dat-old \
/tmp/debian-{{ DIST }}-mm/var/cache/debconf/config.dat-old
rm /tmp/debian-{{ DIST }}-debootstrap/var/cache/debconf/templates.dat-old \
/tmp/debian-{{ DIST }}-mm/var/cache/debconf/templates.dat-old
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status-old \
/tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status-old
rm -f /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/diversions-old \
/tmp/debian-{{ DIST }}-mm/var/lib/dpkg/diversions-old
# remove dpkg files
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/available
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/cmethopt
# remove /var/lib/dpkg/arch
rm /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/arch
# since we installed packages directly from the .deb files, Priorities differ
# thus we first check for equality and then remove the files
chroot /tmp/debian-{{ DIST }}-debootstrap dpkg --list >/tmp/dpkg1
chroot /tmp/debian-{{ DIST }}-mm dpkg --list >/tmp/dpkg2
diff -u /tmp/dpkg1 /tmp/dpkg2 >&2
rm /tmp/dpkg1 /tmp/dpkg2
grep -v '^Priority: ' /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status >/tmp/status1
grep -v '^Priority: ' /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status >/tmp/status2
diff -u /tmp/status1 /tmp/status2 >&2
rm /tmp/status1 /tmp/status2
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status
# debootstrap exposes the hosts's kernel version
if [ -e /tmp/debian-{{ DIST }}-debootstrap/etc/apt/apt.conf.d/01autoremove-kernels ]; then
rm /tmp/debian-{{ DIST }}-debootstrap/etc/apt/apt.conf.d/01autoremove-kernels
fi
if [ -e /tmp/debian-{{ DIST }}-mm/etc/apt/apt.conf.d/01autoremove-kernels ]; then
rm /tmp/debian-{{ DIST }}-mm/etc/apt/apt.conf.d/01autoremove-kernels
fi
# clear out /run except for /run/lock
find /tmp/debian-{{ DIST }}-debootstrap/run/ -mindepth 1 -maxdepth 1 ! -name lock -print0 | xargs --no-run-if-empty -0 rm -r
# debootstrap doesn't clean apt
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_main_binary-{{ HOSTARCH }}_Packages \
/tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_InRelease \
/tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_Release \
/tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_Release.gpg
if [ "{{ VARIANT }}" = "-" ]; then
rm /tmp/debian-{{ DIST }}-debootstrap/etc/machine-id
rm /tmp/debian-{{ DIST }}-mm/etc/machine-id
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/systemd/catalog/database
rm /tmp/debian-{{ DIST }}-mm/var/lib/systemd/catalog/database
case {{ DIST }} in oldstable | stable)
cap=$(chroot /tmp/debian-{{ DIST }}-debootstrap /sbin/getcap /bin/ping)
expected="/bin/ping cap_net_raw=ep"
if [ "$cap" != "$expected" ]; then
echo "expected bin/ping to have capabilities $expected" >&2
echo "but debootstrap produced: $cap" >&2
exit 1
fi
cap=$(chroot /tmp/debian-{{ DIST }}-mm /sbin/getcap /bin/ping)
if [ "$cap" != "$expected" ]; then
echo "expected bin/ping to have capabilities $expected" >&2
echo "but mmdebstrap produced: $cap" >&2
exit 1
fi
;;
esac
fi
rm /tmp/debian-{{ DIST }}-mm/var/cache/apt/archives/lock
rm /tmp/debian-{{ DIST }}-mm/var/lib/apt/extended_states
rm /tmp/debian-{{ DIST }}-mm/var/lib/apt/lists/lock
# the list of shells might be sorted wrongly
# /var/lib/dpkg/triggers/File might be sorted wrongly
for f in "/var/lib/dpkg/triggers/File" "/etc/shells"; do
f1="/tmp/debian-{{ DIST }}-debootstrap/$f"
f2="/tmp/debian-{{ DIST }}-mm/$f"
# both chroots must have the file
if [ ! -e "$f1" ] || [ ! -e "$f2" ]; then
continue
fi
# the file must be different
if cmp "$f1" "$f2" >&2; then
continue
fi
# then sort both
sort -o "$f1" "$f1"
sort -o "$f2" "$f2"
done
# Because of unreproducible uids (#969631) we created the _apt user ourselves
# and because passwd is not Essential:yes we didn't use useradd. But newer
# versions of adduser and shadow will create a different /etc/shadow
if [ "{{ VARIANT }}" = "-" ] && [ "{{ DIST}}" = oldstable ]; then
for f in shadow shadow-; do
if grep -q '^_apt:!:' /tmp/debian-{{ DIST }}-debootstrap/etc/$f; then
sed -i 's/^_apt:\*:\([^:]\+\):0:99999:7:::$/_apt:!:\1::::::/' /tmp/debian-{{ DIST }}-mm/etc/$f
fi
done
fi
for log in faillog lastlog; do
f1="/tmp/debian-{{ DIST }}-debootstrap/var/log/$log"
f2="/tmp/debian-{{ DIST }}-mm/var/log/$log"
# skip cmp if file is absent in both chroots
if [ ! -e "$f1" ] && [ ! -e "$f2" ]; then
continue
fi
if ! cmp "$f1" "$f2" >&2; then
# if the files differ, make sure they are all zeroes
cmp -n "$(stat -c %s "$f1")" "$f1" /dev/zero >&2
cmp -n "$(stat -c %s "$f2")" "$f2" /dev/zero >&2
# then delete them
rm "$f1" "$f2"
fi
done
if [ "{{ VARIANT }}" = "-" ]; then
# the order in which systemd and cron get installed differ and thus the order
# of lines in /etc/group and /etc/gshadow differs
for f in group group- gshadow gshadow-; do
for d in mm debootstrap; do
sort /tmp/debian-{{ DIST }}-$d/etc/$f >/tmp/debian-{{ DIST }}-$d/etc/$f.bak
mv /tmp/debian-{{ DIST }}-$d/etc/$f.bak /tmp/debian-{{ DIST }}-$d/etc/$f
done
done
# the order in which systemd and passwd get installed differ and thus
# the order of lines in /etc/shadow and /etc/shadow- differs
for f in shadow shadow-; do
for d in mm debootstrap; do
sort /tmp/debian-{{ DIST }}-$d/etc/$f >/tmp/debian-{{ DIST }}-$d/etc/$f.bak
mv /tmp/debian-{{ DIST }}-$d/etc/$f.bak /tmp/debian-{{ DIST }}-$d/etc/$f
done
done
# and since the order was different, ignore the *- files
for f in shadow- passwd-; do
for d in mm debootstrap; do
rm /tmp/debian-{{ DIST }}-$d/etc/$f
done
done
fi
# since debootstrap 1.0.133 there is no tzdata in the buildd variant and thus
# debootstrap creates its own /etc/localtime
if [ "{{ VARIANT }}" = "buildd" ] && [ "{{ DIST }}" != "stable" ] && [ "{{ DIST }}" != "oldstable" ]; then
[ "$(readlink /tmp/debian-{{ DIST }}-debootstrap/etc/localtime)" = /usr/share/zoneinfo/UTC ]
rm /tmp/debian-{{ DIST }}-debootstrap/etc/localtime
fi
# starting with systemd 255 upstream dropped splitusr support and depending on
# the installation order, symlink targets are prefixed with /usr or not
# See #1060000 and #1054137
case {{ DIST }} in testing | unstable)
for f in multi-user.target.wants/e2scrub_reap.service timers.target.wants/apt-daily-upgrade.timer timers.target.wants/apt-daily.timer timers.target.wants/e2scrub_all.timer; do
for d in mm debootstrap; do
[ -L "/tmp/debian-{{ DIST }}-$d/etc/systemd/system/$f" ] || continue
oldlink="$(readlink "/tmp/debian-{{ DIST }}-$d/etc/systemd/system/$f")"
case $oldlink in
/usr/*) : ;;
/*) oldlink="/usr$oldlink" ;;
*)
echo unexpected >&2
exit 1
;;
esac
ln -sf "$oldlink" "/tmp/debian-{{ DIST }}-$d/etc/systemd/system/$f"
done
done
;;
esac
# check if the file content differs
diff --unified --no-dereference --recursive /tmp/debian-{{ DIST }}-debootstrap /tmp/debian-{{ DIST }}-mm >&2
# check permissions, ownership, symlink targets, modification times using tar
# directory mtimes will differ, thus we equalize them first
find /tmp/debian-{{ DIST }}-debootstrap /tmp/debian-{{ DIST }}-mm -type d -print0 | xargs -0 touch --date="@{{ SOURCE_DATE_EPOCH }}"
# debootstrap never ran apt -- fixing permissions
for d in ./var/lib/apt/lists/partial ./var/cache/apt/archives/partial; do
unmergedPATH="$PATH$(if [ "{{ DIST }}" = oldstable ]; then echo :/bin:/sbin; fi)"
PATH="$unmergedPATH" chroot /tmp/debian-{{ DIST }}-debootstrap chmod 0700 $d
PATH="$unmergedPATH" chroot /tmp/debian-{{ DIST }}-debootstrap chown "$(id -u _apt):root" $d
done
tar -C /tmp/debian-{{ DIST }}-debootstrap --numeric-owner --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/root1.tar .
tar -C /tmp/debian-{{ DIST }}-mm --numeric-owner --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/root2.tar .
tar --full-time --verbose -tf /tmp/root1.tar >/tmp/root1.tar.list
tar --full-time --verbose -tf /tmp/root2.tar >/tmp/root2.tar.list
diff -u /tmp/root1.tar.list /tmp/root2.tar.list >&2
rm /tmp/root1.tar /tmp/root2.tar /tmp/root1.tar.list /tmp/root2.tar.list
# check if file properties (permissions, ownership, symlink names, modification time) differ
#
# we cannot use this (yet) because it cannot cope with paths that have [ or @ in them
#fmtree -c -p /tmp/debian-{{ DIST }}-debootstrap -k flags,gid,link,mode,size,time,uid | sudo fmtree -p /tmp/debian-{{ DIST }}-mm
rm -r /tmp/debian-{{ DIST }}-debootstrap /tmp/debian-{{ DIST }}-mm

View file

@ -0,0 +1,28 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }}" EXIT INT TERM
case {{ MODE }} in unshare | fakechroot) : ;; *) exit 1 ;; esac
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }} {{ MIRROR }}
cmp ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }} \
|| diffoscope ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }}
# we cannot test chrootless mode here, because mmdebstrap relies on the
# usrmerge package to set up merged-/usr and that doesn't work in chrootless
# mode

View file

@ -0,0 +1,8 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
mkdir /tmp/debian-chroot
chmod 700 /tmp/debian-chroot
{{ CMD }} --mode=root --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

16
tests/chrootless Normal file
View file

@ -0,0 +1,16 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/chrootless.tar /tmp/root.tar" EXIT INT TERM
# we need --hook-dir=./hooks/merged-usr because usrmerge does not understand
# DPKG_ROOT
for INCLUDE in '' 'apt' 'apt,build-essential' 'systemd-sysv'; do
for MODE in root chrootless; do
{{ CMD }} --mode=$MODE --variant={{ VARIANT }} --hook-dir=./hooks/merged-usr \
${INCLUDE:+--include="$INCLUDE"} --skip=check/chrootless \
{{ DIST }} "/tmp/$MODE.tar" {{ MIRROR }}
done
cmp /tmp/root.tar /tmp/chrootless.tar || diffoscope /tmp/root.tar /tmp/chrootless.tar
rm /tmp/chrootless.tar /tmp/root.tar
done

43
tests/chrootless-fakeroot Normal file
View file

@ -0,0 +1,43 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/chrootless.tar /tmp/root.tar" EXIT INT TERM
[ {{ MODE }} = chrootless ]
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
MMTARFILTER=
[ -x /usr/bin/mmtarfilter ] && MMTARFILTER=/usr/bin/mmtarfilter
[ -x ./tarfilter ] && MMTARFILTER=./tarfilter
# we need --hook-dir=./hooks/merged-usr because usrmerge does not understand
# DPKG_ROOT
# permissions drwxr-sr-x and extended attributes of ./var/log/journal/ cannot
# be preserved under fakeroot
# this applies to 'z' lines in files in /usr/lib/tmpfiles.d/
for INCLUDE in '' 'apt' 'apt,build-essential' 'systemd-sysv'; do
{{ CMD }} --variant={{ VARIANT }} --hook-dir=./hooks/merged-usr \
${INCLUDE:+--include="$INCLUDE"} \
{{ DIST }} - {{ MIRROR }} \
| "$MMTARFILTER" --path-exclude="/var/log/journal" --path-exclude="/etc/credstore*" \
>/tmp/root.tar
$prefix fakeroot {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} --hook-dir=./hooks/merged-usr \
${INCLUDE:+--include="$INCLUDE"} \
{{ DIST }} - {{ MIRROR }} \
| "$MMTARFILTER" --path-exclude="/var/log/journal" --path-exclude="/etc/credstore*" \
>/tmp/chrootless.tar
cmp /tmp/root.tar /tmp/chrootless.tar || diffoscope /tmp/root.tar /tmp/chrootless.tar
rm /tmp/chrootless.tar /tmp/root.tar
done

66
tests/chrootless-foreign Normal file
View file

@ -0,0 +1,66 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/chrootless.tar /tmp/root.tar" EXIT INT TERM
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
deb2qemu() {
case "$1" in
amd64) echo x86_64 ;;
arm64) echo aarch64 ;;
armel | armhf) echo arm ;;
ppc64el) echo ppc64le ;;
*) echo "$1" ;;
esac
}
if [ "$(dpkg --print-architecture)" = "arm64" ]; then
arch=amd64
else
arch=arm64
fi
[ "$(id -u)" -eq 0 ]
[ -e "/proc/sys/fs/binfmt_misc/qemu-$(deb2qemu "$arch")" ]
# we need --hook-dir=./hooks/merged-usr because usrmerge does not understand
# DPKG_ROOT
#
# dpkg is unable to install architecture arch:all packages with a
# dependency on an arch:any package (perl-modules-5.34 in this case)
# inside foreign architecture chrootless chroots, because dpkg will use
# its own architecture as the native architecture, see #825385 and #1020533
# So we are not testing the installation of apt,build-essential here.
for INCLUDE in '' 'apt' 'systemd-sysv'; do
echo 1 >"/proc/sys/fs/binfmt_misc/qemu-$(deb2qemu "$arch")"
arch-test "$arch"
{{ CMD }} --mode=root --architecture="$arch" --variant={{ VARIANT }} \
--hook-dir=./hooks/merged-usr ${INCLUDE:+--include="$INCLUDE"} \
{{ DIST }} "/tmp/root.tar" {{ MIRROR }}
echo 0 >"/proc/sys/fs/binfmt_misc/qemu-$(deb2qemu "$arch")"
arch-test "$arch" && exit 1
{{ CMD }} --mode=chrootless --architecture="$arch" --variant={{ VARIANT }} \
--hook-dir=./hooks/merged-usr ${INCLUDE:+--include="$INCLUDE"} \
--skip=check/chrootless {{ DIST }} "/tmp/chrootless.tar" {{ MIRROR }}
# when creating a foreign architecture chroot, the tarballs are not
# bit-by-bit identical but contain a few remaining differences:
#
# * /etc/ld.so.cache -- hard problem, must be solved in glibc upstream
# * /var/lib/dpkg/triggers -- #990712
# * /var/cache/debconf/*.dat-old -- needs investigation
for tar in root chrootless; do
./tarfilter <"/tmp/$tar.tar" \
--path-exclude=/var/cache/debconf/config.dat-old \
--path-exclude=/var/cache/debconf/templates.dat-old \
--path-exclude=/etc/ld.so.cache \
--path-exclude=/var/lib/dpkg/triggers/File \
--path-exclude=/var/lib/dpkg/triggers/ldconfig \
>"/tmp/$tar.tar.tmp"
mv "/tmp/$tar.tar.tmp" "/tmp/$tar.tar"
done
cmp /tmp/root.tar /tmp/chrootless.tar || diffoscope /tmp/root.tar /tmp/chrootless.tar
rm /tmp/chrootless.tar /tmp/root.tar
done

View file

@ -0,0 +1,44 @@
#!/bin/sh
#
# test that the user can drop archives into /var/cache/apt/archives as well as
# into /var/cache/apt/archives/partial
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test requires the cache directory to be mounted on /mnt and should only be run inside a container" >&2
exit 1
fi
tmpdir=$(mktemp -d)
trap 'rm -f "$tmpdir"/*.deb /tmp/orig.tar /tmp/test1.tar /tmp/test2.tar; rmdir "$tmpdir"' EXIT INT TERM
include="--include=doc-debian"
if [ "{{ VARIANT }}" = "custom" ]; then
include="$include,base-files,base-passwd,coreutils,dash,diffutils,dpkg,libc-bin,sed"
fi
{{ CMD }} $include --mode={{ MODE }} --variant={{ VARIANT }} \
--setup-hook='mkdir -p "$1"/var/cache/apt/archives/partial' \
--setup-hook='touch "$1"/var/cache/apt/archives/lock' \
--setup-hook='chmod 0640 "$1"/var/cache/apt/archives/lock' \
{{ DIST }} - {{ MIRROR }} >/tmp/orig.tar
# somehow, when trying to create a tarball from the 9p mount, tar throws the
# following error: tar: ./doc-debian_6.4_all.deb: File shrank by 132942 bytes; padding with zeros
# to reproduce, try: tar --directory /mnt/cache/debian/pool/main/d/doc-debian/ --create --file - . | tar --directory /tmp/ --extract --file -
# this will be different:
# md5sum /mnt/cache/debian/pool/main/d/doc-debian/*.deb /tmp/*.deb
# another reason to copy the files into a new directory is, that we can use shell globs
cp /mnt/cache/debian/pool/main/b/busybox/busybox_*"_{{ HOSTARCH }}.deb" /mnt/cache/debian/pool/main/a/apt/apt_*"_{{ HOSTARCH }}.deb" "$tmpdir"
{{ CMD }} $include --mode={{ MODE }} --variant={{ VARIANT }} \
--setup-hook='mkdir -p "$1"/var/cache/apt/archives/partial' \
--setup-hook='sync-in "'"$tmpdir"'" /var/cache/apt/archives/partial' \
{{ DIST }} - {{ MIRROR }} >/tmp/test1.tar
cmp /tmp/orig.tar /tmp/test1.tar
{{ CMD }} $include --mode={{ MODE }} --variant={{ VARIANT }} \
--customize-hook='touch "$1"/var/cache/apt/archives/partial' \
--setup-hook='mkdir -p "$1"/var/cache/apt/archives/' \
--setup-hook='sync-in "'"$tmpdir"'" /var/cache/apt/archives/' \
--setup-hook='chmod 0755 "$1"/var/cache/apt/archives/' \
--customize-hook='find "'"$tmpdir"'" -type f -exec md5sum "{}" \; | sed "s|"'"$tmpdir"'"|$1/var/cache/apt/archives|" | md5sum --check' \
{{ DIST }} - {{ MIRROR }} >/tmp/test2.tar
cmp /tmp/orig.tar /tmp/test2.tar

10
tests/copy-mirror Normal file
View file

@ -0,0 +1,10 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test requires the cache directory to be mounted on /mnt and should only be run inside a container" >&2
exit 1
fi
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot.tar "deb copy:///mnt/cache/debian {{ DIST }} main"
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar

9
tests/create-directory Normal file
View file

@ -0,0 +1,9 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
{{ CMD }} --mode=root --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
chroot /tmp/debian-chroot dpkg-query --showformat '${binary:Package}\n' --show >pkglist.txt
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort >tar1.txt

View file

@ -0,0 +1,29 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
{{ CMD }} --mode={{ MODE }} --dry-run --variant=apt \
--setup-hook="exit 1" \
--essential-hook="exit 1" \
--customize-hook="exit 1" \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }}
rm /tmp/debian-chroot/dev/console
rm /tmp/debian-chroot/dev/fd
rm /tmp/debian-chroot/dev/full
rm /tmp/debian-chroot/dev/null
rm /tmp/debian-chroot/dev/ptmx
rm /tmp/debian-chroot/dev/random
rm /tmp/debian-chroot/dev/stderr
rm /tmp/debian-chroot/dev/stdin
rm /tmp/debian-chroot/dev/stdout
rm /tmp/debian-chroot/dev/tty
rm /tmp/debian-chroot/dev/urandom
rm /tmp/debian-chroot/dev/zero
rm /tmp/debian-chroot/etc/apt/sources.list
rm /tmp/debian-chroot/etc/fstab
rm /tmp/debian-chroot/etc/hostname
rm /tmp/debian-chroot/etc/resolv.conf
rm /tmp/debian-chroot/var/lib/apt/lists/lock
rm /tmp/debian-chroot/var/lib/dpkg/status
rm /tmp/debian-chroot/var/lib/dpkg/arch
# the rest should be empty directories that we can rmdir recursively
find /tmp/debian-chroot -depth -print0 | xargs -0 rmdir

View file

@ -0,0 +1,78 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
case "$(dpkg --print-architecture)" in
arm64)
native_arch=arm64
native_gnu=aarch64-linux-gnu
foreign_arch=amd64
foreign_gnu=x86_64-linux-gnu
;;
amd64)
native_arch=amd64
native_gnu=x86_64-linux-gnu
foreign_arch=arm64
foreign_gnu=aarch64-linux-gnu
;;
*)
echo "unsupported native architecture" >&2
exit 1
;;
esac
[ "{{ MODE }}" = "fakechroot" ] && prefix="$prefix fakechroot fakeroot"
$prefix {{ CMD }} --mode={{ MODE }} --variant=apt --architectures="$foreign_arch" \
{{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
# we ignore differences between architectures by ignoring some files
# and renaming others
{
tar -tf /tmp/debian-chroot.tar \
| grep -v '^\./usr/bin/i386$' \
| grep -v '^\./usr/bin/x86_64$' \
| grep -v '^\./lib64$' \
| grep -v '^\./usr/lib64/$' \
| grep -v '^\./usr/lib64/ld-linux-x86-64\.so\.2$' \
| grep -v '^\./usr/lib/ld-linux-aarch64\.so\.1$' \
| grep -v "^\\./usr/lib/$foreign_gnu/ld-linux-aarch64\\.so\\.1$" \
| grep -v "^\\./usr/lib/$foreign_gnu/ld-linux-x86-64\\.so\\.2$" \
| grep -v "^\\./usr/lib/$foreign_gnu/perl/5\\.[0-9][.0-9]\\+/.*\\.ph$" \
| grep -v "^\\./usr/lib/$foreign_gnu/libmvec\\.so\\.1$" \
| grep -v "^\\./usr/share/doc/[^/]\\+/changelog\\(\\.Debian\\)\\?\\.$foreign_arch\\.gz$" \
| grep -v '^\./usr/share/man/man8/i386\.8\.gz$' \
| grep -v '^\./usr/share/man/man8/x86_64\.8\.gz$' \
| sed "s/$foreign_gnu/$native_gnu/" \
| sed "s/$foreign_arch/$native_arch/"
} | sort >/tmp/tar2.txt
{
grep <tar1.txt -v '^\./usr/bin/i386$' \
| grep -v '^\./usr/bin/x86_64$' \
| grep -v '^\./lib32$' \
| grep -v '^\./lib64$' \
| grep -v '^\./libx32$' \
| grep -v '^\./usr/lib32/$' \
| grep -v '^\./usr/libx32/$' \
| grep -v '^\./usr/lib64/$' \
| grep -v '^\./usr/lib64/ld-linux-x86-64\.so\.2$' \
| grep -v '^\./usr/lib/ld-linux-aarch64\.so\.1$' \
| grep -v "^\\./usr/lib/$native_gnu/ld-linux-x86-64\\.so\\.2$" \
| grep -v "^\\./usr/lib/$native_gnu/ld-linux-aarch64\\.so\\.1$" \
| grep -v "^\\./usr/lib/$native_gnu/libmvec\\.so\\.1$" \
| grep -v "^\\./usr/lib/$native_gnu/perl/5\\.[0-9][.0-9]\\+/.*\\.ph$" \
| grep -v "^\\./usr/share/doc/[^/]\\+/changelog\\(\\.Debian\\)\\?\\.$native_arch\\.gz$" \
| grep -v '^\./usr/share/man/man8/i386\.8\.gz$' \
| grep -v '^\./usr/share/man/man8/x86_64\.8\.gz$'
} | sort | diff -u - /tmp/tar2.txt >&2
rm /tmp/debian-chroot.tar /tmp/tar2.txt

View file

@ -0,0 +1,20 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
$prefix {{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot.tar.gz {{ MIRROR }}
printf '\037\213\010' | cmp --bytes=3 /tmp/debian-chroot.tar.gz -
tar -tf /tmp/debian-chroot.tar.gz | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar.gz

View file

@ -0,0 +1,27 @@
#!/bin/sh
#
# we are testing all variants here because with 0.7.5 we had a bug:
# mmdebstrap sid /dev/null --simulate ==> E: cannot read /var/cache/apt/archives/
set -eu
export LC_ALL=C.UTF-8
prefix=
include=,
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != root ] && [ "{{ MODE }}" != auto ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
if [ "{{ VARIANT }}" = extract ] || [ "{{ VARIANT }}" = custom ]; then
include="$(tr '\n' ',' <pkglist.txt)"
fi
fi
$prefix {{ CMD }} --mode={{ MODE }} --include="$include" --dry-run --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
if [ -e /tmp/debian-chroot.tar ]; then
echo "/tmp/debian-chroot.tar must not be created with --dry-run" >&2
exit 1
fi

View file

@ -0,0 +1,12 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
mount -t tmpfs -o nodev,nosuid,size=400M tmpfs /tmp
# use --customize-hook to exercise the mounting/unmounting code of block devices in root mode
{{ CMD }} --mode=root --variant=apt --customize-hook='mount | grep /dev/full' --customize-hook='test "$(echo foo | tee /dev/full 2>&1 1>/dev/null)" = "tee: /dev/full: No space left on device"' {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar

33
tests/custom-tmpdir Normal file
View file

@ -0,0 +1,33 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
[ "$(id -u)" -eq 0 ]
[ {{ MODE }} = "unshare" ]
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
# https://www.etalabs.net/sh_tricks.html
quote() { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"; }
homedir=$($prefix sh -c 'cd && pwd')
# apt:test/integration/test-apt-key
TMPDIR_ADD='This is fü$$ing cràzy, $(apt -v)$!'
$prefix mkdir "$homedir/$TMPDIR_ADD"
# make sure the unshared user can traverse into the TMPDIR
chmod 711 "$homedir"
# set permissions and sticky bit like the real /tmp
chmod 1777 "$homedir/$TMPDIR_ADD"
$prefix env TMPDIR="$homedir/$TMPDIR_ADD" {{ CMD }} --mode={{ MODE }} --variant=apt \
--setup-hook='case "$1" in '"$(quote "$homedir/$TMPDIR_ADD/mmdebstrap.")"'??????????) exit 0;; *) echo "$1"; exit 1;; esac' \
{{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
# use rmdir as a quick check that nothing is remaining in TMPDIR
$prefix rmdir "$homedir/$TMPDIR_ADD"
rm /tmp/debian-chroot.tar

16
tests/customize-hook Normal file
View file

@ -0,0 +1,16 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot; rm -f /tmp/customize.sh" EXIT INT TERM
cat <<'SCRIPT' >/tmp/customize.sh
#!/bin/sh
chroot "$1" whoami > "$1/output2"
chroot "$1" pwd >> "$1/output2"
SCRIPT
chmod +x /tmp/customize.sh
{{ CMD }} --mode=root --variant=apt --customize-hook='chroot "$1" sh -c "whoami; pwd" > "$1/output1"' --customize-hook=/tmp/customize.sh {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
printf "root\n/\n" | cmp /tmp/debian-chroot/output1
printf "root\n/\n" | cmp /tmp/debian-chroot/output2
rm /tmp/debian-chroot/output1
rm /tmp/debian-chroot/output2
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

View file

@ -0,0 +1,30 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
[ "$(id -u)" -eq 0 ]
[ {{ MODE }} = "unshare" ]
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
mkdir /tmp/debian-chroot
chmod 700 /tmp/debian-chroot
chown "${SUDO_USER:-user}:${SUDO_USER:-user}" /tmp/debian-chroot
set -- env --chdir=/tmp/debian-chroot
if [ "{{ CMD }}" = "./mmdebstrap" ]; then
set -- "$@" "$(realpath --canonicalize-existing ./mmdebstrap)"
elif [ "{{ CMD }}" = "perl -MDevel::Cover=-silent,-nogcov ./mmdebstrap" ]; then
set -- "$@" perl -MDevel::Cover=-silent,-nogcov "$(realpath --canonicalize-existing ./mmdebstrap)"
else
set -- "$@" {{ CMD }}
fi
$prefix "$@" --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar

45
tests/deb822-1-2 Normal file
View file

@ -0,0 +1,45 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot; rm -f /tmp/sources.list /tmp/deb822.sources" EXIT INT TERM
cat <<SOURCES >/tmp/deb822.sources
Types: deb
URIs: {{ MIRROR }}1
Suites: {{ DIST }}
Components: main
SOURCES
echo "deb {{ MIRROR }}2 {{ DIST }} main" >/tmp/sources.list
echo "deb {{ MIRROR }}3 {{ DIST }} main" \
| {{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} \
/tmp/debian-chroot \
/tmp/deb822.sources \
{{ MIRROR }}4 \
- \
"deb {{ MIRROR }}5 {{ DIST }} main" \
{{ MIRROR }}6 \
/tmp/sources.list
test ! -e /tmp/debian-chroot/etc/apt/sources.list
cat <<SOURCES | cmp /tmp/debian-chroot/etc/apt/sources.list.d/0000deb822.sources -
Types: deb
URIs: {{ MIRROR }}1
Suites: {{ DIST }}
Components: main
SOURCES
cat <<SOURCES | cmp /tmp/debian-chroot/etc/apt/sources.list.d/0001main.list -
deb {{ MIRROR }}4 {{ DIST }} main
deb {{ MIRROR }}3 {{ DIST }} main
deb {{ MIRROR }}5 {{ DIST }} main
deb {{ MIRROR }}6 {{ DIST }} main
SOURCES
echo "deb {{ MIRROR }}2 {{ DIST }} main" | cmp /tmp/debian-chroot/etc/apt/sources.list.d/0002sources.list -
tar -C /tmp/debian-chroot --one-file-system -c . \
| {
tar -t \
| grep -v "^./etc/apt/sources.list.d/0000deb822.sources$" \
| grep -v "^./etc/apt/sources.list.d/0001main.list$" \
| grep -v "^./etc/apt/sources.list.d/0002sources.list"
printf "./etc/apt/sources.list\n"
} | sort | diff -u tar1.txt -

44
tests/deb822-2-2 Normal file
View file

@ -0,0 +1,44 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot; rm -f /tmp/sources /tmp/deb822" EXIT INT TERM
cat <<SOURCES >/tmp/deb822
Types: deb
URIs: {{ MIRROR }}1
Suites: {{ DIST }}
Components: main
SOURCES
echo "deb {{ MIRROR }}2 {{ DIST }} main" >/tmp/sources
cat <<SOURCES | {{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} \
/tmp/debian-chroot \
/tmp/deb822 \
- \
/tmp/sources
Types: deb
URIs: {{ MIRROR }}3
Suites: {{ DIST }}
Components: main
SOURCES
test ! -e /tmp/debian-chroot/etc/apt/sources.list
ls -lha /tmp/debian-chroot/etc/apt/sources.list.d/
cat <<SOURCES | cmp /tmp/debian-chroot/etc/apt/sources.list.d/0000deb822.sources -
Types: deb
URIs: {{ MIRROR }}1
Suites: {{ DIST }}
Components: main
SOURCES
cat <<SOURCES | cmp /tmp/debian-chroot/etc/apt/sources.list.d/0001main.sources -
Types: deb
URIs: {{ MIRROR }}3
Suites: {{ DIST }}
Components: main
SOURCES
echo "deb {{ MIRROR }}2 {{ DIST }} main" | cmp /tmp/debian-chroot/etc/apt/sources.list.d/0002sources.list -
tar -C /tmp/debian-chroot --one-file-system -c . \
| {
tar -t \
| grep -v "^./etc/apt/sources.list.d/0000deb822.sources$" \
| grep -v "^./etc/apt/sources.list.d/0001main.sources$" \
| grep -v "^./etc/apt/sources.list.d/0002sources.list$"
printf "./etc/apt/sources.list\n"
} | sort | diff -u tar1.txt -

16
tests/debootstrap Normal file
View file

@ -0,0 +1,16 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
tmpdir="$(mktemp -d)"
chmod 755 "$tmpdir"
ret=0
debootstrap "$([ "{{ DIST }}" = oldstable ] && echo --no-merged-usr || echo --merged-usr)" --variant={{ VARIANT }} {{ DIST }} "$tmpdir" {{ MIRROR }} || ret=$?
if [ "$ret" -ne 0 ]; then
echo "E: debootstrap failed, dumping $tmpdir/debootstrap/debootstrap.log"
cat "$tmpdir/debootstrap/debootstrap.log"
exit 1
fi
tar --sort=name --mtime=@$SOURCE_DATE_EPOCH --clamp-mtime --numeric-owner --one-file-system --xattrs -C "$tmpdir" -c . >"./cache/debian-{{ DIST }}-{{ VARIANT }}.tar"
rm -r "$tmpdir"

View file

@ -0,0 +1,6 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
{{ CMD }} --mode=root --variant=apt --resolve-deps --merged-usr --no-merged-usr --force-check-gpg {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -
rm -r /tmp/debian-chroot

15
tests/debug Normal file
View file

@ -0,0 +1,15 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
# we use variant standard in verbose mode to see the maximum number of packages
# that was chosen in case of USE_HOST_APT_CONFIG=yes
case {{ VARIANT }} in standard) : ;; *) exit 1 ;; esac
{{ CMD }} --variant={{ VARIANT }} --debug {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
cmp ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar /tmp/debian-chroot.tar \
|| diffoscope ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar /tmp/debian-chroot.tar

View file

@ -0,0 +1,6 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
script -qfc "{{ CMD }} --mode={{ MODE }} --debug --variant=apt {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}" /dev/null
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -

149
tests/dev-ptmx Normal file
View file

@ -0,0 +1,149 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ {{ MODE }} != unshare ] && [ {{ MODE }} != root ]; then
echo "test requires root or unshare mode" >&2
exit 1
fi
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
# this mimics what apt does in apt-pkg/deb/dpkgpm.cc/pkgDPkgPM::StartPtyMagic()
cat >/tmp/test.c <<'END'
#define _GNU_SOURCE
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <signal.h>
int main() {
int ret;
int fd = posix_openpt(O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("posix_openpt");
return 1;
}
char buf[64]; // 64 is used by apt
ret = ptsname_r(fd, buf, sizeof(buf));
if (ret != 0) {
perror("ptsname_r");
return 1;
}
ret = grantpt(fd);
if (ret == -1) {
perror("grantpt");
return 1;
}
struct termios origtt;
ret = tcgetattr(STDIN_FILENO, &origtt);
if (ret != 0) {
perror("tcgetattr1");
return 1;
}
struct termios tt;
ret = tcgetattr(STDOUT_FILENO, &tt);
if (ret != 0) {
perror("tcgetattr2");
return 1;
}
struct winsize win;
ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
if (ret < 0) {
perror("ioctl stdout TIOCGWINSZ");
return 1;
}
ret = ioctl(fd, TIOCSWINSZ, &win);
if (ret < 0) {
perror("ioctl fd TIOCGWINSZ");
return 1;
}
ret = tcsetattr(fd, TCSANOW, &tt);
if (ret != 0) {
perror("tcsetattr1");
return 1;
}
cfmakeraw(&tt);
tt.c_lflag &= ~ECHO;
tt.c_lflag |= ISIG;
sigset_t sigmask;
sigset_t sigmask_old;
ret = sigemptyset(&sigmask);
if (ret != 0) {
perror("sigemptyset");
return 1;
}
ret = sigaddset(&sigmask, SIGTTOU);
if (ret != 0) {
perror("sigaddset");
return 1;
}
ret = sigprocmask(SIG_BLOCK,&sigmask, &sigmask_old);
if (ret != 0) {
perror("sigprocmask1");
return 1;
}
ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
if (ret != 0) {
perror("tcsetattr2");
return 1;
}
ret = sigprocmask(SIG_BLOCK,&sigmask_old, NULL);
if (ret != 0) {
perror("sigprocmask2");
return 1;
}
ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &origtt);
if (ret != 0) {
perror("tcsetattr3");
return 1;
}
return 0;
}
END
# use script to create a fake tty
# run all tests as root and as a normal user (the latter requires ptmxmode=666)
script -qfec "$prefix {{ CMD }} --mode={{ MODE }} --variant=apt \
--include=gcc,libc6-dev,python3,passwd \
--customize-hook='chroot \"\$1\" useradd --home-dir /home/user --create-home user' \
--customize-hook='chroot \"\$1\" python3 -c \"import pty; print(pty.openpty())\"' \
--customize-hook='chroot \"\$1\" runuser -u user -- python3 -c \"import pty; print(pty.openpty())\"' \
--customize-hook='chroot \"\$1\" script -c \"echo foobar\"' \
--customize-hook='chroot \"\$1\" runuser -u user -- env --chdir=/home/user script -c \"echo foobar\"' \
--customize-hook='chroot \"\$1\" apt-get install --yes doc-debian 2>&1 | tee \"\$1\"/tmp/log' \
--customize-hook=\"copy-in /tmp/test.c /tmp\" \
--customize-hook='chroot \"\$1\" gcc /tmp/test.c -o /tmp/test' \
--customize-hook='chroot \"\$1\" /tmp/test' \
--customize-hook='chroot \"\$1\" runuser -u user -- /tmp/test' \
--customize-hook='rm \"\$1\"/tmp/test \"\$1\"/tmp/test.c' \
--customize-hook=\"copy-out /tmp/log /tmp\" \
{{ DIST }} /dev/null {{ MIRROR }}" /dev/null
fail=0
[ -r /tmp/log ] || fail=1
grep '^E:' /tmp/log && fail=1
grep 'Can not write log' /tmp/log && fail=1
grep 'posix_openpt' /tmp/log && fail=1
grep 'No such file or directory' /tmp/log && fail=1
if [ $fail -eq 1 ]; then
echo "apt failed to write log:" >&2
cat /tmp/log >&2
exit 1
fi
rm /tmp/test.c /tmp/log

View file

@ -0,0 +1,12 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
[ "$(whoami)" = "root" ]
trap "rm -rf /tmp/debian-chroot.tar" EXIT INT TERM
{{ CMD }} --mode={{ MODE }} --variant=apt --format=directory {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
ftype=$(stat -c %F /tmp/debian-chroot.tar)
if [ "$ftype" != directory ]; then
echo "expected directory but got: $ftype" >&2
exit 1
fi
tar -C /tmp/debian-chroot.tar --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

13
tests/dist-using-codename Normal file
View file

@ -0,0 +1,13 @@
#!/bin/sh
#
# make sure that using codenames works https://bugs.debian.org/1003191
set -eu
export LC_ALL=C.UTF-8
trap "rm -f InRelease; rm -rf /tmp/debian-chroot.tar /tmp/expected" EXIT INT TERM
/usr/lib/apt/apt-helper download-file "{{ MIRROR }}/dists/{{ DIST }}/InRelease" InRelease
codename=$(awk '/^Codename: / { print $2; }' InRelease)
{{ CMD }} --mode={{ MODE }} --variant=apt "$codename" /tmp/debian-chroot.tar {{ MIRROR }}
echo "deb {{ MIRROR }} $codename main" >/tmp/expected
tar --to-stdout --extract --file /tmp/debian-chroot.tar ./etc/apt/sources.list \
| diff -u /tmp/expected -

14
tests/dpkgopt Normal file
View file

@ -0,0 +1,14 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot; rm -f /tmp/config" EXIT INT TERM
echo no-pager >/tmp/config
{{ CMD }} --mode=root --variant=apt --dpkgopt="path-exclude=/usr/share/doc/*" --dpkgopt=/tmp/config --dpkgopt="path-include=/usr/share/doc/dpkg/copyright" {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
printf 'path-exclude=/usr/share/doc/*\nno-pager\npath-include=/usr/share/doc/dpkg/copyright\n' | cmp /tmp/debian-chroot/etc/dpkg/dpkg.cfg.d/99mmdebstrap -
rm /tmp/debian-chroot/etc/dpkg/dpkg.cfg.d/99mmdebstrap
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort >tar2.txt
{
grep -v '^./usr/share/doc/.' tar1.txt
echo ./usr/share/doc/dpkg/
echo ./usr/share/doc/dpkg/copyright
} | sort | diff -u - tar2.txt

View file

@ -0,0 +1,48 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
cat <<SCRIPT >/tmp/checkeatmydata.sh
#!/bin/sh
set -exu
cat << EOF | diff - "\$1"/usr/bin/dpkg
#!/bin/sh
exec /usr/bin/eatmydata /usr/bin/dpkg.distrib "\\\$@"
EOF
[ -e "\$1"/usr/bin/eatmydata ]
SCRIPT
chmod +x /tmp/checkeatmydata.sh
# first four bytes: magic
elfheader='\177ELF'
# fifth byte: bits
case "$(dpkg-architecture -qDEB_HOST_ARCH_BITS)" in
32) elfheader="$elfheader\\001" ;;
64) elfheader="$elfheader\\002" ;;
*)
echo "bits not supported"
exit 1
;;
esac
# sixth byte: endian
case "$(dpkg-architecture -qDEB_HOST_ARCH_ENDIAN)" in
little) elfheader="$elfheader\\001" ;;
big) elfheader="$elfheader\\002" ;;
*)
echo "endian not supported"
exit 1
;;
esac
# seventh and eigth byte: elf version (1) and abi (unset)
elfheader="$elfheader\\001\\000"
{{ CMD }} --mode=root --variant=apt \
--customize-hook=/tmp/checkeatmydata.sh \
--essential-hook=/tmp/checkeatmydata.sh \
--extract-hook='printf "'"$elfheader"'" | cmp --bytes=8 - "$1"/usr/bin/dpkg' \
--hook-dir=./hooks/eatmydata \
--customize-hook='printf "'"$elfheader"'" | cmp --bytes=8 - "$1"/usr/bin/dpkg' \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }}
tar -C /tmp/debian-chroot --one-file-system -c . \
| tar -t \
| sort \
| diff -u tar1.txt -
rm /tmp/checkeatmydata.sh
rm -r /tmp/debian-chroot

8
tests/empty-sources.list Normal file
View file

@ -0,0 +1,8 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
printf '' | {{ CMD }} --mode={{ MODE }} --variant=apt \
--setup-hook='test -e "$1"/etc/apt/sources.list || echo "deb {{ MIRROR }} {{ DIST }} main" > "$1"/etc/apt/sources.list' \
{{ DIST }} /tmp/debian-chroot.tar -
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -

13
tests/empty-suite Normal file
View file

@ -0,0 +1,13 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
{{ CMD }} --variant={{ VARIANT }} --verbose \
--setup-hook='echo deb {{ MIRROR }} {{ DIST }} main >> "$1"/etc/apt/sources.list' \
'' /tmp/debian-chroot.tar
cmp ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar /tmp/debian-chroot.tar \
|| diffoscope ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar /tmp/debian-chroot.tar

View file

@ -0,0 +1,12 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
ret=0
script -qfec "{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} - {{ MIRROR }}" /dev/null || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

20
tests/essential-hook Normal file
View file

@ -0,0 +1,20 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot; rm -f /tmp/essential.sh" EXIT INT TERM
cat <<'SCRIPT' >/tmp/essential.sh
#!/bin/sh
echo tzdata tzdata/Zones/Europe select Berlin | chroot "$1" debconf-set-selections
SCRIPT
chmod +x /tmp/essential.sh
{{ CMD }} --mode=root --variant=apt --include=tzdata --essential-hook='echo tzdata tzdata/Areas select Europe | chroot "$1" debconf-set-selections' --essential-hook=/tmp/essential.sh {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
[ "$(readlink /tmp/debian-chroot/etc/localtime)" = "/usr/share/zoneinfo/Europe/Berlin" ]
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort \
| grep -v '^./etc/localtime' \
| grep -v '^./etc/timezone' \
| grep -v '^./usr/sbin/tzconfig' \
| grep -v '^./usr/share/doc/tzdata' \
| grep -v '^./usr/share/lintian/overrides/tzdata' \
| grep -v '^./usr/share/zoneinfo' \
| grep -v '^./var/lib/dpkg/info/tzdata.' \
| diff -u tar1.txt -

View file

@ -0,0 +1,9 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
mkdir /tmp/debian-chroot
mkdir /tmp/debian-chroot/lost+found
{{ CMD }} --mode=root --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
rmdir /tmp/debian-chroot/lost+found
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

View file

@ -0,0 +1,7 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
mkdir /tmp/debian-chroot
{{ CMD }} --mode=root --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

View file

@ -0,0 +1,13 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -f /tmp/exists" EXIT INT TERM
touch /tmp/exists
ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/exists {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

View file

@ -0,0 +1,13 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm /tmp/debian-chroot/lost+found/exists; rmdir /tmp/debian-chroot/lost+found /tmp/debian-chroot" EXIT INT TERM
mkdir /tmp/debian-chroot
mkdir /tmp/debian-chroot/lost+found
touch /tmp/debian-chroot/lost+found/exists
ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

View file

@ -0,0 +1,13 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rmdir /tmp/debian-chroot/lost+found; rm /tmp/debian-chroot/exists; rmdir /tmp/debian-chroot" EXIT INT TERM
mkdir /tmp/debian-chroot
mkdir /tmp/debian-chroot/lost+found
touch /tmp/debian-chroot/exists
ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

View file

@ -0,0 +1,9 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} / {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

View file

@ -0,0 +1,9 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/debian-chroot.tar.lz4 {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

View file

@ -0,0 +1,12 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap 'rm -rf /tmp/quoted\"path' EXIT INT TERM
ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/quoted\"path {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

View file

@ -0,0 +1,16 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir /home/user --create-home user
rm /etc/subuid
ret=0
runuser -u user -- {{ CMD }} --mode=unshare --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi
[ ! -e /tmp/debian-chroot ]

View file

@ -0,0 +1,17 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir /home/user --create-home user
awk -F: '$1!="user"' /etc/subuid >/etc/subuid.tmp
mv /etc/subuid.tmp /etc/subuid
ret=0
runuser -u user -- {{ CMD }} --mode=unshare --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi
[ ! -e /tmp/debian-chroot ]

View file

@ -0,0 +1,10 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
ret=0
{{ CMD }} --mode=root --variant=apt --customize-hook='chroot "$1" sh -c "exit 1"' {{ DIST }} /tmp/debian-chroot {{ MIRROR }} || ret=$?
rm -r /tmp/debian-chroot
if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2
exit 1
fi

13
tests/file-mirror Normal file
View file

@ -0,0 +1,13 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test requires the cache directory to be mounted on /mnt and should only be run inside a container" >&2
exit 1
fi
{{ CMD }} --mode={{ MODE }} --variant=apt \
--setup-hook='mkdir -p "$1"/mnt/cache/debian; mount -o ro,bind /mnt/cache/debian "$1"/mnt/cache/debian' \
--customize-hook='umount "$1"/mnt/cache/debian; rmdir "$1"/mnt/cache/debian "$1"/mnt/cache' \
{{ DIST }} /tmp/debian-chroot.tar "deb file:///mnt/cache/debian {{ DIST }} main"
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar

View file

@ -0,0 +1,20 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test requires the cache directory to be mounted on /mnt and should only be run inside a container" >&2
exit 1
fi
if [ "$(id -u)" -eq 0 ] && ! id -u user >/dev/null 2>&1; then
useradd --home-dir /home/user --create-home user
fi
prefix=
[ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && prefix="runuser -u user --"
[ "{{ MODE }}" = "fakechroot" ] && prefix="$prefix fakechroot fakeroot"
$prefix {{ CMD }} --mode={{ MODE }} --variant=apt \
--hook-dir=./hooks/file-mirror-automount \
--customize-hook='[ ! -e "$1"/mnt/cache/debian/ ] || rmdir "$1"/mnt/cache/debian/' \
--customize-hook='rmdir "$1"/mnt/cache' \
{{ DIST }} /tmp/debian-chroot.tar "deb file:///mnt/cache/debian {{ DIST }} main"
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar

6
tests/help Normal file
View file

@ -0,0 +1,6 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
# we redirect to /dev/null instead of using --quiet to not cause a broken pipe
# when grep exits before mmdebstrap was able to write all its output
{{ CMD }} --help | grep --fixed-strings 'mmdebstrap [OPTION...] [SUITE [TARGET [MIRROR...]]]' >/dev/null

49
tests/hook-directory Normal file
View file

@ -0,0 +1,49 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
for h in hookA hookB; do
mkdir /tmp/$h
for s in setup extract essential customize; do
cat <<SCRIPT >/tmp/$h/${s}00.sh
#!/bin/sh
echo $h/${s}00 >> "\$1/$s"
SCRIPT
chmod +x /tmp/$h/${s}00.sh
cat <<SCRIPT >/tmp/$h/${s}01.sh
echo $h/${s}01 >> "\$1/$s"
SCRIPT
chmod +x /tmp/$h/${s}01.sh
done
done
{{ CMD }} --mode=root --variant=apt \
--setup-hook='echo cliA/setup >> "$1"/setup' \
--extract-hook='echo cliA/extract >> "$1"/extract' \
--essential-hook='echo cliA/essential >> "$1"/essential' \
--customize-hook='echo cliA/customize >> "$1"/customize' \
--hook-dir=/tmp/hookA \
--setup-hook='echo cliB/setup >> "$1"/setup' \
--extract-hook='echo cliB/extract >> "$1"/extract' \
--essential-hook='echo cliB/essential >> "$1"/essential' \
--customize-hook='echo cliB/customize >> "$1"/customize' \
--hook-dir=/tmp/hookB \
--setup-hook='echo cliC/setup >> "$1"/setup' \
--extract-hook='echo cliC/extract >> "$1"/extract' \
--essential-hook='echo cliC/essential >> "$1"/essential' \
--customize-hook='echo cliC/customize >> "$1"/customize' \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }}
printf "cliA/setup\nhookA/setup00\nhookA/setup01\ncliB/setup\nhookB/setup00\nhookB/setup01\ncliC/setup\n" | diff -u - /tmp/debian-chroot/setup
printf "cliA/extract\nhookA/extract00\nhookA/extract01\ncliB/extract\nhookB/extract00\nhookB/extract01\ncliC/extract\n" | diff -u - /tmp/debian-chroot/extract
printf "cliA/essential\nhookA/essential00\nhookA/essential01\ncliB/essential\nhookB/essential00\nhookB/essential01\ncliC/essential\n" | diff -u - /tmp/debian-chroot/essential
printf "cliA/customize\nhookA/customize00\nhookA/customize01\ncliB/customize\nhookB/customize00\nhookB/customize01\ncliC/customize\n" | diff -u - /tmp/debian-chroot/customize
for s in setup extract essential customize; do
rm /tmp/debian-chroot/$s
done
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -
for h in hookA hookB; do
for s in setup extract essential customize; do
rm /tmp/$h/${s}00.sh
rm /tmp/$h/${s}01.sh
done
rmdir /tmp/$h
done
rm -r /tmp/debian-chroot

View file

@ -0,0 +1,42 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
# remove qemu just to be sure
apt-get remove --yes qemu-user-binfmt binfmt-support qemu-user
{{ CMD }} --mode={{ MODE }} --variant=apt --architectures=i386 {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
# we ignore differences between architectures by ignoring some files
# and renaming others
{
tar -tf /tmp/debian-chroot.tar \
| grep -v '^\./usr/bin/i386$' \
| grep -v '^\./usr/lib/ld-linux\.so\.2$' \
| grep -v '^\./usr/lib/i386-linux-gnu/ld-linux\.so\.2$' \
| grep -v '^\./usr/lib/gcc/i686-linux-gnu/$' \
| grep -v '^\./usr/lib/gcc/i686-linux-gnu/[0-9]\+/$' \
| grep -v '^\./usr/share/man/man8/i386\.8\.gz$' \
| grep -v '^\./usr/share/doc/[^/]\+/changelog\(\.Debian\)\?\.i386\.gz$' \
| sed 's/i386-linux-gnu/x86_64-linux-gnu/' \
| sed 's/i386/amd64/' \
| sed 's/\/stubs-32.ph$/\/stubs-64.ph/'
} | sort >tar2.txt
{
grep <tar1.txt -v '^\./usr/bin/i386$' \
| grep -v '^\./usr/bin/x86_64$' \
| grep -v '^\./usr/lib32/$' \
| grep -v '^\./lib32$' \
| grep -v '^\./lib64$' \
| grep -v '^\./usr/lib64/$' \
| grep -v '^\./usr/lib64/ld-linux-x86-64\.so\.2$' \
| grep -v '^\./usr/lib/gcc/x86_64-linux-gnu/$' \
| grep -v '^\./usr/lib/gcc/x86_64-linux-gnu/[0-9]\+/$' \
| grep -v '^\./usr/lib/x86_64-linux-gnu/ld-linux-x86-64\.so\.2$' \
| grep -v '^\./usr/lib/x86_64-linux-gnu/libmvec\.so\.1$' \
| grep -v '^\./usr/share/doc/[^/]\+/changelog\(\.Debian\)\?\.amd64\.gz$' \
| grep -v '^\./usr/share/man/man8/i386\.8\.gz$' \
| grep -v '^\./usr/share/man/man8/x86_64\.8\.gz$'
} | sort | diff -u - tar2.txt >&2
rm /tmp/debian-chroot.tar

11
tests/include Normal file
View file

@ -0,0 +1,11 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
{{ CMD }} --mode=root --variant=apt --include=doc-debian {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
rm /tmp/debian-chroot/usr/share/doc-base/doc-debian.debian-*
rm -r /tmp/debian-chroot/usr/share/doc/debian
rm -r /tmp/debian-chroot/usr/share/doc/doc-debian
rm /tmp/debian-chroot/var/lib/dpkg/info/doc-debian.list
rm /tmp/debian-chroot/var/lib/dpkg/info/doc-debian.md5sums
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

40
tests/include-deb-file Normal file
View file

@ -0,0 +1,40 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/dummypkg.deb /tmp/dummypkg" EXIT INT TERM
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
# instead of obtaining a .deb from our cache, we create a new package because
# otherwise apt might decide to download the package with the same name and
# version from the cache instead of using the local .deb
mkdir -p /tmp/dummypkg/DEBIAN
cat <<END >"/tmp/dummypkg/DEBIAN/control"
Package: dummypkg
Priority: optional
Section: oldlibs
Maintainer: Johannes Schauer Marin Rodrigues <josch@debian.org>
Architecture: all
Multi-Arch: foreign
Source: dummypkg
Version: 1
Description: dummypkg
END
dpkg-deb --build "/tmp/dummypkg" "/tmp/dummypkg.deb"
$prefix {{ CMD }} --mode={{ MODE }} --variant=apt --include="/tmp/dummypkg.deb" \
--hook-dir=./hooks/file-mirror-automount \
--customize-hook='chroot "$1" dpkg-query -W -f="\${Status}\n" dummypkg | grep "^install ok installed$"' \
{{ DIST }} /dev/null {{ MIRROR }}

View file

@ -0,0 +1,49 @@
#!/bin/sh
#
# to test foreign architecture package installation we choose a package which
# - is not part of the native installation set
# - does not have any dependencies
# - installs only few files
# - doesn't change its name regularly (like gcc-*-base)
case "$(dpkg --print-architecture)" in
arm64)
native_arch=arm64
foreign_arch=amd64
;;
amd64)
native_arch=amd64
foreign_arch=arm64
;;
*)
echo "unsupported native architecture" >&2
exit 1
;;
esac
set -eu
export LC_ALL=C.UTF-8
{{ CMD }} --mode=root --variant=apt \
--architectures="$native_arch,$foreign_arch" \
--include="libmagic-mgc:$foreign_arch" \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }}
{
echo "$native_arch"
echo "$foreign_arch"
} | cmp /tmp/debian-chroot/var/lib/dpkg/arch -
rm /tmp/debian-chroot/usr/lib/file/magic.mgc
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/README.Debian
rm -f /tmp/debian-chroot/usr/share/doc/libmagic-mgc/"changelog.Debian.$foreign_arch.gz"
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.Debian.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/copyright
rm /tmp/debian-chroot/usr/share/file/magic.mgc
rm /tmp/debian-chroot/usr/share/misc/magic.mgc
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.list
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.md5sums
rmdir /tmp/debian-chroot/usr/share/doc/libmagic-mgc/
rmdir /tmp/debian-chroot/usr/share/file/magic/
rmdir /tmp/debian-chroot/usr/share/file/
rmdir /tmp/debian-chroot/usr/lib/file/
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -
rm -r /tmp/debian-chroot

View file

@ -0,0 +1,50 @@
#!/bin/sh
#
# to test foreign architecture package installation we choose a package which
# - is not part of the native installation set
# - does not have any dependencies
# - installs only few files
# - doesn't change its name regularly (like gcc-*-base)
case "$(dpkg --print-architecture)" in
arm64)
native_arch=arm64
foreign_arch=amd64
;;
amd64)
native_arch=amd64
foreign_arch=arm64
;;
*)
echo "unsupported native architecture" >&2
exit 1
;;
esac
set -eu
export LC_ALL=C.UTF-8
{{ CMD }} --mode=root --variant=apt \
--architectures="$native_arch" \
--architectures="$foreign_arch" \
--include="libmagic-mgc:$foreign_arch" \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }}
{
echo "$native_arch"
echo "$foreign_arch"
} | cmp /tmp/debian-chroot/var/lib/dpkg/arch -
rm /tmp/debian-chroot/usr/lib/file/magic.mgc
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/README.Debian
rm -f /tmp/debian-chroot/usr/share/doc/libmagic-mgc/"changelog.Debian.$foreign_arch.gz"
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.Debian.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/copyright
rm /tmp/debian-chroot/usr/share/file/magic.mgc
rm /tmp/debian-chroot/usr/share/misc/magic.mgc
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.list
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.md5sums
rmdir /tmp/debian-chroot/usr/share/doc/libmagic-mgc/
rmdir /tmp/debian-chroot/usr/share/file/magic/
rmdir /tmp/debian-chroot/usr/share/file/
rmdir /tmp/debian-chroot/usr/lib/file/
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -
rm -r /tmp/debian-chroot

View file

@ -0,0 +1,10 @@
#!/bin/sh
#
# This checks for https://bugs.debian.org/976166
# Since $DEFAULT_DIST varies, we hardcode stable and unstable.
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
{{ CMD }} --mode=root --variant=minbase --include=doc-debian unstable /tmp/debian-chroot "deb {{ MIRROR }} unstable main" "deb {{ MIRROR }} stable main"
chroot /tmp/debian-chroot dpkg-query --show doc-debian

View file

@ -0,0 +1,41 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
pkgs=base-files,base-passwd,busybox,debianutils,dpkg,libc-bin,mawk,tar
# busybox --install -s will install symbolic links into the rootfs, leaving
# existing files untouched. It has to run after extraction (otherwise there is
# no busybox binary) and before first configuration
{{ CMD }} --mode=root --variant=custom \
--include=$pkgs \
--setup-hook='mkdir -p "$1/bin"' \
--setup-hook='echo root:x:0:0:root:/root:/bin/sh > "$1/etc/passwd"' \
--setup-hook='printf "root:x:0:\nmail:x:8:\nutmp:x:43:\n" > "$1/etc/group"' \
--extract-hook='chroot "$1" busybox --install -s' \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }}
echo "$pkgs" | tr ',' '\n' >/tmp/expected
chroot /tmp/debian-chroot dpkg-query -f '${binary:Package}\n' -W \
| comm -12 - /tmp/expected \
| diff -u - /tmp/expected
rm /tmp/expected
for cmd in echo cat sed grep; do
test -L /tmp/debian-chroot/bin/$cmd
test "$(readlink /tmp/debian-chroot/bin/$cmd)" = "/usr/bin/busybox"
done
for cmd in sort tee; do
test -L /tmp/debian-chroot/usr/bin/$cmd
test "$(readlink /tmp/debian-chroot/usr/bin/$cmd)" = "/usr/bin/busybox"
done
# if /bin or /sbin are not symlinks, add /bin and /sbin to PATH
if [ ! -L /tmp/debian-chroot/bin ] || [ ! -L /tmp/debian-chroot/sbin ]; then
export PATH="$PATH:/sbin:/bin"
fi
chroot /tmp/debian-chroot echo foobar \
| chroot /tmp/debian-chroot cat \
| chroot /tmp/debian-chroot sort \
| chroot /tmp/debian-chroot tee /dev/null \
| chroot /tmp/debian-chroot sed 's/foobar/blubber/' \
| chroot /tmp/debian-chroot grep blubber >/dev/null

Some files were not shown because too many files have changed in this diff Show more