Compare commits

..

1 commit

Author SHA1 Message Date
27fca53e1d mmdebstrap: Show APT's dependency trace when in debug mode
A dependency trace is a powerful tool to debug issues related to
APT's selection of packages, especially in custom mode.

Thus it makes sense to ask APT to output a dependency trace when
`mmdebstrap` is run with the `--debug` flag.
2022-08-20 17:05:11 +02:00
146 changed files with 3643 additions and 7772 deletions

View file

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

View file

@ -1,166 +1,3 @@
1.5.4 (2024-10-28)
------------------
- do not generate apt sources.list entry if SUITE is empty
1.5.3 (2024-09-13)
------------------
- tidy up any zombie processes
- chrootless hurd-i386
- add --skip=cleanup/reproducible/machine-id
- m-a-b-q: replace test_installed by dpkg-checkbuilddeps
1.5.2 (2024-06-26)
------------------
- mmdebstrap-autopkgtest-build-qemu produces bit-by-bit reproducible output
1.5.1 (2024-06-03)
------------------
- in root and unshare mode, run 'mount --make-rprivate /' before bind-mounting
- switch apt variant from using 'apt-get dist-upgrade' to apt patterns
1.5.0 (2024-05-14)
------------------
- add --format=ext4
1.4.3 (2024-02-01)
------------------
- take hard links into account when computing disk usage
1.4.2 (2024-01-29)
------------------
- allow for start-stop-daemon to be in either /sbin or /usr/sbin
- mmdebstrap-autopkgtest-build-qemu: fix octal mode computation and hostname
1.4.1 (2024-01-09)
------------------
- set DPkg::Chroot-Directory in APT_CONFIG to simplify calling apt in hooks
- disallow running chrootless as root without fakeroot unless
--skip=check/chrootless is used
- only print short --help output if wrong args are passed
- read files passed as --aptopt and --dpkgopt outside the unshared namespace
1.4.0 (2023-10-24)
------------------
- add mmdebstrap-autopkgtest-build-qemu
- export container=mmdebstrap-unshare env variable in unshare-mode hooks
- add new skip options: output/dev, output/mknod, tar-in/mknod,
copy-in/mknod, sync-in/mknod
- stop copying qemu-$arch-static binary into the chroot
- tarfilter: add --type-exclude option
- set MMDEBSTRAP_FORMAT in hooks
- do not install priority:required in buildd variant following debootstrap
1.3.8 (2023-08-20)
------------------
- hooks/merged-usr: implement post-merging as debootstrap does
- exclude ./lost+found from tarball
1.3.7 (2023-06-21)
------------------
- add hooks/copy-host-apt-sources-and-preferences
1.3.6 (2023-06-16)
------------------
- bugfix release
1.3.5 (2023-03-20)
------------------
- bugfix release
1.3.4 (2023-03-16)
------------------
- more safeguards before automatically choosing unshare mode
1.3.3 (2023-02-19)
------------------
- testsuite improvements
1.3.2 (2023-02-16)
------------------
- unshare mode works in privileged docker containers
1.3.1 (2023-01-20)
------------------
- bugfix release
1.3.0 (2023-01-16)
------------------
- add hooks/maybe-jessie-or-older and hooks/maybe-merged-usr
- add --skip=check/signed-by
- hooks/jessie-or-older: split into two individual hook files
- skip running apt-get update if we are very sure that it was already run
- be more verbose when 'apt-get update' failed
- warn if a hook is named like one but not executable and if a hook is
executable but not named like one
- to find signed-by value, run gpg on the individual keys to print better
error messages in case it fails (gpg doesn't give an indication which file
it was unable to read) and print progress bar
- allow empty sources.list entries
1.2.5 (2023-01-04)
------------------
- bugfix release
1.2.4 (2022-12-23)
------------------
- bugfix release
- add jessie-or-older extract hook
1.2.3 (2022-11-16)
------------------
- use Text::ParseWords::shellwords instead of spawning a new shell
- mount and unmount once, instead for each run_chroot() call
1.2.2 (2022-10-27)
------------------
- allow /etc/apt/trusted.gpg.d/ not to exist
- always create /var/lib/dpkg/arch to make foreign architecture chrootless
tarballs bit-by-bit identical
- write an empty /etc/machine-id instead of writing 'uninitialized'
- only print progress bars on interactive terminals that are wide enough
1.2.1 (2022-09-08)
------------------
- bugfix release
1.2.0 (2022-09-05)
------------------
- remove proot mode
- error out if stdout is an interactive terminal
- replace taridshift by tarfilter --idshift
- tarfilter: add --transform option
- multiple --skip options can be separated by comma or whitespace
- also cleanup the contents of /run
- support apt patterns and paths with commas and whitespace in --include
- hooks: store the values of the --include option in MMDEBSTRAP_INCLUDE
- add new --skip options: chroot/start-stop-daemon, chroot/policy-rc.d
chroot/mount, chroot/mount/dev, chroot/mount/proc, chroot/mount/sys,
cleanup/run
1.1.0 (2022-07-26) 1.1.0 (2022-07-26)
---------------- ----------------

View file

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

View file

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

View file

@ -10,17 +10,16 @@ import argparse
import time import time
from datetime import timedelta from datetime import timedelta
from collections import defaultdict from collections import defaultdict
from itertools import product
have_qemu = os.getenv("HAVE_QEMU", "yes") == "yes" have_qemu = os.getenv("HAVE_QEMU", "yes") == "yes"
have_unshare = os.getenv("HAVE_UNSHARE", "yes") == "yes"
have_binfmt = os.getenv("HAVE_BINFMT", "yes") == "yes" have_binfmt = os.getenv("HAVE_BINFMT", "yes") == "yes"
run_ma_same_tests = os.getenv("RUN_MA_SAME_TESTS", "yes") == "yes" run_ma_same_tests = os.getenv("RUN_MA_SAME_TESTS", "yes") == "yes"
use_host_apt_config = os.getenv("USE_HOST_APT_CONFIG", "no") == "yes"
cmd = os.getenv("CMD", "./mmdebstrap") cmd = os.getenv("CMD", "./mmdebstrap")
default_dist = os.getenv("DEFAULT_DIST", "unstable") default_dist = os.getenv("DEFAULT_DIST", "unstable")
all_dists = ["oldstable", "stable", "testing", "unstable"] all_dists = ["oldstable", "stable", "testing", "unstable"]
default_mode = "auto" default_mode = "auto" if have_unshare else "root"
all_modes = ["auto", "root", "unshare", "fakechroot", "chrootless"] all_modes = ["auto", "root", "unshare", "fakechroot", "chrootless"]
default_variant = "apt" default_variant = "apt"
all_variants = [ all_variants = [
@ -34,12 +33,12 @@ all_variants = [
"standard", "standard",
] ]
default_format = "auto" default_format = "auto"
all_formats = ["auto", "directory", "tar", "squashfs", "ext2", "ext4", "null"] all_formats = ["auto", "directory", "tar", "squashfs", "ext2", "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()
release_path = f"./shared/cache/debian/dists/{default_dist}/InRelease" release_path = f"./shared/cache/debian/dists/{default_dist}/Release"
if not os.path.exists(release_path): if not os.path.exists(release_path):
print("path doesn't exist:", release_path, file=sys.stderr) print("path doesn't exist:", release_path, file=sys.stderr)
print("run ./make_mirror.sh first", file=sys.stderr) print("run ./make_mirror.sh first", file=sys.stderr)
@ -67,96 +66,6 @@ def skip(condition, dist, mode, variant, fmt):
return "" return ""
def parse_config(confname):
config_dict = defaultdict(dict)
config_order = list()
all_vals = {
"Dists": all_dists,
"Modes": all_modes,
"Variants": all_variants,
"Formats": all_formats,
}
with open(confname) as f:
for test in Deb822.iter_paragraphs(f):
if "Test" not in test.keys():
print("Test without name", file=sys.stderr)
exit(1)
name = test["Test"]
config_order.append(name)
for k in test.keys():
v = test[k]
if k not in [
"Test",
"Dists",
"Modes",
"Variants",
"Formats",
"Skip-If",
"Needs-QEMU",
"Needs-Root",
"Needs-APT-Config",
]:
print(f"Unknown field name {k} in test {name}")
exit(1)
if k in all_vals.keys():
if v == "default":
print(
f"Setting {k} to default in Test {name} is redundant",
file=sys.stderr,
)
exit(1)
if v == "any":
v = all_vals[k]
else:
# else, split the value by whitespace
v = v.split()
for i in v:
if i not in all_vals[k]:
print(
f"{i} is not a valid value for {k}", file=sys.stderr
)
exit(1)
config_dict[name][k] = v
return config_order, config_dict
def format_test(num, total, name, dist, mode, variant, fmt, config_dict):
ret = f"({num}/{total}) {name}"
if len(config_dict[name].get("Dists", [])) > 1:
ret += f" --dist={dist}"
if len(config_dict[name].get("Modes", [])) > 1:
ret += f" --mode={mode}"
if len(config_dict[name].get("Variants", [])) > 1:
ret += f" --variant={variant}"
if len(config_dict[name].get("Formats", [])) > 1:
ret += f" --format={fmt}"
return ret
def print_time_per_test(time_per_test, name="test"):
print(
f"average time per {name}:",
sum(time_per_test.values(), start=timedelta()) / len(time_per_test),
file=sys.stderr,
)
print(
f"median time per {name}:",
sorted(time_per_test.values())[len(time_per_test) // 2],
file=sys.stderr,
)
head_tail_num = 10
print(f"{head_tail_num} fastests {name}s:", file=sys.stderr)
for k, v in sorted(time_per_test.items(), key=lambda i: i[1])[
: min(head_tail_num, len(time_per_test))
]:
print(f" {k}: {v}", file=sys.stderr)
print(f"{head_tail_num} slowest {name}s:", file=sys.stderr)
for k, v in sorted(time_per_test.items(), key=lambda i: i[1], reverse=True)[
: min(head_tail_num, len(time_per_test))
]:
print(f" {k}: {v}", file=sys.stderr)
def main(): def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("test", nargs="*", help="only run these tests") parser.add_argument("test", nargs="*", help="only run these tests")
@ -177,34 +86,14 @@ def main():
default=0, default=0,
help="exit after first num failures or errors.", help="exit after first num failures or errors.",
) )
parser.add_argument( parser.add_argument("--mode", metavar="mode", help="only run tests with this mode")
"--mode", parser.add_argument("--dist", metavar="dist", help="only run tests with this dist")
metavar="mode",
help=f"only run tests with this mode (Default = {default_mode})",
)
parser.add_argument(
"--dist",
metavar="dist",
help=f"only run tests with this dist (Default = {default_dist})",
)
parser.add_argument(
"--variant",
metavar="variant",
help=f"only run tests with this variant (Default = {default_variant})",
)
parser.add_argument(
"--format",
metavar="format",
help=f"only run tests with this format (Default = {default_format})",
)
parser.add_argument(
"--skip", metavar="test", action="append", help="skip this test"
)
args = parser.parse_args() 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",
@ -229,57 +118,59 @@ def main():
"/usr/share/mmdebstrap/hooks", "shared/hooks", dirs_exist_ok=True "/usr/share/mmdebstrap/hooks", "shared/hooks", dirs_exist_ok=True
) )
# parse coverage.txt
config_order, config_dict = parse_config("coverage.txt")
indirbutnotcovered = set(
[d for d in os.listdir("tests") if not d.startswith(".")]
) - set(config_order)
if indirbutnotcovered:
print(
"test(s) missing from coverage.txt: %s"
% (", ".join(sorted(indirbutnotcovered))),
file=sys.stderr,
)
exit(1)
coveredbutnotindir = set(config_order) - set(
[d for d in os.listdir("tests") if not d.startswith(".")]
)
if coveredbutnotindir:
print(
"test(s) missing from ./tests: %s"
% (", ".join(sorted(coveredbutnotindir))),
file=sys.stderr,
)
exit(1)
# produce the list of tests using the cartesian product of all allowed
# dists, modes, variants and formats of a given test
tests = [] tests = []
for name in config_order: with open("coverage.txt") as f:
test = config_dict[name] for test in Deb822.iter_paragraphs(f):
for dist, mode, variant, fmt in product( name = test["Test"]
test.get("Dists", [default_dist]), dists = test.get("Dists", default_dist)
test.get("Modes", [default_mode]), if dists == "any":
test.get("Variants", [default_variant]), dists = all_dists
test.get("Formats", [default_format]), elif dists == "default":
): dists = [default_dist]
skipreason = skip(test.get("Skip-If"), dist, mode, variant, fmt) else:
dists = dists.split()
modes = test.get("Modes", default_mode)
if modes == "any":
modes = all_modes
elif modes == "default":
modes = [default_mode]
else:
modes = modes.split()
variants = test.get("Variants", default_variant)
if variants == "any":
variants = all_variants
elif variants == "default":
variants = [default_variant]
else:
variants = variants.split()
formats = test.get("Formats", default_format)
if formats == "any":
formats = all_formats
elif formats == "default":
formats = [default_format]
else:
formats = formats.split()
for dist in dists:
for mode in modes:
for variant in variants:
for fmt in formats:
skipreason = skip(
test.get("Skip-If"), dist, mode, variant, fmt
)
if skipreason: if skipreason:
tt = ("skip", skipreason) tt = ("skip", skipreason)
elif (
test.get("Needs-APT-Config", "false") == "true" and use_host_apt_config
):
tt = ("skip", "test cannot use host apt config")
elif have_qemu: elif have_qemu:
tt = "qemu" tt = "qemu"
elif test.get("Needs-QEMU", "false") == "true": elif test.get("Needs-QEMU", "false") == "true":
tt = ("skip", "test needs QEMU") tt = ("skip", "test needs QEMU")
elif test.get("Needs-Root", "false") == "true": elif test.get("Needs-Root", "false") == "true":
tt = "sudo" tt = "sudo"
elif mode == "auto" and not have_unshare:
tt = "sudo"
elif mode == "root": elif mode == "root":
tt = "sudo" tt = "sudo"
elif mode == "unshare" and not have_unshare:
tt = ("skip", "test needs unshare")
else: else:
tt = "null" tt = "null"
tests.append((tt, name, dist, mode, variant, fmt)) tests.append((tt, name, dist, mode, variant, fmt))
@ -312,8 +203,6 @@ def main():
failed = [] failed = []
num_success = 0 num_success = 0
num_finished = 0 num_finished = 0
time_per_test = {}
acc_time_per_test = defaultdict(list)
for i, (test, name, dist, mode, variant, fmt) in enumerate(tests): for i, (test, name, dist, mode, variant, fmt) in enumerate(tests):
if torun and i not in torun: if torun and i not in torun:
continue continue
@ -334,7 +223,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), file=sys.stderr) print("failed: %d" % len(failed))
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:
@ -347,36 +236,6 @@ def main():
line = line.replace("{{ FORMAT }}", fmt) line = line.replace("{{ FORMAT }}", fmt)
line = line.replace("{{ HOSTARCH }}", hostarch) line = line.replace("{{ HOSTARCH }}", hostarch)
fout.write(line) fout.write(line)
# ignore:
# SC2016 Expressions don't expand in single quotes, use double quotes for that.
# SC2050 This expression is constant. Did you forget the $ on a variable?
# SC2194 This word is constant. Did you forget the $ on a variable?
shellcheck = subprocess.run(
[
"shellcheck",
"--exclude=SC2050,SC2194,SC2016",
"-f",
"gcc",
"shared/test.sh",
],
check=False,
stdout=subprocess.PIPE,
).stdout.decode()
shfmt = subprocess.run(
[
"shfmt",
"--posix",
"--binary-next-line",
"--case-indent",
"--indent",
"2",
"--simplify",
"-d",
"shared/test.sh",
],
check=False,
stdout=subprocess.PIPE,
).stdout.decode()
argv = None argv = None
match test: match test:
case "qemu": case "qemu":
@ -387,29 +246,17 @@ def main():
argv = ["./run_null.sh"] argv = ["./run_null.sh"]
case ("skip", reason): case ("skip", reason):
skipped[reason].append( skipped[reason].append(
format_test( ("(%d/%d) %s" % (i + 1, len(tests), name), dist, mode, variant, fmt)
i + 1, len(tests), name, dist, mode, variant, fmt, config_dict
)
) )
print(f"skipped because of {reason}", file=sys.stderr) print(f"skipped because of {reason}", file=sys.stderr)
continue continue
print(separator, file=sys.stderr) print(separator, file=sys.stderr)
if args.skip and name in args.skip:
print(f"skipping because of --skip={name}", file=sys.stderr)
continue
if args.dist and args.dist != dist: if args.dist and args.dist != dist:
print(f"skipping because of --dist={args.dist}", file=sys.stderr) print(f"skipping because of --dist={args.dist}", file=sys.stderr)
continue continue
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
if args.format and args.format != fmt:
print(f"skipping because of --format={args.format}", file=sys.stderr)
continue
before = time.time()
proc = subprocess.Popen(argv) proc = subprocess.Popen(argv)
try: try:
proc.wait() proc.wait()
@ -417,28 +264,17 @@ def main():
proc.terminate() proc.terminate()
proc.wait() proc.wait()
break break
after = time.time()
walltime = timedelta(seconds=int(after - before))
formated_test_name = format_test(
i + 1, len(tests), name, dist, mode, variant, fmt, config_dict
)
time_per_test[formated_test_name] = walltime
acc_time_per_test[name].append(walltime)
print(separator, file=sys.stderr) print(separator, file=sys.stderr)
print(f"duration: {walltime}", file=sys.stderr) if proc.returncode != 0:
if proc.returncode != 0 or shellcheck != "" or shfmt != "": failed.append(
if shellcheck != "": ("(%d/%d) %s" % (i + 1, len(tests), name), dist, mode, variant, fmt)
print(shellcheck) )
if shfmt != "":
print(shfmt)
failed.append(formated_test_name)
print("result: FAILURE", file=sys.stderr) print("result: FAILURE", file=sys.stderr)
else: else:
print("result: SUCCESS", file=sys.stderr) print("result: SUCCESS", file=sys.stderr)
num_success += 1 num_success += 1
if args.maxfail and len(failed) >= args.maxfail: if args.maxfail and len(failed) >= args.maxfail:
break break
print(separator, file=sys.stderr)
print( print(
"successfully ran %d tests" % num_success, "successfully ran %d tests" % num_success,
file=sys.stderr, file=sys.stderr,
@ -449,24 +285,10 @@ def main():
print(f"skipped because of {reason}:", file=sys.stderr) print(f"skipped because of {reason}:", file=sys.stderr)
for t in l: for t in l:
print(f" {t}", file=sys.stderr) print(f" {t}", file=sys.stderr)
if len(time_per_test) > 1:
print_time_per_test(time_per_test)
if len(acc_time_per_test) > 1:
print_time_per_test(
{
f"{len(v)}x {k}": sum(v, start=timedelta())
for k, v in acc_time_per_test.items()
},
"accumulated test",
)
if failed: if failed:
print("failed %d:" % len(failed), file=sys.stderr) print("failed %d:" % len(failed), file=sys.stderr)
for f in failed: for t in failed:
print(f, file=sys.stderr) print(f" {t}", file=sys.stderr)
currenttime = time.time()
walltime = timedelta(seconds=int(currenttime - starttime))
print(f"total runtime: {walltime}", file=sys.stderr)
if failed:
exit(1) exit(1)

View file

@ -2,22 +2,11 @@
set -eu set -eu
# by default, use the mmdebstrap executable in the current directory together if [ -e ./mmdebstrap -a -e ./taridshift -a -e ./tarfilter -a -e ./coverage.py ]; then
# with perl Devel::Cover but allow to overwrite this
: "${CMD:=perl -MDevel::Cover=-silent,-nogcov ./mmdebstrap}"
case "$CMD" in
"mmdebstrap "* | mmdebstrap | *" mmdebstrap" | *" mmdebstrap "*)
MMSCRIPT="$(command -v mmdebstrap 2>/dev/null)"
;;
*) MMSCRIPT=./mmdebstrap ;;
esac
if [ -e "$MMSCRIPT" ]; then
TMPFILE=$(mktemp) TMPFILE=$(mktemp)
perltidy <"$MMSCRIPT" >"$TMPFILE" perltidy < ./mmdebstrap > "$TMPFILE"
ret=0 ret=0
diff -u "$MMSCRIPT" "$TMPFILE" || ret=$? diff -u ./mmdebstrap "$TMPFILE" || ret=$?
if [ "$ret" -ne 0 ]; then if [ "$ret" -ne 0 ]; then
echo "perltidy failed" >&2 echo "perltidy failed" >&2
rm "$TMPFILE" rm "$TMPFILE"
@ -25,25 +14,16 @@ if [ -e "$MMSCRIPT" ]; then
fi fi
rm "$TMPFILE" rm "$TMPFILE"
if [ "$(sed -e '/^__END__$/,$d' "$MMSCRIPT" | wc --max-line-length)" -gt 79 ]; then if [ $(sed -e '/^__END__$/,$d' ./mmdebstrap | wc --max-line-length) -gt 79 ]; then
echo "exceeded maximum line length of 79 characters" >&2 echo "exceeded maximum line length of 79 characters" >&2
exit 1 exit 1
fi fi
perlcritic --severity 4 --verbose 8 "$MMSCRIPT" perlcritic --severity 4 --verbose 8 ./mmdebstrap
pod2man "$MMSCRIPT" >/dev/null black --check ./taridshift ./tarfilter ./coverage.py
fi fi
for f in tarfilter coverage.py caching_proxy.py; do
[ -e "./$f" ] || continue
black --check "./$f"
done
shellcheck --exclude=SC2016 coverage.sh make_mirror.sh run_null.sh run_qemu.sh gpgvnoexpkeysig mmdebstrap-autopkgtest-build-qemu hooks/*/*.sh
shfmt --binary-next-line --case-indent --indent 2 --simplify -d coverage.sh make_mirror.sh run_null.sh run_qemu.sh mmdebstrap-autopkgtest-build-qemu gpgvnoexpkeysig
mirrordir="./shared/cache/debian" mirrordir="./shared/cache/debian"
if [ ! -e "$mirrordir" ]; then if [ ! -e "$mirrordir" ]; then
@ -60,15 +40,29 @@ rm -f shared/cover_db.img
if [ "$HAVE_QEMU" = "yes" ]; then if [ "$HAVE_QEMU" = "yes" ]; then
# prepare image for cover_db # prepare image for cover_db
fallocate -l 64M shared/cover_db.img guestfish -N shared/cover_db.img=disk:64M -- mkfs vfat /dev/sda
/usr/sbin/mkfs.vfat shared/cover_db.img
if [ ! -e "./shared/cache/debian-$DEFAULT_DIST.ext4" ]; then if [ ! -e "./shared/cache/debian-$DEFAULT_DIST.qcow" ]; then
echo "./shared/cache/debian-$DEFAULT_DIST.ext4 does not exist" >&2 echo "./shared/cache/debian-$DEFAULT_DIST.qcow does not exist" >&2
exit 1 exit 1
fi fi
fi fi
# check if all required debootstrap tarballs exist
notfound=0
for dist in oldstable stable testing unstable; do
for variant in minbase buildd -; do
if [ ! -e "shared/cache/debian-$dist-$variant.tar" ]; then
echo "shared/cache/debian-$dist-$variant.tar does not exist" >&2
notfound=1
fi
done
done
if [ "$notfound" -ne 0 ]; then
echo "not all required debootstrap tarballs are present" >&2
exit 1
fi
# choose the timestamp of the unstable Release file, so that we get # choose the timestamp of the unstable Release file, so that we get
# reproducible results for the same mirror timestamp # reproducible results for the same mirror timestamp
SOURCE_DATE_EPOCH=$(date --date="$(grep-dctrl -s Date -n '' "$mirrordir/dists/$DEFAULT_DIST/Release")" +%s) SOURCE_DATE_EPOCH=$(date --date="$(grep-dctrl -s Date -n '' "$mirrordir/dists/$DEFAULT_DIST/Release")" +%s)
@ -76,13 +70,23 @@ SOURCE_DATE_EPOCH=$(date --date="$(grep-dctrl -s Date -n '' "$mirrordir/dists/$D
# for traditional sort order that uses native byte values # for traditional sort order that uses native byte values
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
: "${HAVE_UNSHARE:=yes}"
: "${HAVE_PROOT:=yes}"
: "${HAVE_BINFMT:=yes}" : "${HAVE_BINFMT:=yes}"
defaultmode="auto"
if [ "$HAVE_UNSHARE" != "yes" ]; then
defaultmode="root"
fi
# by default, use the mmdebstrap executable in the current directory together
# with perl Devel::Cover but allow to overwrite this
: "${CMD:=perl -MDevel::Cover=-silent,-nogcov ./mmdebstrap}"
mirror="http://127.0.0.1/debian" mirror="http://127.0.0.1/debian"
export HAVE_QEMU HAVE_BINFMT RUN_MA_SAME_TESTS DEFAULT_DIST SOURCE_DATE_EPOCH CMD mirror export HAVE_QEMU HAVE_UNSHARE HAVE_BINFMT RUN_MA_SAME_TESTS DEFAULT_DIST SOURCE_DATE_EPOCH CMD mirror
./coverage.py "$@" ./coverage.py
if [ -e shared/cover_db.img ]; then if [ -e shared/cover_db.img ]; then
# produce report inside the VM to make sure that the versions match or # produce report inside the VM to make sure that the versions match or
@ -98,20 +102,15 @@ cover -delete cover_db >&2
END END
if [ "$HAVE_QEMU" = "yes" ]; then if [ "$HAVE_QEMU" = "yes" ]; then
./run_qemu.sh ./run_qemu.sh
elif [ "$mode" = "root" ]; then
./run_null.sh SUDO
else else
./run_null.sh ./run_null.sh
fi fi
echo echo
echo "open file://$(pwd)/shared/report/coverage.html in a browser" echo open file://$(pwd)/shared/report/coverage.html in a browser
echo echo
fi fi
# check if the wiki has to be updated with pod2markdown output rm shared/test.sh shared/tar1.txt shared/tar2.txt shared/pkglist.txt shared/doc-debian.tar.list shared/mmdebstrap shared/taridshift shared/tarfilter shared/proxysolver
if [ "${DEBEMAIL-}" = "josch@debian.org" ]; then
bash -exc "diff -u <(curl --silent https://gitlab.mister-muffin.de/josch/mmdebstrap/wiki/raw/Home | dos2unix; echo) <(pod2markdown < mmdebstrap)" || :
fi
rm -f shared/test.sh shared/tar1.txt shared/tar2.txt shared/pkglist.txt shared/doc-debian.tar.list shared/mmdebstrap shared/tarfilter shared/proxysolver
echo "$0 finished successfully" >&2

View file

@ -1,20 +1,10 @@
Test: debootstrap
Dists: any
Variants: minbase buildd -
Needs-Root: true
Needs-APT-Config: true
Test: check-against-debootstrap-dist Test: check-against-debootstrap-dist
Dists: any Dists: any
Variants: minbase buildd - Variants: minbase buildd -
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: as-debootstrap-unshare-wrapper Test: as-debootstrap-unshare-wrapper
Modes: unshare Needs-QEMU: true
Needs-Root: true
Variants: minbase -
Needs-APT-Config: true
Test: help Test: help
@ -30,7 +20,6 @@ Needs-Root: true
Test: dist-using-codename Test: dist-using-codename
Dists: any Dists: any
Needs-APT-Config: true
Test: fail-without-etc-subuid Test: fail-without-etc-subuid
Needs-QEMU: true Needs-QEMU: true
@ -40,15 +29,12 @@ Needs-QEMU: true
Test: unshare-as-root-user-inside-chroot Test: unshare-as-root-user-inside-chroot
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: root-mode-inside-chroot Test: root-mode-inside-chroot
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: root-mode-inside-unshare-chroot Test: root-mode-inside-unshare-chroot
Modes: unshare Needs-QEMU: true
Needs-APT-Config: true
Test: root-without-cap-sys-admin Test: root-without-cap-sys-admin
Needs-Root: true Needs-Root: true
@ -56,27 +42,19 @@ Needs-Root: true
Test: mount-is-missing Test: mount-is-missing
Needs-QEMU: true Needs-QEMU: true
Test: mmdebstrap
Needs-Root: true
Modes: root
Formats: tar squashfs ext2 ext4
Variants: essential apt minbase buildd - standard
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
mode == "fakechroot" and variant in ["-", "standard"] # no extended attributes
variant == "standard" and dist in ["oldstable", "stable"] and hostarch in ["armel", "armhf", "mipsel"] # #1031276
Test: check-for-bit-by-bit-identical-format-output Test: check-for-bit-by-bit-identical-format-output
Modes: unshare fakechroot
Formats: tar squashfs ext2 ext4
Variants: essential apt minbase buildd - standard
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
mode == "fakechroot" and variant in ["-", "standard"] # no extended attributes
variant == "standard" and dist in ["oldstable", "stable"] and hostarch in ["armel", "armhf", "mipsel"] # #1031276
Test: tarfilter-idshift
Needs-QEMU: true Needs-QEMU: true
Formats: tar squashfs ext2
Variants: essential apt minbase buildd important standard
Skip-If:
variant == "standard" and dist in ["oldstable", "stable"] # #864082, #1004557, #1004558
variant == "important" and dist == "oldstable" # /var/lib/systemd/catalog/database differs
fmt == "squashfs" and dist == "oldstable" # squashfs-tools-ng is not available
fmt == "ext2" and dist == "oldstable" # genext2fs does not support SOURCE_DATE_EPOCH
Test: taridshift-utility
Needs-QEMU: true
Skip-If: dist == "oldstable" # python3 tarfile module does not preserve xattrs
Test: progress-bars-on-fake-tty Test: progress-bars-on-fake-tty
@ -96,21 +74,19 @@ Test: missing-device-nodes-outside-the-chroot
Needs-QEMU: true Needs-QEMU: true
Test: missing-dev-sys-proc-inside-the-chroot Test: missing-dev-sys-proc-inside-the-chroot
Modes: unshare Needs-QEMU: true
Variants: custom
Test: chroot-directory-not-accessible-by-apt-user Test: chroot-directory-not-accessible-by-apt-user
Needs-Root: true Needs-Root: true
Test: cwd-directory-not-accessible-by-unshared-user Test: cwd-directory-not-accessible-by-unshared-user
Needs-Root: true Needs-QEMU: true
Modes: unshare
Test: create-gzip-compressed-tarball Test: create-gzip-compressed-tarball
Needs-QEMU: true
Test: custom-tmpdir Test: custom-tmpdir
Needs-Root: true Needs-QEMU: true
Modes: unshare
Test: xz-compressed-tarball Test: xz-compressed-tarball
@ -133,7 +109,6 @@ Test: read-from-stdin-write-to-stdout
Test: supply-components-manually Test: supply-components-manually
Modes: root Modes: root
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: stable-default-mirror Test: stable-default-mirror
Needs-QEMU: true Needs-QEMU: true
@ -158,23 +133,19 @@ Needs-QEMU: true
Test: mirror-is-deb Test: mirror-is-deb
Test: mirror-is-real-file Test: mirror-is-real-file
Needs-APT-Config: true
Test: deb822-1-2 Test: deb822-1-2
Modes: root Modes: root
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: deb822-2-2 Test: deb822-2-2
Modes: root Modes: root
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: automatic-mirror-from-suite Test: automatic-mirror-from-suite
Needs-QEMU: true Needs-QEMU: true
Test: invalid-mirror Test: invalid-mirror
Needs-APT-Config: true
Test: fail-installing-to-root Test: fail-installing-to-root
Modes: root Modes: root
@ -194,18 +165,16 @@ Skip-If:
hostarch != "amd64" hostarch != "amd64"
not run_ma_same_tests not run_ma_same_tests
Test: include-foreign-libmagic-mgc Test: include-libmagic-mgc-arm64
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Skip-If: Skip-If:
hostarch not in ["amd64", "arm64"] hostarch != "amd64"
not run_ma_same_tests not run_ma_same_tests
Test: include-foreign-libmagic-mgc-with-multiple-arch-options Test: include-libmagic-mgc-arm64-with-multiple-arch-options
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Skip-If: Skip-If:
hostarch not in ["amd64", "arm64"] hostarch != "amd64"
not run_ma_same_tests not run_ma_same_tests
Test: aptopt Test: aptopt
@ -216,7 +185,6 @@ Needs-QEMU: true
Test: keyring-overwrites Test: keyring-overwrites
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: signed-by-without-host-keys Test: signed-by-without-host-keys
Needs-QEMU: true Needs-QEMU: true
@ -226,7 +194,6 @@ Needs-QEMU: true
Test: signed-by-with-host-keys Test: signed-by-with-host-keys
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: dpkgopt Test: dpkgopt
Needs-Root: true Needs-Root: true
@ -240,6 +207,9 @@ Needs-Root: true
Test: include-with-multiple-apt-sources Test: include-with-multiple-apt-sources
Needs-Root: true Needs-Root: true
Test: merged-usr-via-setup-hook
Needs-Root: true
Test: essential-hook Test: essential-hook
Needs-Root: true Needs-Root: true
@ -260,34 +230,28 @@ Needs-Root: true
Test: special-hooks-using-helpers Test: special-hooks-using-helpers
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: special-hooks-using-helpers-and-env-vars Test: special-hooks-using-helpers-and-env-vars
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: special-hooks-with-mode-mode Test: special-hooks-with-mode-mode
Modes: root unshare fakechroot Modes: root unshare fakechroot
Needs-QEMU: true
Test: debootstrap-no-op-options Test: debootstrap-no-op-options
Needs-Root: true Needs-Root: true
Test: verbose Test: verbose
Variants: standard Needs-Root: true
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
Test: debug Test: debug
Variants: standard Needs-Root: true
Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558
Test: quiet Test: quiet
Needs-Root: true Needs-Root: true
Test: logfile Test: logfile
Needs-Root: true Needs-Root: true
Needs-APT-Config: true
Test: without-etc-resolv-conf-and-etc-hostname Test: without-etc-resolv-conf-and-etc-hostname
Needs-QEMU: true Needs-QEMU: true
@ -300,83 +264,56 @@ Test: not-having-to-install-apt-in-include-because-a-hook-did-it-before
Test: remove-start-stop-daemon-and-policy-rc-d-in-hook Test: remove-start-stop-daemon-and-policy-rc-d-in-hook
Test: skip-start-stop-daemon-policy-rc
Test: skip-mount
Modes: unshare
Test: compare-output-with-pre-seeded-var-cache-apt-archives Test: compare-output-with-pre-seeded-var-cache-apt-archives
Needs-QEMU: true Needs-QEMU: true
Variants: any Variants: any
Skip-If: Skip-If:
variant == "standard" and dist == "oldstable" # #864082, #1004557, #1004558 variant == "standard" and dist in ["oldstable", "stable"] # #864082, #1004557, #1004558
variant == "important" and dist == "oldstable" # /var/lib/systemd/catalog/database differs
Test: create-directory-dry-run Test: create-directory-dry-run
Modes: root
Test: create-tarball-dry-run Test: create-tarball-dry-run
Variants: any Variants: any
Modes: any Modes: any
Test: unpack-doc-debian Test: unpack-doc-debian
Modes: root fakechroot Needs-QEMU: true
Modes: any
Variants: extract Variants: extract
Needs-APT-Config: true
Test: install-doc-debian Test: install-doc-debian
Modes: chrootless Modes: chrootless
Variants: custom Variants: custom
Needs-APT-Config: true
Test: chrootless Test: install-known-good-from-essential-yes
Variants: essential Variants: custom
Modes: chrootless
Needs-Root: true
Skip-If:
dist == "oldstable"
Test: chrootless-fakeroot
Variants: essential
Modes: chrootless Modes: chrootless
Skip-If: Skip-If:
dist == "oldstable" True # #1006692
hostarch in ["i386", "armel", "armhf", "mipsel"] # #1023286 dist in ["oldstable", "stable"]
Test: chrootless-foreign
Variants: essential
Modes: chrootless
Skip-If:
dist == "oldstable"
hostarch not in ["amd64", "arm64"]
not run_ma_same_tests
Needs-QEMU: true
Test: install-doc-debian-and-output-tarball Test: install-doc-debian-and-output-tarball
Variants: custom Variants: custom
Modes: chrootless Modes: chrootless
Needs-APT-Config: true
Test: install-doc-debian-and-test-hooks Test: install-doc-debian-and-test-hooks
Variants: custom Variants: custom
Modes: chrootless Modes: chrootless
Needs-APT-Config: true
Test: install-libmagic-mgc-on-foreign Test: install-libmagic-mgc-on-arm64
Variants: custom
Modes: chrootless
Skip-If: Skip-If:
hostarch not in ["amd64", "arm64"] hostarch != "amd64"
not have_binfmt not have_binfmt
Test: install-busybox-based-sub-essential-system Test: install-busybox-based-sub-essential-system
Needs-Root: true Needs-Root: true
Test: create-foreign-tarball Test: create-arm64-tarball
Modes: root unshare fakechroot Modes: root unshare fakechroot
Skip-If: Skip-If:
hostarch not in ["amd64", "arm64"] hostarch != "amd64"
mode == "fakechroot" and not run_ma_same_tests mode == "fakechroot" and not run_ma_same_tests
mode == "fakechroot" and hostarch == "arm64" # usrmerge postinst under fakechroot wants to copy /lib/ld-linux-x86-64.so.2 (which does not exist) instead of /lib64/ld-linux-x86-64.so.2
not have_binfmt not have_binfmt
Test: no-sbin-in-path Test: no-sbin-in-path
@ -384,54 +321,4 @@ Modes: fakechroot
Test: dev-ptmx Test: dev-ptmx
Modes: root unshare Modes: root unshare
Needs-QEMU: true
Test: error-if-stdout-is-tty
Test: variant-custom-timeout
Test: include-deb-file
Modes: root unshare fakechroot
Needs-APT-Config: true
Test: unshare-include-deb
Modes: unshare
Test: pivot_root
Modes: root unshare
Needs-APT-Config: true
Test: jessie-or-older
Needs-Root: true
Modes: root unshare fakechroot
Variants: essential apt minbase
Skip-If: mode == "fakechroot" and hostarch in ["i386", "armel", "armhf", "mipsel"] # #1023286
Test: apt-patterns
Test: apt-patterns-custom
Test: empty-sources.list
Test: merged-fakechroot-inside-unmerged-chroot
Needs-Root: true
Needs-APT-Config: true
Skip-If:
hostarch in ["i386", "armel", "armhf", "mipsel"] # #1023286
dist in ["testing", "unstable"] # #1053671
Test: auto-mode-as-normal-user
Modes: auto
Test: skip-output-dev
Modes: root unshare
Test: skip-output-mknod
Modes: root unshare
Test: skip-tar-in-mknod
Modes: unshare
Test: zombie-reaping
Modes: unshare
Test: empty-suite

View file

@ -1,7 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# No copyright is claimed. This code is in the public domain; do with # This script is in the public domain
# it what you wish.
# #
# Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de> # Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
# #

View file

@ -2,7 +2,7 @@
set -eu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then if [ "$MMDEBSTRAP_VERBOSITY" -ge 3 ]; then
set -x set -x
fi fi

View file

@ -2,7 +2,7 @@
set -eu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then if [ "$MMDEBSTRAP_VERBOSITY" -ge 3 ]; then
set -x set -x
fi fi

View file

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

View file

@ -1,55 +0,0 @@
#!/bin/sh
#
# This script makes sure that the apt sources.list and preferences from outside
# the chroot also exist inside the chroot by *appending* them to any existing
# files. If you do not want to keep the original content, add another setup
# hook before this one which cleans up the files you don't want to keep.
#
# If instead of copying sources.list verbatim you want to mangle its contents,
# consider using python-apt for that. An example can be found in the Debian
# packaging of mmdebstrap in ./debian/tests/sourcesfilter
set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
set -x
fi
if [ -n "${MMDEBSTRAP_SUITE:-}" ]; then
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 1 ]; then
echo "W: using a non-empty suite name $MMDEBSTRAP_SUITE does not make sense with this hook and might select the wrong Essential:yes package set" >&2
fi
fi
rootdir="$1"
SOURCELIST="/etc/apt/sources.list"
eval "$(apt-config shell SOURCELIST Dir::Etc::SourceList/f)"
SOURCEPARTS="/etc/apt/sources.d/"
eval "$(apt-config shell SOURCEPARTS Dir::Etc::SourceParts/d)"
PREFERENCES="/etc/apt/preferences"
eval "$(apt-config shell PREFERENCES Dir::Etc::Preferences/f)"
PREFERENCESPARTS="/etc/apt/preferences.d/"
eval "$(apt-config shell PREFERENCESPARTS Dir::Etc::PreferencesParts/d)"
for f in "$SOURCELIST" \
"$SOURCEPARTS"/*.list \
"$SOURCEPARTS"/*.sources \
"$PREFERENCES" \
"$PREFERENCESPARTS"/*; do
[ -e "$f" ] || continue
mkdir --parents "$(dirname "$rootdir/$f")"
if [ -e "$rootdir/$f" ]; then
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 2 ]; then
echo "I: $f already exists in chroot, appending..." >&2
fi
# Add extra newline between old content and new content.
# This is required in case of deb822 files.
echo >> "$rootdir/$f"
fi
cat "$f" >> "$rootdir/$f"
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
echo "D: contents of $f inside the chroot:" >&2
cat "$rootdir/$f" >&2
fi
done

View file

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

View file

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

View file

@ -1,10 +1,8 @@
#!/bin/sh #!/bin/sh
#
# shellcheck disable=SC2086
set -eu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then if [ "$MMDEBSTRAP_VERBOSITY" -ge 3 ]; then
set -x set -x
fi fi
@ -23,17 +21,17 @@ case $MMDEBSTRAP_MODE in
echo "removing the following directories:" >&2 ;; echo "removing the following directories:" >&2 ;;
esac esac
< "$rootdir/run/mmdebstrap/file-mirror-automount" \ cat "$rootdir/run/mmdebstrap/file-mirror-automount" \
xargs $xargsopts echo " $rootdir/{}" | xargs $xargsopts echo " $rootdir/{}"
case $MMDEBSTRAP_MODE in case $MMDEBSTRAP_MODE in
root|unshare) root|unshare)
< "$rootdir/run/mmdebstrap/file-mirror-automount" \ cat "$rootdir/run/mmdebstrap/file-mirror-automount" \
xargs $xargsopts umount "$rootdir/{}" | xargs $xargsopts umount "$rootdir/{}"
;; ;;
*) *)
< "$rootdir/run/mmdebstrap/file-mirror-automount" \ cat "$rootdir/run/mmdebstrap/file-mirror-automount" \
xargs $xargsopts rm -r "$rootdir/{}" | xargs $xargsopts rm -r "$rootdir/{}"
;; ;;
esac esac

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -41,12 +41,44 @@
set -eu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then if [ "$MMDEBSTRAP_VERBOSITY" -ge 3 ]; then
set -x set -x
fi fi
TARGET="$1" TARGET="$1"
ARCH=$(dpkg --print-architecture)
eval "$(APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" apt-config shell ARCH Apt::Architecture)"
if [ -e /usr/share/debootstrap/functions ]; then
. /usr/share/debootstrap/functions
doing_variant () { [ $1 != "buildd" ]; }
MERGED_USR="yes"
setup_merged_usr
else
link_dir=""
case $ARCH in
hurd-*) exit 0;;
amd64) link_dir="lib32 lib64 libx32" ;;
i386) link_dir="lib64 libx32" ;;
mips|mipsel) link_dir="lib32 lib64" ;;
mips64*|mipsn32*) link_dir="lib32 lib64 libo32" ;;
powerpc) link_dir="lib64" ;;
ppc64) link_dir="lib32 lib64" ;;
ppc64el) link_dir="lib64" ;;
s390x) link_dir="lib32" ;;
sparc) link_dir="lib64" ;;
sparc64) link_dir="lib32 lib64" ;;
x32) link_dir="lib32 lib64 libx32" ;;
esac
link_dir="bin sbin lib $link_dir"
for dir in $link_dir; do
ln -s usr/"$dir" "$TARGET/$dir"
mkdir -p "$TARGET/usr/$dir"
done
fi
# now install an empty "usr-is-merged" package to avoid installing the # now install an empty "usr-is-merged" package to avoid installing the
# usrmerge package on this system even after init-system-helpers starts # usrmerge package on this system even after init-system-helpers starts
# depending on "usrmerge | usr-is-merged". # depending on "usrmerge | usr-is-merged".

View file

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

View file

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

View file

@ -11,14 +11,13 @@
set -eu set -eu
if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then if [ "$MMDEBSTRAP_VERBOSITY" -ge 3 ]; then
set -x set -x
fi fi
TARGET="$1" TARGET="$1"
echo "Warning: starting with Debian 12 (Bookworm), systems without merged-/usr are not supported anymore" >&2 echo "Warning: starting with Debian 12 (Bookworm), systems without merged-/usr are not supported anymore" >&2
echo "Warning: starting with Debian 13 (Trixie), merged-/usr symlinks are shipped by packages in the essential-set making this hook ineffective" >&2
echo "this system will not be supported in the future" > "$TARGET/etc/unsupported-skip-usrmerge-conversion" echo "this system will not be supported in the future" > "$TARGET/etc/unsupported-skip-usrmerge-conversion"

View file

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

View file

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

4126
mmdebstrap

File diff suppressed because it is too large Load diff

View file

@ -1,460 +1,255 @@
#!/bin/sh #!/bin/sh
# Copyright 2023 Johannes Schauer Marin Rodrigues <josch@debian.org>
# Copyright 2023 Helmut Grohne <helmut@subdivi.de>
# SPDX-License-Identifier: MIT
# We generally use single quotes to avoid variable expansion:
# shellcheck disable=SC2016
# Replacement for autopkgtest-build-qemu and vmdb2 for all architectures
# supporting EFI booting (amd64, arm64, armhf, i386, riscv64).
# For use as replacement for autopkgtest-build-qemu and vmdb2 on ppc64el which
# neither supports extlinux nor efi booting there is an unmaintained script
# which uses grub instead to boot:
# #
# https://gitlab.mister-muffin.de/josch/mmdebstrap/src/commit/ # © 2022 Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
# e523741610a4ed8579642bfc755956f64c847ef3/mmdebstrap-autopkgtest-build-qemu #
# Permission is hereby granted, free of charge, to any person obtaining a copy
: <<'POD2MAN' # of this software and associated documentation files (the "Software"), to
=head1 NAME # deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
mmdebstrap-autopkgtest-build-qemu - autopkgtest-build-qemu without vmdb2 but mmdebstrap and EFI boot # sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
=head1 SYNOPSIS #
# The above copyright notice and this permission notice shall be included in
B<mmdebstrap-autopkgtest-build-qemu> [I<OPTIONS>] B<--boot>=B<efi> I<RELEASE> I<IMAGE> # all copies or substantial portions of the Software.
#
=head1 DESCRIPTION # The software is provided "as is", without warranty of any kind, express or
# implied, including but not limited to the warranties of merchantability,
B<mmdebstrap-autopkgtest-build-qemu> is a mostly compatible drop-in replacement # fitness for a particular purpose and noninfringement. In no event shall the
for L<autopkgtest-build-qemu(1)> with two main differences: Firstly, it uses # authors or copyright holders be liable for any claim, damages or other
L<mmdebstrap(1)> instead of L<vmdb2(1)> and thus is able to create QEMU disk # liability, whether in an action of contract, tort or otherwise, arising
images without requiring superuser privileges and with bit-by-bit reproducible # from, out of or in connection with the software or the use or other dealings
output. Secondly, it uses L<systemd-boot(7)> and thus only supports booting via # in the software.
EFI. For architectures for which L<autopkgtest-virt-qemu(1)> does not default
to EFI booting you must pass B<--boot=efi> when invoking the autopkgtest virt
backend.
=head1 POSITIONAL PARAMETERS
=over 8
=item I<RELEASE>
The release to download from the I<MIRROR>. This parameter is required.
=item I<IMAGE>
The file to write, in raw format. This parameter is required.
=back
=head1 OPTIONS
=over 8
=item B<--mirror>=I<MIRROR>
Specify which distribution to install. It defaults to
http://deb.debian.org/debian (i.e. Debian), but you can pass a mirror of any
Debian derivative.
=item B<--architecture>=I<ARCHITECTURE>
Set the architecture for the virtual machine image, specified as a L<dpkg(1)>
architecture. If omitted, the host architecture is assumed.
B<--arch>=I<ARCH> is an alias for this option.
=item B<--script>=I<SCRIPT>
Specifies a user script that will be called with the root filesystem of the
image as its first parameter. This script can them make any necesssary
modifications to the root filesystem.
The script must be a POSIX shell script, and should not depend on bash-specific
features. This script will be executed inside a L<chroot(1)> call in the
virtual machine root filesystem.
=item B<--size>=I<SIZE>
Specifies the image size for the virtual machine, defaulting to 25G.
=item B<--apt-proxy>=I<PROXY>
Specify an apt proxy to use in the virtual machine. By default, if you have
an apt proxy configured on the host, the virtual machine will automatically use
this, otherwise there is no default.
=item B<--boot>=B<efi>, B<--efi>
Select the way the generated image will expect to be booted. Unless you
explicitly select --boot=efi, operation will fail.
=item B<--keyring>=I<KEYRING>
Passes an additional B<--keyring> parameter to B<mmdebstrap>.
=back
=head1 EXAMPLES
Make sure, that F</path/to/debian-unstable.img> is a path that the unshared
user has access to. This can be done by ensuring world-execute permissions on
all path components or by creating the image in a world-readable directory like
/tmp before copying it into its final location.
$ mmdebstrap-autopkgtest-build-qemu --boot=efi --arch=amd64 unstable /path/to/debian-unstable.img
[...]
$ autopkgtest mypackage -- qemu --boot=efi --dpkg-architecture=amd64 /path/to/debian-unstable.img
Make sure to add B<--boot=efi> to both the B<mmdebstrap-autopkgtest-build-qemu>
as well as the B<autopkgtest-virt-qemu> invocation.
Create bit-by-bit reproducible images from a given snapshot.d.o timestamp.
SOURCE_DATE_EPOCH=1612543740 mmdebstrap-autopkgtest-build-qemu --boot=efi \
--mirror=http://snapshot.debian.org/archive/debian/20210205T164900Z/ \
unstable /path/to/debian-unstable.img
=head1 SEE ALSO
L<autopkgtest-build-qemu(1)>, L<autopkgtest-virt-qemu(1)>, L<mmdebstrap(1)>, L<autopkgtest(1)>
=cut
POD2MAN
set -eu set -eu
die() { # This script creates debian-$RELEASE.qcow2 in the current directory which can
echo "$*" 1>&2 # then be used by the autopkgtest qemu backend.
exit 1 #
} # Thanks to Francesco Poli for providing ideas and testing this.
#
# Thanks to Lars Wirzenius of vmdb2 where the grub and efi magic comes from.
#
# Only the native architecture is supported because guestfish doesn't support
# foreign architectures.
usage() { usage() {
die "usage: $0 [--architecture=|--apt-proxy=|--keyring=|--mirror=|--script=|--size=] --boot=efi <RELEASE> <IMAGE>" echo "Usage: $0 [--size=SIZE] [--boot=BOOT] RELEASE IMAGE" >&2
echo >&2
echo "RELEASE is a Debian release like unstable" >&2
echo "IMAGE will be stored in qcow2 format" >&2
echo "SIZE is 25G by default" >&2
echo "BOOT is either auto (the default), bios, efi or ieee1275" >&2
} }
usage_error() {
echo "error: $*" 1>&2 nativearch="$(dpkg --print-architecture)"
SIZE="25G" # default from autopkgtest-build-qemu
BOOT="auto"
if [ "$#" -lt 2 ]; then
echo "Error: Insufficient number of arguments" >&2
usage usage
} exit 1
elif [ "$#" -eq 2 ]; then
BOOT=auto RELEASE=$1
ARCHITECTURE=$(dpkg --print-architecture) IMAGE=$2
IMAGE=
MIRROR=
KEYRING=
RELEASE=
SIZE=25G
SCRIPT=
# consumed by setup-testbed
export AUTOPKGTEST_BUILD_QEMU=1
opt_boot() {
BOOT="$1"
}
opt_architecture() {
ARCHITECTURE="$1"
}
opt_arch() {
ARCHITECTURE="$1"
}
opt_apt_proxy() {
# consumed by setup-testbed
export AUTOPKGTEST_APT_PROXY="$1"
# consumed by mmdebstrap
if test "$1" = DIRECT; then
unset http_proxy
else else
export http_proxy="$1" # parse options
OPTS=$(getopt -n "$0" -o h --long size:,boot:,architecture:,help -- "$@")
if [ "$?" -ne 0 ]; then
echo "Error: Cannot parse arguments" >&2
usage
exit 1
fi fi
} eval set -- "$OPTS"
opt_keyring() { while true; do
KEYRING="$1"
}
opt_mirror() {
# consumed by setup-testbed
export MIRROR="$1"
}
opt_script() {
test -f "$1" || die "passed script '$1' does not refer to a file"
SCRIPT="$1"
}
opt_size() {
SIZE="$1"
}
positional=1
positional_1() {
# consumed by setup-testbed
export RELEASE="$1"
}
positional_2() {
IMAGE="$1"
}
positional_3() { opt_mirror "$@"; }
positional_4() { opt_architecture "$@"; }
positional_5() { opt_script "$@"; }
positional_6() { opt_size "$@"; }
positional_7() {
die "too many positional options"
}
while test "$#" -gt 0; do
case "$1" in case "$1" in
--architecture=* | --arch=* | --boot=* | --keyring=* | --mirror=* | --script=* | --size=*) --size) SIZE="$2"; shift 2; continue;;
optname="${1%%=*}" --boot) BOOT="$2"; shift 2; continue;;
"opt_${optname#--}" "${1#*=}" --help) usage; exit 1;;
;; --architecture)
--apt-proxy=*) echo "Error: cannot (yet) create foreign architecture images" >&2
opt_apt_proxy "${1#*=}" exit 1
;;
--architecture | --arch | --boot | --keyring | --mirror | --script | --size)
test "$#" -ge 2 || usage_error "missing argument for $1"
"opt_${1#--}" "$2"
shift
;;
--apt-proxy)
test "$#" -ge 2 || usage_error "missing argument for $1"
opt_apt_proxy "$2"
shift
;;
--efi)
opt_boot efi
;;
--*)
usage_error "unrecognized argument $1"
;; ;;
--) shift; break;;
*) *)
"positional_$positional" "$1" echo "Error: unknown option $1" >&2
positional=$((positional + 1)) usage
exit 1
;; ;;
esac esac
shift
done done
RELEASE=$1
IMAGE=$2
fi
test -z "$RELEASE" -o -z "$IMAGE" && usage_error "missing positional arguments" # By default with --boot=auto (the default), bios boot is chosen for
test "$BOOT" = efi \ # amd64 and i386. Compare /usr/share/autopkgtest/lib/autopkgtest_qemu.py
|| die "this tool does not support boot modes other than efi" # But in practice, amd64 and i386 also support efi boot. But then
# autopkgtest-virt-qemu has to be run with --boot=efi
case "$BOOT" in
auto)
case "$nativearch" in
amd64|i386) BOOT=bios;;
armhf|arm64) BOOT=efi;;
ppc64el) BOOT=ieee1275;;
esac
;;
bios)
case "$nativearch" in amd64|i386);;
*)
echo "bios booting only possible on amd64 and i386" >&2
exit 1
;;
esac
;;
efi)
case "$nativearch" in amd64|i386|armhf|arm64);;
*)
echo "efi booting only possible on amd64, i386, armhf and arm64" >&2
exit 1
;;
esac
;;
ieee1275)
if [ "$nativearch" != "ppc64el" ]; then
echo "ieee1275 booting only possible on ppc64el" >&2
exit 1
fi
;;
*)
echo "invalid value for --boot" >&2;;
esac
case "$ARCHITECTURE" in case "$nativearch" in
amd64) amd64)
EFIIMG=bootx64.efi [ $BOOT = bios ] || [ $BOOT = efi ]
QEMUARCH=x86_64 if [ $BOOT = bios ]; then
VMFPKG=ovmf include="linux-image-amd64 grub-pc"
LINUXIMAGE=linux-image-amd64 grub_target="i386-pc"
elif [ $BOOT = efi ]; then
include="linux-image-amd64 grub-efi"
grub_target="x86_64-efi"
fi
;; ;;
arm64) arm64)
EFIIMG=bootaa64.efi [ $BOOT = efi ]
QEMUARCH=aarch64 include="linux-image-arm64 grub-efi"
VMFPKG=qemu-efi-aarch64 grub_target="arm64-efi"
LINUXIMAGE=linux-image-arm64
;; ;;
armhf) armhf)
EFIIMG=bootarm.efi [ $BOOT = efi ]
QEMUARCH=arm include="linux-image-armmp-lpae grub-efi"
VMFPKG=qemu-efi-arm grub_target="arm-efi"
LINUXIMAGE=linux-image-armmp
;; ;;
i386) i386)
EFIIMG=bootia32.efi [ $BOOT = bios ] || [ $BOOT = efi ]
QEMUARCH=i386 if [ $BOOT = bios ]; then
VMFPKG=ovmf-ia32 include="linux-image-686-pae grub-pc"
LINUXIMAGE=linux-image-686-pae grub_target="i386-efi"
elif [ $BOOT = efi ]; then
include="linux-image-686-pae grub-efi"
grub_target="i386-efi"
fi
;; ;;
riscv64) ppc64el)
EFIIMG=bootriscv64.efi [ $BOOT = ieee1275 ]
QEMUARCH=riscv64 include="linux-image-powerpc64le grub-ieee1275"
VMFPKG= grub_target="powerpc-ieee1275"
LINUXIMAGE=linux-image-riscv64
;; ;;
*) *)
die "unsupported architecture: $ARCHITECTURE" echo "architecture $nativearch not yet supported" >&2
exit 1
;; ;;
esac esac
if test "$(dpkg-query -f '${db:Status-Status}' -W binutils-multiarch)" = installed; then case "$nativearch" in
GNU_PREFIX= arm64|armhf) serial="loglevel=3 console=tty0 console=ttyAMA0,115200n8" ;;
BINUTILS= ppc64el) serial="loglevel=3 console=tty0 console=hvc0,115200n8" ;;
else *) serial="loglevel=3 console=tty0 console=ttyS0,115200n8" ;;
GNU_ARCHITECTURE="$(dpkg-architecture "-a$ARCHITECTURE" -qDEB_HOST_GNU_TYPE)"
GNU_PREFIX="$GNU_ARCHITECTURE-"
GNU_SUFFIX="-$(echo "$GNU_ARCHITECTURE" | tr _ -)"
BINUTILS=", binutils$GNU_SUFFIX | binutils-multiarch"
fi
arches=" $(dpkg --print-architecture) $(dpkg --print-foreign-architectures | tr '\n' ' ') "
case $arches in
*" $ARCHITECTURE "*) : ;; # nothing to do
*) die "enable $ARCHITECTURE by running: sudo dpkg --add-architecture $ARCHITECTURE && sudo apt update" ;;
esac esac
test "$(dpkg-query -f '${db:Status-Status}' -W "dpkg-dev")" = installed \ if ! command -v guestfish >/dev/null; then
|| die "please install dpkg-dev" echo "Error: requires guestfish being installed" >&2
exit 1
fi
dpkg-checkbuilddeps -d "autopkgtest, dosfstools, e2fsprogs, fdisk, mount, mtools, passwd, uidmap, libarchive13, systemd-boot-efi:$ARCHITECTURE $BINUTILS" /dev/null \ if [ ! -e /usr/share/autopkgtest/setup-commands/setup-testbed ]; then
|| die "please install the required packages listed above" echo "Error: requires autopkgtest being installed" >&2
exit 1
fi
BOOTSTUB="/usr/lib/systemd/boot/efi/linux${EFIIMG#boot}.stub" run_mmdebstrap() {
mmdebstrap --variant=important --include="$include" \
WORKDIR= --customize-hook='chroot "$1" passwd --delete root' \
--customize-hook='chroot "$1" useradd --home-dir /home/user --create-home user' \
cleanup() { --customize-hook='chroot "$1" passwd --delete user' \
test -n "$WORKDIR" && rm -Rf "$WORKDIR" --customize-hook='echo host > "$1/etc/hostname"' \
--customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \
--customize-hook='env AUTOPKGTEST_BUILD_QEMU=1 /usr/share/autopkgtest/setup-commands/setup-testbed "$1"' \
"$RELEASE" -
} }
trap cleanup EXIT INT TERM QUIT guestfish_bios() {
guestfish -- \
disk-create "$IMAGE" qcow2 "$SIZE" : \
add-drive "$IMAGE" format:qcow2 : \
launch : \
part-disk /dev/sda mbr : \
part-set-bootable /dev/sda 1 true : \
mkfs ext4 /dev/sda1 : mount /dev/sda1 / : \
tar-in - / xattrs:true : \
command "sh -c 'echo UUID=\$(blkid -c /dev/null -o value -s UUID /dev/sda1) / ext4 errors=remount-ro 0 1 > /etc/fstab'" : \
command "update-initramfs -u" : \
command "grub-mkconfig -o /boot/grub/grub.cfg" : \
command "grub-install /dev/sda --target=$grub_target --no-nvram --force-extra-removable --no-floppy --modules=part_gpt --grub-mkdevicemap=/boot/grub/device.map" : \
sync : umount / : shutdown
}
WORKDIR=$(mktemp -d) guestfish_efi() {
guestfish -- \
disk-create "$IMAGE" qcow2 "$SIZE" : \
add-drive "$IMAGE" format:qcow2 : \
launch : \
part-init /dev/sda gpt : \
part-add /dev/sda primary 8192 262144 : \
part-add /dev/sda primary 262145 -34 : \
part-set-gpt-type /dev/sda 1 C12A7328-F81F-11D2-BA4B-00A0C93EC93B : \
mkfs ext4 /dev/sda2 : mount /dev/sda2 / : \
tar-in - / xattrs:true : \
mkdir-p /boot/efi : \
mkfs vfat /dev/sda1 : mount /dev/sda1 /boot/efi : \
command "sh -c 'echo UUID=\$(blkid -c /dev/null -o value -s UUID /dev/sda2) / ext4 errors=remount-ro 0 1 > /etc/fstab'" : \
command "sh -c 'echo UUID=\$(blkid -c /dev/null -o value -s UUID /dev/sda1) /boot/efi vfat errors=remount-ro 0 2 >> /etc/fstab'" : \
command "sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT=/GRUB_CMDLINE_LINUX_DEFAULT=\"biosdevname=0 net.ifnames=0 consoleblank=0 rw $serial\"/' /etc/default/grub" : \
command "update-initramfs -u" : \
command "grub-mkconfig -o /boot/grub/grub.cfg" : \
command "grub-install /dev/sda --target=$grub_target --no-nvram --force-extra-removable --no-floppy --modules=part_gpt --grub-mkdevicemap=/boot/grub/device.map" : \
sync : umount /boot/efi : umount / : shutdown
}
FAT_OFFSET_SECTORS=$((1024 * 2)) guestfish_ieee1275() {
FAT_SIZE_SECTORS=$((1024 * 254)) guestfish -- \
disk-create "$IMAGE" qcow2 "$SIZE" : \
add-drive "$IMAGE" format:qcow2 : \
launch : \
part-init /dev/sda gpt : \
part-add /dev/sda primary 8192 20480 : \
part-add /dev/sda primary 20481 -34 : \
part-set-gpt-type /dev/sda 1 9E1A2D38-C612-4316-AA26-8B49521E5A8B : \
mkfs ext4 /dev/sda2 : mount /dev/sda2 / : \
tar-in - / xattrs:true : \
command "sh -c 'echo UUID=\$(blkid -c /dev/null -o value -s UUID /dev/sda2) / ext4 errors=remount-ro 0 1 > /etc/fstab'" : \
command "sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT=/GRUB_CMDLINE_LINUX_DEFAULT=\"biosdevname=0 net.ifnames=0 consoleblank=0 rw $serial\"/' /etc/default/grub" : \
command "update-initramfs -u" : \
command "grub-mkconfig -o /boot/grub/grub.cfg" : \
command "grub-install /dev/sda --target=$grub_target --no-nvram --force-extra-removable --no-floppy --modules=part_gpt --grub-mkdevicemap=/boot/grub/device.map" : \
sync : umount / : shutdown
}
# The image is raw and not in qcow2 format because: case "$BOOT" in
# - faster run-time as the "qemu-image convert" step is not needed bios) run_mmdebstrap | guestfish_bios;;
# - image can be used independent of qemu tooling efi) run_mmdebstrap | guestfish_efi;;
# - modifying the image just with "mount" instead of requiring qemu-nbd ieee1275) run_mmdebstrap | guestfish_ieee1275;;
# - sparse images make the file just as small as with qcow2
# - trim support is more difficult on qcow2
# - snapshots and overlays work just as well with raw images
# - users who prefer qcow2 get to choose to run it themselves with their own
# custom options like compression
set -- \
--mode=unshare \
--format=tar \
--variant=important \
--architecture="$ARCHITECTURE"
case $MIRROR in http://snapshot.debian.org/archive/* | https://snapshot.debian.org/archive/*)
set -- "$@" --aptopt='Acquire::Check-Valid-Until "false"'
;;
esac esac
EXT_FEATURES= echo "Success! The image is stored as $IMAGE" >&2
if test "$RELEASE" = jessie; then
set -- "$@" --keyring=/usr/share/keyrings/debian-archive-removed-keys.gpg
set -- "$@" --aptopt='Apt::Key::gpgvcommand "/usr/libexec/mmdebstrap/gpgvnoexpkeysig"'
set -- "$@" --hook-dir=/usr/share/mmdebstrap/hooks/jessie-or-older
EXT_FEATURES="^metadata_csum,^metadata_csum_seed,^orphan_file"
fi
set -- "$@" \
"--include=init,$LINUXIMAGE,python3" \
'--customize-hook=echo host >"$1/etc/hostname"' \
'--customize-hook=echo 127.0.0.1 localhost host >"$1/etc/hosts"' \
'--customize-hook=passwd --root "$1" --delete root' \
'--customize-hook=useradd --root "$1" --home-dir /home/user --create-home user' \
'--customize-hook=passwd --root "$1" --delete user' \
'--customize-hook=/usr/share/autopkgtest/setup-commands/setup-testbed'
if test -n "$SCRIPT"; then
set -- "$@" \
"--customize-hook=upload '$SCRIPT' /userscript" \
"--chrooted-customize-hook=sh /userscript" \
'--customize-hook=rm -f "$1/userscript"'
fi
set -- "$@" \
"--customize-hook=download vmlinuz '$WORKDIR/kernel'" \
"--customize-hook=download initrd.img '$WORKDIR/initrd'" \
"$RELEASE" \
-
test -n "$MIRROR" && set -- "$@" "$MIRROR"
test -n "$KEYRING" && set -- "$@" "--keyring=$KEYRING"
echo "+ mmdebstrap $*" >&2
# https://github.com/koalaman/shellcheck/issues/2555
# shellcheck disable=SC3040
set -o pipefail
mmdebstrap "$@" | {
set -- -t ext4 -L autopkgtestvm -d -
if test -n "$EXT_FEATURES"; then
set -- "$@" -O "$EXT_FEATURES"
fi
EXTOPTS="offset=$(((FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS) * 512))"
if test -n "${SOURCE_DATE_EPOCH-}"; then
uuid="$(uuidgen --sha1 --namespace="$(uuidgen --sha1 --namespace='@dns' --name mister-muffin.de)" --name "$SOURCE_DATE_EPOCH")"
set -- "$@" -U "$uuid"
EXTOPTS="$EXTOPTS,hash_seed=$uuid"
fi
set -- "$@" -E "$EXTOPTS" "$IMAGE" "$SIZE"
echo "+ mke2fs $*" >&2
/sbin/mke2fs "$@"
}
echo "root=LABEL=autopkgtestvm rw console=ttyS0" >"$WORKDIR/cmdline"
align_size() {
echo "$((($1) + ($2) - 1 - (($1) + ($2) - 1) % ($2)))"
}
alignment=$("${GNU_PREFIX}objdump" -p "$BOOTSTUB" | sed 's/^SectionAlignment\s\+\([0-9]\)/0x/;t;d')
test -z "$alignment" && die "failed to discover the alignment of the efi stub"
echo "determined efi vma alignment as $alignment"
test "$RELEASE" = jessie -a "$((alignment))" -lt "$((1024 * 1024))" && {
echo "increasing efi vma alignment for jessie"
alignment=$((1024 * 1024))
}
lastoffset=0
# shellcheck disable=SC2034 # unused variables serve documentation
lastoffset="$("${GNU_PREFIX}objdump" -h "$BOOTSTUB" \
| while read -r idx name size vma lma fileoff algn behind; do
test -z "$behind" -a "${algn#"2**"}" != "$algn" || continue
offset=$((0x$vma + 0x$size))
test "$offset" -gt "$lastoffset" || continue
lastoffset="$offset"
echo "$lastoffset"
done | tail -n1)"
lastoffset=$(align_size "$lastoffset" "$alignment")
echo "determined minimum efi vma offset as $lastoffset"
cmdline_size="$(stat -Lc%s "$WORKDIR/cmdline")"
cmdline_size="$(align_size "$cmdline_size" "$alignment")"
linux_size="$(stat -Lc%s "$WORKDIR/kernel")"
linux_size="$(align_size "$linux_size" "$alignment")"
cmdline_offset="$lastoffset"
linux_offset=$((cmdline_offset + cmdline_size))
initrd_offset=$((linux_offset + linux_size))
SOURCE_DATE_EPOCH=0 \
"${GNU_PREFIX}objcopy" \
--enable-deterministic-archives \
--add-section .cmdline="$WORKDIR/cmdline" \
--change-section-vma .cmdline="$(printf 0x%x "$cmdline_offset")" \
--add-section .linux="$WORKDIR/kernel" \
--change-section-vma .linux="$(printf 0x%x "$linux_offset")" \
--add-section .initrd="$WORKDIR/initrd" \
--change-section-vma .initrd="$(printf 0x%x "$initrd_offset")" \
"$BOOTSTUB" "$WORKDIR/efiimg"
rm -f "$WORKDIR/kernel" "$WORKDIR/initrd"
truncate -s "$((FAT_SIZE_SECTORS * 512))" "$WORKDIR/fat"
/sbin/mkfs.fat -F 32 --invariant "$WORKDIR/fat"
mmd -i "$WORKDIR/fat" EFI EFI/BOOT
mcopy -i "$WORKDIR/fat" "$WORKDIR/efiimg" "::EFI/BOOT/$EFIIMG"
rm -f "$WORKDIR/efiimg"
truncate --size="+$((34 * 512))" "$IMAGE"
/sbin/sfdisk "$IMAGE" <<EOF
label: gpt
unit: sectors
start=$FAT_OFFSET_SECTORS, size=$FAT_SIZE_SECTORS, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
start=$((FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS)), type=0FC63DAF-8483-4772-8E79-3D69D8477DE4
EOF
dd if="$WORKDIR/fat" of="$IMAGE" conv=notrunc,sparse bs=512 "seek=$FAT_OFFSET_SECTORS" status=none
if test "$(dpkg --print-architecture)" != "$ARCHITECTURE" && test "$(dpkg-query -f '${db:Status-Status}' -W "qemu-system-$QEMUARCH")" != installed; then
echo "I: you might need to install a package providing qemu-system-$QEMUARCH to use this image with autopkgtest-virt-qemu" >&2
fi
if test -n "$VMFPKG" && test "$(dpkg-query -f '${db:Status-Status}' -W "$VMFPKG")" != installed; then
echo "I: you might need to install $VMFPKG to use this image with autopkgtest-virt-qemu" >&2
fi
echo "I: SUCCESS! Your new image can be found here: $IMAGE" >&2
echo "I: Don't forget to pass --boot=efi when running autopkgtest-virt-qemu with this image" >&2

View file

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

View file

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

171
tarfilter
View file

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

67
taridshift Executable file
View file

@ -0,0 +1,67 @@
#!/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

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

View file

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

View file

@ -5,7 +5,7 @@ if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
apt-get remove --yes qemu-user-binfmt binfmt-support qemu-user apt-get remove --yes qemu-user-static binfmt-support qemu-user
# the following is not necessary anymore since systemd-binfmt # the following is not necessary anymore since systemd-binfmt
# successfully disables support upon removal of qemu-user with # successfully disables support upon removal of qemu-user with
# the upload of src:systemd 251.2-4: https://bugs.debian.org/1012163 # the upload of src:systemd 251.2-4: https://bugs.debian.org/1012163

View file

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

View file

@ -5,17 +5,14 @@ if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
for f in /etc/apt/trusted.gpg.d/*.gpg /etc/apt/trusted.gpg.d/*.asc; do
[ -e "$f" ] || continue
rm "$f"
done
rmdir /etc/apt/trusted.gpg.d
mkdir /etc/apt/trusted.gpg.d
for f in /usr/share/keyrings/*.gpg; do for f in /usr/share/keyrings/*.gpg; do
name=$(basename "$f" .gpg) name=$(basename "$f" .gpg)
gpg --no-default-keyring --keyring="/usr/share/keyrings/$name.gpg" --armor --output="/etc/apt/trusted.gpg.d/$name.asc" --export gpg --enarmor < /usr/share/keyrings/$name.gpg \
rm "/usr/share/keyrings/$name.gpg" | sed 's/ PGP ARMORED FILE/ PGP PUBLIC KEY BLOCK/;/^Comment: /d' \
> /etc/apt/trusted.gpg.d/$name.asc
done done
rm /etc/apt/trusted.gpg.d/*.gpg
rm /usr/share/keyrings/*.gpg
{{ CMD }} --mode=root --variant=apt {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }} {{ CMD }} --mode=root --variant=apt {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt - tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm -r /tmp/debian-chroot.tar rm -r /tmp/debian-chroot.tar

View file

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

View file

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

View file

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

View file

@ -10,10 +10,14 @@ echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH"
# order in comparison to the systemd users # order in comparison to the systemd users
# https://bugs.debian.org/969631 # https://bugs.debian.org/969631
# we cannot use useradd because passwd is not Essential:yes # we cannot use useradd because passwd is not Essential:yes
#
# with cron 3.0pl1-142 and the introduction of cron-daemon-common, installation
# order of cron and systemd started to differ between debootstrap and
# mmdebstrap, resulting in different gid values
{{ CMD }} --variant={{ VARIANT }} --mode={{ MODE }} \ {{ CMD }} --variant={{ VARIANT }} --mode={{ MODE }} \
--essential-hook='[ {{ DIST }} = oldstable ] && [ {{ VARIANT }} = - ] && echo _apt:*:100:65534::/nonexistent:/usr/sbin/nologin >> "$1"/etc/passwd || :' \ --essential-hook='if [ {{ VARIANT }} = - ]; then echo _apt:*:100:65534::/nonexistent:/usr/sbin/nologin >> "$1"/etc/passwd; fi' \
"$(if [ {{ DIST }} = oldstable ]; then echo --merged-usr; else echo --hook-dir=./hooks/merged-usr; 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' \
"$(case {{ DIST }} in oldstable) echo --include=e2fsprogs,mount,tzdata,gcc-9-base ;; stable) echo --include=e2fsprogs,mount,tzdata ;; *) echo --include=base-files ;; 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 }}
mkdir /tmp/debian-{{ DIST }}-mm mkdir /tmp/debian-{{ DIST }}-mm
@ -25,34 +29,34 @@ tar --xattrs --xattrs-include='*' -C /tmp/debian-{{ DIST }}-debootstrap -xf "cac
# diff cannot compare device nodes, so we use tar to do that for us and then # diff cannot compare device nodes, so we use tar to do that for us and then
# delete the directory # delete the directory
tar -C /tmp/debian-{{ DIST }}-debootstrap -cf /tmp/dev1.tar ./dev tar -C /tmp/debian-{{ DIST }}-debootstrap -cf dev1.tar ./dev
tar -C /tmp/debian-{{ DIST }}-mm -cf /tmp/dev2.tar ./dev tar -C /tmp/debian-{{ DIST }}-mm -cf dev2.tar ./dev
ret=0 ret=0
cmp /tmp/dev1.tar /tmp/dev2.tar >&2 || ret=$? cmp dev1.tar dev2.tar || ret=$?
if [ "$ret" -ne 0 ]; then if [ "$ret" -ne 0 ]; then
if type diffoscope >/dev/null; then if type diffoscope >/dev/null; then
diffoscope /tmp/dev1.tar /tmp/dev2.tar diffoscope dev1.tar dev2.tar
exit 1 exit 1
else else
echo "no diffoscope installed" >&2 echo "no diffoscope installed" >&2
fi fi
if type base64 >/dev/null; then if type base64 >/dev/null; then
base64 /tmp/dev1.tar base64 dev1.tar
base64 /tmp/dev2.tar base64 dev2.tar
exit 1 exit 1
else else
echo "no base64 installed" >&2 echo "no base64 installed" >&2
fi fi
if type xxd >/dev/null; then if type xxd >/dev/null; then
xxd /tmp/dev1.tar xxd dev1.tar
xxd /tmp/dev2.tar xxd dev2.tar
exit 1 exit 1
else else
echo "no xxd installed" >&2 echo "no xxd installed" >&2
fi fi
exit 1 exit 1
fi fi
rm /tmp/dev1.tar /tmp/dev2.tar rm dev1.tar dev2.tar
rm -r /tmp/debian-{{ DIST }}-debootstrap/dev /tmp/debian-{{ DIST }}-mm/dev rm -r /tmp/debian-{{ DIST }}-debootstrap/dev /tmp/debian-{{ DIST }}-mm/dev
# remove downloaded deb packages # remove downloaded deb packages
@ -70,23 +74,19 @@ rm /tmp/debian-{{ DIST }}-debootstrap/var/cache/debconf/templates.dat-old \
/tmp/debian-{{ DIST }}-mm/var/cache/debconf/templates.dat-old /tmp/debian-{{ DIST }}-mm/var/cache/debconf/templates.dat-old
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status-old \ rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status-old \
/tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status-old /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status-old
rm -f /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/diversions-old \
/tmp/debian-{{ DIST }}-mm/var/lib/dpkg/diversions-old
# remove dpkg files # remove dpkg files
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/available rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/available
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/cmethopt rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/cmethopt
# remove /var/lib/dpkg/arch
rm /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/arch
# since we installed packages directly from the .deb files, Priorities differ # since we installed packages directly from the .deb files, Priorities differ
# thus we first check for equality and then remove the files # thus we first check for equality and then remove the files
chroot /tmp/debian-{{ DIST }}-debootstrap dpkg --list >/tmp/dpkg1 chroot /tmp/debian-{{ DIST }}-debootstrap dpkg --list > dpkg1
chroot /tmp/debian-{{ DIST }}-mm dpkg --list >/tmp/dpkg2 chroot /tmp/debian-{{ DIST }}-mm dpkg --list > dpkg2
diff -u /tmp/dpkg1 /tmp/dpkg2 >&2 diff -u dpkg1 dpkg2
rm /tmp/dpkg1 /tmp/dpkg2 rm dpkg1 dpkg2
grep -v '^Priority: ' /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status >/tmp/status1 grep -v '^Priority: ' /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status > status1
grep -v '^Priority: ' /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status >/tmp/status2 grep -v '^Priority: ' /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status > status2
diff -u /tmp/status1 /tmp/status2 >&2 diff -u status1 status2
rm /tmp/status1 /tmp/status2 rm status1 status2
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/dpkg/status /tmp/debian-{{ DIST }}-mm/var/lib/dpkg/status
# debootstrap exposes the hosts's kernel version # debootstrap exposes the hosts's kernel version
if [ -e /tmp/debian-{{ DIST }}-debootstrap/etc/apt/apt.conf.d/01autoremove-kernels ]; then if [ -e /tmp/debian-{{ DIST }}-debootstrap/etc/apt/apt.conf.d/01autoremove-kernels ]; then
@ -95,11 +95,15 @@ fi
if [ -e /tmp/debian-{{ DIST }}-mm/etc/apt/apt.conf.d/01autoremove-kernels ]; then if [ -e /tmp/debian-{{ DIST }}-mm/etc/apt/apt.conf.d/01autoremove-kernels ]; then
rm /tmp/debian-{{ DIST }}-mm/etc/apt/apt.conf.d/01autoremove-kernels rm /tmp/debian-{{ DIST }}-mm/etc/apt/apt.conf.d/01autoremove-kernels
fi fi
# clear out /run except for /run/lock # who creates /run/mount?
find /tmp/debian-{{ DIST }}-debootstrap/run/ -mindepth 1 -maxdepth 1 ! -name lock -print0 | xargs --no-run-if-empty -0 rm -r if [ -e "/tmp/debian-{{ DIST }}-debootstrap/run/mount/utab" ]; then
rm "/tmp/debian-{{ DIST }}-debootstrap/run/mount/utab"
fi
if [ -e "/tmp/debian-{{ DIST }}-debootstrap/run/mount" ]; then
rmdir "/tmp/debian-{{ DIST }}-debootstrap/run/mount"
fi
# debootstrap doesn't clean apt # debootstrap doesn't clean apt
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_main_binary-{{ HOSTARCH }}_Packages \ rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_main_binary-{{ HOSTARCH }}_Packages \
/tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_InRelease \
/tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_Release \ /tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_Release \
/tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_Release.gpg /tmp/debian-{{ DIST }}-debootstrap/var/lib/apt/lists/127.0.0.1_debian_dists_{{ DIST }}_Release.gpg
@ -109,9 +113,11 @@ if [ "{{ VARIANT }}" = "-" ]; then
rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/systemd/catalog/database rm /tmp/debian-{{ DIST }}-debootstrap/var/lib/systemd/catalog/database
rm /tmp/debian-{{ DIST }}-mm/var/lib/systemd/catalog/database rm /tmp/debian-{{ DIST }}-mm/var/lib/systemd/catalog/database
case {{ DIST }} in oldstable | stable)
cap=$(chroot /tmp/debian-{{ DIST }}-debootstrap /sbin/getcap /bin/ping) cap=$(chroot /tmp/debian-{{ DIST }}-debootstrap /sbin/getcap /bin/ping)
expected="/bin/ping cap_net_raw=ep" expected="/bin/ping cap_net_raw=ep"
if [ "{{ DIST }}" = oldstable ]; then
expected="/bin/ping = cap_net_raw+ep"
fi
if [ "$cap" != "$expected" ]; then if [ "$cap" != "$expected" ]; then
echo "expected bin/ping to have capabilities $expected" >&2 echo "expected bin/ping to have capabilities $expected" >&2
echo "but debootstrap produced: $cap" >&2 echo "but debootstrap produced: $cap" >&2
@ -123,10 +129,7 @@ if [ "{{ VARIANT }}" = "-" ]; then
echo "but mmdebstrap produced: $cap" >&2 echo "but mmdebstrap produced: $cap" >&2
exit 1 exit 1
fi fi
;;
esac
fi fi
rm /tmp/debian-{{ DIST }}-mm/var/cache/apt/archives/lock rm /tmp/debian-{{ DIST }}-mm/var/cache/apt/archives/lock
rm /tmp/debian-{{ DIST }}-mm/var/lib/apt/extended_states rm /tmp/debian-{{ DIST }}-mm/var/lib/apt/extended_states
rm /tmp/debian-{{ DIST }}-mm/var/lib/apt/lists/lock rm /tmp/debian-{{ DIST }}-mm/var/lib/apt/lists/lock
@ -141,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" >&2; then if cmp "$f1" "$f2"; then
continue continue
fi fi
# then sort both # then sort both
@ -152,83 +155,49 @@ done
# Because of unreproducible uids (#969631) we created the _apt user ourselves # Because of unreproducible uids (#969631) we created the _apt user ourselves
# and because passwd is not Essential:yes we didn't use useradd. But newer # and because passwd is not Essential:yes we didn't use useradd. But newer
# versions of adduser and shadow will create a different /etc/shadow # versions of adduser and shadow will create a different /etc/shadow
if [ "{{ VARIANT }}" = "-" ] && [ "{{ DIST}}" = oldstable ]; then
for f in shadow shadow-; do for f in shadow shadow-; do
if grep -q '^_apt:!:' /tmp/debian-{{ DIST }}-debootstrap/etc/$f; then if grep -q '^_apt:!:' /tmp/debian-{{ DIST }}-debootstrap/etc/$f; then
sed -i 's/^_apt:\*:\([^:]\+\):0:99999:7:::$/_apt:!:\1::::::/' /tmp/debian-{{ DIST }}-mm/etc/$f sed -i 's/^_apt:\*:\([^:]\+\):0:99999:7:::$/_apt:!:\1::::::/' /tmp/debian-{{ DIST }}-mm/etc/$f
fi fi
done done
# same as above but for cron and systemd groups
for f in gshadow gshadow-; do
for group in systemd-journal systemd-network systemd-resolve crontab; do
if grep -q '^'"$group"':!:' /tmp/debian-{{ DIST }}-debootstrap/etc/$f; then
sed -i 's/^'"$group"':x::/'"$group"':!::/' /tmp/debian-{{ DIST }}-mm/etc/$f
fi fi
done
for log in faillog lastlog; do
f1="/tmp/debian-{{ DIST }}-debootstrap/var/log/$log"
f2="/tmp/debian-{{ DIST }}-mm/var/log/$log"
# skip cmp if file is absent in both chroots
if [ ! -e "$f1" ] && [ ! -e "$f2" ]; then
continue
fi
if ! cmp "$f1" "$f2" >&2; then
# if the files differ, make sure they are all zeroes
cmp -n "$(stat -c %s "$f1")" "$f1" /dev/zero >&2
cmp -n "$(stat -c %s "$f2")" "$f2" /dev/zero >&2
# then delete them
rm "$f1" "$f2"
fi
done done
if [ "{{ VARIANT }}" = "-" ]; then # workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=917773
# the order in which systemd and cron get installed differ and thus the order if ! cmp /tmp/debian-{{ DIST }}-debootstrap/etc/shadow /tmp/debian-{{ DIST }}-mm/etc/shadow; then
# of lines in /etc/group and /etc/gshadow differs echo patching /etc/shadow on {{ DIST }} {{ VARIANT }} >&2
for f in group group- gshadow gshadow-; do 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
for d in mm debootstrap; do cat /tmp/debian-{{ DIST }}-mm/etc/shadow.bak > /tmp/debian-{{ DIST }}-mm/etc/shadow
sort /tmp/debian-{{ DIST }}-$d/etc/$f >/tmp/debian-{{ DIST }}-$d/etc/$f.bak rm /tmp/debian-{{ DIST }}-mm/etc/shadow.bak
mv /tmp/debian-{{ DIST }}-$d/etc/$f.bak /tmp/debian-{{ DIST }}-$d/etc/$f else
done echo no difference for /etc/shadow on {{ DIST }} {{ VARIANT }} >&2
done fi
# the order in which systemd and passwd get installed differ and thus if ! cmp /tmp/debian-{{ DIST }}-debootstrap/etc/shadow- /tmp/debian-{{ DIST }}-mm/etc/shadow-; then
# the order of lines in /etc/shadow and /etc/shadow- differs echo patching /etc/shadow- on {{ DIST }} {{ VARIANT }} >&2
for f in shadow shadow-; do 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
for d in mm debootstrap; do cat /tmp/debian-{{ DIST }}-mm/etc/shadow-.bak > /tmp/debian-{{ DIST }}-mm/etc/shadow-
sort /tmp/debian-{{ DIST }}-$d/etc/$f >/tmp/debian-{{ DIST }}-$d/etc/$f.bak rm /tmp/debian-{{ DIST }}-mm/etc/shadow-.bak
mv /tmp/debian-{{ DIST }}-$d/etc/$f.bak /tmp/debian-{{ DIST }}-$d/etc/$f else
done echo no difference for /etc/shadow- on {{ DIST }} {{ VARIANT }} >&2
done
# and since the order was different, ignore the *- files
for f in shadow- passwd-; do
for d in mm debootstrap; do
rm /tmp/debian-{{ DIST }}-$d/etc/$f
done
done
fi fi
# since debootstrap 1.0.133 there is no tzdata in the buildd variant and thus # Because of unreproducible uids (#969631) we created the _apt user ourselves
# debootstrap creates its own /etc/localtime # and because passwd is not Essential:yes we didn't use useradd. But passwd
if [ "{{ VARIANT }}" = "buildd" ] && [ "{{ DIST }}" != "stable" ] && [ "{{ DIST }}" != "oldstable" ]; then # since 1:4.11.1+dfsg1-1 will create empty mail files, so we create it too.
[ "$(readlink /tmp/debian-{{ DIST }}-debootstrap/etc/localtime)" = /usr/share/zoneinfo/UTC ] # https://bugs.debian.org/1004710
rm /tmp/debian-{{ DIST }}-debootstrap/etc/localtime if [ {{ VARIANT }} = - ]; then
if [ -e /tmp/debian-{{ DIST }}-debootstrap/var/mail/_apt ]; then
touch /tmp/debian-{{ DIST }}-mm/var/mail/_apt
chmod 660 /tmp/debian-{{ DIST }}-mm/var/mail/_apt
chown 100:8 /tmp/debian-{{ DIST }}-mm/var/mail/_apt
fi
fi fi
# starting with systemd 255 upstream dropped splitusr support and depending on
# the installation order, symlink targets are prefixed with /usr or not
# See #1060000 and #1054137
case {{ DIST }} in testing | unstable)
for f in multi-user.target.wants/e2scrub_reap.service timers.target.wants/apt-daily-upgrade.timer timers.target.wants/apt-daily.timer timers.target.wants/e2scrub_all.timer; do
for d in mm debootstrap; do
[ -L "/tmp/debian-{{ DIST }}-$d/etc/systemd/system/$f" ] || continue
oldlink="$(readlink "/tmp/debian-{{ DIST }}-$d/etc/systemd/system/$f")"
case $oldlink in
/usr/*) : ;;
/*) oldlink="/usr$oldlink" ;;
*)
echo unexpected >&2
exit 1
;;
esac
ln -sf "$oldlink" "/tmp/debian-{{ DIST }}-$d/etc/systemd/system/$f"
done
done
;;
esac
# check if the file content differs # check if the file content differs
diff --unified --no-dereference --recursive /tmp/debian-{{ DIST }}-debootstrap /tmp/debian-{{ DIST }}-mm >&2 diff --unified --no-dereference --recursive /tmp/debian-{{ DIST }}-debootstrap /tmp/debian-{{ DIST }}-mm >&2
@ -238,15 +207,14 @@ diff --unified --no-dereference --recursive /tmp/debian-{{ DIST }}-debootstrap /
find /tmp/debian-{{ DIST }}-debootstrap /tmp/debian-{{ DIST }}-mm -type d -print0 | xargs -0 touch --date="@{{ SOURCE_DATE_EPOCH }}" find /tmp/debian-{{ DIST }}-debootstrap /tmp/debian-{{ DIST }}-mm -type d -print0 | xargs -0 touch --date="@{{ SOURCE_DATE_EPOCH }}"
# debootstrap never ran apt -- fixing permissions # debootstrap never ran apt -- fixing permissions
for d in ./var/lib/apt/lists/partial ./var/cache/apt/archives/partial; do for d in ./var/lib/apt/lists/partial ./var/cache/apt/archives/partial; do
unmergedPATH="$PATH$(if [ "{{ DIST }}" = oldstable ]; then echo :/bin:/sbin; fi)" chroot /tmp/debian-{{ DIST }}-debootstrap chmod 0700 $d
PATH="$unmergedPATH" chroot /tmp/debian-{{ DIST }}-debootstrap chmod 0700 $d chroot /tmp/debian-{{ DIST }}-debootstrap chown _apt:root $d
PATH="$unmergedPATH" chroot /tmp/debian-{{ DIST }}-debootstrap chown "$(id -u _apt):root" $d
done done
tar -C /tmp/debian-{{ DIST }}-debootstrap --numeric-owner --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/root1.tar . tar -C /tmp/debian-{{ DIST }}-debootstrap --numeric-owner --sort=name --clamp-mtime --mtime=$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds) -cf /tmp/root1.tar .
tar -C /tmp/debian-{{ DIST }}-mm --numeric-owner --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/root2.tar . tar -C /tmp/debian-{{ DIST }}-mm --numeric-owner --sort=name --clamp-mtime --mtime=$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds) -cf /tmp/root2.tar .
tar --full-time --verbose -tf /tmp/root1.tar > /tmp/root1.tar.list tar --full-time --verbose -tf /tmp/root1.tar > /tmp/root1.tar.list
tar --full-time --verbose -tf /tmp/root2.tar > /tmp/root2.tar.list tar --full-time --verbose -tf /tmp/root2.tar > /tmp/root2.tar.list
diff -u /tmp/root1.tar.list /tmp/root2.tar.list >&2 diff -u /tmp/root1.tar.list /tmp/root2.tar.list
rm /tmp/root1.tar /tmp/root2.tar /tmp/root1.tar.list /tmp/root2.tar.list rm /tmp/root1.tar /tmp/root2.tar /tmp/root1.tar.list /tmp/root2.tar.list
# check if file properties (permissions, ownership, symlink names, modification time) differ # check if file properties (permissions, ownership, symlink names, modification time) differ

View file

@ -1,28 +1,35 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }}" EXIT INT TERM
case {{ MODE }} in unshare | fakechroot) : ;; *) exit 1 ;; esac
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" adduser --gecos user --disabled-password user
sysctl -w kernel.unprivileged_userns_clone=1
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
mount -o size=4G -t tmpfs tmpfs /tmp # workaround for #1010957
{{ CMD }} --mode=root --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot-root.{{ FORMAT }} {{ MIRROR }}
if [ "{{ FORMAT }}" = tar ]; then
printf 'ustar ' | cmp --bytes=6 --ignore-initial=257:0 /tmp/debian-chroot-root.tar -
elif [ "{{ FORMAT }}" = squashfs ]; then
printf 'hsqs' | cmp --bytes=4 /tmp/debian-chroot-root.squashfs -
elif [ "{{ FORMAT }}" = ext2 ]; then
printf '\123\357' | cmp --bytes=2 --ignore-initial=1080:0 /tmp/debian-chroot-root.ext2 -
else
echo "unknown format: {{ FORMAT }}" >&2
exit 1
fi fi
prefix="runuser -u ${SUDO_USER:-user} --" runuser -u user -- {{ CMD }} --mode=unshare --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot-unshare.{{ FORMAT }} {{ MIRROR }}
fi cmp /tmp/debian-chroot-root.{{ FORMAT }} /tmp/debian-chroot-unshare.{{ FORMAT }}
rm /tmp/debian-chroot-unshare.{{ FORMAT }}
$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }} {{ MIRROR }} case {{ VARIANT }} in essential|apt|minbase|buildd)
cmp ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }} \ # variants important and standard differ because permissions drwxr-sr-x
|| diffoscope ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.{{ FORMAT }} /tmp/debian-chroot-{{ MODE }}.{{ FORMAT }} # and extended attributes of ./var/log/journal/ cannot be preserved
# in fakechroot mode
# we cannot test chrootless mode here, because mmdebstrap relies on the runuser -u user -- {{ CMD }} --mode=fakechroot --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot-fakechroot.{{ FORMAT }} {{ MIRROR }}
# usrmerge package to set up merged-/usr and that doesn't work in chrootless cmp /tmp/debian-chroot-root.{{ FORMAT }} /tmp/debian-chroot-fakechroot.{{ FORMAT }}
# mode rm /tmp/debian-chroot-fakechroot.{{ FORMAT }}
;;
esac
rm /tmp/debian-chroot-root.{{ FORMAT }}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,45 @@
#!/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
if [ "{{ MODE }}" = unshare ]; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
sysctl -w kernel.unprivileged_userns_clone=1
fi
prefix=
[ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && prefix="runuser -u user --"
[ "{{ MODE }}" = "fakechroot" ] && prefix="$prefix fakechroot fakeroot"
$prefix {{ CMD }} --mode={{ MODE }} --variant=apt --architectures=arm64 {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
# we ignore differences between architectures by ignoring some files
# and renaming others
# in proot mode, some extra files are put there by proot
{ tar -tf /tmp/debian-chroot.tar \
| grep -v '^\./lib/ld-linux-aarch64\.so\.1$' \
| grep -v '^\./lib/aarch64-linux-gnu/ld-linux-aarch64\.so\.1$' \
| grep -v '^\./usr/share/doc/[^/]\+/changelog\(\.Debian\)\?\.arm64\.gz$' \
| sed 's/aarch64-linux-gnu/x86_64-linux-gnu/' \
| sed 's/arm64/amd64/';
} | sort > tar2.txt
{ cat tar1.txt \
| grep -v '^\./usr/bin/i386$' \
| grep -v '^\./usr/bin/x86_64$' \
| grep -v '^\./lib64/$' \
| grep -v '^\./lib64/ld-linux-x86-64\.so\.2$' \
| grep -v '^\./lib/x86_64-linux-gnu/ld-linux-x86-64\.so\.2$' \
| grep -v '^\./lib/x86_64-linux-gnu/libmvec-2\.[0-9]\+\.so$' \
| grep -v '^\./lib/x86_64-linux-gnu/libmvec\.so\.1$' \
| grep -v '^\./usr/share/doc/[^/]\+/changelog\(\.Debian\)\?\.amd64\.gz$' \
| grep -v '^\./usr/share/man/man8/i386\.8\.gz$' \
| grep -v '^\./usr/share/man/man8/x86_64\.8\.gz$';
[ "{{ MODE }}" = "proot" ] && printf "./etc/ld.so.preload\n";
} | sort | diff -u - tar2.txt
rm /tmp/debian-chroot.tar

View file

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

View file

@ -24,6 +24,5 @@ rm /tmp/debian-chroot/etc/hostname
rm /tmp/debian-chroot/etc/resolv.conf rm /tmp/debian-chroot/etc/resolv.conf
rm /tmp/debian-chroot/var/lib/apt/lists/lock rm /tmp/debian-chroot/var/lib/apt/lists/lock
rm /tmp/debian-chroot/var/lib/dpkg/status rm /tmp/debian-chroot/var/lib/dpkg/status
rm /tmp/debian-chroot/var/lib/dpkg/arch
# the rest should be empty directories that we can rmdir recursively # the rest should be empty directories that we can rmdir recursively
find /tmp/debian-chroot -depth -print0 | xargs -0 rmdir find /tmp/debian-chroot -depth -print0 | xargs -0 rmdir

View file

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

View file

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

View file

@ -6,21 +6,29 @@
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
prefix= prefix=
include=, include=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != root ] && [ "{{ MODE }}" != auto ]; then if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != root ] && [ "{{ MODE }}" != auto ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then # this must be qemu
if ! id -u user >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" adduser --gecos user --disabled-password user
fi fi
prefix="runuser -u ${SUDO_USER:-user} --" if [ "{{ MODE }}" = unshare ]; then
if [ "{{ VARIANT }}" = extract ] || [ "{{ VARIANT }}" = custom ]; then if [ ! -e /mmdebstrap-testenv ]; then
include="$(tr '\n' ',' <pkglist.txt)" echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
sysctl -w kernel.unprivileged_userns_clone=1
fi
prefix="runuser -u user --"
if [ "{{ MODE }}" = extract ] || [ "{{ MODE }}" = custom ]; then
include="--include=$(cat pkglist.txt | tr '\n' ',')"
fi fi
fi fi
$prefix {{ CMD }} --mode={{ MODE }} --include="$include" --dry-run --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }} $prefix {{ CMD }} --mode={{ MODE }} $include --dry-run --variant={{ VARIANT }} {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
if [ -e /tmp/debian-chroot.tar ]; then if [ -e /tmp/debian-chroot.tar ]; then
echo "/tmp/debian-chroot.tar must not be created with --dry-run" >&2 echo "/tmp/debian-chroot.tar must not be created with --dry-run" >&2
exit 1 exit 1

View file

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

View file

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

View file

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

View file

@ -40,6 +40,6 @@ tar -C /tmp/debian-chroot --one-file-system -c . \
tar -t \ tar -t \
| grep -v "^./etc/apt/sources.list.d/0000deb822.sources$" \ | grep -v "^./etc/apt/sources.list.d/0000deb822.sources$" \
| grep -v "^./etc/apt/sources.list.d/0001main.list$" \ | grep -v "^./etc/apt/sources.list.d/0001main.list$" \
| grep -v "^./etc/apt/sources.list.d/0002sources.list" | grep -v "^./etc/apt/sources.list.d/0002sources.list";
printf "./etc/apt/sources.list\n" printf "./etc/apt/sources.list\n";
} | sort | diff -u tar1.txt - } | sort | diff -u tar1.txt -

View file

@ -39,6 +39,6 @@ tar -C /tmp/debian-chroot --one-file-system -c . \
tar -t \ tar -t \
| grep -v "^./etc/apt/sources.list.d/0000deb822.sources$" \ | grep -v "^./etc/apt/sources.list.d/0000deb822.sources$" \
| grep -v "^./etc/apt/sources.list.d/0001main.sources$" \ | grep -v "^./etc/apt/sources.list.d/0001main.sources$" \
| grep -v "^./etc/apt/sources.list.d/0002sources.list$" | grep -v "^./etc/apt/sources.list.d/0002sources.list$";
printf "./etc/apt/sources.list\n" printf "./etc/apt/sources.list\n";
} | sort | diff -u tar1.txt - } | sort | diff -u tar1.txt -

View file

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

View file

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

View file

@ -7,17 +7,15 @@ if [ {{ MODE }} != unshare ] && [ {{ MODE }} != root ]; then
exit 1 exit 1
fi fi
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" if [ "$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then
fi adduser --gecos user --disabled-password user
prefix="runuser -u ${SUDO_USER:-user} --"
fi fi
prefix=
[ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && prefix="runuser -u user --"
# this mimics what apt does in apt-pkg/deb/dpkgpm.cc/pkgDPkgPM::StartPtyMagic() # this mimics what apt does in apt-pkg/deb/dpkgpm.cc/pkgDPkgPM::StartPtyMagic()
cat > /tmp/test.c << 'END' cat > /tmp/test.c << 'END'
@ -118,24 +116,22 @@ END
# use script to create a fake tty # use script to create a fake tty
# run all tests as root and as a normal user (the latter requires ptmxmode=666) # run all tests as root and as a normal user (the latter requires ptmxmode=666)
script -qfec "$prefix {{ CMD }} --mode={{ MODE }} --variant=apt \ script -qfc "$prefix {{ CMD }} --mode={{ MODE }} --variant=apt \
--include=gcc,libc6-dev,python3,passwd \ --include=gcc,libc6-dev,python3 \
--customize-hook='chroot \"\$1\" useradd --home-dir /home/user --create-home user' \ --customize-hook='chroot \"\$1\" adduser --gecos user --disabled-password user' \
--customize-hook='chroot \"\$1\" python3 -c \"import pty; print(pty.openpty())\"' \ --customize-hook='chroot \"\$1\" python3 -c \"import pty; print(pty.openpty())\"' \
--customize-hook='chroot \"\$1\" runuser -u user -- python3 -c \"import pty; print(pty.openpty())\"' \ --customize-hook='chroot \"\$1\" runuser -u user -- python3 -c \"import pty; print(pty.openpty())\"' \
--customize-hook='chroot \"\$1\" script -c \"echo foobar\"' \ --customize-hook='chroot \"\$1\" script -c \"echo foobar\"' \
--customize-hook='chroot \"\$1\" runuser -u user -- env --chdir=/home/user script -c \"echo foobar\"' \ --customize-hook='chroot \"\$1\" runuser -u user -- env --chdir=/home/user script -c \"echo foobar\"' \
--customize-hook='chroot \"\$1\" apt-get install --yes doc-debian 2>&1 | tee \"\$1\"/tmp/log' \ --customize-hook='chroot \"\$1\" apt-get install --yes doc-debian 2>&1 | tee /tmp/log' \
--customize-hook=\"copy-in /tmp/test.c /tmp\" \ --customize-hook=\"copy-in /tmp/test.c /tmp\" \
--customize-hook='chroot \"\$1\" gcc /tmp/test.c -o /tmp/test' \ --customize-hook='chroot \"\$1\" gcc /tmp/test.c -o /tmp/test' \
--customize-hook='chroot \"\$1\" /tmp/test' \ --customize-hook='chroot \"\$1\" /tmp/test' \
--customize-hook='chroot \"\$1\" runuser -u user -- /tmp/test' \ --customize-hook='chroot \"\$1\" runuser -u user -- /tmp/test' \
--customize-hook='rm \"\$1\"/tmp/test \"\$1\"/tmp/test.c' \ --customize-hook='rm \"\$1\"/tmp/test \"\$1\"/tmp/test.c' \
--customize-hook=\"copy-out /tmp/log /tmp\" \
{{ 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

@ -4,10 +4,8 @@
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
trap "rm -f InRelease; rm -rf /tmp/debian-chroot.tar /tmp/expected" EXIT INT TERM trap "rm -f Release; rm -rf /tmp/debian-chroot" EXIT INT TERM
/usr/lib/apt/apt-helper download-file "{{ MIRROR }}/dists/{{ DIST }}/InRelease" InRelease /usr/lib/apt/apt-helper download-file "{{ MIRROR }}/dists/{{ DIST }}/Release" Release
codename=$(awk '/^Codename: / { print $2; }' InRelease) codename=$(awk '/^Codename: / { print $2; }' Release)
{{ CMD }} --mode={{ MODE }} --variant=apt "$codename" /tmp/debian-chroot.tar {{ MIRROR }} {{ CMD }} --mode={{ MODE }} --variant=apt $codename /tmp/debian-chroot {{ MIRROR }}
echo "deb {{ MIRROR }} $codename main" >/tmp/expected echo "deb {{ MIRROR }} $codename main" | diff -u - /tmp/debian-chroot/etc/apt/sources.list
tar --to-stdout --extract --file /tmp/debian-chroot.tar ./etc/apt/sources.list \
| diff -u /tmp/expected -

View file

@ -7,8 +7,4 @@ echo no-pager >/tmp/config
printf 'path-exclude=/usr/share/doc/*\nno-pager\npath-include=/usr/share/doc/dpkg/copyright\n' | cmp /tmp/debian-chroot/etc/dpkg/dpkg.cfg.d/99mmdebstrap - printf 'path-exclude=/usr/share/doc/*\nno-pager\npath-include=/usr/share/doc/dpkg/copyright\n' | cmp /tmp/debian-chroot/etc/dpkg/dpkg.cfg.d/99mmdebstrap -
rm /tmp/debian-chroot/etc/dpkg/dpkg.cfg.d/99mmdebstrap rm /tmp/debian-chroot/etc/dpkg/dpkg.cfg.d/99mmdebstrap
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort > tar2.txt tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort > tar2.txt
{ { grep -v '^./usr/share/doc/.' tar1.txt; echo ./usr/share/doc/dpkg/; echo ./usr/share/doc/dpkg/copyright; } | sort | diff -u - tar2.txt
grep -v '^./usr/share/doc/.' tar1.txt
echo ./usr/share/doc/dpkg/
echo ./usr/share/doc/dpkg/copyright
} | sort | diff -u - tar2.txt

View file

@ -12,24 +12,18 @@ EOF
SCRIPT SCRIPT
chmod +x /tmp/checkeatmydata.sh chmod +x /tmp/checkeatmydata.sh
# first four bytes: magic # first four bytes: magic
elfheader='\177ELF' elfheader="\\177ELF"
# fifth byte: bits # fifth byte: bits
case "$(dpkg-architecture -qDEB_HOST_ARCH_BITS)" in case "$(dpkg-architecture -qDEB_HOST_ARCH_BITS)" in
32) elfheader="$elfheader\\001";; 32) elfheader="$elfheader\\001";;
64) elfheader="$elfheader\\002";; 64) elfheader="$elfheader\\002";;
*) *) echo "bits not supported"; exit 1;;
echo "bits not supported"
exit 1
;;
esac esac
# sixth byte: endian # sixth byte: endian
case "$(dpkg-architecture -qDEB_HOST_ARCH_ENDIAN)" in case "$(dpkg-architecture -qDEB_HOST_ARCH_ENDIAN)" in
little) elfheader="$elfheader\\001";; little) elfheader="$elfheader\\001";;
big) elfheader="$elfheader\\002";; big) elfheader="$elfheader\\002";;
*) *) echo "endian not supported"; exit 1;;
echo "endian not supported"
exit 1
;;
esac esac
# seventh and eigth byte: elf version (1) and abi (unset) # seventh and eigth byte: elf version (1) and abi (unset)
elfheader="$elfheader\\001\\000" elfheader="$elfheader\\001\\000"
@ -40,9 +34,6 @@ elfheader="$elfheader\\001\\000"
--hook-dir=./hooks/eatmydata \ --hook-dir=./hooks/eatmydata \
--customize-hook='printf "'"$elfheader"'" | cmp --bytes=8 - "$1"/usr/bin/dpkg' \ --customize-hook='printf "'"$elfheader"'" | cmp --bytes=8 - "$1"/usr/bin/dpkg' \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }} {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
tar -C /tmp/debian-chroot --one-file-system -c . \ tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -
| tar -t \
| sort \
| diff -u tar1.txt -
rm /tmp/checkeatmydata.sh rm /tmp/checkeatmydata.sh
rm -r /tmp/debian-chroot rm -r /tmp/debian-chroot

View file

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

View file

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

View file

@ -1,12 +0,0 @@
#!/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

@ -8,13 +8,13 @@ echo tzdata tzdata/Zones/Europe select Berlin | chroot "$1" debconf-set-selectio
SCRIPT SCRIPT
chmod +x /tmp/essential.sh chmod +x /tmp/essential.sh
{{ CMD }} --mode=root --variant=apt --include=tzdata --essential-hook='echo tzdata tzdata/Areas select Europe | chroot "$1" debconf-set-selections' --essential-hook=/tmp/essential.sh {{ DIST }} /tmp/debian-chroot {{ MIRROR }} {{ CMD }} --mode=root --variant=apt --include=tzdata --essential-hook='echo tzdata tzdata/Areas select Europe | chroot "$1" debconf-set-selections' --essential-hook=/tmp/essential.sh {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
[ "$(readlink /tmp/debian-chroot/etc/localtime)" = "/usr/share/zoneinfo/Europe/Berlin" ] echo Europe/Berlin | cmp /tmp/debian-chroot/etc/timezone
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort \ tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort \
| grep -v '^./etc/localtime' \ | grep -v '^./etc/localtime' \
| grep -v '^./etc/timezone' \ | grep -v '^./etc/timezone' \
| grep -v '^./usr/sbin/tzconfig' \ | grep -v '^./usr/sbin/tzconfig' \
| grep -v '^./usr/share/doc/tzdata' \ | grep -v '^./usr/share/doc/tzdata' \
| grep -v '^./usr/share/lintian/overrides/tzdata' \
| grep -v '^./usr/share/zoneinfo' \ | grep -v '^./usr/share/zoneinfo' \
| grep -v '^./var/lib/dpkg/info/tzdata.' \ | grep -v '^./var/lib/dpkg/info/tzdata.' \
| grep -v '^./var/lib/apt/extended_states$' \
| diff -u tar1.txt - | diff -u tar1.txt -

View file

@ -1,9 +1,6 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
trap "rm -f /tmp/exists" EXIT INT TERM
touch /tmp/exists touch /tmp/exists
ret=0 ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/exists {{ MIRROR }} || ret=$? {{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/exists {{ MIRROR }} || ret=$?

View file

@ -1,9 +1,6 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
trap 'rm -rf /tmp/quoted\"path' EXIT INT TERM
ret=0 ret=0
{{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/quoted\"path {{ MIRROR }} || ret=$? {{ CMD }} --mode={{ MODE }} --variant=apt {{ DIST }} /tmp/quoted\"path {{ MIRROR }} || ret=$?
if [ "$ret" = 0 ]; then if [ "$ret" = 0 ]; then

View file

@ -5,7 +5,8 @@ if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir /home/user --create-home user adduser --gecos user --disabled-password user
sysctl -w kernel.unprivileged_userns_clone=1
rm /etc/subuid rm /etc/subuid
ret=0 ret=0
runuser -u user -- {{ CMD }} --mode=unshare --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }} || ret=$? runuser -u user -- {{ CMD }} --mode=unshare --variant=apt {{ DIST }} /tmp/debian-chroot {{ MIRROR }} || ret=$?
@ -13,4 +14,4 @@ if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2 echo expected failure but got exit $ret >&2
exit 1 exit 1
fi fi
[ ! -e /tmp/debian-chroot ] rm -r /tmp/debian-chroot

View file

@ -5,7 +5,8 @@ if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir /home/user --create-home user adduser --gecos user --disabled-password user
sysctl -w kernel.unprivileged_userns_clone=1
awk -F: '$1!="user"' /etc/subuid > /etc/subuid.tmp awk -F: '$1!="user"' /etc/subuid > /etc/subuid.tmp
mv /etc/subuid.tmp /etc/subuid mv /etc/subuid.tmp /etc/subuid
ret=0 ret=0
@ -14,4 +15,4 @@ if [ "$ret" = 0 ]; then
echo expected failure but got exit $ret >&2 echo expected failure but got exit $ret >&2
exit 1 exit 1
fi fi
[ ! -e /tmp/debian-chroot ] rm -r /tmp/debian-chroot

View file

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

View file

@ -6,7 +6,7 @@ if [ ! -e /mmdebstrap-testenv ]; then
exit 1 exit 1
fi fi
if [ "$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then if [ "$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then
useradd --home-dir /home/user --create-home user adduser --gecos user --disabled-password user
fi fi
prefix= prefix=
[ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && prefix="runuser -u user --" [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && prefix="runuser -u user --"

View file

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

View file

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

View file

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

View file

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

View file

@ -6,41 +6,21 @@
# - installs only few files # - installs only few files
# - doesn't change its name regularly (like gcc-*-base) # - doesn't change its name regularly (like gcc-*-base)
case "$(dpkg --print-architecture)" in
arm64)
native_arch=arm64
foreign_arch=amd64
;;
amd64)
native_arch=amd64
foreign_arch=arm64
;;
*)
echo "unsupported native architecture" >&2
exit 1
;;
esac
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
{{ CMD }} --mode=root --variant=apt \ {{ CMD }} --mode=root --variant=apt --architectures=amd64,arm64 --include=libmagic-mgc:arm64 {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
--architectures="$native_arch,$foreign_arch" \ { echo "amd64"; echo "arm64"; } | cmp /tmp/debian-chroot/var/lib/dpkg/arch -
--include="libmagic-mgc:$foreign_arch" \ rm /tmp/debian-chroot/var/lib/dpkg/arch
{{ DIST }} /tmp/debian-chroot {{ MIRROR }} rm /tmp/debian-chroot/var/lib/apt/extended_states
{ rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.list
echo "$native_arch" rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.md5sums
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/lib/file/magic.mgc
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/README.Debian rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/README.Debian
rm -f /tmp/debian-chroot/usr/share/doc/libmagic-mgc/"changelog.Debian.$foreign_arch.gz"
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.Debian.gz rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.Debian.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.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/copyright
rm /tmp/debian-chroot/usr/share/file/magic.mgc rm /tmp/debian-chroot/usr/share/file/magic.mgc
rm /tmp/debian-chroot/usr/share/misc/magic.mgc rm /tmp/debian-chroot/usr/share/misc/magic.mgc
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.list
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.md5sums
rmdir /tmp/debian-chroot/usr/share/doc/libmagic-mgc/ rmdir /tmp/debian-chroot/usr/share/doc/libmagic-mgc/
rmdir /tmp/debian-chroot/usr/share/file/magic/ rmdir /tmp/debian-chroot/usr/share/file/magic/
rmdir /tmp/debian-chroot/usr/share/file/ rmdir /tmp/debian-chroot/usr/share/file/

View file

@ -0,0 +1,22 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
{{ CMD }} --mode=root --variant=apt --architectures=amd64 --architectures=arm64 --include=libmagic-mgc:arm64 {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
{ echo "amd64"; echo "arm64"; } | cmp /tmp/debian-chroot/var/lib/dpkg/arch -
rm /tmp/debian-chroot/var/lib/dpkg/arch
rm /tmp/debian-chroot/var/lib/apt/extended_states
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.list
rm /tmp/debian-chroot/var/lib/dpkg/info/libmagic-mgc.md5sums
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.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/copyright
rm /tmp/debian-chroot/usr/share/file/magic.mgc
rm /tmp/debian-chroot/usr/share/misc/magic.mgc
rmdir /tmp/debian-chroot/usr/share/doc/libmagic-mgc/
rmdir /tmp/debian-chroot/usr/share/file/magic/
rmdir /tmp/debian-chroot/usr/share/file/
rmdir /tmp/debian-chroot/usr/lib/file/
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -
rm -r /tmp/debian-chroot

View file

@ -1,9 +1,6 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
pkgs=base-files,base-passwd,busybox,debianutils,dpkg,libc-bin,mawk,tar pkgs=base-files,base-passwd,busybox,debianutils,dpkg,libc-bin,mawk,tar
# busybox --install -s will install symbolic links into the rootfs, leaving # busybox --install -s will install symbolic links into the rootfs, leaving
# existing files untouched. It has to run after extraction (otherwise there is # existing files untouched. It has to run after extraction (otherwise there is
@ -22,20 +19,15 @@ chroot /tmp/debian-chroot dpkg-query -f '${binary:Package}\n' -W \
rm /tmp/expected rm /tmp/expected
for cmd in echo cat sed grep; do for cmd in echo cat sed grep; do
test -L /tmp/debian-chroot/bin/$cmd test -L /tmp/debian-chroot/bin/$cmd
test "$(readlink /tmp/debian-chroot/bin/$cmd)" = "/usr/bin/busybox" test "$(readlink /tmp/debian-chroot/bin/$cmd)" = "/bin/busybox"
done done
for cmd in sort tee; do for cmd in sort; do
test -L /tmp/debian-chroot/usr/bin/$cmd test -L /tmp/debian-chroot/usr/bin/$cmd
test "$(readlink /tmp/debian-chroot/usr/bin/$cmd)" = "/usr/bin/busybox" test "$(readlink /tmp/debian-chroot/usr/bin/$cmd)" = "/bin/busybox"
done done
# if /bin or /sbin are not symlinks, add /bin and /sbin to PATH
if [ ! -L /tmp/debian-chroot/bin ] || [ ! -L /tmp/debian-chroot/sbin ]; then
export PATH="$PATH:/sbin:/bin"
fi
chroot /tmp/debian-chroot echo foobar \ chroot /tmp/debian-chroot echo foobar \
| chroot /tmp/debian-chroot cat \ | chroot /tmp/debian-chroot cat \
| chroot /tmp/debian-chroot sort \ | chroot /tmp/debian-chroot sort \
| chroot /tmp/debian-chroot tee /dev/null \
| chroot /tmp/debian-chroot sed 's/foobar/blubber/' \ | chroot /tmp/debian-chroot sed 's/foobar/blubber/' \
| chroot /tmp/debian-chroot grep blubber >/dev/null | chroot /tmp/debian-chroot grep blubber >/dev/null
rm -r /tmp/debian-chroot

View file

@ -1,30 +1,21 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
if [ "$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then
[ {{ VARIANT }} = "custom" ]
[ {{ MODE }} = "chrootless" ]
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" adduser --gecos user --disabled-password user
fi fi
prefix="runuser -u ${SUDO_USER:-user} --" prefix=
fi [ "$(id -u)" -eq 0 ] && prefix="runuser -u user --"
$prefix {{ CMD }} --mode=chrootless --variant=custom --include=doc-debian {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} --include=doc-debian {{ DIST }} /tmp/debian-chroot {{ MIRROR }} tar -C /tmp/debian-chroot --owner=0 --group=0 --numeric-owner --sort=name --clamp-mtime --mtime=$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds) -cf /tmp/debian-chroot.tar .
tar -C /tmp/debian-chroot --owner=0 --group=0 --numeric-owner --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/debian-chroot.tar .
tar tvf /tmp/debian-chroot.tar > doc-debian.tar.list tar tvf /tmp/debian-chroot.tar > doc-debian.tar.list
rm /tmp/debian-chroot.tar rm /tmp/debian-chroot.tar
# delete contents of doc-debian # delete contents of doc-debian
rm /tmp/debian-chroot/usr/share/doc-base/doc-debian.debian-* rm /tmp/debian-chroot/usr/share/doc-base/debian-*
rm -r /tmp/debian-chroot/usr/share/doc/debian rm -r /tmp/debian-chroot/usr/share/doc/debian
rm -r /tmp/debian-chroot/usr/share/doc/doc-debian rm -r /tmp/debian-chroot/usr/share/doc/doc-debian
# delete real files # delete real files
@ -33,7 +24,6 @@ rm /tmp/debian-chroot/etc/fstab
rm /tmp/debian-chroot/etc/hostname rm /tmp/debian-chroot/etc/hostname
rm /tmp/debian-chroot/etc/resolv.conf rm /tmp/debian-chroot/etc/resolv.conf
rm /tmp/debian-chroot/var/lib/dpkg/status rm /tmp/debian-chroot/var/lib/dpkg/status
rm /tmp/debian-chroot/var/lib/dpkg/arch
rm /tmp/debian-chroot/var/cache/apt/archives/lock rm /tmp/debian-chroot/var/cache/apt/archives/lock
rm /tmp/debian-chroot/var/lib/dpkg/lock rm /tmp/debian-chroot/var/lib/dpkg/lock
rm /tmp/debian-chroot/var/lib/dpkg/lock-frontend rm /tmp/debian-chroot/var/lib/dpkg/lock-frontend

View file

@ -2,22 +2,15 @@
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }} export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
if [ "$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then
[ {{ VARIANT }} = "custom" ]
[ {{ MODE }} = "chrootless" ]
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" adduser --gecos user --disabled-password user
fi fi
prefix="runuser -u ${SUDO_USER:-user} --" prefix=
fi [ "$(id -u)" -eq 0 ] && prefix="runuser -u user --"
$prefix {{ CMD }} --mode=chrootless --variant=custom --include=doc-debian {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} --include=doc-debian {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar tvf /tmp/debian-chroot.tar | grep -v ' ./dev' | diff -u doc-debian.tar.list - tar tvf /tmp/debian-chroot.tar | grep -v ' ./dev' | diff -u doc-debian.tar.list -
rm /tmp/debian-chroot.tar rm /tmp/debian-chroot.tar

View file

@ -2,32 +2,23 @@
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }} export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
if [ "$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then
[ {{ VARIANT }} = "custom" ]
[ {{ MODE }} = "chrootless" ]
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" adduser --gecos user --disabled-password user
fi fi
prefix="runuser -u ${SUDO_USER:-user} --" prefix=
fi [ "$(id -u)" -eq 0 ] && prefix="runuser -u user --"
$prefix {{ CMD }} --mode=chrootless --skip=cleanup/tmp --variant=custom --include=doc-debian --setup-hook='touch "$1/tmp/setup"' --customize-hook='touch "$1/tmp/customize"' {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
$prefix {{ CMD }} --mode={{ MODE }} --skip=cleanup/tmp --variant={{ VARIANT }} --include=doc-debian --setup-hook='touch "$1/tmp/setup"' --customize-hook='touch "$1/tmp/customize"' {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
rm /tmp/debian-chroot/tmp/setup rm /tmp/debian-chroot/tmp/setup
rm /tmp/debian-chroot/tmp/customize rm /tmp/debian-chroot/tmp/customize
tar -C /tmp/debian-chroot --owner=0 --group=0 --numeric-owner --sort=name --clamp-mtime --mtime="$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds)" -cf /tmp/debian-chroot.tar . tar -C /tmp/debian-chroot --owner=0 --group=0 --numeric-owner --sort=name --clamp-mtime --mtime=$(date --utc --date=@{{ SOURCE_DATE_EPOCH }} --iso-8601=seconds) -cf /tmp/debian-chroot.tar .
tar tvf /tmp/debian-chroot.tar | grep -v ' ./dev' | diff -u doc-debian.tar.list - tar tvf /tmp/debian-chroot.tar | grep -v ' ./dev' | diff -u doc-debian.tar.list -
rm /tmp/debian-chroot.tar rm /tmp/debian-chroot.tar
# delete contents of doc-debian # delete contents of doc-debian
rm /tmp/debian-chroot/usr/share/doc-base/doc-debian.debian-* rm /tmp/debian-chroot/usr/share/doc-base/debian-*
rm -r /tmp/debian-chroot/usr/share/doc/debian rm -r /tmp/debian-chroot/usr/share/doc/debian
rm -r /tmp/debian-chroot/usr/share/doc/doc-debian rm -r /tmp/debian-chroot/usr/share/doc/doc-debian
# delete real files # delete real files
@ -36,7 +27,6 @@ rm /tmp/debian-chroot/etc/fstab
rm /tmp/debian-chroot/etc/hostname rm /tmp/debian-chroot/etc/hostname
rm /tmp/debian-chroot/etc/resolv.conf rm /tmp/debian-chroot/etc/resolv.conf
rm /tmp/debian-chroot/var/lib/dpkg/status rm /tmp/debian-chroot/var/lib/dpkg/status
rm /tmp/debian-chroot/var/lib/dpkg/arch
rm /tmp/debian-chroot/var/cache/apt/archives/lock rm /tmp/debian-chroot/var/cache/apt/archives/lock
rm /tmp/debian-chroot/var/lib/dpkg/lock rm /tmp/debian-chroot/var/lib/dpkg/lock
rm /tmp/debian-chroot/var/lib/dpkg/lock-frontend rm /tmp/debian-chroot/var/lib/dpkg/lock-frontend

View file

@ -0,0 +1,43 @@
#!/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

@ -1,43 +1,22 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
if [ "$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then
[ {{ VARIANT }} = "custom" ]
[ {{ MODE }} = "chrootless" ]
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" adduser --gecos user --disabled-password user
fi fi
prefix="runuser -u ${SUDO_USER:-user} --" prefix=
fi [ "$(id -u)" -eq 0 ] && prefix="runuser -u user --"
$prefix {{ CMD }} --mode=chrootless --variant=custom --architectures=arm64 --include=libmagic-mgc {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
case "$(dpkg --print-architecture)" in
arm64)
foreign_arch=amd64
;;
amd64)
foreign_arch=arm64
;;
*)
echo "unsupported native architecture" >&2
exit 1
;;
esac
$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} --architectures="$foreign_arch" --include=libmagic-mgc {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
# delete contents of libmagic-mgc # delete contents of libmagic-mgc
rm /tmp/debian-chroot/usr/lib/file/magic.mgc 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/README.Debian
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/changelog.Debian.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/changelog.gz
rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/copyright rm /tmp/debian-chroot/usr/share/doc/libmagic-mgc/copyright
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/file/magic.mgc
rm /tmp/debian-chroot/usr/share/misc/magic.mgc rm /tmp/debian-chroot/usr/share/misc/magic.mgc
# delete real files # delete real files

View file

@ -1,43 +0,0 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/debian-chroot-{{ MODE }}.tar /tmp/debian-chroot-root-normal.tar" EXIT INT TERM
[ "$(id -u)" -eq 0 ]
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}"
fi
prefix="runuser -u ${SUDO_USER:-user} --"
fi
MMTARFILTER=
[ -x /usr/bin/mmtarfilter ] && MMTARFILTER=/usr/bin/mmtarfilter
[ -x ./tarfilter ] && MMTARFILTER=./tarfilter
filter() {
"$MMTARFILTER" \
--path-exclude=/usr/bin/uncompress \
--path-exclude=/var/cache/debconf/config.dat-old \
--path-exclude=/var/cache/debconf/templates.dat-old \
--path-exclude=/var/lib/dpkg/available \
--path-exclude=/var/lib/dpkg/diversions \
--path-exclude=/var/lib/dpkg/diversions-old \
--path-exclude=/var/lib/dpkg/cmethopt \
--path-exclude=/var/lib/dpkg/status-old \
--path-exclude=/var/lib/shells.state
}
# base for comparison without jessie-or-older hook
{{ CMD }} --mode=root --variant={{ VARIANT }} {{ DIST }} - {{ MIRROR }} >/tmp/debian-chroot-root-normal.tar
$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} --hook-dir=./hooks/jessie-or-older {{ DIST }} - {{ MIRROR }} | filter >/tmp/debian-chroot-{{ MODE }}.tar
filter </tmp/debian-chroot-root-normal.tar | cmp - /tmp/debian-chroot-{{ MODE }}.tar

View file

@ -5,12 +5,7 @@ if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
for f in /etc/apt/trusted.gpg.d/*.gpg /etc/apt/trusted.gpg.d/*.asc; do rm /etc/apt/trusted.gpg.d/*.gpg
[ -e "$f" ] || continue
rm "$f"
done
rmdir /etc/apt/trusted.gpg.d
mkdir /etc/apt/trusted.gpg.d
{{ CMD }} --mode=root --variant=apt --keyring=/usr/share/keyrings/debian-archive-keyring.gpg --keyring=/usr/share/keyrings/ {{ DIST }} /tmp/debian-chroot "deb {{ MIRROR }} {{ DIST }} main" {{ CMD }} --mode=root --variant=apt --keyring=/usr/share/keyrings/debian-archive-keyring.gpg --keyring=/usr/share/keyrings/ {{ DIST }} /tmp/debian-chroot "deb {{ MIRROR }} {{ DIST }} main"
# make sure that no [signedby=...] managed to make it into the sources.list # make sure that no [signedby=...] managed to make it into the sources.list
echo "deb {{ MIRROR }} {{ DIST }} main" | cmp /tmp/debian-chroot/etc/apt/sources.list - echo "deb {{ MIRROR }} {{ DIST }} main" | cmp /tmp/debian-chroot/etc/apt/sources.list -

View file

@ -1,23 +1,20 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot /tmp/log /tmp/trimmed" EXIT INT TERM
# we check the full log to also prevent debug printfs to accidentally make it into a commit # we check the full log to also prevent debug printfs to accidentally make it into a commit
{{ CMD }} --mode=root --variant=apt --logfile=/tmp/log {{ DIST }} /tmp/debian-chroot {{ MIRROR }} {{ CMD }} --mode=root --variant=apt --logfile=/tmp/log {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
# omit the last line which should contain the runtime # omit the last line which should contain the runtime
head --lines=-1 /tmp/log > /tmp/trimmed head --lines=-1 /tmp/log > /tmp/trimmed
cat << LOG | diff -u - /tmp/trimmed cat << LOG | diff -u - /tmp/trimmed
I: chroot architecture {{ HOSTARCH }} is equal to the host's architecture I: chroot architecture {{ HOSTARCH }} is equal to the host's architecture
I: finding correct signed-by value...
I: automatically chosen format: directory I: automatically chosen format: directory
I: running apt-get update... I: running apt-get update...
I: downloading packages with apt... I: downloading packages with apt...
I: extracting archives... I: extracting archives...
I: installing essential packages... I: installing essential packages...
I: installing remaining packages inside the chroot...
I: cleaning package lists and apt cache... I: cleaning package lists and apt cache...
LOG LOG
tail --lines=1 /tmp/log | grep '^I: success in .* seconds$' tail --lines=1 /tmp/log | grep '^I: success in .* seconds$'
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt - tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -
rm -r /tmp/debian-chroot
rm /tmp/log /tmp/trimmed

View file

@ -1,49 +0,0 @@
#!/bin/sh
#
# make sure that the $FAKECHROOT_CMD_SUBST environment variable is set up
# such that one can create a merged-/usr chroot from an unmerged-/usr system
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/chroot-fakechroot.tar /tmp/chroot-root.tar" EXIT INT TERM
[ "$(whoami)" = "root" ]
{{ CMD }} --mode=root --variant=apt --hook-dir=./hooks/merged-usr {{ DIST }} /tmp/chroot-root.tar {{ MIRROR }}
cat <<'SCRIPT' >script.sh
#!/bin/sh
set -exu
rootfs="$1"
mkdir -p "$rootfs/mnt/hooks"
[ -e /usr/libexec/mmdebstrap/ldconfig.fakechroot ] && cp -a /usr/libexec/mmdebstrap/ldconfig.fakechroot "$rootfs/mnt"
[ -e ./ldconfig.fakechroot ] && cp -a ./ldconfig.fakechroot "$rootfs/mnt"
[ -e /usr/share/mmdebstrap/hooks/merged-usr ] && cp -a /usr/share/mmdebstrap/hooks/merged-usr "$rootfs/mnt/hooks"
[ -e ./hooks/merged-usr ] && cp -a ./hooks/merged-usr "$rootfs/mnt/hooks"
[ -e /usr/bin/mmdebstrap ] && cp -aT /usr/bin/mmdebstrap "$rootfs/usr/bin/mmdebstrap"
[ -e ./mmdebstrap ] && cp -aT ./mmdebstrap "$rootfs/mnt/mmdebstrap"
chroot "$rootfs" env --chdir=/mnt \
runuser -u user -- \
{{ CMD }} --mode=fakechroot --variant=apt \
--hook-dir=./hooks/merged-usr \
--customize-hook='chroot "$1" echo "$FAKECHROOT_CMD_SUBST" | tr ":" "\n" | sort' \
--customize-hook='chroot "$1" sh -c "exec test \"\$(readlink /bin)\" = usr/bin"' \
--customize-hook='chroot "$1" sh -c "exec test \"\$(realpath -e /bin/ldd)\" = /usr/bin/ldd"' \
--customize-hook='chroot "$1" echo ":$FAKECHROOT_CMD_SUBST" | grep --quiet :/usr/bin/ldd=' \
--customize-hook='chroot "$1" echo ":$FAKECHROOT_CMD_SUBST" | grep --quiet :/bin/ldd=' \
--customize-hook='chroot "$1" env PATH=/bin ldd /bin/true 2>&1 | grep --quiet "fakeldd: objdump: command not found: install binutils package"' \
--customize-hook='chroot "$1" sh -c "exec test \"\$(readlink /sbin)\" = usr/sbin"' \
--customize-hook='chroot "$1" sh -c "exec test \"\$(realpath -e /sbin/ldconfig)\" = /usr/sbin/ldconfig"' \
--customize-hook='chroot "$1" echo ":$FAKECHROOT_CMD_SUBST" | grep --quiet :/usr/sbin/ldconfig=' \
--customize-hook='chroot "$1" echo ":$FAKECHROOT_CMD_SUBST" | grep --quiet :/sbin/ldconfig=' \
--customize-hook='chroot "$1" env PATH=/sbin ldconfig 2>&1 | grep --quiet "/usr/bin/env: python3: No such file or directory"' \
{{ DIST }} /tmp/chroot-fakechroot.tar {{ MIRROR }}
SCRIPT
chmod +x script.sh
{{ CMD }} --mode=root --variant=apt --include=perl,python3,passwd,fakeroot,fakechroot \
--hook-dir=./hooks/no-merged-usr \
--customize-hook='chroot "$1" useradd --home-dir /home/user --create-home user' \
--customize-hook='chroot "$1" sh -c "exec test \"\$(realpath -e /usr/bin/ldd)\" = /usr/bin/ldd"' \
--customize-hook='chroot "$1" sh -c "exec test ! -e /usr/sbin/ldconfig"' \
--customize-hook=./script.sh \
--customize-hook="copy-out /tmp/chroot-fakechroot.tar /tmp" \
{{ DIST }} /dev/null {{ MIRROR }}
cmp /tmp/chroot-fakechroot.tar /tmp/chroot-root.tar || diffoscope /tmp/chroot-fakechroot.tar /tmp/chroot-root.tar

View file

@ -0,0 +1,50 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
{{ CMD }} --mode=root --variant=apt \
--essential-hook=./hooks/merged-usr/essential00.sh \
--setup-hook=./hooks/merged-usr/setup00.sh \
--customize-hook='[ -L "$1"/bin -a -L "$1"/sbin -a -L "$1"/lib ]' \
{{ DIST }} /tmp/debian-chroot {{ MIRROR }}
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort > tar2.txt
{
sed -e 's/^\.\/bin\//.\/usr\/bin\//;s/^\.\/lib\//.\/usr\/lib\//;s/^\.\/sbin\//.\/usr\/sbin\//;' tar1.txt | {
case {{ HOSTARCH }} in
amd64) sed -e 's/^\.\/lib32\//.\/usr\/lib32\//;s/^\.\/lib64\//.\/usr\/lib64\//;s/^\.\/libx32\//.\/usr\/libx32\//;';;
ppc64el) sed -e 's/^\.\/lib64\//.\/usr\/lib64\//;';;
*) cat;;
esac
};
echo ./bin;
echo ./lib;
echo ./sbin;
case {{ HOSTARCH }} in
amd64)
echo ./lib32;
echo ./lib64;
echo ./libx32;
echo ./usr/lib32/;
echo ./usr/libx32/;
;;
i386)
echo ./lib64;
echo ./libx32;
echo ./usr/lib64/;
echo ./usr/libx32/;
;;
ppc64el)
echo ./lib64;
;;
s390x)
echo ./lib32;
echo ./usr/lib32/;
;;
esac
echo ./usr/share/doc/usr-is-merged/
echo ./usr/share/doc/usr-is-merged/changelog.gz
echo ./usr/share/doc/usr-is-merged/copyright
echo ./var/lib/dpkg/info/usr-is-merged.list
echo ./var/lib/dpkg/info/usr-is-merged.md5sums
echo ./var/lib/dpkg/info/usr-is-merged.preinst
} | sort -u | diff -u - tar2.txt

View file

@ -1,24 +1,10 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
[ {{ MODE }} = "unshare" ]
[ {{ VARIANT }} = "custom" ]
prefix=
if [ "$(id -u)" -eq 0 ] && [ "{{ MODE }}" != "root" ] && [ "{{ MODE }}" != "auto" ]; then
if ! id "${SUDO_USER:-user}" >/dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2 echo "this test modifies the system and should only be run inside a container" >&2
exit 1 exit 1
fi fi
useradd --home-dir "/home/${SUDO_USER:-user}" --create-home "${SUDO_USER:-user}" adduser --gecos user --disabled-password user
fi sysctl -w kernel.unprivileged_userns_clone=1
prefix="runuser -u ${SUDO_USER:-user} --" runuser -u user -- {{ CMD }} --mode=unshare --variant=custom --include=dpkg,dash,diffutils,coreutils,libc-bin,sed {{ DIST }} /dev/null {{ MIRROR }}
fi
# do not install base-files, so that /proc, /sys and /dev are missing
$prefix {{ CMD }} --mode={{ MODE }} --variant={{ VARIANT }} \
--setup-hook='for dir in bin lib lib32 lib64 libo32 libx32 sbin; do ln -s "usr/$dir" "$1/$dir"; done' \
--include=dpkg,dash,diffutils,coreutils,libc-bin,sed \
{{ DIST }} /dev/null {{ MIRROR }}

View file

@ -6,7 +6,8 @@ if [ ! -e /mmdebstrap-testenv ]; then
exit 1 exit 1
fi fi
rm /dev/console rm /dev/console
useradd --home-dir /home/user --create-home user adduser --gecos user --disabled-password user
sysctl -w kernel.unprivileged_userns_clone=1
runuser -u user -- {{ CMD }} --mode=unshare --variant=apt {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }} runuser -u user -- {{ CMD }} --mode=unshare --variant=apt {{ DIST }} /tmp/debian-chroot.tar {{ MIRROR }}
tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt - tar -tf /tmp/debian-chroot.tar | sort | diff -u tar1.txt -
rm /tmp/debian-chroot.tar rm /tmp/debian-chroot.tar

View file

@ -1,31 +0,0 @@
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
[ "$(id -u)" -eq 0 ]
[ {{ MODE }} = "root" ]
case {{ FORMAT }} in tar | squashfs | ext2 | ext4) : ;; *) exit 1 ;; esac
{{ 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\0' | 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
fi

View file

@ -3,18 +3,21 @@ set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
trap "rm -rf /tmp/debian-chroot" EXIT INT TERM trap "rm -rf /tmp/debian-chroot" EXIT INT TERM
{{ CMD }} --mode=root --variant=apt --include=doc-debian --include=tzdata {{ DIST }} /tmp/debian-chroot {{ MIRROR }} {{ CMD }} --mode=root --variant=apt --include=doc-debian --include=tzdata {{ DIST }} /tmp/debian-chroot {{ MIRROR }}
rm /tmp/debian-chroot/usr/share/doc-base/doc-debian.debian-* rm /tmp/debian-chroot/usr/share/doc-base/debian-*
rm -r /tmp/debian-chroot/usr/share/doc/debian rm -r /tmp/debian-chroot/usr/share/doc/debian
rm -r /tmp/debian-chroot/usr/share/doc/doc-debian rm -r /tmp/debian-chroot/usr/share/doc/doc-debian
rm /tmp/debian-chroot/usr/share/lintian/overrides/tzdata
rm /tmp/debian-chroot/etc/localtime rm /tmp/debian-chroot/etc/localtime
rm /tmp/debian-chroot/etc/timezone rm /tmp/debian-chroot/etc/timezone
rm /tmp/debian-chroot/usr/sbin/tzconfig
rm -r /tmp/debian-chroot/usr/share/doc/tzdata rm -r /tmp/debian-chroot/usr/share/doc/tzdata
rm -r /tmp/debian-chroot/usr/share/zoneinfo rm -r /tmp/debian-chroot/usr/share/zoneinfo
for p in doc-debian tzdata; do rm /tmp/debian-chroot/var/lib/apt/extended_states
for f in list md5sums config postinst postrm templates preinst prerm; do rm /tmp/debian-chroot/var/lib/dpkg/info/doc-debian.list
[ -e "/tmp/debian-chroot/var/lib/dpkg/info/$p.$f" ] || continue rm /tmp/debian-chroot/var/lib/dpkg/info/doc-debian.md5sums
rm "/tmp/debian-chroot/var/lib/dpkg/info/$p.$f" rm /tmp/debian-chroot/var/lib/dpkg/info/tzdata.list
done rm /tmp/debian-chroot/var/lib/dpkg/info/tzdata.md5sums
done rm /tmp/debian-chroot/var/lib/dpkg/info/tzdata.config
rm /tmp/debian-chroot/var/lib/dpkg/info/tzdata.postinst
rm /tmp/debian-chroot/var/lib/dpkg/info/tzdata.postrm
rm /tmp/debian-chroot/var/lib/dpkg/info/tzdata.templates
tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt - tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort | diff -u tar1.txt -

View file

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

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