1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
#!/usr/bin/env python3
#
# This script is in the public domain
#
# Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
#
# This is command substitution for ldconfig under fakechroot:
#
# export FAKECHROOT_CMD_SUBST=/sbin/ldconfig=/path/to/ldconfig.fakechroot
#
# Statically linked binaries cannot work with fakechroot and thus have to be
# replaced by either /bin/true or a more clever solution like this one. The
# ldconfig command supports the -r option which allows passing a chroot
# directory for ldconfig to work in. This can be used to run ldconfig without
# fakechroot but still let it create /etc/ld.so.cache inside the chroot.
#
# Since absolute symlinks are broken without fakechroot to translate them,
# we read /etc/ld.so.conf and turn all absolute symlink shared libraries into
# relative ones. At program exit, the original state is restored.
import os
import sys
import subprocess
import atexit
import glob
from pathlib import Path
symlinks = []
def restore_symlinks():
for (link, target, atime, mtime) in symlinks:
link.unlink()
link.symlink_to(target)
os.utime(link, times=None, ns=(atime, mtime), follow_symlinks=False)
atexit.register(restore_symlinks)
def get_libdirs(chroot, configs):
res = []
for conf in configs:
for line in (Path(conf)).read_text().splitlines():
line = line.strip()
if not line:
continue
if line.startswith("#"):
continue
if line.startswith("include "):
assert line.startswith("include /")
res.extend(
get_libdirs(chroot, chroot.glob(line.removeprefix("include /")))
)
continue
assert line.startswith("/"), line
line = line.lstrip("/")
if not (chroot / Path(line)).is_dir():
continue
for f in (chroot / Path(line)).iterdir():
if not f.is_symlink():
continue
linktarget = f.readlink()
# make sure that the linktarget is an absolute path inside the
# chroot
if not str(linktarget).startswith("/"):
continue
if chroot not in linktarget.parents:
continue
# store original link so that we can restore it later
symlinks.append(
(f, linktarget, f.lstat().st_atime_ns, f.lstat().st_mtime_ns)
)
# replace absolute symlink by relative link
relative = os.path.relpath(linktarget, f.parent)
f.unlink()
f.symlink_to(relative)
return res
def main():
if "FAKECHROOT_BASE_ORIG" not in os.environ:
print("FAKECHROOT_BASE_ORIG is not set", file=sys.stderr)
print(
"must be executed under fakechroot using FAKECHROOT_CMD_SUBST",
file=sys.stderr,
)
sys.exit(1)
chroot = Path(os.environ["FAKECHROOT_BASE_ORIG"])
# if chrootless mode is used from within a fakechroot chroot, then
# FAKECHROOT_BASE_ORIG will point at the outer chroot. We want to use
# the path from DPKG_ROOT inside of that instead
if os.environ.get("DPKG_ROOT", "") not in ["", "/"]:
chroot /= os.environ["DPKG_ROOT"].lstrip("/")
if not (chroot / "sbin" / "ldconfig").exists():
sys.exit(0)
(chroot / "var" / "cache" / "ldconfig").mkdir(
mode=0o700, parents=True, exist_ok=True
)
for d in get_libdirs(chroot, [chroot / "etc" / "ld.so.conf"]):
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"
# option will be overwritten by the one we set
subprocess.check_call(
[chroot / "sbin" / "ldconfig"] + sys.argv[1:] + ["-r", rootarg]
)
if __name__ == "__main__":
main()
|