@@ -21,13 +21,44 @@ orig_argv=("$0" "$@")
2121src_owner=${SUDO_USER:- $USER }
2222function check_root() {
2323 (( EUID == 0 )) && return
24- if type -P sudo > /dev/null; then
24+ if (( rootless_userns)) ; then
25+ exec become-root unshare --mount " ${orig_argv[@]} "
26+ elif type -P sudo > /dev/null; then
2527 exec sudo -- " ${orig_argv[@]} "
2628 else
2729 exec su root -c " $( printf ' %q' " ${orig_argv[@]} " ) "
2830 fi
2931}
3032
33+ function require_userns_tools() {
34+ if command -v become-root > /dev/null \
35+ && command -v nsjail > /dev/null \
36+ && command -v fuse-overlayfs > /dev/null
37+ then
38+ return 0
39+ fi
40+ warning " nsjail, fuse-overlayfs and become-root are necessary for rootless operation"
41+ warning " https://github.com/giuseppe/become-root"
42+ warning " https://github.com/containers/fuse-overlayfs"
43+ warning " https://github.com/google/nsjail"
44+ return 1
45+ }
46+
47+ function mountoverlay() {
48+ if (( rootless_userns)) ; then
49+ fuse-overlayfs " $@ "
50+ else
51+ mount -t overlayfs overlayfs " $@ "
52+ fi
53+ }
54+ function umountoverlay() {
55+ if (( rootless_userns)) ; then
56+ fusermount -u " $@ "
57+ else
58+ umount " $@ "
59+ fi
60+ }
61+
3162# Use a private gpg keyring
3263function gpg() {
3364 command gpg --homedir=" $BUILDDIRECTORY /gnupg" " $@ "
@@ -103,6 +134,44 @@ function exec_nspawn(){
103134 -D " $BUILDDIRECTORY /$container " " ${@: 2} "
104135}
105136
137+
138+ # Desc: Executes an command inside a given nsjail container
139+ # 1: Container name
140+ # 2: Optional: one --bind=... bindmount option
141+ # 2/3: Command to execute
142+ function exec_nsjail(){
143+ local container=$1
144+ local args=( -Mo --quiet
145+ --disable_clone_newuser
146+ --disable_clone_newnet
147+ --disable_rlimits
148+ --keep_caps
149+ --tmpfsmount /tmp
150+ --bindmount_ro /dev
151+ -E " PATH=/usr/local/sbin:/usr/local/bin:/usr/bin"
152+ --chroot " $BUILDDIRECTORY /$container " --rw
153+ )
154+ # # use a no-op forking unshare as pid1
155+ if [[ $2 == --bind= * ]] ; then
156+ echo " !Execute: " nsjail " ${args[@]} " --bindmount " ${2# --bind=} " -- /usr/bin/unshare -f " ${@: 3} " >> /tmp/log
157+ nsjail " ${args[@]} " --bindmount " ${2# --bind=} " -- /usr/bin/unshare -f " ${@: 3} "
158+ else
159+ echo " !Execute: " nsjail " ${args[@]} " -- /usr/bin/unshare -f " ${@: 2} " >> /tmp/log
160+ nsjail " ${args[@]} " -- /usr/bin/unshare -f " ${@: 2} "
161+ fi
162+ }
163+
164+ function exec_container(){
165+ if (( rootless_userns)) ; then
166+ exec_nsjail " $@ "
167+ else
168+ exec_nspawn " $@ "
169+ fi
170+ }
171+
172+
173+
174+
106175# Desc: Removes the root container
107176function cleanup_root_volume(){
108177 warning " Removing root container..."
@@ -114,7 +183,7 @@ function cleanup_root_volume(){
114183function remove_snapshot (){
115184 local build=$1
116185 msg2 " Delete snapshot for $build ..."
117- umount " $BUILDDIRECTORY /$build " || true
186+ umountoverlay " $BUILDDIRECTORY /$build " || true
118187 rm -rf " ${BUILDDIRECTORY:? } /${build} "
119188 rm -rf " ${BUILDDIRECTORY:? } /${build} _upperdir"
120189 rm -rf " ${BUILDDIRECTORY:? } /${build} _workdir"
@@ -131,7 +200,7 @@ function create_snapshot (){
131200 msg2 " Create snapshot for $build ..."
132201 mkdir -p " $BUILDDIRECTORY /" {" ${build} " ," ${build} _upperdir" ," ${build} _workdir" }
133202 # shellcheck disable=SC2140
134- mount -t overlay overlay \
203+ mountoverlay \
135204 -o lowerdir=" $BUILDDIRECTORY /root" ,upperdir=" $BUILDDIRECTORY /${build} _upperdir" ,workdir=" $BUILDDIRECTORY /${build} _workdir" \
136205 " $BUILDDIRECTORY /${build} "
137206 touch " $BUILDDIRECTORY /$build "
@@ -143,7 +212,7 @@ function create_snapshot (){
143212function build_package(){
144213 local build=$1
145214 local builddir=${2:- " /startdir" }
146- exec_nspawn " $build " \
215+ exec_container " $build " \
147216 --bind=" $PWD :/srcdest" \
148217bash << -__END__
149218set -e
@@ -180,23 +249,23 @@ function init_chroot(){
180249 printf ' %s.UTF-8 UTF-8\n' en_US de_DE > " $BUILDDIRECTORY " /root/etc/locale.gen
181250 printf ' LANG=en_US.UTF-8\n' > " $BUILDDIRECTORY " /root/etc/locale.conf
182251
183- systemd-machine-id-setup --root= " $BUILDDIRECTORY " /root
252+ exec_container root systemd-machine-id-setup
184253 msg2 " Setting up keyring, this might take a while..."
185- exec_nspawn root pacman-key --init & > /dev/null
186- exec_nspawn root pacman-key --populate archlinux & > /dev/null
254+ exec_container root pacman-key --init & > /dev/null
255+ exec_container root pacman-key --populate archlinux & > /dev/null
187256
188257 msg2 " Updating and installing base & base-devel"
189- exec_nspawn root pacman -Syu base-devel --noconfirm
190- exec_nspawn root pacman -R arch-install-scripts --noconfirm
191- exec_nspawn root locale-gen
258+ exec_container root pacman -Syu base-devel --noconfirm
259+ exec_container root pacman -R arch-install-scripts --noconfirm
260+ exec_container root locale-gen
192261
193262 printf ' builduser ALL = NOPASSWD: /usr/bin/pacman\n' > " $BUILDDIRECTORY " /root/etc/sudoers.d/builduser-pacman
194- exec_nspawn root useradd -m -G wheel -s /bin/bash -d /build builduser
263+ exec_container root useradd -m -G wheel -s /bin/bash -d /build builduser
195264 echo " keyserver-options auto-key-retrieve" | install -Dm644 /dev/stdin " $BUILDDIRECTORY /root" /build/.gnupg/gpg.conf
196- exec_nspawn root chown -R builduser /build/.gnupg
265+ exec_container root chown -R builduser /build/.gnupg
197266 else
198267 printf ' Server = %s\n' " $HOSTMIRROR " > " $BUILDDIRECTORY " /root/etc/pacman.d/mirrorlist
199- exec_nspawn root pacman -Syu --noconfirm
268+ exec_container root pacman -Syu --noconfirm
200269 fi
201270
202271 trap - ERR INT
@@ -247,7 +316,7 @@ function cmd_check(){
247316 } >> " $BUILDDIRECTORY /$build /etc/makepkg.conf"
248317
249318 # Father I have sinned
250- exec_nspawn " build" \
319+ exec_container " build" \
251320 bash << -__END__
252321shopt -s globstar
253322install -d -o builduser -g builduser /startdir
@@ -273,16 +342,16 @@ __END__
273342 msg2 " Finished preparing packages"
274343 msg " Installing packages"
275344 # shellcheck disable=SC2086
276- exec_nspawn build --bind= " $( readlink -e ${cachedir} ) :/cache" bash -c \
345+ exec_container build --bind= " $( readlink -e ${cachedir} ) :/cache" bash -c \
277346 ' yes y | pacman -Udd --overwrite "*" -- "$@"' -b ash " ${packages[@]} "
278347
279348 read -r -a buildinfo_packages <<< " $( buildinfo -f installed " ${pkg} " ) "
280349 uninstall= $( comm -13 \
281350 <( printf ' %s\n' " ${buildinfo_packages[@]} " | rev | cut -d- -f4- | rev | sort) \
282- <( exec_nspawn build --bind=" $( readlink -e ${cachedir} ) :/cache" pacman -Qq | sort) )
351+ <( exec_container build --bind=" $( readlink -e ${cachedir} ) :/cache" pacman -Qq | sort) )
283352
284353 if [ -n " $uninstall " ]; then
285- exec_nspawn build pacman -Rdd --noconfirm -- $uninstall
354+ exec_container build pacman -Rdd --noconfirm -- $uninstall
286355 fi
287356
288357 build_package " build" " $builddir "
@@ -346,10 +415,15 @@ elif [[ -r "$HOME/.repro.conf" ]]; then
346415fi
347416
348417
349- while getopts :hdoC :P:M: arg; do
418+ while getopts :hdorC :P:M: arg; do
350419 case $arg in
351420 h) print_help; exit 0;;
352421 d) run_diffoscope=1;;
422+ r) rootless_userns=1;
423+ require_userns_tools || exit 1
424+ # TODO: better detection for valid writable build directory
425+ [[ $BUILDDIRECTORY == /var/lib/repro ]] && BUILDDIRECTORY=" ${XDG_CACHE_HOME:- $HOME / .cache} /archlinux-repro"
426+ ;;
353427 * ) ;;
354428 esac
355429done
0 commit comments