aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@unit193.net>2022-07-05 01:31:24 -0400
committerLibravatarUnit 193 <unit193@unit193.net>2022-07-05 01:31:24 -0400
commita5d5363b8948f79d0dd9bf2c6739f690bf3074ba (patch)
treee54566ea58199bad04e71eba82aa3aa4555f93f1
parent5779479fe0c17c49db276b713ca6a7c99e039124 (diff)
parent22faa4b00068fc50c66614c4e4812273aa231d55 (diff)
Update upstream source from tag 'upstream/25'
Update to upstream version '25' with Debian dir cfd9e373c982c9f1336c636e16031caabdba96be
-rw-r--r--README.md1
-rw-r--r--arch-chroot.in103
-rw-r--r--common75
-rw-r--r--completion/arch-chroot.bash2
-rw-r--r--completion/pacstrap.bash2
-rw-r--r--doc/arch-chroot.8.asciidoc7
-rw-r--r--doc/pacstrap.8.asciidoc5
-rw-r--r--pacstrap.in57
8 files changed, 189 insertions, 63 deletions
diff --git a/README.md b/README.md
index 34aff43..221b020 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ tasks when installing [Arch Linux](https://www.archlinux.org).
* util-linux (>= 2.23)
* POSIX awk
* bash (>= 4.1)
+* asciidoc (for generating man pages)
## License
diff --git a/arch-chroot.in b/arch-chroot.in
index fd6140e..bcb38df 100644
--- a/arch-chroot.in
+++ b/arch-chroot.in
@@ -4,11 +4,15 @@ shopt -s extglob
m4_include(common)
+setup=chroot_setup
+unshare="$root_unshare"
+
usage() {
cat <<EOF
-usage: ${0##*/} chroot-dir [command]
+usage: ${0##*/} chroot-dir [command] [arguments...]
-h Print this help message
+ -N Run in unshare mode as a regular user
-u <user>[:group] Specify non-root user and optional group to use
If 'command' is unspecified, ${0##*/} will launch /bin/bash.
@@ -23,40 +27,63 @@ itself to make it a mountpoint, i.e. 'mount --bind /your/chroot /your/chroot'.
EOF
}
+resolve_link() {
+ local target=$1
+ local root=$2
+
+ # If a root was given, make sure it ends in a slash.
+ [[ -n $root && $root != */ ]] && root=$root/
+
+ while [[ -L $target ]]; do
+ target=$(readlink -m "$target")
+ # If a root was given, make sure the target is under it.
+ # Make sure to strip any leading slash from target first.
+ [[ -n $root && $target != $root* ]] && target=$root${target#/}
+ done
+
+ printf %s "$target"
+}
+
chroot_add_resolv_conf() {
- local chrootdir=$1 resolv_conf=$1/etc/resolv.conf
-
- [[ -e /etc/resolv.conf ]] || return 0
-
- # Handle resolv.conf as a symlink to somewhere else.
- if [[ -L $chrootdir/etc/resolv.conf ]]; then
- # readlink(1) should always give us *something* since we know at this point
- # it's a symlink. For simplicity, ignore the case of nested symlinks.
- resolv_conf=$(readlink "$chrootdir/etc/resolv.conf")
- if [[ $resolv_conf = /* ]]; then
- resolv_conf=$chrootdir$resolv_conf
- else
- resolv_conf=$chrootdir/etc/$resolv_conf
- fi
-
- # ensure file exists to bind mount over
- if [[ ! -f $resolv_conf ]]; then
- install -Dm644 /dev/null "$resolv_conf" || return 1
- fi
- elif [[ ! -e $chrootdir/etc/resolv.conf ]]; then
- # The chroot might not have a resolv.conf.
- return 0
+ local chrootdir=$1
+ local src=$(resolve_link /etc/resolv.conf)
+ local dest=$(resolve_link "$chrootdir/etc/resolv.conf" "$chrootdir")
+
+ # If we don't have a source resolv.conf file, there's nothing useful we can do.
+ [[ -e $src ]] || return 0
+
+ if [[ ! -e $dest ]]; then
+ # There are two reasons the destination might not exist:
+ #
+ # 1. There may be no resolv.conf in the chroot. In this case, $dest won't exist,
+ # and it will be equal to $1/etc/resolv.conf. In this case, we'll just exit.
+ # The chroot environment must not be concerned with DNS resolution.
+ #
+ # 2. $1/etc/resolv.conf is (or resolves to) a broken link. The environment
+ # clearly intends to handle DNS resolution, but something's wrong. Maybe it
+ # normally creates the target at boot time. We'll (try to) take care of it by
+ # creating a dummy file at the target, so that we have something to bind to.
+
+ # Case 1.
+ [[ $dest = $chrootdir/etc/resolv.conf ]] && return 0
+
+ # Case 2.
+ install -Dm644 /dev/null "$dest" || return 1
fi
- chroot_add_mount /etc/resolv.conf "$resolv_conf" --bind
+ chroot_add_mount "$src" "$dest" --bind
}
-while getopts ':hu:' flag; do
+while getopts ':hNu:' flag; do
case $flag in
h)
usage
exit 0
;;
+ N)
+ setup=unshare_setup
+ unshare="$user_unshare"
+ ;;
u)
userspec=$OPTARG
;;
@@ -70,21 +97,27 @@ while getopts ':hu:' flag; do
done
shift $(( OPTIND - 1 ))
-(( EUID == 0 )) || die 'This script must be run with root privileges'
(( $# )) || die 'No chroot directory specified'
chrootdir=$1
shift
-[[ -d $chrootdir ]] || die "Can't create chroot on non-directory %s" "$chrootdir"
+arch-chroot() {
+ (( EUID == 0 )) || die 'This script must be run with root privileges'
+
+ [[ -d $chrootdir ]] || die "Can't create chroot on non-directory %s" "$chrootdir"
-if ! mountpoint -q "$chrootdir"; then
- warning "$chrootdir is not a mountpoint. This may have undesirable side effects."
-fi
+ $setup "$chrootdir" || die "failed to setup chroot %s" "$chrootdir"
+ chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"
-chroot_setup "$chrootdir" || die "failed to setup chroot %s" "$chrootdir"
-chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"
+ if ! mountpoint -q "$chrootdir"; then
+ warning "$chrootdir is not a mountpoint. This may have undesirable side effects."
+ fi
-chroot_args=()
-[[ $userspec ]] && chroot_args+=(--userspec "$userspec")
+ chroot_args=()
+ [[ $userspec ]] && chroot_args+=(--userspec "$userspec")
+
+ SHELL=/bin/bash chroot "${chroot_args[@]}" -- "$chrootdir" "${args[@]}"
+}
-SHELL=/bin/bash unshare --fork --pid chroot "${chroot_args[@]}" -- "$chrootdir" "$@"
+args=("$@")
+$unshare bash -c "$(declare_all); arch-chroot"
diff --git a/common b/common
index fa040e7..bc2a334 100644
--- a/common
+++ b/common
@@ -39,6 +39,7 @@ declare -A fsck_types=([cramfs]=1
[ext3]=1
[ext4]=1
[ext4dev]=1
+ [f2fs]=1
[jfs]=1
[minix]=1
[msdos]=1
@@ -89,7 +90,7 @@ chroot_setup() {
chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid &&
chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec &&
chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev &&
- chroot_add_mount /run "$1/run" --bind &&
+ chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 &&
chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid
}
@@ -100,6 +101,77 @@ chroot_teardown() {
unset CHROOT_ACTIVE_MOUNTS
}
+chroot_add_mount_lazy() {
+ mount "$@" && CHROOT_ACTIVE_LAZY=("$2" "${CHROOT_ACTIVE_LAZY[@]}")
+}
+
+chroot_bind_device() {
+ touch "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
+ chroot_add_mount $1 "$2" --bind
+}
+
+chroot_add_link() {
+ ln -sf "$1" "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
+}
+
+unshare_setup() {
+ CHROOT_ACTIVE_MOUNTS=()
+ CHROOT_ACTIVE_LAZY=()
+ CHROOT_ACTIVE_FILES=()
+ [[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
+ trap 'unshare_teardown' EXIT
+
+ chroot_add_mount_lazy "$1" "$1" --bind &&
+ chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev &&
+ chroot_add_mount_lazy /sys "$1/sys" --rbind &&
+ chroot_add_link "$1/proc/self/fd" "$1/dev/fd" &&
+ chroot_add_link "$1/proc/self/fd/0" "$1/dev/stdin" &&
+ chroot_add_link "$1/proc/self/fd/1" "$1/dev/stdout" &&
+ chroot_add_link "$1/proc/self/fd/2" "$1/dev/stderr" &&
+ chroot_bind_device /dev/full "$1/dev/full" &&
+ chroot_bind_device /dev/null "$1/dev/null" &&
+ chroot_bind_device /dev/random "$1/dev/random" &&
+ chroot_bind_device /dev/tty "$1/dev/tty" &&
+ chroot_bind_device /dev/urandom "$1/dev/urandom" &&
+ chroot_bind_device /dev/zero "$1/dev/zero" &&
+ chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 &&
+ chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid
+}
+
+unshare_teardown() {
+ chroot_teardown
+
+ if (( ${#CHROOT_ACTIVE_LAZY[@]} )); then
+ umount --lazy "${CHROOT_ACTIVE_LAZY[@]}"
+ fi
+ unset CHROOT_ACTIVE_LAZY
+
+ if (( ${#CHROOT_ACTIVE_FILES[@]} )); then
+ rm "${CHROOT_ACTIVE_FILES[@]}"
+ fi
+ unset CHROOT_ACTIVE_FILES
+}
+
+root_unshare="unshare --fork --pid"
+user_unshare="$root_unshare --mount --map-auto --map-root-user --setuid 0 --setgid 0"
+
+# This outputs code for declaring all variables to stdout. For example, if
+# FOO=BAR, then running
+# declare -p FOO
+# will result in the output
+# declare -- FOO="bar"
+# This function may be used to re-declare all currently used variables and
+# functions in a new shell.
+declare_all() {
+ # Remove read-only variables to avoid warnings. Unfortunately, declare +r -p
+ # doesn't work like it looks like it should (declaring only read-write
+ # variables). However, declare -rp will print out read-only variables, which
+ # we can then use to remove those definitions.
+ declare -p | grep -Fvf <(declare -rp)
+ # Then declare functions
+ declare -pf
+}
+
try_cast() (
_=$(( $1#$2 ))
) 2>/dev/null
@@ -243,7 +315,6 @@ dm_name_for_devnode() {
else
# don't leave the caller hanging, just print the original name
# along with the failure.
- print '%s' "$1"
error 'Failed to resolve device mapper name for: %s' "$1"
fi
}
diff --git a/completion/arch-chroot.bash b/completion/arch-chroot.bash
index 707208a..583bd8f 100644
--- a/completion/arch-chroot.bash
+++ b/completion/arch-chroot.bash
@@ -2,7 +2,7 @@ _arch_chroot() {
compopt +o dirnames
local cur prev opts i
_init_completion -n : || return
- opts="-u -h"
+ opts="-N -u -h"
for i in "${COMP_WORDS[@]:1:COMP_CWORD-1}"; do
if [[ -d ${i} ]]; then
diff --git a/completion/pacstrap.bash b/completion/pacstrap.bash
index fb948f0..a77cb04 100644
--- a/completion/pacstrap.bash
+++ b/completion/pacstrap.bash
@@ -8,7 +8,7 @@ _pacstrap() {
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
- opts="-C -c -G -i -M -h"
+ opts="-C -c -G -i -M -N -h"
for i in "${COMP_WORDS[@]:1:COMP_CWORD-1}"; do
if [[ -d ${i} ]]; then
diff --git a/doc/arch-chroot.8.asciidoc b/doc/arch-chroot.8.asciidoc
index 5586a46..a361043 100644
--- a/doc/arch-chroot.8.asciidoc
+++ b/doc/arch-chroot.8.asciidoc
@@ -7,7 +7,7 @@ arch-chroot - enhanced chroot command
Synopsis
--------
-arch-chroot [options] chroot-dir [command]
+arch-chroot [options] chroot-dir [command] [arguments...]
Description
-----------
@@ -32,6 +32,11 @@ i.e.:
Options
-------
+*-N*::
+ Run in unshare mode. This will use linkman:unshare[1] to create a new
+ mount and user namespace, allowing regular users to create new system
+ installations.
+
*-u <user>[:group]*::
Specify non-root user and optional group to use.
diff --git a/doc/pacstrap.8.asciidoc b/doc/pacstrap.8.asciidoc
index d3d517c..6eed23e 100644
--- a/doc/pacstrap.8.asciidoc
+++ b/doc/pacstrap.8.asciidoc
@@ -37,6 +37,11 @@ Options
*-M*::
Avoid copying the host's mirrorlist to the target.
+*-N*::
+ Run in unshare mode. This will use linkman:unshare[1] to create a new
+ mount and user namespace, allowing regular users to create new system
+ installations.
+
*-U*::
Use pacman -U to install packages. Useful for obtaining fine-grained
control over the installed packages.
diff --git a/pacstrap.in b/pacstrap.in
index 703df11..9ffe17c 100644
--- a/pacstrap.in
+++ b/pacstrap.in
@@ -16,6 +16,8 @@ hostcache=0
copykeyring=1
copymirrorlist=1
pacmode=-Sy
+setup=chroot_setup
+unshare="$root_unshare"
usage() {
cat <<EOF
@@ -27,6 +29,7 @@ usage: ${0##*/} [options] root [packages...]
-G Avoid copying the host's pacman keyring to the target
-i Prompt for package confirmation when needed (run interactively)
-M Avoid copying the host's mirrorlist to the target
+ -N Run in unshare mode as a regular user
-U Use pacman -U to install packages
-h Print this help message
@@ -42,9 +45,7 @@ if [[ -z $1 || $1 = @(-h|--help) ]]; then
exit $(( $# ? 0 : 1 ))
fi
-(( EUID == 0 )) || die 'This script must be run with root privileges'
-
-while getopts ':C:cdGiMU' flag; do
+while getopts ':C:cdGiMNU' flag; do
case $flag in
C)
pacman_config=$OPTARG
@@ -64,6 +65,10 @@ while getopts ':C:cdGiMU' flag; do
M)
copymirrorlist=0
;;
+ N)
+ setup=unshare_setup
+ unshare="$user_unshare"
+ ;;
U)
pacmode=-U
;;
@@ -95,30 +100,36 @@ fi
[[ -d $newroot ]] || die "%s is not a directory" "$newroot"
-# create obligatory directories
-msg 'Creating install root at %s' "$newroot"
-mkdir -m 0755 -p "$newroot"/var/{cache/pacman/pkg,lib/pacman,log} "$newroot"/{dev,run,etc/pacman.d}
-mkdir -m 1777 -p "$newroot"/tmp
-mkdir -m 0555 -p "$newroot"/{sys,proc}
+pacstrap() {
+ (( EUID == 0 )) || die 'This script must be run with root privileges'
+
+ # create obligatory directories
+ msg 'Creating install root at %s' "$newroot"
+ mkdir -m 0755 -p "$newroot"/var/{cache/pacman/pkg,lib/pacman,log} "$newroot"/{dev,run,etc/pacman.d}
+ mkdir -m 1777 -p "$newroot"/tmp
+ mkdir -m 0555 -p "$newroot"/{sys,proc}
-# mount API filesystems
-chroot_setup "$newroot" || die "failed to setup chroot %s" "$newroot"
+ # mount API filesystems
+ $setup "$newroot" || die "failed to setup chroot %s" "$newroot"
-if (( copykeyring )); then
- # if there's a keyring on the host, copy it into the new root, unless it exists already
- if [[ -d /etc/pacman.d/gnupg && ! -d $newroot/etc/pacman.d/gnupg ]]; then
- cp -a /etc/pacman.d/gnupg "$newroot/etc/pacman.d/"
+ if (( copykeyring )); then
+ # if there's a keyring on the host, copy it into the new root, unless it exists already
+ if [[ -d /etc/pacman.d/gnupg && ! -d $newroot/etc/pacman.d/gnupg ]]; then
+ cp -a --no-preserve=ownership /etc/pacman.d/gnupg "$newroot/etc/pacman.d/"
+ fi
fi
-fi
-msg 'Installing packages to %s' "$newroot"
-if ! unshare --fork --pid pacman -r "$newroot" $pacmode "${pacman_args[@]}"; then
- die 'Failed to install packages to new root'
-fi
+ msg 'Installing packages to %s' "$newroot"
+ if ! pacman -r "$newroot" $pacmode "${pacman_args[@]}"; then
+ die 'Failed to install packages to new root'
+ fi
-if (( copymirrorlist )); then
- # install the host's mirrorlist onto the new root
- cp -a /etc/pacman.d/mirrorlist "$newroot/etc/pacman.d/"
-fi
+ if (( copymirrorlist )); then
+ # install the host's mirrorlist onto the new root
+ cp -a /etc/pacman.d/mirrorlist "$newroot/etc/pacman.d/"
+ fi
+}
+
+$unshare bash -c "$(declare_all); pacstrap"
# vim: et ts=2 sw=2 ft=sh: