Compare commits

..

13 commits

Author SHA1 Message Date
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
13 changed files with 223 additions and 180 deletions

View file

@ -88,12 +88,14 @@ def main():
) )
parser.add_argument("--mode", metavar="mode", help="only run tests with this mode") parser.add_argument("--mode", metavar="mode", help="only run tests with this mode")
parser.add_argument("--dist", metavar="dist", help="only run tests with this dist") parser.add_argument("--dist", metavar="dist", help="only run tests with this dist")
parser.add_argument(
"--variant", metavar="variant", help="only run tests with this variant"
)
args = parser.parse_args() args = parser.parse_args()
# copy over files from git or as distributed # copy over files from git or as distributed
for (git, dist, target) in [ for (git, dist, target) in [
("./mmdebstrap", "/usr/bin/mmdebstrap", "mmdebstrap"), ("./mmdebstrap", "/usr/bin/mmdebstrap", "mmdebstrap"),
("./taridshift", "/usr/bin/mmtaridshift", "taridshift"),
("./tarfilter", "/usr/bin/mmtarfilter", "tarfilter"), ("./tarfilter", "/usr/bin/mmtarfilter", "tarfilter"),
( (
"./proxysolver", "./proxysolver",
@ -223,7 +225,7 @@ def main():
) )
print("time left: %s" % timeleft, file=sys.stderr) print("time left: %s" % timeleft, file=sys.stderr)
if failed: if failed:
print("failed: %d" % len(failed)) print("failed: %d" % len(failed), file=sys.stderr)
num_finished += 1 num_finished += 1
with open("tests/" + name) as fin, open("shared/test.sh", "w") as fout: with open("tests/" + name) as fin, open("shared/test.sh", "w") as fout:
for line in fin: for line in fin:
@ -257,6 +259,9 @@ def main():
if args.mode and args.mode != mode: if args.mode and args.mode != mode:
print(f"skipping because of --mode={args.mode}", file=sys.stderr) print(f"skipping because of --mode={args.mode}", file=sys.stderr)
continue continue
if args.variant and args.variant != variant:
print(f"skipping because of --variant={args.variant}", file=sys.stderr)
continue
proc = subprocess.Popen(argv) proc = subprocess.Popen(argv)
try: try:
proc.wait() proc.wait()

View file

@ -52,7 +52,7 @@ Skip-If:
fmt == "squashfs" and dist == "oldstable" # squashfs-tools-ng is not available fmt == "squashfs" and dist == "oldstable" # squashfs-tools-ng is not available
fmt == "ext2" and dist == "oldstable" # genext2fs does not support SOURCE_DATE_EPOCH fmt == "ext2" and dist == "oldstable" # genext2fs does not support SOURCE_DATE_EPOCH
Test: taridshift-utility Test: tarfilter-idshift
Needs-QEMU: true Needs-QEMU: true
Skip-If: dist == "oldstable" # python3 tarfile module does not preserve xattrs Skip-If: dist == "oldstable" # python3 tarfile module does not preserve xattrs
@ -286,11 +286,10 @@ Test: install-doc-debian
Modes: chrootless Modes: chrootless
Variants: custom Variants: custom
Test: install-known-good-from-essential-yes Test: chrootless-essential
Variants: custom Variants: custom
Modes: chrootless Modes: chrootless
Skip-If: Skip-If:
True # #1006692
dist in ["oldstable", "stable"] dist in ["oldstable", "stable"]
Test: install-doc-debian-and-output-tarball Test: install-doc-debian-and-output-tarball
@ -322,3 +321,7 @@ Modes: fakechroot
Test: dev-ptmx Test: dev-ptmx
Modes: root unshare Modes: root unshare
Needs-QEMU: true Needs-QEMU: true
Test: error-if-stdout-is-tty
Test: variant-custom-timeout

View file

@ -37,7 +37,8 @@ use Cwd qw(abs_path getcwd);
require "syscall.ph"; ## no critic (Modules::RequireBarewordIncludes) require "syscall.ph"; ## no critic (Modules::RequireBarewordIncludes)
use Fcntl qw(S_IFCHR S_IFBLK FD_CLOEXEC F_GETFD F_SETFD); use Fcntl qw(S_IFCHR S_IFBLK FD_CLOEXEC F_GETFD F_SETFD);
use List::Util qw(any none); use List::Util qw(any none);
use POSIX qw(SIGINT SIGHUP SIGPIPE SIGTERM SIG_BLOCK SIG_UNBLOCK strftime); use POSIX
qw(SIGINT SIGHUP SIGPIPE SIGTERM SIG_BLOCK SIG_UNBLOCK strftime isatty);
use Carp; use Carp;
use Term::ANSIColor; use Term::ANSIColor;
use Socket; use Socket;
@ -978,19 +979,24 @@ sub run_apt_download_progress {
my $flags = fcntl($wfh, F_GETFD, 0) or error "fcntl F_GETFD: $!"; my $flags = fcntl($wfh, F_GETFD, 0) or error "fcntl F_GETFD: $!";
fcntl($wfh, F_SETFD, $flags & ~FD_CLOEXEC) or error "fcntl F_SETFD: $!"; fcntl($wfh, F_SETFD, $flags & ~FD_CLOEXEC) or error "fcntl F_SETFD: $!";
my $fd = fileno $wfh; my $fd = fileno $wfh;
# run_apt_progress() can raise an exception which would leave this function
# without cleaning up the other thread we started, making mmdebstrap hang
# in case run_apt_progress() fails -- so wrap this in eval() instead
eval {
# 2022-05-02, #debian-apt on OFTC, times in UTC+2 # 2022-05-02, #debian-apt on OFTC, times in UTC+2
# 16:57 < josch> DonKult: how is -oDebug::pkgDpkgPm=1 -oDir::Log=/dev/null # 16:57 < josch> DonKult: how is -oDebug::pkgDpkgPm=1
# a "fancy no-op"? # -oDir::Log=/dev/null a "fancy no-op"?
# 11:52 < DonKult> josch: "fancy no-op" in sofar as it does nothing to the # 11:52 < DonKult> josch: "fancy no-op" in sofar as it does nothing to
# system even through its not in a special mode ala # the system even through its not in a special mode
# simulation or download-only. It does all the things it # ala simulation or download-only. It does all the
# normally does, except that it just prints the dpkg calls # things it normally does, except that it just prints
# instead of execv() them which in practice amounts means # the dpkg calls instead of execv() them which in
# it does nothing (the Dir::Log just prevents libapt from # practice amounts means it does nothing (the Dir::Log
# creating the /var/log/apt directories. As the code # just prevents libapt from creating the /var/log/apt
# creates them even if no logs will be placed there…). As # directories. As the code creates them even if no
# said, midterm an apt --print-install-packages or # logs will be placed there…). As said, midterm an apt
# something would be nice to avoid running everything. # --print-install-packages or something would be nice
# to avoid running everything.
run_apt_progress({ run_apt_progress({
ARGV => [ ARGV => [
'apt-get', 'apt-get',
@ -1011,13 +1017,21 @@ sub run_apt_download_progress {
@{ $options->{APT_ARGV} }, @{ $options->{APT_ARGV} },
], ],
}); });
};
my $err = '';
if ($@) {
$err = "apt download failed: $@";
}
# signal the child process that we are done # signal the child process that we are done
close $wfh; close $wfh;
# and then read from it what it got # and then read from it what it got
my @listofdebs = <$fh>; my @listofdebs = <$fh>;
close $fh; close $fh;
if ($? != 0) { if ($? != 0) {
error "status child failed"; $err = "status child failed";
}
if ($err) {
error $err;
} }
# remove trailing newlines # remove trailing newlines
chomp @listofdebs; chomp @listofdebs;
@ -4149,7 +4163,7 @@ sub main() {
# this is like: # this is like:
# lxc-usernsexec -- lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' ... # lxc-usernsexec -- lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' ...
# but without needing lxc # but without needing lxc
if ($ARGV[0] eq "--unshare-helper") { if (scalar @ARGV >= 1 && $ARGV[0] eq "--unshare-helper") {
if ($EFFECTIVE_USER_ID != 0 && !test_unshare_userns(1)) { if ($EFFECTIVE_USER_ID != 0 && !test_unshare_userns(1)) {
exit 1; exit 1;
} }
@ -4451,7 +4465,7 @@ sub main() {
); );
close $fh; close $fh;
if ( $? == 0 if ( $? == 0
and $content =~ /^apt ([0-9]+\.[0-9]+\.[0-9]+)[a-z0-9]* \([a-z0-9-]+\)$/m) { and $content =~ /^apt (\d+\.\d+\.\d+)\w* \(\S+\)$/am) {
$aptversion = version->new($1); $aptversion = version->new($1);
} }
if ($aptversion < "2.3.14") { if ($aptversion < "2.3.14") {
@ -5126,7 +5140,11 @@ sub main() {
$options->{sourceslists} = $sourceslists; $options->{sourceslists} = $sourceslists;
} }
if ($options->{target} ne '-') { if ($options->{target} eq '-') {
if (POSIX::isatty STDOUT) {
error "stdout is a an interactive tty";
}
} else {
my $abs_path = abs_path($options->{target}); my $abs_path = abs_path($options->{target});
if (!defined $abs_path) { if (!defined $abs_path) {
error "unable to get absolute path of target directory" error "unable to get absolute path of target directory"

View file

@ -43,17 +43,53 @@ class PaxFilterAction(argparse.Action):
setattr(namespace, "paxfilter", items) setattr(namespace, "paxfilter", 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. This is useful in cases
where a tool only accepts certain xattr prefixes. For example tar2sqfs only where a tool only accepts certain xattr prefixes. For example tar2sqfs only
supports SCHILY.xattr.user.*, SCHILY.xattr.trusted.* and supports SCHILY.xattr.user.*, SCHILY.xattr.trusted.* and
SCHILY.xattr.security.* but not SCHILY.xattr.system.posix_acl_default.*. SCHILY.xattr.security.* but not SCHILY.xattr.system.posix_acl_default.*.
@ -65,41 +101,65 @@ 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, transform the path of tar members using a sed expression just as with
GNU tar --transform.
Fourthly, 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(
"--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")
@ -157,6 +217,8 @@ their path are not passed through.
continue 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 +227,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()

View file

@ -16,7 +16,7 @@ echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH"
# mmdebstrap, resulting in different gid values # mmdebstrap, resulting in different gid values
{{ CMD }} --variant={{ VARIANT }} --mode={{ MODE }} \ {{ CMD }} --variant={{ VARIANT }} --mode={{ MODE }} \
--essential-hook='if [ {{ VARIANT }} = - ]; then echo _apt:*:100:65534::/nonexistent:/usr/sbin/nologin >> "$1"/etc/passwd; fi' \ --essential-hook='if [ {{ VARIANT }} = - ]; then echo _apt:*:100:65534::/nonexistent:/usr/sbin/nologin >> "$1"/etc/passwd; fi' \
--essential-hook='if [ {{ VARIANT }} = - ] && [ {{ DIST }} = unstable -o {{ DIST }} = testing ]; then printf "systemd-journal:x:101:\nsystemd-network:x:102:\nsystemd-resolve:x:103:\ncrontab:x:104:" >> "$1"/etc/group; fi' \ --essential-hook='if [ {{ VARIANT }} = - ] && [ {{ DIST }} = unstable -o {{ DIST }} = testing ]; then printf "systemd-journal:x:999:\nsystemd-network:x:998:\ncrontab:x:101:" >> "$1"/etc/group; fi' \
$(case {{ DIST }} in oldstable|stable) : ;; *) echo --hook-dir=./hooks/merged-usr ;; esac) \ $(case {{ DIST }} in oldstable|stable) : ;; *) echo --hook-dir=./hooks/merged-usr ;; esac) \
{{ DIST }} /tmp/debian-{{ DIST }}-mm.tar {{ MIRROR }} {{ DIST }} /tmp/debian-{{ DIST }}-mm.tar {{ MIRROR }}
@ -32,7 +32,7 @@ tar --xattrs --xattrs-include='*' -C /tmp/debian-{{ DIST }}-debootstrap -xf "cac
tar -C /tmp/debian-{{ DIST }}-debootstrap -cf dev1.tar ./dev tar -C /tmp/debian-{{ DIST }}-debootstrap -cf dev1.tar ./dev
tar -C /tmp/debian-{{ DIST }}-mm -cf dev2.tar ./dev tar -C /tmp/debian-{{ DIST }}-mm -cf dev2.tar ./dev
ret=0 ret=0
cmp dev1.tar dev2.tar || ret=$? cmp dev1.tar dev2.tar >&2 || ret=$?
if [ "$ret" -ne 0 ]; then if [ "$ret" -ne 0 ]; then
if type diffoscope >/dev/null; then if type diffoscope >/dev/null; then
diffoscope dev1.tar dev2.tar diffoscope dev1.tar dev2.tar
@ -144,7 +144,7 @@ for f in "/var/lib/dpkg/triggers/File" "/etc/shells"; do
continue continue
fi fi
# the file must be different # the file must be different
if cmp "$f1" "$f2"; then if cmp "$f1" "$f2" >&2; then
continue continue
fi fi
# then sort both # then sort both
@ -162,15 +162,27 @@ for f in shadow shadow-; do
done done
# same as above but for cron and systemd groups # same as above but for cron and systemd groups
for f in gshadow gshadow-; do for f in gshadow gshadow-; do
for group in systemd-journal systemd-network systemd-resolve crontab; do for group in systemd-journal systemd-network crontab; do
if grep -q '^'"$group"':!:' /tmp/debian-{{ DIST }}-debootstrap/etc/$f; then for password in "!" "!\\*"; do
sed -i 's/^'"$group"':x::/'"$group"':!::/' /tmp/debian-{{ DIST }}-mm/etc/$f if grep -q '^'"$group"':'"$password"':' /tmp/debian-{{ DIST }}-debootstrap/etc/$f; then
sed -i 's/^'"$group"':x::/'"$group"':'"$password"'::/' /tmp/debian-{{ DIST }}-mm/etc/$f
fi fi
done done
done
done
for log in faillog lastlog; do
if ! cmp /tmp/debian-{{ DIST }}-debootstrap/var/log/$log /tmp/debian-{{ DIST }}-mm/var/log/$log >&2;then
# if the files differ, make sure they are all zeroes
cmp -n $(stat -c %s /tmp/debian-{{ DIST }}-debootstrap/var/log/$log) /tmp/debian-{{ DIST }}-debootstrap/var/log/$log /dev/zero >&2
cmp -n $(stat -c %s /tmp/debian-{{ DIST }}-mm/var/log/$log) /tmp/debian-{{ DIST }}-mm/var/log/$log /dev/zero >&2
# then delete them
rm /tmp/debian-{{ DIST }}-debootstrap/var/log/$log /tmp/debian-{{ DIST }}-mm/var/log/$log
fi
done done
# workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=917773 # workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=917773
if ! cmp /tmp/debian-{{ DIST }}-debootstrap/etc/shadow /tmp/debian-{{ DIST }}-mm/etc/shadow; then if ! cmp /tmp/debian-{{ DIST }}-debootstrap/etc/shadow /tmp/debian-{{ DIST }}-mm/etc/shadow >&2; then
echo patching /etc/shadow on {{ DIST }} {{ VARIANT }} >&2 echo patching /etc/shadow on {{ DIST }} {{ VARIANT }} >&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-{{ DIST }}-mm/etc/shadow > /tmp/debian-{{ DIST }}-mm/etc/shadow.bak 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-{{ DIST }}-mm/etc/shadow > /tmp/debian-{{ DIST }}-mm/etc/shadow.bak
cat /tmp/debian-{{ DIST }}-mm/etc/shadow.bak > /tmp/debian-{{ DIST }}-mm/etc/shadow cat /tmp/debian-{{ DIST }}-mm/etc/shadow.bak > /tmp/debian-{{ DIST }}-mm/etc/shadow
@ -178,7 +190,7 @@ if ! cmp /tmp/debian-{{ DIST }}-debootstrap/etc/shadow /tmp/debian-{{ DIST }}-mm
else else
echo no difference for /etc/shadow on {{ DIST }} {{ VARIANT }} >&2 echo no difference for /etc/shadow on {{ DIST }} {{ VARIANT }} >&2
fi fi
if ! cmp /tmp/debian-{{ DIST }}-debootstrap/etc/shadow- /tmp/debian-{{ DIST }}-mm/etc/shadow-; then if ! cmp /tmp/debian-{{ DIST }}-debootstrap/etc/shadow- /tmp/debian-{{ DIST }}-mm/etc/shadow- >&2; then
echo patching /etc/shadow- on {{ DIST }} {{ VARIANT }} >&2 echo patching /etc/shadow- on {{ DIST }} {{ VARIANT }} >&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-{{ DIST }}-mm/etc/shadow- > /tmp/debian-{{ DIST }}-mm/etc/shadow-.bak 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-{{ DIST }}-mm/etc/shadow- > /tmp/debian-{{ DIST }}-mm/etc/shadow-.bak
cat /tmp/debian-{{ DIST }}-mm/etc/shadow-.bak > /tmp/debian-{{ DIST }}-mm/etc/shadow- cat /tmp/debian-{{ DIST }}-mm/etc/shadow-.bak > /tmp/debian-{{ DIST }}-mm/etc/shadow-

View file

@ -0,0 +1,13 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ "$(id -u)" -eq 0 ] && ! id -u 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
adduser --gecos user --disabled-password user
fi
prefix=
[ "$(id -u)" -eq 0 ] && prefix="runuser -u user --"
$prefix fakechroot fakeroot {{ CMD }} --mode=chrootless --variant=essential {{ DIST }} /dev/null {{ MIRROR }}

View file

@ -13,6 +13,10 @@ homedir=$(runuser -u user -- sh -c 'cd && pwd')
# apt:test/integration/test-apt-key # apt:test/integration/test-apt-key
TMPDIR_ADD="This is fü\$\$ing cràzy, \$(apt -v)\$!" TMPDIR_ADD="This is fü\$\$ing cràzy, \$(apt -v)\$!"
runuser -u user -- mkdir "$homedir/$TMPDIR_ADD" runuser -u user -- 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"
runuser -u user -- env TMPDIR="$homedir/$TMPDIR_ADD" {{ CMD }} --mode=unshare --variant=apt \ runuser -u user -- env TMPDIR="$homedir/$TMPDIR_ADD" {{ CMD }} --mode=unshare --variant=apt \
--setup-hook='case "$1" in '"$(quote "$homedir/$TMPDIR_ADD/mmdebstrap.")"'??????????) exit 0;; *) echo "$1"; exit 1;; esac' \ --setup-hook='case "$1" in '"$(quote "$homedir/$TMPDIR_ADD/mmdebstrap.")"'??????????) exit 0;; *) echo "$1"; exit 1;; esac' \
{{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }} {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}

View file

@ -132,6 +132,7 @@ script -qfc "$prefix {{ CMD }} --mode={{ MODE }} --variant=apt \
{{ DIST }} /dev/null {{ MIRROR }}" /dev/null {{ DIST }} /dev/null {{ MIRROR }}" /dev/null
fail=0 fail=0
[ -r /tmp/log ] || fail=1
grep '^E:' /tmp/log && fail=1 grep '^E:' /tmp/log && fail=1
grep 'Can not write log' /tmp/log && fail=1 grep 'Can not write log' /tmp/log && fail=1
grep 'posix_openpt' /tmp/log && fail=1 grep 'posix_openpt' /tmp/log && fail=1

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

View file

@ -1,43 +0,0 @@
#!/bin/sh
#
# regularly check whether more packages work with chrootless:
# for p in $(grep-aptavail -F Essential yes -s Package -n | sort -u); do ./mmdebstrap -- mode=chrootless --variant=custom --include=bsdutils,coreutils,debianutils,diffutils,dpkg, findutils,grep,gzip,hostname,init-system-helpers,ncurses-base,ncurses-bin,perl-base,sed, sysvinit-utils,tar,$p unstable /dev/null; done
#
# see https://bugs.debian.org/cgi-bin/pkgreport.cgi?users=debian-dpkg@lists.debian.org;tag=dpkg- root-support
#
# base-files: #824594
# base-passwd: debconf
# bash: depends base-files
# bsdutils: ok
# coreutils: ok
# dash: debconf
# debianutils: ok
# diffutils: ok
# dpkg: ok
# findutils: ok
# grep: ok
# gzip: ok
# hostname: ok
# init-system-helpers: ok
# libc-bin: #983412
# login: debconf
# ncurses-base: ok
# ncurses-bin: ok
# perl-base: ok
# sed: ok
# sysvinit-utils: ok
# tar: ok
# util-linux: debconf
set -eu
export LC_ALL=C.UTF-8
if [ "$(id -u)" -eq 0 ] && ! id -u 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
adduser --gecos user --disabled-password user
fi
prefix=
[ "$(id -u)" -eq 0 ] && prefix="runuser -u user --"
$prefix {{ CMD }} --mode=chrootless --variant=custom --include=bsdutils,coreutils,debianutils,diffutils,dpkg,findutils,grep,gzip,hostname,init-system-helpers,ncurses-base,ncurses-bin,perl-base,sed,sysvinit-utils,tar {{ DIST }} /dev/null {{ MIRROR }}

View file

@ -10,12 +10,12 @@ adduser --gecos user --disabled-password user
echo user:100000:65536 | cmp /etc/subuid - echo user:100000:65536 | cmp /etc/subuid -
echo user:100000:65536 | cmp /etc/subgid - echo user:100000:65536 | cmp /etc/subgid -
sysctl -w kernel.unprivileged_userns_clone=1 sysctl -w kernel.unprivileged_userns_clone=1
# include iputils-ping so that we can verify that taridshift does not remove # include iputils-ping so that we can verify that tarfilter does not remove
# extended attributes # extended attributes
# run through tarshift no-op to create a tarball that should be bit-by-bit # run through tarshift no-op to create a tarball that should be bit-by-bit
# identical to a round trip through "taridshift X" and "taridshift -X" # identical to a round trip through "tarfilter --idshift X" and "tarfilter --idshift -X"
runuser -u user -- {{ CMD }} --mode=unshare --variant=apt --include=iputils-ping {{ DIST }} - {{ MIRROR }} \ runuser -u user -- {{ CMD }} --mode=unshare --variant=apt --include=iputils-ping {{ DIST }} - {{ MIRROR }} \
| ./taridshift 0 > /tmp/debian-chroot.tar | ./tarfilter --idshift 0 > /tmp/debian-chroot.tar
# make sure that xattrs are set in the original tarball # make sure that xattrs are set in the original tarball
mkdir /tmp/debian-chroot mkdir /tmp/debian-chroot
tar --xattrs --xattrs-include='*' --directory /tmp/debian-chroot -xf /tmp/debian-chroot.tar ./bin/ping tar --xattrs --xattrs-include='*' --directory /tmp/debian-chroot -xf /tmp/debian-chroot.tar ./bin/ping
@ -25,9 +25,9 @@ rm /tmp/debian-chroot/bin/ping
rmdir /tmp/debian-chroot/bin rmdir /tmp/debian-chroot/bin
rmdir /tmp/debian-chroot rmdir /tmp/debian-chroot
# shift the uid/gid forward by 100000 and backward by 100000 # shift the uid/gid forward by 100000 and backward by 100000
./taridshift 100000 < /tmp/debian-chroot.tar > /tmp/debian-chroot-shifted.tar ./tarfilter --idshift 100000 < /tmp/debian-chroot.tar > /tmp/debian-chroot-shifted.tar
./taridshift -100000 < /tmp/debian-chroot-shifted.tar > /tmp/debian-chroot-shiftedback.tar ./tarfilter --idshift -100000 < /tmp/debian-chroot-shifted.tar > /tmp/debian-chroot-shiftedback.tar
# the tarball before and after the roundtrip through taridshift should be bit # the tarball before and after the roundtrip through tarfilter should be bit
# by bit identical # by bit identical
cmp /tmp/debian-chroot.tar /tmp/debian-chroot-shiftedback.tar cmp /tmp/debian-chroot.tar /tmp/debian-chroot-shiftedback.tar
# manually adjust uid/gid and compare "tar -t" output # manually adjust uid/gid and compare "tar -t" output

View file

@ -0,0 +1,11 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
# mmdebstrap used to hang forever if apt in custom mode failed to resolve
# dependencies because a process died without cleaning up its children.
# https://bugs.debian.org/1017795
ret=0
{{ CMD }} --mode={{ MODE }} --variant=custom \
--include=this-package-does-not-exist {{ DIST }} /dev/null {{ MIRROR }} || ret=1
[ $ret -eq 1 ]