index : devtools32 | |
Archlinux32 fork of devtools | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | makechrootpkg.in | 250 |
diff --git a/makechrootpkg.in b/makechrootpkg.in index 7ff2fd7..c6ef240 100644 --- a/makechrootpkg.in +++ b/makechrootpkg.in @@ -102,12 +102,37 @@ btrfs_subvolume_id() ( # # 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". +# 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 - mountpoint="$(df --output=target "$1" | sed 1d)" || return $? - LC_ALL=C btrfs subvolume list -a "$mountpoint" | sed -r 's|^ID ([0-9]+) .* path (<FS_TREE>/)?(\S*).*|\1 \3|' + + 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 @@ -120,9 +145,9 @@ 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="$(sed -n "s/^$id //p" <<<"$all")" + 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}/"}" @@ -139,11 +164,20 @@ btrfs_subvolume_list() { # `btrfs subvolume delete`. btrfs_subvolume_delete() { local dir="$1" - local subvolumes subvolume - subvolumes=($(btrfs_subvolume_list "$dir")) || return $? - for subvolume in "${subvolumes[@]}"; do - btrfs subvolume delete "$dir/$subvolume" || return $? - done + + # 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" } @@ -217,21 +251,17 @@ delete_chroot() { install_packages() { local copydir=$1 local install_pkgs=("${@:2}") - declare -i ret=0 - local pkgname - local install_pkg - for install_pkg in "${install_pkgs[@]}"; do - pkgname="${install_pkg##*/}" - cp "$install_pkg" "$copydir/$pkgname" + local -a pkgnames + local ret - arch-nspawn "$copydir" \ - "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ - pacman -U /$pkgname --noconfirm - (( ret += !! $? )) + pkgnames=("${install_pkgs[@]##*/}") - rm "$copydir/$pkgname" - done + cp -- "${install_pkgs[@]}" "$copydir/root/" + arch-nspawn "$copydir" "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ + pacman -U --noconfirm -- "${pkgnames[@]/#//root/}" + ret=$? + rm -- "${pkgnames[@]/#/$copydir/root/}" return $ret } @@ -248,67 +278,38 @@ prepare_chroot() { $repack || rm -rf "$copydir/build" - mkdir -p "$copydir/build" - if ! grep -q 'BUILDDIR="/build"' "$copydir/etc/makepkg.conf"; then - echo 'BUILDDIR="/build"' >> "$copydir/etc/makepkg.conf" - fi - - # Read .makepkg.conf and gnupg pubring - if [[ -r $USER_HOME/.gnupg/pubring.kbx ]]; then - install -D "$USER_HOME/.gnupg/pubring.kbx" "$copydir/build/.gnupg/pubring.kbx" - fi - if [[ -r $USER_HOME/.gnupg/pubring.gpg ]]; then - install -D "$USER_HOME/.gnupg/pubring.gpg" "$copydir/build/.gnupg/pubring.gpg" - fi - - mkdir -p "$copydir/pkgdest" - if ! grep -q 'PKGDEST="/pkgdest"' "$copydir/etc/makepkg.conf"; then - echo 'PKGDEST="/pkgdest"' >> "$copydir/etc/makepkg.conf" - fi - - mkdir -p "$copydir/srcpkgdest" - if ! grep -q 'SRCPKGDEST="/srcpkgdest"' "$copydir/etc/makepkg.conf"; then - echo 'SRCPKGDEST="/srcpkgdest"' >> "$copydir/etc/makepkg.conf" - fi - - mkdir -p "$copydir/logdest" - if ! grep -q 'LOGDEST="/logdest"' "$copydir/etc/makepkg.conf"; then - echo 'LOGDEST="/logdest"' >> "$copydir/etc/makepkg.conf" - fi - - # These two get bind-mounted read-only - # XXX: makepkg dislikes having these dirs read-only, so separate them - mkdir -p "$copydir/startdir" "$copydir/startdir_host" - mkdir -p "$copydir/srcdest" "$copydir/srcdest_host" - if ! grep -q 'SRCDEST="/srcdest"' "$copydir/etc/makepkg.conf"; then - echo 'SRCDEST="/srcdest"' >> "$copydir/etc/makepkg.conf" - fi - - builduser_uid=${SUDO_UID:-$UID} + local builduser_uid="${SUDO_UID:-$UID}" + local builduser_gid="$(id -g "$builduser_uid")" + local install="install -o $builduser_uid -g $builduser_gid" + local x # We can't use useradd without chrooting, otherwise it invokes PAM modules # which we might not be able to load (i.e. when building i686 packages on # an x86_64 host). - printf 'builduser:x:%d:100:builduser:/build:/bin/bash\n' "$builduser_uid" >>"$copydir/etc/passwd" - chown -R "$builduser_uid" "$copydir"/{build,pkgdest,srcpkgdest,logdest,srcdest,startdir} + 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 - if [[ -n ${MAKEFLAGS:-} ]]; then - sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf" - echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf" - fi + $install -d "$copydir"/{build,build/.gnupg,startdir,{pkg,srcpkg,src,log}dest} - if [[ -n ${PACKAGER:-} ]]; then - sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf" - echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf" - fi + for x in .gnupg/pubring.{kbx,gpg}; do + [[ -r $USER_HOME/$x ]] || continue + $install -m 644 "$USER_HOME/$x" "$copydir/build/$x" + done - if [[ ! -f $copydir/etc/sudoers.d/builduser-pacman ]]; then - cat > "$copydir/etc/sudoers.d/builduser-pacman" <<EOF + sed -e '/^MAKEFLAGS=/d' -e '/^PACKAGER=/d' -i "$copydir/etc/makepkg.conf" + for x in BUILDDIR=/build PKGDEST=/pkgdest SRCPKGDEST=/srcpkgdest SRCDEST=/srcdest LOGDEST=/logdest \ + "MAKEFLAGS='${MAKEFLAGS:-}'" "PACKAGER='${PACKAGER:-}'" + do + grep -q "^$x" "$copydir/etc/makepkg.conf" && continue + echo "$x" >>"$copydir/etc/makepkg.conf" + 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" - fi + 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') @@ -318,6 +319,9 @@ Server = file:///repo ' sed -i "${line}i${ins//$'\n'/\\n}" "$copydir/etc/pacman.conf" fi + # Avoid having to use `pacman -Sy` to update [repo], as + # networking might be disabled inside of the chroot. + cp "$copydir/repo/repo.db" "$copydir/var/lib/pacman/sync/repo.db" # This is a little gross, but this way the script is recreated every time in the # working copy @@ -333,18 +337,33 @@ Server = file:///repo printf '_chrootbuild "$@" || exit\n' if $run_namcap; then - cat <<'EOF' -pacman -S --needed --noconfirm namcap -for pkgfile in /startdir/PKGBUILD /pkgdest/*; do - echo "Checking ${pkgfile##*/}" - sudo -u builduser namcap "$pkgfile" 2>&1 | tee "/logdest/${pkgfile##*/}-namcap.log" -done -EOF + declare -f _chrootnamcap + printf '_chrootnamcap || exit\n' fi } >"$copydir/chrootbuild" chmod +x "$copydir/chrootbuild" } +# These functions aren't run in makechrootpkg, +# so no global variables +_chrootprepare() { + . /etc/profile + sudo -iu builduser bash -c 'cd /startdir; makepkg "$@" --nobuild' -bash "$@" +} + +_chrootbuild() { + . /etc/profile + sudo -iu builduser bash -c 'cd /startdir; makepkg "$@" --noextract --noprepare' -bash "$@" +} + +_chrootnamcap() { + pacman -S --needed --noconfirm namcap + for pkgfile in /startdir/PKGBUILD /pkgdest/*; do + echo "Checking ${pkgfile##*/}" + sudo -u builduser namcap "$pkgfile" 2>&1 | tee "/logdest/${pkgfile##*/}-namcap.log" + done +} + # Usage: download_sources $copydir $src_owner # Globals: # - SRCDEST @@ -371,63 +390,6 @@ download_sources() { rm -rf "$builddir" } -_chrootprepare() { - # This function isn't run in makechrootpkg, - # so no global variables - - . /etc/profile - export HOME=/build - shopt -s nullglob - - # XXX: Workaround makepkg disliking read-only dirs - rm -rf -- /srcdest/* /startdir/* - ln -sft /srcdest /srcdest_host/* - ln -sft /startdir /startdir_host/* - - # XXX: Keep bzr and svn sources writable - # Since makepkg 4.1.1 they get checked out via cp -a, copying the symlink - for dir in /srcdest /startdir; do - for vcs in bzr svn; do - cd "$dir" - for vcsdir in */.$vcs; do - rm "${vcsdir%/.$vcs}" - cp -a "${dir}_host/${vcsdir%/.$vcs}" . - chown -R builduser "${vcsdir%/.$vcs}" - done - done - done - - cd /startdir - - # XXX: Keep PKGBUILD writable for pkgver() - rm PKGBUILD* - cp /startdir_host/PKGBUILD* . - chown builduser PKGBUILD* - - # Safety check - if [[ ! -w PKGBUILD ]]; then - echo "Can't write to PKGBUILD!" - exit 1 - fi - - # Sync deps now, as networking may be disabled during _chrootbuild - cp /repo/repo.db /var/lib/pacman/sync/repo.db - sudo -u builduser makepkg "$@" --nobuild -} - -_chrootbuild() { - # This function isn't run in makechrootpkg, - # so no global variables - - . /etc/profile - export HOME=/build - shopt -s nullglob - - cd /startdir - - sudo -u builduser makepkg "$@" --noextract --noprepare -} - # Usage: move_products $copydir $owner # Globals: # - PKGDEST @@ -515,7 +477,7 @@ main() { umask 0022 - load_vars "$USER_HOME/.makepkg.conf" + load_vars "${XDG_CONFIG_HOME:-$USER_HOME/.config}/pacman/makepkg.conf" || load_vars "$USER_HOME/.makepkg.conf" load_vars /etc/makepkg.conf # Use PKGBUILD directory if these don't exist @@ -547,13 +509,13 @@ main() { prepare_chroot "$copydir" "$USER_HOME" "$repack" if arch-nspawn "$copydir" \ - --bind-ro="$PWD:/startdir_host" \ - --bind-ro="$SRCDEST:/srcdest_host" \ + --bind="$PWD:/startdir" \ + --bind="$SRCDEST:/srcdest" \ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ /chrootprepare "${makepkg_args[@]}" && arch-nspawn "$copydir" \ - --bind-ro="$PWD:/startdir_host" \ - --bind-ro="$SRCDEST:/srcdest_host" \ + --bind="$PWD:/startdir" \ + --bind="$SRCDEST:/srcdest" \ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ /chrootbuild "${makepkg_args[@]}" then |