@@ -117,8 +117,25 @@ function error() {
117117 printf " ${RED} ==> $( gettext " ERROR:" ) ${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
118118}
119119
120+ # #
121+ # usage : nlock( $fd, $file, $message, [ $message_arguments... ] )
122+ #
123+ # Desc: non-blocking exclusive (write) lock
124+ # #
125+ nlock () {
126+ # Only reopen the FD if it wasn't handed to us
127+ if ! [[ " /dev/fd/$1 " -ef " $2 " ]]; then
128+ mkdir -p -- " $( dirname -- " $2 " ) "
129+ eval " exec $1 >" ' "$2"'
130+ fi
131+
132+ flock -n " $1 "
133+ }
134+
120135# #
121136# usage : lock( $fd, $file, $message, [ $message_arguments... ] )
137+ #
138+ # Desc: normal - blocking exclusive (write) lock
122139# #
123140lock () {
124141 # Only reopen the FD if it wasn't handed to us
@@ -127,20 +144,22 @@ lock() {
127144 eval " exec $1 >" ' "$2"'
128145 fi
129146
130- flock -n " $1 "
147+ flock " $1 "
131148}
132149
133150# #
134151# usage : slock( $fd, $file, $message, [ $message_arguments... ] )
152+ #
153+ # Desc: blocking shared (read) lock
135154# #
136- nlock () {
155+ slock () {
137156 # Only reopen the FD if it wasn't handed to us
138157 if ! [[ " /dev/fd/$1 " -ef " $2 " ]]; then
139158 mkdir -p -- " $( dirname -- " $2 " ) "
140159 eval " exec $1 >" ' "$2"'
141160 fi
142161
143- flock " $1 "
162+ flock -s " $1 "
144163}
145164
146165# #
@@ -158,13 +177,28 @@ lock_close() {
158177# 2: Command to execute
159178function exec_nspawn(){
160179 local container=$1
180+
181+ # EPHEMERAL in systemd-nspawn uses implicit overlayfs mounts to provide
182+ # the container. If the root container is being updated or files are in
183+ # the lower directory disappear the results are unspecified and might
184+ # cause weird behaviour.
185+ #
186+ # Thus we acquire read locks on the build container to ensure nothing gets
187+ # a write lock. The code is weird because the locking mechanism here is
188+ # implicit as opposed to explicit in the top level of cmd_check.
189+ if (( EPHEMERAL)) ; then
190+ slock 8 " $BUILDDIRECTORY /$container .lock"
191+ fi
161192 systemd-nspawn -q \
162- --as-pid2 \
163- --register=no \
164- ${EPHEMERAL: +--ephemeral} \
165- --pipe \
166- -E " PATH=/usr/local/sbin:/usr/local/bin:/usr/bin" \
167- -D " $BUILDDIRECTORY /$container " " ${@: 2} "
193+ --as-pid2 \
194+ --register=no \
195+ ${EPHEMERAL: +--ephemeral} \
196+ --pipe \
197+ -E " PATH=/usr/local/sbin:/usr/local/bin:/usr/bin" \
198+ -D " $BUILDDIRECTORY /$container " " ${@: 2} "
199+ if (( EPHEMERAL)) ; then
200+ lock_close 8 " $BUILDDIRECTORY /$container .lock"
201+ fi
168202}
169203
170204# Desc: Removes the root container
@@ -221,11 +255,13 @@ __END__
221255function init_chroot(){
222256 mkdir -p " $BUILDDIRECTORY "
223257
224- # Prepare root chroot
258+ # Always lock first. Otherwise we might end up...
259+ # - doing the same thing again - if using test/lock/mkdir
260+ # - with empty directory in the follow-up lock - if using test/mkdir/lock
261+ lock 9 " $BUILDDIRECTORY " /root.lock
225262 if [ ! -d " $BUILDDIRECTORY " /root ]; then
226263 get_bootstrap_img
227264
228- lock 9 " $BUILDDIRECTORY " /root.lock
229265 msg " Preparing chroot"
230266 trap ' { cleanup_root_volume; exit 1; }' ERR
231267 trap ' { cleanup_root_volume; trap - INT; kill -INT $$; }' INT
@@ -241,23 +277,23 @@ function init_chroot(){
241277 msg2 " Setting up keyring, this might take a while..."
242278 exec_nspawn root pacman-key --init & > /dev/null
243279 exec_nspawn root pacman-key --populate archlinux & > /dev/null
244- exec_nspawn root pacman -Sy
245- exec_nspawn root pacman -Syu --noconfirm
246280 touch " $BUILDDIRECTORY /root/.repro-2"
247- lock_close 9
248281 else
249282 if [ ! -f " $BUILDDIRECTORY /root/.repro-2" ]; then
250283 error " Please delete $BUILDDIRECTORY and initialize the chroots again"
251284 exit 1
252285 fi
253- if lock 9 " $BUILDDIRECTORY " /root.lock; then
254- msg " Reusing existing container"
255- printf ' Server = %s\n' " $HOSTMIRROR " > " $BUILDDIRECTORY " /root/etc/pacman.d/mirrorlist
256- exec_nspawn root pacman -Syu --noconfirm
257- lock_close 9
258- else
259- msg " Couldn't acquire lock on root chroot, didn't update."
260- fi
286+ msg " Reusing existing container"
287+ fi
288+ lock_close 9
289+
290+ if nlock 9 " $BUILDDIRECTORY " /root.lock; then
291+ msg " Updating container"
292+ printf ' Server = %s\n' " $HOSTMIRROR " > " $BUILDDIRECTORY " /root/etc/pacman.d/mirrorlist
293+ exec_nspawn root pacman -Syu --noconfirm
294+ lock_close 9
295+ else
296+ msg " Couldn't acquire container lock, didn't update."
261297 fi
262298 trap - ERR INT
263299}
@@ -306,6 +342,10 @@ function cmd_check(){
306342 # Father I have sinned
307343 if (( ! pkgbuild_file)) ; then
308344 msg2 " Fetching PKGBUILD from ASP..."
345+
346+ # Lock the cachedir as we might have a race condition with pacman -S and the cachedir
347+ lock 9 " ${cachedir} .lock"
348+
309349 EPHEMERAL=1 exec_nspawn root --bind=" ${build_root_dir} /startdir:/startdir" --bind=" $( readlink -e ${cachedir} ) :/var/cache/pacman/pkg" \
310350 bash << -__END__
311351shopt -s globstar
@@ -323,6 +363,7 @@ for rev in \$(git rev-list --all -- repos/); do
323363done
324364exit 1
325365__END__
366+ lock_close 9 " ${cachedir} .lock"
326367 elif [[ -r " PKGBUILD" ]]; then
327368 if [[ " $( sha256sum PKGBUILD | awk ' {print $1}' ) " != " $pkgbuild_sha256sum " ]]; then
328369 error " PKGBUILD doesn't match the checksum"
@@ -343,10 +384,13 @@ __END__
343384
344385 mkdir -p " $KEYRINGCACHE "
345386
387+ # Always lock first. Otherwise we might end up...
388+ # - doing the same thing again - if using test/lock/mkdir
389+ # - with empty directory in the follow-up lock - if using test/mkdir/lock
390+ lock 9 " $KEYRINGCACHE /$keyring_package .lock"
346391 if [ ! -d " $KEYRINGCACHE /$keyring_package " ]; then
347392 msg2 " Setting up $keyring_package in keyring cache, this might take a while..."
348393
349- nlock 9 " $KEYRINGCACHE /$keyring_package .lock"
350394 # shellcheck disable=SC2086
351395 keyring=$( printf -- ' %s\n' ${packages[*]} | grep -E " archlinux-keyring" )
352396 EPHEMERAL=1 exec_nspawn root --bind=" ${build_root_dir} :/mnt" --bind=" $( readlink -e " ${cachedir} " ) :/cache" bash -c \
@@ -386,12 +430,14 @@ echo "faked-system-time ${keyring_build_date}" >> /mnt/gpg.conf
386430pacman-key --init
387431pacman-key --populate archlinux
388432__END__
389- lock_close 9 " $KEYRINGCACHE /$keyring_package .lock"
390433 trap - ERR INT
391434 else
392435 msg2 " Found $keyring_package in keyring cache"
393436 fi
437+ lock_close 9 " $KEYRINGCACHE /$keyring_package .lock"
394438
439+ # Acquire shared locks for keyring as it could still be initialized at this point
440+ slock 9 " $KEYRINGCACHE /$keyring_package .lock"
395441 msg " Installing packages"
396442 # shellcheck disable=SC2086
397443 EPHEMERAL= 1 exec_nspawn root \
@@ -404,6 +450,7 @@ cp --target-directory=/etc/pacman.d/ --recursive /gnupg
404450echo " faked-system-time ${SOURCE_DATE_EPOCH} " >> /etc/pacman.d/gnupg/gpg.conf
405451pacstrap -G -U /mnt --needed " \$ @"
406452__END__
453+ lock_close 9 " $KEYRINGCACHE /$keyring_package .lock"
407454
408455 # Setup environment
409456 {
0 commit comments