diff options
-rw-r--r-- | CHANGELOG.md | 5 | ||||
-rwxr-xr-x | coverage.py | 2 | ||||
-rw-r--r-- | coverage.txt | 6 | ||||
-rwxr-xr-x | hooks/copy-host-apt-sources-and-preferences/setup00.sh | 9 | ||||
-rwxr-xr-x | hooks/file-mirror-automount/setup00.sh | 2 | ||||
-rwxr-xr-x | hooks/maybe-merged-usr/essential00.sh | 4 | ||||
-rwxr-xr-x | hooks/maybe-merged-usr/extract00.sh | 20 | ||||
-rwxr-xr-x | hooks/maybe-merged-usr/setup00.sh | 20 | ||||
-rwxr-xr-x | make_mirror.sh | 11 | ||||
-rwxr-xr-x | mmdebstrap | 442 | ||||
-rwxr-xr-x | mmdebstrap-autopkgtest-build-qemu | 33 | ||||
-rw-r--r-- | tests/include-foreign-libmagic-mgc | 2 | ||||
-rw-r--r-- | tests/include-foreign-libmagic-mgc-with-multiple-arch-options | 2 | ||||
-rw-r--r-- | tests/install-libmagic-mgc-on-foreign | 2 | ||||
-rw-r--r-- | tests/missing-dev-sys-proc-inside-the-chroot | 6 | ||||
-rw-r--r-- | tests/mmdebstrap | 15 | ||||
-rw-r--r-- | tests/skip-tar-in-mknod | 5 |
17 files changed, 367 insertions, 219 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index fea8b9a..eea34e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +1.5.0 (2024-05-14) +------------------ + + - add --format=ext4 + 1.4.3 (2024-02-01) ------------------ diff --git a/coverage.py b/coverage.py index 7e911cf..01ee7b5 100755 --- a/coverage.py +++ b/coverage.py @@ -34,7 +34,7 @@ all_variants = [ "standard", ] default_format = "auto" -all_formats = ["auto", "directory", "tar", "squashfs", "ext2", "null"] +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() diff --git a/coverage.txt b/coverage.txt index fb09b19..dde5b33 100644 --- a/coverage.txt +++ b/coverage.txt @@ -59,7 +59,7 @@ Needs-QEMU: true Test: mmdebstrap Needs-Root: true Modes: root -Formats: tar squashfs ext2 +Formats: tar squashfs ext2 ext4 Variants: essential apt minbase buildd - standard Skip-If: variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558 @@ -68,7 +68,7 @@ Skip-If: Test: check-for-bit-by-bit-identical-format-output Modes: unshare fakechroot -Formats: tar squashfs ext2 +Formats: tar squashfs ext2 ext4 Variants: essential apt minbase buildd - standard Skip-If: variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558 @@ -433,4 +433,4 @@ Test: skip-output-mknod Modes: root unshare Test: skip-tar-in-mknod -Modes: unshare +Modes: root diff --git a/hooks/copy-host-apt-sources-and-preferences/setup00.sh b/hooks/copy-host-apt-sources-and-preferences/setup00.sh index 07caa78..9aafd78 100755 --- a/hooks/copy-host-apt-sources-and-preferences/setup00.sh +++ b/hooks/copy-host-apt-sources-and-preferences/setup00.sh @@ -1,4 +1,13 @@ #!/bin/sh +# +# This script makes sure that the apt sources.list and preferences from outside +# the chroot also exist inside the chroot by *appending* them to any existing +# files. If you do not want to keep the original content, add another setup +# hook before this one which cleans up the files you don't want to keep. +# +# If instead of copying sources.list verbatim you want to mangle its contents, +# consider using python-apt for that. An example can be found in the Debian +# packaging of mmdebstrap in ./debian/tests/sourcesfilter set -eu diff --git a/hooks/file-mirror-automount/setup00.sh b/hooks/file-mirror-automount/setup00.sh index 61f60f2..6ccbdaf 100755 --- a/hooks/file-mirror-automount/setup00.sh +++ b/hooks/file-mirror-automount/setup00.sh @@ -15,7 +15,7 @@ env APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get indextargets --no-release-info - | while read -r path; do mkdir -p "$rootdir/run/mmdebstrap" if [ ! -d "/$path" ]; then - echo "/$path is not an existing directory" >&2 + echo "W: /$path is not an existing directory" >&2 continue fi case $MMDEBSTRAP_MODE in diff --git a/hooks/maybe-merged-usr/essential00.sh b/hooks/maybe-merged-usr/essential00.sh index a23f2f7..656057e 100755 --- a/hooks/maybe-merged-usr/essential00.sh +++ b/hooks/maybe-merged-usr/essential00.sh @@ -15,6 +15,10 @@ case "$ver" in echo "usr-is-merged package from src:usrmerge installed -- not running merged-usr essential hook" >&2 exit 0 ;; + 'not-installed ') + echo "usr-is-merged was not installed in a previous hook -- not running merged-usr essential hook" >&2 + exit 0 + ;; *) echo "unexpected situation for package usr-is-merged: $ver" >&2 exit 1 diff --git a/hooks/maybe-merged-usr/extract00.sh b/hooks/maybe-merged-usr/extract00.sh index dc88450..00bb037 100755 --- a/hooks/maybe-merged-usr/extract00.sh +++ b/hooks/maybe-merged-usr/extract00.sh @@ -4,12 +4,22 @@ set -eu env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get update --error-on=any -# if the usr-is-merged package cannot be installed with apt, do nothing -if ! env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then - echo "no package called usr-is-merged found -- not running merged-usr extract hook" >&2 - exit 0 +if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then + # if apt-cache exited successfully, then usr-is-merged exists either as + # a real or virtual package + if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged 2>/dev/null | grep -q "Package: usr-is-merged"; then + echo "usr-is-merged found -- running merged-usr extract hook" >&2 + else + # The usr-is-merged must be virtual, so assume that nothing + # has to be done. This is the case with Debian Trixie or later + # or with Ubuntu Lunar or later + echo "usr-is-merged found but not real -- not running merged-usr extract hook" >&2 + exit 0 + fi else - echo "package usr-is-merged found -- running merged-usr extract hook" >&2 + # if the usr-is-merged package cannot be installed with apt, do nothing + echo "no package providing usr-is-merged found -- not running merged-usr extract hook" >&2 + exit 0 fi # resolve the script path using several methods in order: diff --git a/hooks/maybe-merged-usr/setup00.sh b/hooks/maybe-merged-usr/setup00.sh index a6bd712..6568af2 100755 --- a/hooks/maybe-merged-usr/setup00.sh +++ b/hooks/maybe-merged-usr/setup00.sh @@ -4,12 +4,22 @@ set -eu env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-get update --error-on=any -# if the usr-is-merged package cannot be installed with apt, do nothing -if ! env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then - echo "no package called usr-is-merged found -- not running merged-usr setup hook" >&2 - exit 0 +if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged > /dev/null 2>&1; then + # if apt-cache exited successfully, then usr-is-merged exists either as + # a real or virtual package + if env --chdir="$1" APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-cache show --no-all-versions usr-is-merged 2>/dev/null | grep -q "Package: usr-is-merged"; then + echo "usr-is-merged found -- running merged-usr setup hook" >&2 + else + # The usr-is-merged must be virtual, so assume that nothing + # has to be done. This is the case with Debian Trixie or later + # or with Ubuntu Lunar or later + echo "usr-is-merged found but not real -- not running merged-usr setup hook" >&2 + exit 0 + fi else - echo "package usr-is-merged found -- running merged-usr setup hook" >&2 + # if the usr-is-merged package cannot be installed with apt, do nothing + echo "no package providing usr-is-merged found -- not running merged-usr setup hook" >&2 + exit 0 fi # resolve the script path using several methods in order: diff --git a/make_mirror.sh b/make_mirror.sh index 8849ee3..3f8aae4 100755 --- a/make_mirror.sh +++ b/make_mirror.sh @@ -33,7 +33,7 @@ deletecache() { done # deleting artifacts from test "mmdebstrap" for variant in essential apt minbase buildd - standard; do - for format in tar ext2 squashfs; 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" @@ -236,7 +236,11 @@ END esac # shellcheck disable=SC2086 - APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install $pkgs + APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install $pkgs \ + || APT_CONFIG="$rootdir/etc/apt/apt.conf" apt-get --yes install \ + -oDebug::pkgProblemResolver=true -oDebug::pkgDepCache::Marker=1 \ + -oDebug::pkgDepCache::AutoInstall=1 \ + $pkgs rm "$rootdir/var/cache/apt/archives/lock" rmdir "$rootdir/var/cache/apt/archives/partial" @@ -449,10 +453,11 @@ if [ "$HAVE_QEMU" = "yes" ]; then tmpdir="$(mktemp -d)" trap 'kill "$PROXYPID" || :;cleanuptmpdir; cleanup_newcachedir' EXIT INT TERM - pkgs=perl-doc,systemd-sysv,perl,arch-test,fakechroot,fakeroot,mount,uidmap,qemu-user-static,qemu-user,dpkg-dev,mini-httpd,libdevel-cover-perl,libtemplate-perl,debootstrap,procps,apt-cudf,aspcud,python3,libcap2-bin,gpg,debootstrap,distro-info-data,iproute2,ubuntu-keyring,apt-utils,squashfs-tools-ng,genext2fs,linux-image-generic,passwd + pkgs=perl-doc,systemd-sysv,perl,arch-test,fakechroot,fakeroot,mount,uidmap,qemu-user-static,qemu-user,dpkg-dev,mini-httpd,libdevel-cover-perl,libtemplate-perl,debootstrap,procps,apt-cudf,aspcud,python3,libcap2-bin,gpg,debootstrap,distro-info-data,iproute2,ubuntu-keyring,apt-utils,squashfs-tools-ng,genext2fs,linux-image-generic,passwd,e2fsprogs,uuid-runtime if [ ! -e ./mmdebstrap ]; then pkgs="$pkgs,mmdebstrap" fi + pkgs="$pkgs,auditd" arches=$HOSTARCH if [ "$RUN_MA_SAME_TESTS" = "yes" ]; then case "$HOSTARCH" in @@ -23,7 +23,7 @@ use strict; use warnings; -our $VERSION = '1.4.3'; +our $VERSION = '1.5.0'; use English; use Getopt::Long; @@ -46,6 +46,7 @@ use Socket; use Time::HiRes; use Math::BigInt; use Text::ParseWords; +use Digest::SHA; use version; ## no critic (InputOutput::RequireBriefOpen) @@ -66,13 +67,16 @@ use version; *MS_BIND = \0x1000; *MS_REC = \0x4000; *MNT_DETACH = \2; +# uuid_t NameSpace_DNS in rfc4122 +*UUID_NS_DNS = \'6ba7b810-9dad-11d1-80b4-00c04fd430c8'; our ( $CLONE_NEWNS, $CLONE_NEWUTS, $CLONE_NEWIPC, $CLONE_NEWUSER, $CLONE_NEWPID, $CLONE_NEWNET, $_LINUX_CAPABILITY_VERSION_3, $CAP_SYS_ADMIN, $PR_CAPBSET_READ, $MS_BIND, - $MS_REC, $MNT_DETACH + $MS_REC, $MNT_DETACH, + $UUID_NS_DNS ); #<<< @@ -215,11 +219,12 @@ sub minor { } sub can_execute { - my $tool = shift; - my $pid = open my $fh, '-|' // return 0; + my $tool = shift; + my $verbose = shift // '--version'; + my $pid = open my $fh, '-|' // return 0; if ($pid == 0) { open(STDERR, '>&', STDOUT) or die; - exec {$tool} $tool, '--version' or die; + exec {$tool} $tool, $verbose or die; } chomp( my $content = do { local $/; <$fh> } @@ -303,6 +308,28 @@ sub shellescape { return "'$string'"; } +sub create_v5_uuid { + use bytes; + my $ns_uuid = shift; + my $name = shift; + my $version = 0x50; + # convert the namespace uuid to binary + $ns_uuid =~ tr/-//d; + $ns_uuid = pack 'H*', $ns_uuid; + # concatenate namespace and name and take sha1 + my $digest = Digest::SHA->new(1); + $digest->add($ns_uuid); + $digest->add($name); + # only the first 16 bytes matter + my $uuid = substr($digest->digest(), 0, 16); + # set the version + substr $uuid, 6, 1, chr(ord(substr($uuid, 6, 1)) & 0x0f | $version); + substr $uuid, 8, 1, chr(ord(substr $uuid, 8, 1) & 0x3f | 0x80); + # convert binary back to uuid formatting + return join '-', map { unpack 'H*', $_ } + map { substr $uuid, 0, $_, '' } (4, 2, 2, 2, 6); +} + sub test_unshare_userns { my $verbose = shift; @@ -4380,15 +4407,16 @@ sub guess_sources_format { } sub approx_disk_usage { - my $directory = shift; + my $directory = shift; + my $block_size = shift; info "approximating disk usage..."; # the "du" utility reports different results depending on the underlying # filesystem, see https://bugs.debian.org/650077 for a discussion # # we use code similar to the one used by dpkg-gencontrol instead # - # Regular files are measured in number of 1024 byte blocks. All other - # entries are assumed to take one block of space. + # Regular files are measured in number of $block_size byte blocks. All + # other entries are assumed to take one block of space. # # We ignore /dev because depending on the mode, the directory might be # populated or not and we want consistent disk usage results independent @@ -4412,8 +4440,8 @@ sub approx_disk_usage { return if exists $hardlink{"$dev:$ino"}; # Track hardlinks to avoid repeated additions. $hardlink{"$dev:$ino"} = 1 if $nlink > 1; - # add file size in 1024 byte blocks, rounded up - $installed_size += int(((-s _) + 1024) / 1024); + # add file size in $block_size byte blocks, rounded up + $installed_size += int(((-s _) + $block_size) / $block_size); } else { # all other entries are assumed to only take up one block $installed_size += 1; @@ -4805,7 +4833,7 @@ sub main() { $options->{format} = 'directory'; } my @valid_formats - = ('auto', 'directory', 'tar', 'squashfs', 'ext2', 'null'); + = ('auto', 'directory', 'tar', 'squashfs', 'ext2', 'ext4', 'null'); if (none { $_ eq $options->{format} } @valid_formats) { error "invalid format. Choose from " . (join ', ', @valid_formats); } @@ -5670,6 +5698,30 @@ sub main() { if ($exitstatus != 0) { error "genext2fs failed with exit status: $exitstatus"; } + } elsif ($options->{target} =~ /\.ext4$/) { + $options->{format} = 'ext4'; + # check if the installed version of e2fsprogs supports tarballs on + # stdin + (undef, my $filename) = tempfile( + "mmdebstrap.ext4.XXXXXXXXXXXX", + OPEN => 0, + TMPDIR => 1 + ); + # creating file to suppress message "Creating regular file ..." + { open my $fh, '>', $filename; } + open my $fh, '|-', 'mke2fs', '-q', '-F', '-o', 'Linux', '-T', + 'ext4', '-b', '4096', '-d', '-', $filename, + '16384' // error "failed to fork(): $!"; + # write 10240 null-bytes to mke2fs -- this represents an empty + # tar archive + print $fh ("\0" x 10240) + or error "cannot write to mke2fs process"; + close $fh; + my $exitstatus = $?; + unlink $filename // die "cannot unlink $filename"; + if ($exitstatus != 0) { + error "mke2fs failed with exit status: $exitstatus"; + } } else { $options->{format} = 'directory'; } @@ -5687,21 +5739,30 @@ sub main() { info "ignoring target $options->{target} with null format"; } + my $blocksize = -1; if ($options->{format} eq 'ext2') { if (!can_execute 'genext2fs') { error "need genext2fs for ext2 format"; } + $blocksize = 1024; + } elsif ($options->{format} eq 'ext4') { + if (!can_execute 'mke2fs', '-V') { + error "need mke2fs for ext4 format"; + } + $blocksize = 4096; } elsif ($options->{format} eq 'squashfs') { if (!can_execute 'tar2sqfs') { error "need tar2sqfs binary from the squashfs-tools-ng package"; } + $blocksize = 1048576; } - if (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2', 'null')) { + if (any { $_ eq $options->{format} } + ('tar', 'squashfs', 'ext2', 'ext4', 'null')) { if ($options->{format} ne 'null') { if (any { $_ eq $options->{variant} } ('extract', 'custom') and $options->{mode} eq 'fakechroot') { - info "creating a tarball or squashfs image or ext2 image in" + info "creating a tarball, squashfs, ext2 or ext4 image in" . " fakechroot mode might fail in extract and" . " custom variants because there might be no tar inside the" . " chroot"; @@ -5885,7 +5946,7 @@ sub main() { # If both the above assertion change, we can stop creating /dev entries as # well. my $devtar = ''; - if (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2')) { + if (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2', 'ext4')) { foreach my $file (@devfiles) { my ($fname, $mode, $type, $linkname, $devmajor, $devminor) = @{$file}; @@ -5954,6 +6015,9 @@ sub main() { push @taropts, '--xattrs', '--xattrs-exclude=system.*'; } elsif ($options->{format} eq "ext2") { warning "genext2fs does not support extended attributes"; + warning "ext2 does not support sub-second precision timestamps"; + warning "ext2 does not support timestamps beyond 2038 January 18"; + warning "ext2 inode size of 128 prevents removing these limitations"; } else { push @taropts, '--xattrs'; } @@ -5963,8 +6027,6 @@ sub main() { my $sigset = POSIX::SigSet->new(SIGINT, SIGHUP, SIGPIPE, SIGTERM); POSIX::sigprocmask(SIG_BLOCK, $sigset) or error "Can't block signals: $!"; - my $pid; - # a pipe to transfer the final tarball from the child to the parent pipe my $rfh, my $wfh; @@ -5979,163 +6041,108 @@ sub main() { # b) it puts code writing the protocol outside of the helper/listener # c) the forked listener process cannot communicate to its parent pipe my $nblkreader, my $nblkwriter or error "pipe failed: $!"; - if ($options->{mode} eq 'unshare') { - $pid = get_unshare_cmd( - sub { - # child - local $SIG{'INT'} = 'DEFAULT'; - local $SIG{'HUP'} = 'DEFAULT'; - local $SIG{'PIPE'} = 'DEFAULT'; - local $SIG{'TERM'} = 'DEFAULT'; - # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) - or error "Can't unblock signals: $!"; + my $worker = sub { + # child + local $SIG{'INT'} = 'DEFAULT'; + local $SIG{'HUP'} = 'DEFAULT'; + local $SIG{'PIPE'} = 'DEFAULT'; + local $SIG{'TERM'} = 'DEFAULT'; - close $rfh; - close $parentsock; - open(STDOUT, '>&', STDERR) or error "cannot open STDOUT: $!"; + # unblock all delayed signals (and possibly handle them) + POSIX::sigprocmask(SIG_UNBLOCK, $sigset) + or error "Can't unblock signals: $!"; - setup($options); + close $rfh; + close $parentsock; + open(STDOUT, '>&', STDERR) or error "cannot open STDOUT: $!"; - print $childsock (pack('n', 0) . 'adios'); - $childsock->flush(); + setup($options); - close $childsock; + print $childsock (pack('n', 0) . 'adios'); + $childsock->flush(); - close $nblkreader; - if (!$options->{dryrun} && $options->{format} eq 'ext2') { - my $numblocks = approx_disk_usage($options->{root}); - print $nblkwriter "$numblocks\n"; - $nblkwriter->flush(); - } - close $nblkwriter; + close $childsock; - if ($options->{dryrun}) { - info "simulate creating tarball..."; - } elsif (any { $_ eq $options->{format} } - ('tar', 'squashfs', 'ext2')) { - info "creating tarball..."; - - # redirect tar output to the writing end of the pipe so - # that the parent process can capture the output - open(STDOUT, '>&', $wfh) or error "cannot open STDOUT: $!"; - - # Add ./dev as the first entries of the tar file. - # We cannot add them after calling tar, because there is no - # way to prevent tar from writing NULL entries at the end. - if (any { $_ eq 'output/dev' } @{ $options->{skip} }) { - info "skipping output/dev as requested"; - } else { - print $devtar; - } + close $nblkreader; + if (!$options->{dryrun} && any { $_ eq $options->{format} } + ('ext2', 'ext4')) { + my $numblocks = approx_disk_usage($options->{root}, $blocksize); + print $nblkwriter "$numblocks\n"; + $nblkwriter->flush(); + } + close $nblkwriter; - # pack everything except ./dev - 0 == system('tar', @taropts, '-C', $options->{root}, '.') - or error "tar failed: $?"; + if ($options->{dryrun}) { + info "simulate creating tarball..."; + } elsif (any { $_ eq $options->{format} } + ('tar', 'squashfs', 'ext2', 'ext4')) { + info "creating tarball..."; + + # redirect tar output to the writing end of the pipe so + # that the parent process can capture the output + open(STDOUT, '>&', $wfh) or error "cannot open STDOUT: $!"; + + # Add ./dev as the first entries of the tar file. + # We cannot add them after calling tar, because there is no + # way to prevent tar from writing NULL entries at the end. + if (any { $_ eq 'output/dev' } @{ $options->{skip} }) { + info "skipping output/dev as requested"; + } else { + print $devtar; + } - info "done"; - } elsif (any { $_ eq $options->{format} } - ('directory', 'null')) { - # nothing to do - } else { - error "unknown format: $options->{format}"; + if ($options->{mode} eq 'unshare') { + # pack everything except ./dev + 0 == system('tar', @taropts, '-C', $options->{root}, '.') + or error "tar failed: $?"; + } elsif ($options->{mode} eq 'fakechroot') { + # By default, FAKECHROOT_EXCLUDE_PATH includes /proc and /sys + # which means that the resulting tarball will contain the + # permission and ownership information of /proc and /sys from + # the outside, which we want to avoid. + ## no critic (Variables::RequireLocalizedPunctuationVars) + $ENV{FAKECHROOT_EXCLUDE_PATH} = "/dev"; + # Fakechroot requires tar to run inside the chroot or otherwise + # absolute symlinks will include the path to the root directory + 0 == system('chroot', $options->{root}, 'tar', + @taropts, '-C', '/', '.') + or error "tar failed: $?"; + } elsif (any { $_ eq $options->{mode} } ('root', 'chrootless')) { + # If the chroot directory is not owned by the root user, then + # we assume that no measure was taken to fake root permissions. + # Since the final tarball should contain entries with root + # ownership, we instruct tar to do so. + my @owneropts = (); + if ((stat $options->{root})[4] != 0) { + push @owneropts, '--owner=0', '--group=0', + '--numeric-owner'; } + 0 == system('tar', @taropts, @owneropts, '-C', + $options->{root}, '.') + or error "tar failed: $?"; + } else { + error "unknown mode: $options->{mode}"; + } - exit 0; - }, - \@idmap - ); + info "done"; + } elsif (any { $_ eq $options->{format} } ('directory', 'null')) { + # nothing to do + } else { + error "unknown format: $options->{format}"; + } + + exit 0; + }; + + my $pid; + if ($options->{mode} eq 'unshare') { + $pid = get_unshare_cmd($worker, \@idmap); } elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'chrootless')) { $pid = fork() // error "fork() failed: $!"; if ($pid == 0) { - local $SIG{'INT'} = 'DEFAULT'; - local $SIG{'HUP'} = 'DEFAULT'; - local $SIG{'PIPE'} = 'DEFAULT'; - local $SIG{'TERM'} = 'DEFAULT'; - - # unblock all delayed signals (and possibly handle them) - POSIX::sigprocmask(SIG_UNBLOCK, $sigset) - or error "Can't unblock signals: $!"; - - close $rfh; - close $parentsock; - open(STDOUT, '>&', STDERR) or error "cannot open STDOUT: $!"; - - setup($options); - - print $childsock (pack('n', 0) . 'adios'); - $childsock->flush(); - - close $childsock; - - close $nblkreader; - if (!$options->{dryrun} && $options->{format} eq 'ext2') { - my $numblocks = approx_disk_usage($options->{root}); - print $nblkwriter $numblocks; - $nblkwriter->flush(); - } - close $nblkwriter; - - if ($options->{dryrun}) { - info "simulate creating tarball..."; - } elsif (any { $_ eq $options->{format} } - ('tar', 'squashfs', 'ext2')) { - info "creating tarball..."; - - # redirect tar output to the writing end of the pipe so that - # the parent process can capture the output - open(STDOUT, '>&', $wfh) or error "cannot open STDOUT: $!"; - - # Add ./dev as the first entries of the tar file. - # We cannot add them after calling tar, because there is no way - # to prevent tar from writing NULL entries at the end. - if (any { $_ eq 'output/dev' } @{ $options->{skip} }) { - info "skipping output/dev as requested"; - } else { - print $devtar; - } - - if ($options->{mode} eq 'fakechroot') { - # By default, FAKECHROOT_EXCLUDE_PATH includes /proc and - # /sys which means that the resulting tarball will contain - # the permission and ownership information of /proc and - # /sys from the outside, which we want to avoid. - ## no critic (Variables::RequireLocalizedPunctuationVars) - $ENV{FAKECHROOT_EXCLUDE_PATH} = "/dev"; - # Fakechroot requires tar to run inside the chroot or - # otherwise absolute symlinks will include the path to the - # root directory - 0 == system('chroot', $options->{root}, 'tar', - @taropts, '-C', '/', '.') - or error "tar failed: $?"; - } elsif (any { $_ eq $options->{mode} } ('root', 'chrootless')) - { - # If the chroot directory is not owned by the root user, - # then we assume that no measure was taken to fake root - # permissions. Since the final tarball should contain - # entries with root ownership, we instruct tar to do so. - my @owneropts = (); - if ((stat $options->{root})[4] != 0) { - push @owneropts, '--owner=0', '--group=0', - '--numeric-owner'; - } - 0 == system('tar', @taropts, @owneropts, '-C', - $options->{root}, '.') - or error "tar failed: $?"; - } else { - error "unknown mode: $options->{mode}"; - } - - info "done"; - } elsif (any { $_ eq $options->{format} } ('directory', 'null')) { - # nothing to do - } else { - error "unknown format: $options->{format}"; - } - - exit 0; + $worker->(); } } else { error "unknown mode: $options->{mode}"; @@ -6190,7 +6197,8 @@ sub main() { my $numblocks = 0; close $nblkwriter; - if (!$options->{dryrun} && $options->{format} eq 'ext2') { + if (!$options->{dryrun} && any { $_ eq $options->{format} } + ('ext2', 'ext4')) { $numblocks = <$nblkreader>; if (defined $numblocks) { chomp $numblocks; @@ -6209,9 +6217,11 @@ sub main() { # nothing to do } elsif (any { $_ eq $options->{format} } ('directory', 'null')) { # nothing to do - } elsif ($options->{format} eq 'ext2' && $numblocks <= 0) { + } elsif ((any { $_ eq $options->{format} } ('ext2', 'ext4')) + && $numblocks <= 0) { # nothing to do because of invalid $numblocks - } elsif (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2')) { + } elsif (any { $_ eq $options->{format} } + ('tar', 'squashfs', 'ext2', 'ext4')) { # we use eval() so that error() doesn't take this process down and # thus leaves the setup() process without a parent eval { @@ -6220,16 +6230,16 @@ sub main() { error "cannot copy to standard output: $!"; } } else { - if ( $options->{format} eq 'squashfs' - or $options->{format} eq 'ext2' - or defined $tar_compressor) { + if (any { $_ eq $options->{format} } + ('squashfs', 'ext2', 'ext4') + or defined $tar_compressor) { my @argv = (); if ($options->{format} eq 'squashfs') { push @argv, 'tar2sqfs', '--quiet', '--no-skip', '--force', '--exportable', '--compressor', 'xz', - '--block-size', '1048576', + '--block-size', $blocksize, $options->{target}; } elsif ($options->{format} eq 'ext2') { if ($numblocks <= 0) { @@ -6237,6 +6247,26 @@ sub main() { } push @argv, 'genext2fs', '-B', 1024, '-b', $numblocks, '-i', '16384', '-a', '-', $options->{target}; + } elsif ($options->{format} eq 'ext4') { + if ($numblocks <= 0) { + error "invalid number of blocks: $numblocks"; + } + push @argv, 'mke2fs', '-q', '-F', '-o', 'Linux', '-T', + 'ext4'; + if (exists $ENV{SOURCE_DATE_EPOCH}) { + # if SOURCE_DATE_EPOCH was set, make the image + # reproducible by setting a fixed uuid and + # hash_seed + my $uuid = create_v5_uuid( + create_v5_uuid( + $UUID_NS_DNS, "mister-muffin.de" + ), + $mtime + ); + push @argv, '-U', $uuid, '-E', "hash_seed=$uuid"; + } + push @argv, '-b', $blocksize, '-d', '-', + $options->{target}, $numblocks; } elsif ($options->{format} eq 'tar') { push @argv, @{$tar_compressor}; } else { @@ -6258,8 +6288,8 @@ sub main() { or error "Can't unblock signals: $!"; # redirect stdout to file or /dev/null - if ( $options->{format} eq 'squashfs' - or $options->{format} eq 'ext2') { + if (any { $_ eq $options->{format} } + ('squashfs', 'ext2', 'ext4')) { open(STDOUT, '>', '/dev/null') or error "cannot open /dev/null for writing: $!"; } elsif ($options->{format} eq 'tar') { @@ -6340,7 +6370,7 @@ sub main() { if (any { $_ eq $options->{format} } ('directory')) { # nothing to do } elsif (any { $_ eq $options->{format} } - ('tar', 'squashfs', 'ext2', 'null')) { + ('tar', 'squashfs', 'ext2', 'ext4', 'null')) { if (!-e $options->{root}) { error "$options->{root} does not exist"; } @@ -6439,12 +6469,12 @@ can be disabled by choosing the empty string for I<SUITE>. See the section B<VARIANTS> for more information. The I<TARGET> option may either be the path to a directory, the path to a -tarball filename, the path to a squashfs image, the path to an ext2 image, a -FIFO, a character special device, or C<->. The I<TARGET> option is optional if -no I<MIRROR> option is provided. If I<TARGET> is missing or if I<TARGET> is -C<->, an uncompressed tarball will be sent to standard output. Without the -B<--format> option, I<TARGET> will be used to choose the format. See the -section B<FORMATS> for more information. +tarball filename, the path to a squashfs image, the path to an ext2 or ext4 +image, a FIFO, a character special device, or C<->. The I<TARGET> option is +optional if no I<MIRROR> option is provided. If I<TARGET> is missing or if +I<TARGET> is C<->, an uncompressed tarball will be sent to standard output. +Without the B<--format> option, I<TARGET> will be used to choose the format. +See the section B<FORMATS> for more information. The I<MIRROR> option may either be provided as a URI, in apt one-line format, as a path to a file in apt's one-line or deb822-format, or C<->. If no @@ -6521,8 +6551,8 @@ information. =item B<--format>=I<name> Choose the output format. Valid format I<name>s are B<auto>, B<directory>, -B<tar>, B<squashfs>, B<ext2> and B<null>. The default format is B<auto>. See -the section B<FORMATS> for more information. +B<tar>, B<squashfs>, B<ext2>, B<ext4> and B<null>. The default format is +B<auto>. See the section B<FORMATS> for more information. =item B<--aptopt>=I<option>|I<file> @@ -6935,7 +6965,24 @@ Or without LXC: $ mmdebstrap --unshare-helper /usr/sbin/chroot ./debian-rootfs /bin/bash -Or, if you don't mind using superuser privileges and have systemd-nspawn +Or without mmdebstrap: + + $ unshare --map-auto --map-user=65536 --map-group=65536 --keep-caps -- \ + > /usr/sbin/chroot ./debian-rootfs /bin/bash + +The above uses C<--map-auto> to map the block of user/group ids for the +effective user/group to a block starting at user/group ID 0. We also want to +map the current effective user/group ID into the subuid/subgid range using +C<--map-user> and C<--map-group>, respectively. But if that uid/gid overlaps +with the respective range, a "hole" will be removed from the mapping and the +remaining uid/gid values will get shifted. Thus, we map the current effective +user/group ID to the highest possible uid/gid, putting them at the end. Since +that means that the user/group will be "nobody" and not "root" inside the +namespace, C<--keep-caps> propagate permitted capabilities into the ambient set +and thus give the user C<CAP_DAC_OVERRIDE> and other capabilities that it +would've had. + +Lastly, if you don't mind using superuser privileges and have systemd-nspawn available and you know your subuid/subgid offset (100000 in this example): $ sudo systemd-nspawn --private-users=100000 \ @@ -6946,6 +6993,11 @@ Instead, use something like this: $ unshare --map-root-user --map-auto rm -rf ./debian-rootfs +The above L<unshare(1)> command will map user and group ids into different +ranges compared to the mapping used by B<mmdebstrap> (effectively shifting them +one up) but it will provide the required capabilities for the removal +operation. + If this mode is used as the root user, the user namespace is not unshared (but the mount namespace and other still are) and created directories will have correct ownership information. This is also useful in cases where the root user @@ -7079,6 +7131,7 @@ I<TARGET> equals C<->, or if I<TARGET> is a named pipe (fifo) or if I<TARGET> is a character special file, then the B<tar> format will be chosen. If I<TARGET> ends with C<.squashfs> or C<.sqfs>, then the B<squashfs> format will be chosen. If I<TARGET> ends with C<.ext2> then the B<ext2> format will be +chosen. If I<TARGET> ends with C<.ext4> then the B<ext4> format will be chosen. If none of these conditions apply, the B<directory> format will be chosen. @@ -7128,8 +7181,24 @@ with this format because C<genext2fs> can only write to a regular file. If you need your ext2 image be named C<->, then just explicitly pass the relative path to it like F<./->. To convert the result to an ext3 image, use C<tune2fs -O has_journal TARGET> and to convert it to ext4, use C<tune2fs -O -extents,uninit_bg,dir_index,has_journal TARGET>. Since C<genext2fs> does not -support extended attributes, the resulting image will not contain them. +extents,uninit_bg,dir_index,has_journal TARGET>. + +B<CAUTION>: the ext2 format does not support timestamps beyond 2038 January 19, +does not support sub-second precision timestamps and does not support extended +attributes. Its inode size of 128 prevents adding these features with tune2fs +later on. + +=item B<ext4> + +A temporary chroot directory will be created in C<$TMPDIR> or F</tmp> if +C<$TMPDIR> is not set. A tarball of that directory will be piped to the +C<mke2fs> utility, which will create an ext4 image that will be approximately +90% full in I<TARGET>. The special I<TARGET> C<-> does not work with this +format because C<mke2fs> can only write to a regular file. If you need your +ext4 image be named C<->, then just explicitly pass the relative path to it +like F<./->. If C<SOURCE_DATE_EPOCH> is set, the filesystem UUID and hash_seed +will be set to a UUID derived from SOURCE_DATE_EPOCH to create reproducible +images. =item B<null> @@ -7226,7 +7295,12 @@ B<--skip=tar-in/mknod>. =item B<tar-out> I<pathinside> I<outside.tar> Packs the path I<pathinside> from inside the chroot into a tarball, placing it -into a certain location I<outside.tar> outside the chroot. +into a certain location I<outside.tar> outside the chroot. To emulate behaviour +of C<cp> and to provide control over the path which gets put into the tarball, +a C<chdir()> is performed to the C<dirname()> of I<pathinside> and then the +C<basename()> of I<pathinside> is packaged as a tarball. For example, if +I<pathinside> is C</boot/.> then first a C<chdir()> into C</boot> will be +performed before packing up the contents of C<.> inside C</boot>. =item B<download> I<fileinside> I<fileoutside> @@ -7438,8 +7512,8 @@ Performs cleanup tasks, unless B<--skip=cleanup> is used: =item B<output> For formats other than B<directory>, pack up the temporary chroot directory -into a tarball, ext2 image or squashfs image and delete the temporary chroot -directory. +into a tarball, ext2 image, ext4 image or squashfs image and delete the +temporary chroot directory. If B<--skip=output/dev> is added, the resulting chroot will not contain the device nodes, directories and symlinks that B<debootstrap> creates but just @@ -7525,7 +7599,7 @@ translated manual packages (but not the untranslated ones), and documentation Create a bootable USB Stick that boots into a full Debian desktop: $ mmdebstrap --aptopt='Apt::Install-Recommends "true"' --customize-hook \ - 'chroot "$1" adduser --gecos user --disabled-password user' \ + 'chroot "$1" adduser --comment user --disabled-password user' \ --customize-hook='echo 'user:live' | chroot "$1" chpasswd' \ --customize-hook='echo host > "$1/etc/hostname"' \ --customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \ @@ -7774,7 +7848,7 @@ Limitations in comparison to debootstrap: =item * Some debootstrap options don't exist, namely: I<--second-stage>, I<--exclude>, I<--resolve-deps>, I<--force-check-gpg>, -I<--merged-usr> and I<--no-merged-usr> +I<--merged-usr>, I<--no-merged-usr> and I<--cache-dir>. =back diff --git a/mmdebstrap-autopkgtest-build-qemu b/mmdebstrap-autopkgtest-build-qemu index 19175e5..2d1b803 100755 --- a/mmdebstrap-autopkgtest-build-qemu +++ b/mmdebstrap-autopkgtest-build-qemu @@ -261,9 +261,21 @@ case "$ARCHITECTURE" in ;; esac +test_installed() { + pkg="$1" + if [ "$(dpkg-query -f '${db:Status-Status}' -W "$pkg")" != installed ]; then + die "please install $pkg" + fi +} + +for pkg in autopkgtest dosfstools e2fsprogs fdisk mount mtools passwd uidmap; do + test_installed "$pkg" +done + if test "$(dpkg-query -f '${db:Status-Status}' -W binutils-multiarch)" = installed; then GNU_PREFIX= else + test_installed dpkg-dev GNU_ARCHITECTURE="$(dpkg-architecture "-a$ARCHITECTURE" -qDEB_HOST_GNU_TYPE)" GNU_PREFIX="$GNU_ARCHITECTURE-" GNU_SUFFIX="-$(echo "$GNU_ARCHITECTURE" | tr _ -)" @@ -277,11 +289,7 @@ case $arches in *) die "enable $ARCHITECTURE by running: sudo dpkg --add-architecture $ARCHITECTURE && sudo apt update" ;; esac -for pkg in autopkgtest dosfstools e2fsprogs fdisk mount mtools passwd "systemd-boot-efi:$ARCHITECTURE" uidmap; do - if [ "$(dpkg-query -f '${db:Status-Status}' -W "$pkg")" != installed ]; then - die "please install $pkg" - fi -done +test_installed "systemd-boot-efi:$ARCHITECTURE" BOOTSTUB="/usr/lib/systemd/boot/efi/linux${EFIIMG#boot}.stub" @@ -308,12 +316,15 @@ FAT_SIZE_SECTORS=$((1024*254)) # - users who prefer qcow2 get to choose to run it themselves with their own # custom options like compression # +# --map-users=auto --map-user=0 => 0:$UID:1 + 1:$SUBUIDBASE:65535 +# --map-users=auto --map-user=65536 => 0:$SUBUIDBASE:65536 + 65536:$UID:1 +# # Make the image writeable to the first subgid. mmdebstrap will map this gid to # the root group. unshare instead will map the current gid to 0 and the first # subgid to 1. Therefore mmdebstrap will be able to write to the image. rm -f "$IMAGE" : >"$IMAGE" -unshare -U -r --map-groups=auto chown 0:1 "$IMAGE" +unshare --map-user=0 --map-group=0 --map-groups=auto chown 0:1 "$IMAGE" chmod 0660 "$IMAGE" # Make sure that the unshared user is able to access the file. @@ -350,13 +361,17 @@ fi EXT4_OFFSET_BYTES=$(( (FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS) * 512)) EXT4_OPTIONS="offset=$EXT4_OFFSET_BYTES,assume_storage_prezeroed=1" + +# the --no-mtab option to mount is a workaround for https://github.com/util-linux/util-linux/issues/2981 +# revert 8c0ddc32660ca4e98c988966251f9c05d6bcccef once it is no longer needed set -- "$@" \ "--customize-hook=download vmlinuz '$WORKDIR/kernel'" \ "--customize-hook=download initrd.img '$WORKDIR/initrd'" \ - '--customize-hook=mount --bind "$1" "$1/mnt"' \ - '--customize-hook=mount --bind "$1/mnt/mnt" "$1/mnt/dev"' \ + '--customize-hook=mount --no-mtab --bind "$1" "$1/mnt"' \ + '--customize-hook=mount --no-mtab --bind "$1/mnt/mnt" "$1/mnt/dev"' \ '--customize-hook=/sbin/mkfs.ext4 -d "$1/mnt" -L autopkgtestvm -E '"'$EXT4_OPTIONS' '$IMAGE' '$SIZE'" \ - '--customize-hook=umount --lazy "$1/mnt"' \ + '--customize-hook=umount --lazy --no-mtab "$1/mnt/dev"' \ + '--customize-hook=umount --lazy --no-mtab "$1/mnt"' \ "$RELEASE" \ /dev/null diff --git a/tests/include-foreign-libmagic-mgc b/tests/include-foreign-libmagic-mgc index 127a84e..7845b31 100644 --- a/tests/include-foreign-libmagic-mgc +++ b/tests/include-foreign-libmagic-mgc @@ -30,7 +30,7 @@ export LC_ALL=C.UTF-8 { 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 /tmp/debian-chroot/usr/share/doc/libmagic-mgc/"changelog.Debian.$foreign_arch.gz" +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 diff --git a/tests/include-foreign-libmagic-mgc-with-multiple-arch-options b/tests/include-foreign-libmagic-mgc-with-multiple-arch-options index 3108134..8155727 100644 --- a/tests/include-foreign-libmagic-mgc-with-multiple-arch-options +++ b/tests/include-foreign-libmagic-mgc-with-multiple-arch-options @@ -31,7 +31,7 @@ export LC_ALL=C.UTF-8 { 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 /tmp/debian-chroot/usr/share/doc/libmagic-mgc/"changelog.Debian.$foreign_arch.gz" +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 diff --git a/tests/install-libmagic-mgc-on-foreign b/tests/install-libmagic-mgc-on-foreign index 918224b..3254ff6 100644 --- a/tests/install-libmagic-mgc-on-foreign +++ b/tests/install-libmagic-mgc-on-foreign @@ -37,7 +37,7 @@ rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/README.Debian 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/doc/libmagic-mgc/"changelog.Debian.$foreign_arch.gz" +rm -f /tmp/debian-chroot/usr/share/doc/libmagic-mgc/"changelog.Debian.$foreign_arch.gz" rm /tmp/debian-chroot/usr/share/file/magic.mgc rm /tmp/debian-chroot/usr/share/misc/magic.mgc # delete real files diff --git a/tests/missing-dev-sys-proc-inside-the-chroot b/tests/missing-dev-sys-proc-inside-the-chroot index d127911..9c9b1c0 100644 --- a/tests/missing-dev-sys-proc-inside-the-chroot +++ b/tests/missing-dev-sys-proc-inside-the-chroot @@ -17,4 +17,8 @@ if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto prefix="runuser -u ${SUDO_USER:-user} --" fi -$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} --include=dpkg,dash,diffutils,coreutils,libc-bin,sed {{ DIST }} /dev/null {{ MIRROR }} +# creating /sbin manually because of #1071078 +$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} \ + --setup-hook='mkdir "$1/sbin"' \ + --include=dpkg,dash,diffutils,coreutils,libc-bin,sed \ + {{ DIST }} /dev/null {{ MIRROR }} diff --git a/tests/mmdebstrap b/tests/mmdebstrap index 3327fc6..f383ec4 100644 --- a/tests/mmdebstrap +++ b/tests/mmdebstrap @@ -5,15 +5,26 @@ export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }} [ "$(id -u)" -eq 0 ] [ {{ MODE }} = "root" ] -case {{ FORMAT }} in tar|squashfs|ext2) : ;; *) exit 1;; esac +case {{ FORMAT }} in tar|squashfs|ext2|ext4) : ;; *) exit 1;; esac -{{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} {{ DIST }} ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} {{ MIRROR }} +{{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} {{ DIST }} /tmp/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} {{ MIRROR }} +# creating an ext4 image on a 9p filesystem produces different results compared +# to creating it on a tmpfs or ext4 fs because 9p does not support discards and +# even when running with -E nodiscard, the number of written bytes will differ +# https://lore.kernel.org/linux-ext4/171484520952.2626447.2160419274451668597@localhost/T/#t +mv /tmp/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} if [ "{{ FORMAT }}" = tar ]; then printf 'ustar ' | cmp --bytes=6 --ignore-initial=257:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar - elif [ "{{ FORMAT }}" = squashfs ]; then printf 'hsqs' | cmp --bytes=4 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.squashfs - elif [ "{{ FORMAT }}" = ext2 ]; then printf '\123\357' | cmp --bytes=2 --ignore-initial=1080:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.ext2 - + printf '\000\000\000\000\000\000\000\000\000\000\000\000' | cmp --bytes=12 --ignore-initial=1116:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.ext2 - +elif [ "{{ FORMAT }}" = ext4 ]; then + printf '\123\357' | cmp --bytes=2 --ignore-initial=1080:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.ext4 - + printf '\074\020\000\000\302\042\000\000\153\004\000\000' | cmp --bytes=12 --ignore-initial=1116:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.ext4 - + + [ "$(/sbin/blkid --match-tag UUID --output value ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.ext4)" = "$(uuidgen --sha1 --namespace="$(uuidgen --sha1 --namespace='@dns' --name mister-muffin.de)" --name $SOURCE_DATE_EPOCH)" ] else echo "unknown format: {{ FORMAT }}" >&2 exit 1 diff --git a/tests/skip-tar-in-mknod b/tests/skip-tar-in-mknod index eb3027a..cc18977 100644 --- a/tests/skip-tar-in-mknod +++ b/tests/skip-tar-in-mknod @@ -3,7 +3,7 @@ set -eu export LC_ALL=C.UTF-8 export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }} -[ {{ MODE }} = "unshare" ] +#[ {{ MODE }} = "unshare" ] trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM @@ -22,7 +22,8 @@ fi $prefix {{ CMD }} --mode={{ MODE }} --variant=custom \ --skip=update,setup,cleanup,tar-in/mknod \ --setup-hook='tar-in ./cache/mmdebstrap-{{ DIST }}-apt.tar /' \ - '' /tmp/debian-chroot.tar + --setup-hook='/sbin/auditctl -w "$1" -p wxa -k mykey' \ + '' /tmp/debian-chroot.tar || /sbin/ausearch --format text -k mykey cmp ./cache/mmdebstrap-{{ DIST }}-apt.tar /tmp/debian-chroot.tar \ || diffoscope ./cache/mmdebstrap-{{ DIST }}-apt.tar /tmp/debian-chroot.tar |