Compare commits

..

6 commits

8 changed files with 183 additions and 50 deletions

View file

@ -1,3 +1,8 @@
1.5.0 (2024-05-14)
------------------
- add --format=ext4
1.4.3 (2024-02-01) 1.4.3 (2024-02-01)
------------------ ------------------

View file

@ -34,7 +34,7 @@ all_variants = [
"standard", "standard",
] ]
default_format = "auto" 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") mirror = os.getenv("mirror", "http://127.0.0.1/debian")
hostarch = subprocess.check_output(["dpkg", "--print-architecture"]).decode().strip() hostarch = subprocess.check_output(["dpkg", "--print-architecture"]).decode().strip()

View file

@ -59,7 +59,7 @@ Needs-QEMU: true
Test: mmdebstrap Test: mmdebstrap
Needs-Root: true Needs-Root: true
Modes: root Modes: root
Formats: tar squashfs ext2 Formats: tar squashfs ext2 ext4
Variants: essential apt minbase buildd - standard Variants: essential apt minbase buildd - standard
Skip-If: Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558 variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
@ -68,7 +68,7 @@ Skip-If:
Test: check-for-bit-by-bit-identical-format-output Test: check-for-bit-by-bit-identical-format-output
Modes: unshare fakechroot Modes: unshare fakechroot
Formats: tar squashfs ext2 Formats: tar squashfs ext2 ext4
Variants: essential apt minbase buildd - standard Variants: essential apt minbase buildd - standard
Skip-If: Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558 variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558

View file

