Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/makechrootpkg.in
diff options
context:
space:
mode:
Diffstat (limited to 'makechrootpkg.in')
-rw-r--r--makechrootpkg.in234
1 files changed, 87 insertions, 147 deletions
diff --git a/makechrootpkg.in b/makechrootpkg.in
index c6ef240..7589737 100644
--- a/makechrootpkg.in
+++ b/makechrootpkg.in
@@ -11,6 +11,7 @@
# GNU General Public License for more details.
m4_include(lib/common.sh)
+m4_include(lib/archroot.sh)
shopt -s nullglob
@@ -20,13 +21,13 @@ init_variables() {
repack=false
update_first=false
clean_first=false
- install_pkg=
run_namcap=false
temp_chroot=false
chrootdir=
passeddir=
- declare -a install_pkgs
- declare -i ret=0
+ makepkg_user=
+ declare -ga install_pkgs
+ declare -gi ret=0
bindmounts_ro=()
bindmounts_rw=()
@@ -51,6 +52,10 @@ usage() {
echo 'command:'
echo ' mkarchroot <chrootdir>/root base-devel'
echo ''
+ echo 'This script reads {SRC,SRCPKG,PKG,LOG}DEST, MAKEFLAGS and PACKAGER'
+ echo 'from makepkg.conf(5), if those variables are not part of the'
+ echo 'environment.'
+ echo ''
echo "Default makepkg args: ${default_makepkg_args[*]}"
echo ''
echo 'Flags:'
@@ -68,6 +73,7 @@ usage() {
echo " Default: $copy"
echo '-n Run namcap on the package'
echo '-T Build in a temporary directory'
+ echo '-U Run makepkg as a specified user'
exit 1
}
@@ -86,136 +92,43 @@ load_vars() {
[[ -f $makepkg_conf ]] || return 1
for var in {SRC,SRCPKG,PKG,LOG}DEST MAKEFLAGS PACKAGER; do
- [[ -z ${!var:-} ]] && eval $(grep "^${var}=" "$makepkg_conf")
+ [[ -z ${!var:-} ]] && eval "$(grep "^${var}=" "$makepkg_conf")"
done
return 0
}
-# Usage: btrfs_subvolume_id $SUBVOLUME
-btrfs_subvolume_id() (
- set -o pipefail
- LC_ALL=C btrfs subvolume show "$1" | sed -n 's/^\tSubvolume ID:\s*//p'
-)
-
-# Usage: btrfs_subvolume_list_all $FILEPATH
-#
-# Given $FILEPATH somewhere on a mounted btrfs filesystem, print the
-# ID and full path of every subvolume on the filesystem, one per line
-# in the format "$ID $PATH", where $PATH is relative to the top-level
-# subvolume (which might not be what is mounted).
-#
-# BUG: Due to limitations in the `btrfs` tool, this will not correctly
-# list subvolumes whose path contains a space.
-btrfs_subvolume_list_all() (
- set -o pipefail
-
- local mountpoint all
- mountpoint="$(df --output=target "$1" | sed 1d)" || return
- # The output of `btrfs subvolume list -a` is a space-separated
- # sequence of "key value key value...". Unfortunately both
- # keys and values can contain space, and there's no escaping
- # or indication of when this happens. So we assume
- # 1. ID is the first column
- # 2. That no key or value will contain " path"
- # 3. That the "path" value does not contain a space.
- all="$(LC_ALL=C btrfs subvolume list -a "$mountpoint" | sed -r 's|^ID ([0-9]+) .* path (<FS_TREE>/)?(\S*).*|\1 \3|')" || return
-
- # Sanity check the output
- local id path
- while read -r id path; do
- # ID should be numeric
- [[ "$id" =~ ^-?[0-9]+$ ]] || return
- # While a path could countain a space, the above code
- # doesn't support it; if there is space, then it means
- # we got a line not matching the expected format.
- [[ "$path" != *' '* ]] || return
- done <<<"$all"
-
- printf '%s\n' "$all"
-)
-
-# Usage: btrfs_subvolume_list $SUBVOLUME
-#
-# Assuming that $SUBVOLUME is a btrfs subvolume, list all child
-# subvolumes; from most deeply nested to most shallowly nested.
-#
-# This is intended to be a sane version of `btrfs subvolume list`.
-btrfs_subvolume_list() {
- local subvolume=$1
-
- local id all path subpath
- id="$(btrfs_subvolume_id "$subvolume")" || return
- all="$(btrfs_subvolume_list_all "$subvolume")" || return
- path=$(awk -v id="$id" '$1 == id { sub($1 FS, ""); print }' <<<"$all")
- while read -r id subpath; do
- if [[ "$subpath" = "$path"/* ]]; then
- printf '%s\n' "${subpath#"${path}/"}"
- fi
- done <<<"$all" | LC_ALL=C sort --reverse
-}
-
-# Usage: btrfs_subvolume_delete $SUBVOLUME
-#
-# Assuming that $SUBVOLUME is a btrfs subvolume, delete it and all
-# subvolumes below it.
-#
-# This is intended to be a recursive version of
-# `btrfs subvolume delete`.
-btrfs_subvolume_delete() {
- local dir="$1"
-
- # We store the result as a variable because we want to see if
- # btrfs_subvolume_list fails or succeeds before we start
- # deleting things. (Then we have to work around the subshell
- # trimming the trailing newlines.)
- local subvolumes
- subvolumes="$(btrfs_subvolume_list "$dir")" || return
- [[ -z "$subvolumes" ]] || subvolumes+=$'\n'
-
- local subvolume
- while read -r subvolume; do
- btrfs subvolume delete "$dir/$subvolume" || return
- done < <(printf '%s' "$subvolumes")
-
- btrfs subvolume delete "$dir"
-}
-
-# Usage: sync_chroot $CHROOTDIR/$CHROOT <$CHROOTCOPY|$copydir>
+# Usage: sync_chroot $rootdir $copydir [$copy]
sync_chroot() {
- local chrootdir=$1
- local copy=$2
- local copydir=''
- if [[ ${copy:0:1} = / ]]; then
- copydir=$copy
- else
- copydir="$chrootdir/$copy"
- fi
+ local rootdir=$1
+ local copydir=$2
+ local copy=${3:-$2}
- if [[ "$chrootdir/root" -ef "$copydir" ]]; then
+ if [[ "$rootdir" -ef "$copydir" ]]; then
error 'Cannot sync copy with itself: %s' "$copydir"
return 1
fi
- # Detect chrootdir filesystem type
- local chroottype=$(stat -f -c %T "$chrootdir")
-
# Get a read lock on the root chroot to make
# sure we don't clone a half-updated chroot
- slock 8 "$chrootdir/root.lock" \
- "Locking clean chroot [%s]" "$chrootdir/root"
+ slock 8 "$rootdir.lock" \
+ "Locking clean chroot [%s]" "$rootdir"
- stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$chrootdir/root" "$copydir"
- if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
- if [[ -d $copydir ]]; then
- btrfs_subvolume_delete "$copydir" >/dev/null ||
+ stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$rootdir" "$copy"
+ if is_subvolume "$rootdir" && is_same_fs "$rootdir" "$(dirname -- "$copydir")" && ! mountpoint -q "$copydir"; then
+ if is_subvolume "$copydir"; then
+ subvolume_delete_recursive "$copydir" ||
die "Unable to delete subvolume %s" "$copydir"
+ else
+ # avoid change of filesystem in case of an umount failure
+ rm --recursive --force --one-file-system "$copydir" ||
+ die "Unable to delete %s" "$copydir"
fi
- btrfs subvolume snapshot "$chrootdir/root" "$copydir" >/dev/null ||
+ btrfs subvolume snapshot "$rootdir" "$copydir" >/dev/null ||
die "Unable to create subvolume %s" "$copydir"
else
mkdir -p "$copydir"
- rsync -a --delete -q -W -x "$chrootdir/root/" "$copydir"
+ rsync -a --delete -q -W -x "$rootdir/" "$copydir"
fi
stat_done
@@ -226,15 +139,14 @@ sync_chroot() {
touch "$copydir"
}
-# Usage: delete_chroot $copydir
+# Usage: delete_chroot $copydir [$copy]
delete_chroot() {
local copydir=$1
- # Detect chrootdir filesystem type
- local chroottype=$(stat -f -c %T "$copydir")
+ local copy=${1:-$2}
- stat_busy "Removing chroot copy [%s]" "$copydir"
- if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
- btrfs_subvolume_delete "$copydir" >/dev/null ||
+ stat_busy "Removing chroot copy [%s]" "$copy"
+ if is_subvolume "$copydir" && ! mountpoint -q "$copydir"; then
+ subvolume_delete_recursive "$copydir" ||
die "Unable to delete subvolume %s" "$copydir"
else
# avoid change of filesystem in case of an umount failure
@@ -278,8 +190,9 @@ prepare_chroot() {
$repack || rm -rf "$copydir/build"
- local builduser_uid="${SUDO_UID:-$UID}"
- local builduser_gid="$(id -g "$builduser_uid")"
+ local builduser_uid builduser_gid
+ builduser_uid="${SUDO_UID:-$UID}"
+ builduser_gid="$(id -g "$builduser_uid")"
local install="install -o $builduser_uid -g $builduser_gid"
local x
@@ -287,8 +200,8 @@ prepare_chroot() {
# which we might not be able to load (i.e. when building i686 packages on
# an x86_64 host).
sed -e '/^builduser:/d' -i "$copydir"/etc/{passwd,group}
- printf >>"$copydir/etc/group" 'builduser:x:%d:\n' $builduser_gid
- printf >>"$copydir/etc/passwd" 'builduser:x:%d:%d:builduser:/build:/bin/bash\n' $builduser_uid $builduser_gid
+ printf >>"$copydir/etc/group" 'builduser:x:%d:\n' "$builduser_gid"
+ printf >>"$copydir/etc/passwd" 'builduser:x:%d:%d:builduser:/build:/bin/bash\n' "$builduser_uid" "$builduser_gid"
$install -d "$copydir"/{build,build/.gnupg,startdir,{pkg,srcpkg,src,log}dest}
@@ -306,13 +219,13 @@ prepare_chroot() {
done
cat > "$copydir/etc/sudoers.d/builduser-pacman" <<EOF
-Defaults env_keep += "HOME"
builduser ALL = NOPASSWD: /usr/bin/pacman
EOF
chmod 440 "$copydir/etc/sudoers.d/builduser-pacman"
if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then
- local line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q')
+ local line
+ line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q')
local ins='[repo]
SigLevel = Optional TrustAll
Server = file:///repo
@@ -347,12 +260,36 @@ Server = file:///repo
# These functions aren't run in makechrootpkg,
# so no global variables
_chrootprepare() {
+ # shellcheck source=/dev/null
. /etc/profile
+ # Beware, there are some stupid arbitrary rules on how you can
+ # use "$" in arguments to commands with "sudo -i". ${foo} or
+ # ${1} is OK, but $foo or $1 isn't.
+ # https://bugzilla.sudo.ws/show_bug.cgi?id=765
sudo -iu builduser bash -c 'cd /startdir; makepkg "$@" --nobuild' -bash "$@"
}
_chrootbuild() {
+ # shellcheck source=/dev/null
. /etc/profile
+ local srcext
+ srcext="$(
+ # shellcheck source=makepkg-x86_64.conf
+ . /etc/makepkg.conf || exit
+ # shellcheck source=PKGBUILD.proto
+ . /startdir/PKGBUILD || exit
+ if [ "$arch" = any ]; then
+ pkgarch=any
+ else
+ pkgarch=$CARCH
+ fi
+ printf '%s\n' "-$pkgarch$SRCEXT"
+ )" || return
+ # Beware, there are some stupid arbitrary rules on how you can
+ # use "$" in arguments to commands with "sudo -i". ${foo} or
+ # ${1} is OK, but $foo or $1 isn't.
+ # https://bugzilla.sudo.ws/show_bug.cgi?id=765
+ sudo -iu builduser bash -c 'cd /startdir; SRCEXT="${1}" makepkg "${@:2}" --allsource' -bash "$srcext" "$@" || return
sudo -iu builduser bash -c 'cd /startdir; makepkg "$@" --noextract --noprepare' -bash "$@"
}
@@ -364,27 +301,27 @@ _chrootnamcap() {
done
}
-# Usage: download_sources $copydir $src_owner
+# Usage: download_sources $copydir $makepkg_user
# Globals:
# - SRCDEST
# - USER
download_sources() {
local copydir=$1
- local src_owner=$2
+ local makepkg_user=$2
- local builddir="$(mktemp -d)"
+ local builddir
+ builddir="$(mktemp -d)"
chmod 1777 "$builddir"
# Ensure sources are downloaded
- if [[ $USER != $src_owner ]]; then
- sudo -u $src_owner env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \
- makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o
+ if [[ "$(id -u "$makepkg_user")" != 0 ]]; then
+ sudo -u "$makepkg_user" env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \
+ makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o ||
+ die "Could not download sources."
else
- ( export SRCDEST BUILDDIR="$builddir"
- makepkg --asroot --config="$copydir/etc/makepkg.conf" --verifysource -o
- )
+ error "Running makepkg as root is not allowed."
+ exit 1
fi
- (( $? != 0 )) && die "Could not download sources."
# Clean up garbage from verifysource
rm -rf "$builddir"
@@ -402,8 +339,10 @@ move_products() {
for pkgfile in "$copydir"/pkgdest/*; do
chown "$src_owner" "$pkgfile"
mv "$pkgfile" "$PKGDEST"
- if [[ $PKGDEST != $PWD ]]; then
- ln -sf "$PKGDEST/${pkgfile##*/}" .
+
+ # Fix broken symlink because of temporary chroot PKGDEST /pkgdest
+ if [[ "$PWD" != "$PKGDEST" && -L "$PWD/${pkgfile##*/}" ]]; then
+ ln -sf "$PKGDEST/${pkgfile##*/}"
fi
done
@@ -424,26 +363,27 @@ move_products() {
main() {
init_variables
- orig_argv=("$@")
-
- while getopts 'hcur:I:l:nTD:d:' arg; do
+ while getopts 'hcur:I:l:nTD:d:U:' arg; do
case "$arg" in
c) clean_first=true ;;
- D) bindmounts_ro+=(--bind-ro="$OPTARG") ;;
- d) bindmounts_rw+=(--bind="$OPTARG") ;;
+ D) bindmounts_ro+=("--bind-ro=$OPTARG") ;;
+ d) bindmounts_rw+=("--bind=$OPTARG") ;;
u) update_first=true ;;
r) passeddir="$OPTARG" ;;
I) install_pkgs+=("$OPTARG") ;;
l) copy="$OPTARG" ;;
n) run_namcap=true; makepkg_args+=(-i) ;;
T) temp_chroot=true; copy+="-$$" ;;
+ U) makepkg_user="$OPTARG" ;;
h|*) usage ;;
esac
done
[[ ! -f PKGBUILD && -z "${install_pkgs[*]}" ]] && die 'This must be run in a directory containing a PKGBUILD.'
+ [[ -n $makepkg_user && -z $(id -u "$makepkg_user") ]] && die 'Invalid makepkg user.'
+ makepkg_user=${makepkg_user:-${SUDO_USER:-$USER}}
- check_root "$0" "${orig_argv[@]}"
+ check_root
# Canonicalize chrootdir, getting rid of trailing /
chrootdir=$(readlink -e "$passeddir")
@@ -490,7 +430,7 @@ main() {
lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy"
if [[ ! -d $copydir ]] || $clean_first; then
- sync_chroot "$chrootdir" "$copy"
+ sync_chroot "$chrootdir/root" "$copydir" "$copy"
fi
$update_first && arch-nspawn "$copydir" \
@@ -504,7 +444,7 @@ main() {
[[ -f PKGBUILD ]] || return $ret
fi
- download_sources "$copydir" "$src_owner"
+ download_sources "$copydir" "$makepkg_user"
prepare_chroot "$copydir" "$USER_HOME" "$repack"
@@ -524,7 +464,7 @@ main() {
(( ret += 1 ))
fi
- $temp_chroot && delete_chroot "$copydir"
+ $temp_chroot && delete_chroot "$copydir" "$copy"
if (( ret != 0 )); then
if $temp_chroot; then