@ -33,7 +33,7 @@ deletecache() {
done done
# deleting artifacts from test "mmdebstrap" # deleting artifacts from test "mmdebstrap"
for variant in essential apt minbase buildd - standard; do 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 if [ -e "$dir/mmdebstrap-$dist-$variant.$format" ]; then
# attempt to delete for all dists because DEFAULT_DIST might've been different the last time # attempt to delete for all dists because DEFAULT_DIST might've been different the last time
rm "$dir/mmdebstrap-$dist-$variant.$format" rm "$dir/mmdebstrap-$dist-$variant.$format"
@ -453,7 +453,7 @@ if [ "$HAVE_QEMU" = "yes" ]; then
tmpdir="$(mktemp -d)" tmpdir="$(mktemp -d)"
trap 'kill "$PROXYPID" || :;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,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 if [ ! -e ./mmdebstrap ]; then
pkgs="$pkgs,mmdebstrap" pkgs="$pkgs,mmdebstrap"
fi fi

View file

@ -23,7 +23,7 @@
use strict; use strict;
use warnings; use warnings;
our $VERSION = '1.4.3'; our $VERSION = '1.5.0';
use English; use English;
use Getopt::Long; use Getopt::Long;
@ -46,6 +46,7 @@ use Socket;
use Time::HiRes; use Time::HiRes;
use Math::BigInt; use Math::BigInt;
use Text::ParseWords; use Text::ParseWords;
use Digest::SHA;
use version; use version;
## no critic (InputOutput::RequireBriefOpen) ## no critic (InputOutput::RequireBriefOpen)
@ -66,13 +67,16 @@ use version;
*MS_BIND = \0x1000; *MS_BIND = \0x1000;
*MS_REC = \0x4000; *MS_REC = \0x4000;
*MNT_DETACH = \2; *MNT_DETACH = \2;
# uuid_t NameSpace_DNS in rfc4122
*UUID_NS_DNS = \'6ba7b810-9dad-11d1-80b4-00c04fd430c8';
our ( our (
$CLONE_NEWNS, $CLONE_NEWUTS, $CLONE_NEWNS, $CLONE_NEWUTS,
$CLONE_NEWIPC, $CLONE_NEWUSER, $CLONE_NEWIPC, $CLONE_NEWUSER,
$CLONE_NEWPID, $CLONE_NEWNET, $CLONE_NEWPID, $CLONE_NEWNET,
$_LINUX_CAPABILITY_VERSION_3, $CAP_SYS_ADMIN, $_LINUX_CAPABILITY_VERSION_3, $CAP_SYS_ADMIN,
$PR_CAPBSET_READ, $MS_BIND, $PR_CAPBSET_READ, $MS_BIND,
$MS_REC, $MNT_DETACH $MS_REC, $MNT_DETACH,
$UUID_NS_DNS
); );
#<<< #<<<
@ -216,10 +220,11 @@ sub minor {
sub can_execute { sub can_execute {
my $tool = shift; my $tool = shift;
my $verbose = shift // '--version';
my $pid = open my $fh, '-|' // return 0; my $pid = open my $fh, '-|' // return 0;
if ($pid == 0) { if ($pid == 0) {
open(STDERR, '>&', STDOUT) or die; open(STDERR, '>&', STDOUT) or die;
exec {$tool} $tool, '--version' or die; exec {$tool} $tool, $verbose or die;
} }
chomp( chomp(
my $content = do { local $/; <$fh> } my $content = do { local $/; <$fh> }
@ -303,6 +308,28 @@ sub shellescape {
return "'$string'"; 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 { sub test_unshare_userns {
my $verbose = shift; my $verbose = shift;
@ -4381,14 +4408,15 @@ sub guess_sources_format {
sub approx_disk_usage { sub approx_disk_usage {
my $directory = shift; my $directory = shift;
my $block_size = shift;
info "approximating disk usage..."; info "approximating disk usage...";
# the "du" utility reports different results depending on the underlying # the "du" utility reports different results depending on the underlying
# filesystem, see https://bugs.debian.org/650077 for a discussion # filesystem, see https://bugs.debian.org/650077 for a discussion
# #
# we use code similar to the one used by dpkg-gencontrol instead # we use code similar to the one used by dpkg-gencontrol instead
# #
# Regular files are measured in number of 1024 byte blocks. All other # Regular files are measured in number of $block_size byte blocks. All
# entries are assumed to take one block of space. # other entries are assumed to take one block of space.
# #
# We ignore /dev because depending on the mode, the directory might be # We ignore /dev because depending on the mode, the directory might be
# populated or not and we want consistent disk usage results independent # 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"}; return if exists $hardlink{"$dev:$ino"};
# Track hardlinks to avoid repeated additions. # Track hardlinks to avoid repeated additions.
$hardlink{"$dev:$ino"} = 1 if $nlink > 1; $hardlink{"$dev:$ino"} = 1 if $nlink > 1;
# add file size in 1024 byte blocks, rounded up # add file size in $block_size byte blocks, rounded up
$installed_size += int(((-s _) + 1024) / 1024); $installed_size += int(((-s _) + $block_size) / $block_size);
} else { } else {
# all other entries are assumed to only take up one block # all other entries are assumed to only take up one block
$installed_size += 1; $installed_size += 1;
@ -4805,7 +4833,7 @@ sub main() {
$options->{format} = 'directory'; $options->{format} = 'directory';
} }
my @valid_formats my @valid_formats
= ('auto', 'directory', 'tar', 'squashfs', 'ext2', 'null'); = ('auto', 'directory', 'tar', 'squashfs', 'ext2', 'ext4', 'null');
if (none { $_ eq $options->{format} } @valid_formats) { if (none { $_ eq $options->{format} } @valid_formats) {
error "invalid format. Choose from " . (join ', ', @valid_formats); error "invalid format. Choose from " . (join ', ', @valid_formats);
} }
@ -5670,6 +5698,30 @@ sub main() {
if ($exitstatus != 0) { if ($exitstatus != 0) {
error "genext2fs failed with exit status: $exitstatus"; 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 { } else {
$options->{format} = 'directory'; $options->{format} = 'directory';
} }
@ -5687,21 +5739,30 @@ sub main() {
info "ignoring target $options->{target} with null format"; info "ignoring target $options->{target} with null format";
} }
my $blocksize = -1;
if ($options->{format} eq 'ext2') { if ($options->{format} eq 'ext2') {
if (!can_execute 'genext2fs') { if (!can_execute 'genext2fs') {
error "need genext2fs for ext2 format"; 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') { } elsif ($options->{format} eq 'squashfs') {
if (!can_execute 'tar2sqfs') { if (!can_execute 'tar2sqfs') {
error "need tar2sqfs binary from the squashfs-tools-ng package"; 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 ($options->{format} ne 'null') {
if (any { $_ eq $options->{variant} } ('extract', 'custom') if (any { $_ eq $options->{variant} } ('extract', 'custom')
and $options->{mode} eq 'fakechroot') { 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" . " fakechroot mode might fail in extract and"
. " custom variants because there might be no tar inside the" . " custom variants because there might be no tar inside the"
. " chroot"; . " chroot";
@ -5885,7 +5946,7 @@ sub main() {
# If both the above assertion change, we can stop creating /dev entries as # If both the above assertion change, we can stop creating /dev entries as
# well. # well.
my $devtar = ''; my $devtar = '';
if (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2')) { if (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2', 'ext4')) {
foreach my $file (@devfiles) { foreach my $file (@devfiles) {
my ($fname, $mode, $type, $linkname, $devmajor, $devminor) my ($fname, $mode, $type, $linkname, $devmajor, $devminor)
= @{$file}; = @{$file};
@ -5954,6 +6015,9 @@ sub main() {
push @taropts, '--xattrs', '--xattrs-exclude=system.*'; push @taropts, '--xattrs', '--xattrs-exclude=system.*';
} elsif ($options->{format} eq "ext2") { } elsif ($options->{format} eq "ext2") {
warning "genext2fs does not support extended attributes"; 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 { } else {
push @taropts, '--xattrs'; push @taropts, '--xattrs';
} }
@ -6001,8 +6065,9 @@ sub main() {
close $childsock; close $childsock;
close $nblkreader; close $nblkreader;
if (!$options->{dryrun} && $options->{format} eq 'ext2') { if (!$options->{dryrun} && any { $_ eq $options->{format} }
my $numblocks = approx_disk_usage($options->{root}); ('ext2', 'ext4')) {
my $numblocks = approx_disk_usage($options->{root}, $blocksize);
print $nblkwriter "$numblocks\n"; print $nblkwriter "$numblocks\n";
$nblkwriter->flush(); $nblkwriter->flush();
} }
@ -6010,8 +6075,8 @@ sub main() {
if ($options->{dryrun}) { if ($options->{dryrun}) {
info "simulate creating tarball..."; info "simulate creating tarball...";
} elsif (any { $_ eq $options->{format} } ('tar', 'squashfs', 'ext2')) } elsif (any { $_ eq $options->{format} }
{ ('tar', 'squashfs', 'ext2', 'ext4')) {
info "creating tarball..."; info "creating tarball...";
# redirect tar output to the writing end of the pipe so # redirect tar output to the writing end of the pipe so
@ -6132,7 +6197,8 @@ sub main() {
my $numblocks = 0; my $numblocks = 0;
close $nblkwriter; close $nblkwriter;
if (!$options->{dryrun} && $options->{format} eq 'ext2') { if (!$options->{dryrun} && any { $_ eq $options->{format} }
('ext2', 'ext4')) {
$numblocks = <$nblkreader>; $numblocks = <$nblkreader>;
if (defined $numblocks) { if (defined $numblocks) {
chomp $numblocks; chomp $numblocks;
@ -6151,9 +6217,11 @@ sub main() {
# nothing to do # nothing to do
} elsif (any { $_ eq $options->{format} } ('directory', 'null')) { } elsif (any { $_ eq $options->{format} } ('directory', 'null')) {
# nothing to do # 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 # 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 # we use eval() so that error() doesn't take this process down and
# thus leaves the setup() process without a parent # thus leaves the setup() process without a parent
eval { eval {
@ -6162,8 +6230,8 @@ sub main() {
error "cannot copy to standard output: $!"; error "cannot copy to standard output: $!";
} }
} else { } else {
if ( $options->{format} eq 'squashfs' if (any { $_ eq $options->{format} }
or $options->{format} eq 'ext2' ('squashfs', 'ext2', 'ext4')
or defined $tar_compressor) { or defined $tar_compressor) {
my @argv = (); my @argv = ();
if ($options->{format} eq 'squashfs') { if ($options->{format} eq 'squashfs') {
@ -6171,7 +6239,7 @@ sub main() {
'--quiet', '--no-skip', '--force', '--quiet', '--no-skip', '--force',
'--exportable', '--exportable',
'--compressor', 'xz', '--compressor', 'xz',
'--block-size', '1048576', '--block-size', $blocksize,
$options->{target}; $options->{target};
} elsif ($options->{format} eq 'ext2') { } elsif ($options->{format} eq 'ext2') {
if ($numblocks <= 0) { if ($numblocks <= 0) {
@ -6179,6 +6247,26 @@ sub main() {
} }
push @argv, 'genext2fs', '-B', 1024, '-b', $numblocks, push @argv, 'genext2fs', '-B', 1024, '-b', $numblocks,
'-i', '16384', '-a', '-', $options->{target}; '-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') { } elsif ($options->{format} eq 'tar') {
push @argv, @{$tar_compressor}; push @argv, @{$tar_compressor};
} else { } else {
@ -6200,8 +6288,8 @@ sub main() {
or error "Can't unblock signals: $!"; or error "Can't unblock signals: $!";
# redirect stdout to file or /dev/null # redirect stdout to file or /dev/null
if ( $options->{format} eq 'squashfs' if (any { $_ eq $options->{format} }
or $options->{format} eq 'ext2') { ('squashfs', 'ext2', 'ext4')) {
open(STDOUT, '>', '/dev/null') open(STDOUT, '>', '/dev/null')
or error "cannot open /dev/null for writing: $!"; or error "cannot open /dev/null for writing: $!";
} elsif ($options->{format} eq 'tar') { } elsif ($options->{format} eq 'tar') {
@ -6282,7 +6370,7 @@ sub main() {
if (any { $_ eq $options->{format} } ('directory')) { if (any { $_ eq $options->{format} } ('directory')) {
# nothing to do # nothing to do
} elsif (any { $_ eq $options->{format} } } elsif (any { $_ eq $options->{format} }
('tar', 'squashfs', 'ext2', 'null')) { ('tar', 'squashfs', 'ext2', 'ext4', 'null')) {
if (!-e $options->{root}) { if (!-e $options->{root}) {
error "$options->{root} does not exist"; error "$options->{root} does not exist";
} }
@ -6381,12 +6469,12 @@ can be disabled by choosing the empty string for I<SUITE>. See the section
B<VARIANTS> for more information. B<VARIANTS> for more information.
The I<TARGET> option may either be the path to a directory, the path to a 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 tarball filename, the path to a squashfs image, the path to an ext2 or ext4
FIFO, a character special device, or C<->. The I<TARGET> option is optional if image, a FIFO, a character special device, or C<->. The I<TARGET> option is
no I<MIRROR> option is provided. If I<TARGET> is missing or if I<TARGET> is optional if no I<MIRROR> option is provided. If I<TARGET> is missing or if
C<->, an uncompressed tarball will be sent to standard output. Without the I<TARGET> is C<->, an uncompressed tarball will be sent to standard output.
B<--format> option, I<TARGET> will be used to choose the format. See the Without the B<--format> option, I<TARGET> will be used to choose the format.
section B<FORMATS> for more information. See the section B<FORMATS> for more information.
The I<MIRROR> option may either be provided as a URI, in apt one-line format, 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 as a path to a file in apt's one-line or deb822-format, or C<->. If no
@ -6463,8 +6551,8 @@ information.
=item B<--format>=I<name> =item B<--format>=I<name>
Choose the output format. Valid format I<name>s are B<auto>, B<directory>, 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 B<tar>, B<squashfs>, B<ext2>, B<ext4> and B<null>. The default format is
the section B<FORMATS> for more information. B<auto>. See the section B<FORMATS> for more information.
=item B<--aptopt>=I<option>|I<file> =item B<--aptopt>=I<option>|I<file>
@ -7043,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 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 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 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. If none of these conditions apply, the B<directory> format will be
chosen. chosen.
@ -7092,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 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 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 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 extents,uninit_bg,dir_index,has_journal TARGET>.
support extended attributes, the resulting image will not contain them.
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> =item B<null>
@ -7190,7 +7295,12 @@ B<--skip=tar-in/mknod>.
=item B<tar-out> I<pathinside> I<outside.tar> =item B<tar-out> I<pathinside> I<outside.tar>
Packs the path I<pathinside> from inside the chroot into a tarball, placing it 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> =item B<download> I<fileinside> I<fileoutside>
@ -7402,8 +7512,8 @@ Performs cleanup tasks, unless B<--skip=cleanup> is used:
=item B<output> =item B<output>
For formats other than B<directory>, pack up the temporary chroot directory 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 into a tarball, ext2 image, ext4 image or squashfs image and delete the
directory. temporary chroot directory.
If B<--skip=output/dev> is added, the resulting chroot will not contain the 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 device nodes, directories and symlinks that B<debootstrap> creates but just
@ -7738,7 +7848,7 @@ Limitations in comparison to debootstrap:
=item * Some debootstrap options don't exist, namely: =item * Some debootstrap options don't exist, namely:
I<--second-stage>, I<--exclude>, I<--resolve-deps>, I<--force-check-gpg>, 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 =back

View file

@ -361,6 +361,9 @@ fi
EXT4_OFFSET_BYTES=$(( (FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS) * 512)) EXT4_OFFSET_BYTES=$(( (FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS) * 512))
EXT4_OPTIONS="offset=$EXT4_OFFSET_BYTES,assume_storage_prezeroed=1" 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 -- "$@" \ set -- "$@" \
"--customize-hook=download vmlinuz '$WORKDIR/kernel'" \ "--customize-hook=download vmlinuz '$WORKDIR/kernel'" \
"--customize-hook=download initrd.img '$WORKDIR/initrd'" \ "--customize-hook=download initrd.img '$WORKDIR/initrd'" \

View file

@ -17,4 +17,8 @@ if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto
prefix="runuser -u ${SUDO_USER:-user} --" prefix="runuser -u ${SUDO_USER:-user} --"
fi 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 }}

View file

@ -5,15 +5,26 @@ export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
[ "$(id -u)" -eq 0 ] [ "$(id -u)" -eq 0 ]
[ {{ MODE }} = "root" ] [ {{ 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 if [ "{{ FORMAT }}" = tar ]; then
printf 'ustar ' | cmp --bytes=6 --ignore-initial=257:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar - printf 'ustar ' | cmp --bytes=6 --ignore-initial=257:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar -
elif [ "{{ FORMAT }}" = squashfs ]; then elif [ "{{ FORMAT }}" = squashfs ]; then
printf 'hsqs' | cmp --bytes=4 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.squashfs - printf 'hsqs' | cmp --bytes=4 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.squashfs -
elif [ "{{ FORMAT }}" = ext2 ]; then elif [ "{{ FORMAT }}" = ext2 ]; then
printf '\123\357' | cmp --bytes=2 --ignore-initial=1080:0 ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.ext2 - 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 else
echo "unknown format: {{ FORMAT }}" >&2 echo "unknown format: {{ FORMAT }}" >&2
exit 1 exit 1