Compare commits
13 Commits
master
...
github/for
Author | SHA1 | Date | |
---|---|---|---|
|
0e14a1a13b | ||
|
f40fc1f2b0 | ||
|
bc1b449f1f | ||
|
7881d7ebce | ||
|
9aa95365c2 | ||
|
2b9027a0ab | ||
|
05d231aff9 | ||
|
c21e56e88e | ||
|
e13ce65908 | ||
|
092226862b | ||
|
4be53d3488 | ||
|
e3f9cb6498 | ||
|
33cf482649 |
2
Makefile
2
Makefile
@ -26,7 +26,7 @@ edit = $(V_GEN) m4 -P $@.in >$@ && chmod go-w,+x $@
|
||||
%: %.in common
|
||||
$(edit)
|
||||
|
||||
doc/%: doc/%.asciidoc doc/asciidoc.conf doc/footer.asciidoc
|
||||
doc/%: doc/%.asciidoc doc/asciidoc.conf
|
||||
$(V_GEN) a2x --no-xmllint --asciidoc-opts="-f doc/asciidoc.conf" -d manpage -f manpage -D doc $<
|
||||
|
||||
clean:
|
||||
|
108
arch-chroot.in
108
arch-chroot.in
@ -2,126 +2,86 @@
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
# m4_include() is recognized as a function definition
|
||||
# shellcheck source=common disable=SC1073,SC1065,SC1064,SC1072
|
||||
m4_include(common)
|
||||
|
||||
setup=chroot_setup
|
||||
unshare=0
|
||||
userspec=''
|
||||
chroot_args=()
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: ${0##*/} chroot-dir [command] [arguments...]
|
||||
usage: ${0##*/} [options] chroot_dir [command [arguments...]]
|
||||
|
||||
Options:
|
||||
-N Run in unshare mode as a regular user
|
||||
-u <user>[:group] Specify non-root user and group (optional) to use
|
||||
|
||||
-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.
|
||||
If 'command' is unspecified, arch-chroot will launch /bin/bash.
|
||||
|
||||
Note that when using arch-chroot, the target chroot directory *should* be a
|
||||
mountpoint. This ensures that tools such as pacman(8) or findmnt(8) have an
|
||||
accurate hierarchy of the mounted filesystems within the chroot.
|
||||
|
||||
If your chroot target is not a mountpoint, you can bind mount the directory on
|
||||
itself to make it a mountpoint, i.e. 'mount --bind /your/chroot /your/chroot'.
|
||||
itself to make it one, i.e. 'mount --bind chroot_dir chroot_dir'.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
resolve_link() {
|
||||
local target=$1
|
||||
local root=$2
|
||||
if [[ -z $1 || $1 = @(-h|--help) ]]; then
|
||||
usage
|
||||
exit $(( $# ? 0 : 1 ))
|
||||
fi
|
||||
|
||||
# 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
|
||||
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 "$src" "$dest" --bind
|
||||
}
|
||||
|
||||
while getopts ':hNu:' flag; do
|
||||
while getopts ':Nu:' flag; do
|
||||
case $flag in
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
N)
|
||||
setup=unshare_setup
|
||||
unshare=1
|
||||
;;
|
||||
u)
|
||||
userspec=$OPTARG
|
||||
userspec="$OPTARG"
|
||||
;;
|
||||
:)
|
||||
die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
die "%s: option requires an argument -- '%s'" "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
?)
|
||||
die '%s: invalid option -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
die "%s: invalid option -- '%s'" "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(( OPTIND - 1 ))
|
||||
|
||||
(( $# )) || die 'No chroot directory specified'
|
||||
chrootdir=$1
|
||||
shift
|
||||
chrootdir="$1"; shift
|
||||
|
||||
if ! mountpoint -q "$chrootdir"; then
|
||||
warning '%s is not a mountpoint, and thus may cause undesirable side effects. (See help for more info)' "$chrootdir"
|
||||
fi
|
||||
|
||||
command=("$@")
|
||||
|
||||
arch-chroot() {
|
||||
(( EUID == 0 )) || die 'This script must be run with root privileges'
|
||||
check_root
|
||||
|
||||
[[ -d $chrootdir ]] || die "Can't create chroot on non-directory %s" "$chrootdir"
|
||||
[[ -d "$chrootdir" ]] || die '%s: not a directory' "$chrootdir"
|
||||
|
||||
$setup "$chrootdir" || die "failed to setup chroot %s" "$chrootdir"
|
||||
chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"
|
||||
$setup "$chrootdir"
|
||||
chroot_add_resolv_conf "$chrootdir" || die 'Failed to setup resolv.conf in chroot'
|
||||
|
||||
if ! mountpoint -q "$chrootdir"; then
|
||||
warning "$chrootdir is not a mountpoint. This may have undesirable side effects."
|
||||
fi
|
||||
[[ $userspec ]] && chroot_args+=(--userspec="$userspec")
|
||||
|
||||
chroot_args=()
|
||||
[[ $userspec ]] && chroot_args+=(--userspec "$userspec")
|
||||
|
||||
SHELL=/bin/bash $pid_unshare chroot "${chroot_args[@]}" -- "$chrootdir" "${args[@]}"
|
||||
SHELL=/bin/bash $pid_unshare chroot "${chroot_args[@]}" -- "$chrootdir" "${command[@]}"
|
||||
}
|
||||
|
||||
args=("$@")
|
||||
if (( unshare )); then
|
||||
$mount_unshare bash -c "$(declare_all); arch-chroot"
|
||||
else
|
||||
arch-chroot
|
||||
fi
|
||||
|
||||
# vim: et ts=2 sw=2 ft=sh:
|
||||
|
364
common
364
common
@ -1,3 +1,6 @@
|
||||
#!/hint/bash
|
||||
# shellcheck disable=SC2155,SC2064
|
||||
|
||||
# generated from util-linux source: libmount/src/utils.c
|
||||
declare -A pseudofs_types=([anon_inodefs]=1
|
||||
[apparmorfs]=1
|
||||
@ -72,112 +75,210 @@ declare -A fsck_types=([btrfs]=0 # btrfs doesn't need a regular fsck utility
|
||||
[vfat]=1
|
||||
[xfs]=1)
|
||||
|
||||
# shellcheck disable=SC2059
|
||||
out() { printf "$1 $2\n" "${@:3}"; }
|
||||
error() { out "==> ERROR:" "$@"; } >&2
|
||||
warning() { out "==> WARNING:" "$@"; } >&2
|
||||
msg() { out "==>" "$@"; }
|
||||
msg2() { out " ->" "$@";}
|
||||
msg2() { out " ->" "$@"; }
|
||||
warning() { out "==> WARNING:" "$@"; } >&2
|
||||
error() { out "==> ERROR:" "$@"; } >&2
|
||||
die() { error "$@"; exit 1; }
|
||||
|
||||
ignore_error() {
|
||||
"$@" 2>/dev/null
|
||||
return 0
|
||||
"$@" 2>/dev/null || return 0
|
||||
}
|
||||
|
||||
in_array() {
|
||||
local i
|
||||
for i in "${@:2}"; do
|
||||
[[ $1 = "$i" ]] && return 0
|
||||
local -n _arr="$1"
|
||||
|
||||
for i in "${_arr[@]}"; do
|
||||
[[ "$i" = "$1" ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
rev_array() {
|
||||
local -n _arr="$1"
|
||||
|
||||
mapfile -t _arr < <(printf '%s\n' "${_arr[@]}" | tac)
|
||||
}
|
||||
|
||||
check_root() {
|
||||
(( EUID == 0 )) || die 'This script must be run with root privileges'
|
||||
}
|
||||
|
||||
resolve_link() {
|
||||
local link="$1" root="$2" target
|
||||
|
||||
if [[ ! $root ]]; then
|
||||
target="$(realpath -eq "$link")"
|
||||
else
|
||||
# This is tricky to do. We read from $link in a loop
|
||||
# and prepend it with $root if it's not under it.
|
||||
# This can't handle e.g. $root is /mnt and $link
|
||||
# is /mnt/1 pointing to /mnt/2, which should actually
|
||||
# be /mnt/mnt/2. Luckily these edge cases shouldn't
|
||||
# bother most of the time.
|
||||
|
||||
root="$(realpath -e "$root")"
|
||||
target="$link"
|
||||
while [[ -L "$target" ]]; do
|
||||
target="$(readlink -m "$target")"
|
||||
if [[ "$target" != "$root"/* ]]; then
|
||||
target="$root/${target#/}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Normalize the path and make sure the resolved target exists
|
||||
target="$(realpath -eq "$target")"
|
||||
fi
|
||||
|
||||
printf '%s' "$target"
|
||||
}
|
||||
|
||||
chroot_add_mount() {
|
||||
mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}")
|
||||
mount "$@" && CHROOT_ACTIVE_MOUNTS+=("$2")
|
||||
}
|
||||
|
||||
chroot_maybe_add_mount() {
|
||||
local cond=$1; shift
|
||||
local cond="$1"; shift
|
||||
|
||||
if eval "$cond"; then
|
||||
chroot_add_mount "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
chroot_setup() {
|
||||
CHROOT_ACTIVE_MOUNTS=()
|
||||
[[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
|
||||
trap 'chroot_teardown' EXIT
|
||||
|
||||
chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev &&
|
||||
chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro &&
|
||||
ignore_error chroot_maybe_add_mount "[[ -d '$1/sys/firmware/efi/efivars' ]]" \
|
||||
efivarfs "$1/sys/firmware/efi/efivars" -t efivarfs -o nosuid,noexec,nodev &&
|
||||
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" -t tmpfs -o nosuid,nodev,mode=0755 &&
|
||||
chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid
|
||||
}
|
||||
|
||||
chroot_teardown() {
|
||||
if (( ${#CHROOT_ACTIVE_MOUNTS[@]} )); then
|
||||
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
|
||||
fi
|
||||
unset CHROOT_ACTIVE_MOUNTS
|
||||
}
|
||||
|
||||
chroot_add_mount_lazy() {
|
||||
mount "$@" && CHROOT_ACTIVE_LAZY=("$2" "${CHROOT_ACTIVE_LAZY[@]}")
|
||||
mount "$@" && CHROOT_ACTIVE_LAZY+=("$2")
|
||||
}
|
||||
|
||||
chroot_bind_device() {
|
||||
touch "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
|
||||
chroot_add_mount $1 "$2" --bind
|
||||
touch "$2" && CHROOT_ACTIVE_FILES+=("$2")
|
||||
chroot_add_mount "$1" "$2" --bind
|
||||
}
|
||||
|
||||
chroot_add_link() {
|
||||
ln -sf "$1" "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
|
||||
ln -sf "$1" "$2" && CHROOT_ACTIVE_FILES+=("$2")
|
||||
}
|
||||
|
||||
chroot_add_resolv_conf() {
|
||||
local src="$(resolve_link /etc/resolv.conf)" dest="$1/etc/resolv.conf"
|
||||
|
||||
# If we don't have a source resolv.conf file, there's nothing useful we can do.
|
||||
if [[ ! -f "$src" ]]; then
|
||||
warning 'Cannot find a usable resolv.conf. DNS requests might fail in chroot'
|
||||
return 0
|
||||
fi
|
||||
|
||||
# If resolv.conf in the chroot is a symlink, we make a backup of it
|
||||
# and create a plain file so we can bind mount to it.
|
||||
# The backup is restore during chroot_teardown().
|
||||
|
||||
if [[ -L "$dest" ]]; then
|
||||
mv "$dest" "$dest.orig"
|
||||
fi
|
||||
touch -a "$dest"
|
||||
|
||||
chroot_add_mount "$src" "$dest" --bind
|
||||
}
|
||||
|
||||
chroot_setup() {
|
||||
CHROOT_ACTIVE_MOUNTS=()
|
||||
local root="$1"
|
||||
local errtrap_old="$(trap -p ERR)" shellopts_old="$SHELLOPTS"
|
||||
|
||||
[[ $(trap -p EXIT) ]] && die 'An EXIT trap already exists (likely a bug)'
|
||||
trap "chroot_teardown $root" EXIT
|
||||
|
||||
trap "$errtrap_old; die 'Failed to setup chroot %s' '$root'" ERR
|
||||
set -e
|
||||
|
||||
chroot_add_mount proc "$root"/proc -t proc -o nosuid,noexec,nodev
|
||||
chroot_add_mount sys "$root"/sys -t sysfs -o nosuid,noexec,nodev,ro
|
||||
ignore_error chroot_maybe_add_mount "[[ -d '$root/sys/firmware/efi/efivars' ]]" \
|
||||
efivarfs "$root"/sys/firmware/efi/efivars -t efivarfs -o nosuid,noexec,nodev
|
||||
chroot_add_mount udev "$root"/dev -t devtmpfs -o mode=0755,nosuid
|
||||
chroot_add_mount devpts "$root"/dev/pts -t devpts -o mode=0620,gid=5,nosuid,noexec
|
||||
chroot_add_mount shm "$root"/dev/shm -t tmpfs -o mode=1777,nosuid,nodev
|
||||
chroot_add_mount run "$root"/run -t tmpfs -o nosuid,nodev,mode=0755
|
||||
chroot_add_mount tmp "$root"/tmp -t tmpfs -o mode=1777,strictatime,nodev,nosuid
|
||||
|
||||
[[ "$shellopts_old" = *'errexit'* ]] || set +e
|
||||
if [[ $errtrap_old ]]; then
|
||||
trap "$errtrap_old" ERR
|
||||
else
|
||||
trap - ERR
|
||||
fi
|
||||
}
|
||||
|
||||
chroot_teardown() {
|
||||
local root="$1"
|
||||
|
||||
if (( ${#CHROOT_ACTIVE_MOUNTS[@]} )); then
|
||||
rev_array CHROOT_ACTIVE_MOUNTS
|
||||
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
|
||||
fi
|
||||
unset CHROOT_ACTIVE_MOUNTS
|
||||
|
||||
if [[ -L "$root"/etc/resolv.conf.orig ]]; then
|
||||
mv "$root"/etc/resolv.conf.orig "$root"/etc/resolv.conf
|
||||
fi
|
||||
}
|
||||
|
||||
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_ACTIVE_MOUNTS=() CHROOT_ACTIVE_LAZY=() CHROOT_ACTIVE_FILES=()
|
||||
local root="$1"
|
||||
local errtrap_old="$(trap -p ERR)" shellopts_old="$SHELLOPTS"
|
||||
|
||||
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
|
||||
[[ $(trap -p EXIT) ]] && die 'An EXIT trap already exists (likely a bug)'
|
||||
trap "unshare_teardown $root" EXIT
|
||||
|
||||
trap "$errtrap_old; die 'Failed to setup chroot %s' '$root'" ERR
|
||||
set -e
|
||||
|
||||
chroot_add_mount_lazy "$root" "$root" --bind
|
||||
chroot_add_mount proc "$root"/proc -t proc -o nosuid,noexec,nodev
|
||||
chroot_add_mount_lazy /sys "$root"/sys --rbind
|
||||
chroot_add_link "$root"/proc/self/fd "$root"/dev/fd
|
||||
chroot_add_link "$root"/proc/self/fd/0 "$root"/dev/stdin
|
||||
chroot_add_link "$root"/proc/self/fd/1 "$root"/dev/stdout
|
||||
chroot_add_link "$root"/proc/self/fd/2 "$root"/dev/stderr
|
||||
chroot_bind_device /dev/full "$root"/dev/full
|
||||
chroot_bind_device /dev/null "$root"/dev/null
|
||||
chroot_bind_device /dev/zero "$root"/dev/zero
|
||||
chroot_bind_device /dev/random "$root"/dev/random
|
||||
chroot_bind_device /dev/urandom "$root"/dev/urandom
|
||||
chroot_bind_device /dev/tty "$root"/dev/tty
|
||||
chroot_add_mount run "$root"/run -t tmpfs -o nosuid,nodev,mode=0755
|
||||
chroot_add_mount tmp "$root"/tmp -t tmpfs -o mode=1777,strictatime,nodev,nosuid
|
||||
|
||||
[[ "$shellopts_old" = *'errexit'* ]] || set +e
|
||||
if [[ $errtrap_old ]]; then
|
||||
trap "$errtrap_old" ERR
|
||||
else
|
||||
trap - ERR
|
||||
fi
|
||||
}
|
||||
|
||||
unshare_teardown() {
|
||||
chroot_teardown
|
||||
chroot_teardown "$1"
|
||||
|
||||
if (( ${#CHROOT_ACTIVE_LAZY[@]} )); then
|
||||
rev_array CHROOT_ACTIVE_LAZY
|
||||
umount --lazy "${CHROOT_ACTIVE_LAZY[@]}"
|
||||
fi
|
||||
unset CHROOT_ACTIVE_LAZY
|
||||
|
||||
if (( ${#CHROOT_ACTIVE_FILES[@]} )); then
|
||||
rev_array CHROOT_ACTIVE_FILES
|
||||
rm "${CHROOT_ACTIVE_FILES[@]}"
|
||||
fi
|
||||
unset CHROOT_ACTIVE_FILES
|
||||
}
|
||||
|
||||
pid_unshare="unshare --fork --pid"
|
||||
# shellcheck disable=SC2034
|
||||
mount_unshare="$pid_unshare --mount --map-auto --map-root-user --setuid 0 --setgid 0"
|
||||
|
||||
# This outputs code for declaring all variables to stdout. For example, if
|
||||
@ -198,11 +299,11 @@ declare_all() {
|
||||
}
|
||||
|
||||
try_cast() (
|
||||
_=$(( $1#$2 ))
|
||||
_=$(( ${1#"$2"} ))
|
||||
) 2>/dev/null
|
||||
|
||||
valid_number_of_base() {
|
||||
local base=$1 len=${#2} i=
|
||||
local base="$1" len="${#2}" i
|
||||
|
||||
for (( i = 0; i < len; i++ )); do
|
||||
try_cast "$base" "${2:i:1}" || return 1
|
||||
@ -212,29 +313,29 @@ valid_number_of_base() {
|
||||
}
|
||||
|
||||
mangle() {
|
||||
local i= chr= out=
|
||||
local {a..f}= {A..F}=
|
||||
local i chr out
|
||||
local {a..f}='' {A..F}=''
|
||||
|
||||
for (( i = 0; i < ${#1}; i++ )); do
|
||||
chr=${1:i:1}
|
||||
chr="${1:i:1}"
|
||||
case $chr in
|
||||
[[:space:]\\])
|
||||
printf -v chr '%03o' "'$chr"
|
||||
out+=\\
|
||||
;;
|
||||
esac
|
||||
out+=$chr
|
||||
out+="$chr"
|
||||
done
|
||||
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
unmangle() {
|
||||
local i= chr= out= len=$(( ${#1} - 4 ))
|
||||
local {a..f}= {A..F}=
|
||||
local i chr out len="$(( ${#1} - 4 ))"
|
||||
local {a..f}='' {A..F}=''
|
||||
|
||||
for (( i = 0; i < len; i++ )); do
|
||||
chr=${1:i:1}
|
||||
chr="${1:i:1}"
|
||||
case $chr in
|
||||
\\)
|
||||
if valid_number_of_base 8 "${1:i+1:3}" ||
|
||||
@ -244,97 +345,109 @@ unmangle() {
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
out+=$chr
|
||||
out+="$chr"
|
||||
done
|
||||
|
||||
printf '%s' "$out${1:i}"
|
||||
}
|
||||
|
||||
optstring_match_option() {
|
||||
local candidate pat patterns
|
||||
optstring_match_one_option() {
|
||||
local options=() target="$2"
|
||||
local -n _optstring_match="$1"
|
||||
|
||||
IFS=, read -ra patterns <<<"$1"
|
||||
for pat in "${patterns[@]}"; do
|
||||
if [[ $pat = *=* ]]; then
|
||||
# "key=val" will only ever match "key=val"
|
||||
candidate=$2
|
||||
else
|
||||
# "key" will match "key", but also "key=anyval"
|
||||
candidate=${2%%=*}
|
||||
fi
|
||||
IFS=, read -ra options <<<"$_optstring_match"
|
||||
if [[ "$target" != *'='* ]]; then
|
||||
options=("${options[@]%%=*}")
|
||||
fi
|
||||
|
||||
[[ $pat = "$candidate" ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
in_array options "$target"
|
||||
}
|
||||
|
||||
optstring_remove_option() {
|
||||
local o options_ remove=$2 IFS=,
|
||||
optstring_get_options() {
|
||||
local i options=()
|
||||
local -n _ret="$1" _optstring_get="$2"
|
||||
local -i got=0
|
||||
|
||||
read -ra options_ <<<"${!1}"
|
||||
IFS=, read -ra options <<<"$_optstring_get"
|
||||
|
||||
for o in "${!options_[@]}"; do
|
||||
optstring_match_option "$remove" "${options_[o]}" && unset 'options_[o]'
|
||||
shift 2
|
||||
for i in "${!options[@]}"; do
|
||||
if optstring_match_one_option 'options[i]' "$1"; then
|
||||
_ret+=(["${options[i]%%=*}"]="${options[i]//*=}")
|
||||
got=1
|
||||
fi
|
||||
shift || break
|
||||
done
|
||||
|
||||
declare -g "$1=${options_[*]}"
|
||||
(( got ))
|
||||
}
|
||||
|
||||
optstring_from_array() {
|
||||
local optstring
|
||||
local -n _optarray="$1"
|
||||
|
||||
optstring="$(printf ',%s' "${_optarray[@]}")"
|
||||
optstring="${optstring:1}" # Remove the leading comma
|
||||
|
||||
printf '%s' "$optstring"
|
||||
}
|
||||
|
||||
optstring_normalize() {
|
||||
local o options_ norm IFS=,
|
||||
local i options=()
|
||||
local -n _optstring_norm="$1"
|
||||
|
||||
read -ra options_ <<<"${!1}"
|
||||
IFS=, read -ra options <<<"$_optstring_norm"
|
||||
|
||||
# remove empty fields
|
||||
for o in "${options_[@]}"; do
|
||||
[[ $o ]] && norm+=("$o")
|
||||
for i in "${!options[@]}"; do
|
||||
[[ ${options[i]} ]] || unset 'options[i]'
|
||||
done
|
||||
|
||||
# avoid empty strings, reset to "defaults"
|
||||
declare -g "$1=${norm[*]:-defaults}"
|
||||
}
|
||||
|
||||
optstring_append_option() {
|
||||
if ! optstring_has_option "$1" "$2"; then
|
||||
declare -g "$1=${!1},$2"
|
||||
if (( ! ${#options[@]} )); then
|
||||
_optstring_norm="defaults"
|
||||
else
|
||||
_optstring_norm="$(optstring_from_array options)"
|
||||
fi
|
||||
|
||||
optstring_normalize "$1"
|
||||
}
|
||||
|
||||
optstring_prepend_option() {
|
||||
local options_=$1
|
||||
optstring_remove_options() {
|
||||
local i options=() target="$2"
|
||||
local -n _optstring_remove="$1"
|
||||
|
||||
if ! optstring_has_option "$1" "$2"; then
|
||||
declare -g "$1=$2,${!1}"
|
||||
fi
|
||||
IFS=, read -ra options <<<"$_optstring_remove"
|
||||
|
||||
optstring_normalize "$1"
|
||||
}
|
||||
|
||||
optstring_get_option() {
|
||||
local _opts o
|
||||
|
||||
IFS=, read -ra _opts <<<"${!1}"
|
||||
for o in "${_opts[@]}"; do
|
||||
if optstring_match_option "$2" "$o"; then
|
||||
declare -g "$o"
|
||||
return 0
|
||||
fi
|
||||
for i in "${!options[@]}"; do
|
||||
optstring_match_one_option 'options[i]' "$target" && unset 'options[i]'
|
||||
done
|
||||
|
||||
return 1
|
||||
_optstring_remove="$(optstring_from_array options)"
|
||||
}
|
||||
|
||||
optstring_has_option() {
|
||||
local "${2%%=*}"
|
||||
optstring_append_one_option() {
|
||||
local option="$2"
|
||||
local -n _optstring_append="$1"
|
||||
|
||||
optstring_get_option "$1" "$2"
|
||||
if ! optstring_match_one_option _optstring_append "$option"; then
|
||||
_optstring_append+=",$option"
|
||||
fi
|
||||
|
||||
optstring_normalize _optstring_append
|
||||
}
|
||||
|
||||
optstring_prepend_one_option() {
|
||||
local option="$2"
|
||||
local -n _optstring_prepend="$1"
|
||||
|
||||
if ! optstring_match_one_option _optstring_prepend "$option"; then
|
||||
_optstring_prepend="$option,$_optstring_prepend"
|
||||
fi
|
||||
|
||||
optstring_normalize _optstring_prepend
|
||||
}
|
||||
|
||||
dm_name_for_devnode() {
|
||||
read dm_name <"/sys/class/block/${1#/dev/}/dm/name"
|
||||
read -r dm_name <"/sys/class/block/${1#/dev/}/dm/name"
|
||||
if [[ $dm_name ]]; then
|
||||
printf '/dev/mapper/%s' "$dm_name"
|
||||
else
|
||||
@ -345,9 +458,12 @@ dm_name_for_devnode() {
|
||||
}
|
||||
|
||||
fstype_is_pseudofs() {
|
||||
(( pseudofs_types["$1"] ))
|
||||
(( pseudofs_types["$1"] )) || findmnt --pseudo "$1" &>/dev/null
|
||||
}
|
||||
|
||||
fstype_has_fsck() {
|
||||
(( fsck_types["$1"] ))
|
||||
(( fsck_types["$1"] == 0 )) && return 1
|
||||
(( fsck_types["$1"] )) || command -v "fsck.$1" &>/dev/null
|
||||
}
|
||||
|
||||
# vim: et ts=2 sw=2 ft=sh:
|
||||
|
@ -3,7 +3,7 @@ _genfstab() {
|
||||
local cur prev words cword
|
||||
_init_completion || return
|
||||
|
||||
local opts="-f -L -p -P -t -U -h"
|
||||
local opts="-f -L -P -p -t -U -h"
|
||||
|
||||
case ${prev} in
|
||||
-f)
|
||||
|
@ -7,28 +7,26 @@ arch-chroot - enhanced chroot command
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
arch-chroot [options] chroot-dir [command] [arguments...]
|
||||
arch-chroot [options] chroot_dir [command [arguments...]]
|
||||
|
||||
Description
|
||||
-----------
|
||||
arch-chroot wraps the linkman:chroot[1] command while ensuring that important
|
||||
functionality is available, e.g. mounting '/dev/', '/proc' and other API
|
||||
functionality is available, e.g. mounting '/dev', '/proc' and other API
|
||||
filesystems, or exposing linkman:resolv.conf[5] to the chroot.
|
||||
|
||||
If 'command' is unspecified, arch-chroot will launch */bin/bash*.
|
||||
|
||||
[NOTE]
|
||||
======
|
||||
The target chroot-dir *should* be a mountpoint. This ensures that tools such as
|
||||
linkman:pacman[8] or linkman:findmnt[8] have an accurate hierarchy of the
|
||||
mounted filesystems within the chroot. If your chroot target is not a
|
||||
mountpoint, you can bind mount the directory on itself to make it a mountpoint,
|
||||
i.e.:
|
||||
The target chroot directory *should* be a mountpoint. This ensures that tools
|
||||
such as linkman:pacman[8] or linkman:findmnt[8] have an accurate hierarchy of
|
||||
the mounted filesystems within the chroot. If your chroot target is not a
|
||||
mountpoint, you can bind mount the directory on itself to make it a one, i.e.
|
||||
|
||||
'mount --bind /your/chroot /your/chroot'
|
||||
'mount --bind chroot_dir chroot_dir'
|
||||
======
|
||||
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
@ -38,7 +36,7 @@ Options
|
||||
installations.
|
||||
|
||||
*-u <user>[:group]*::
|
||||
Specify non-root user and optional group to use.
|
||||
Specify non-root user and group (optional) to use.
|
||||
|
||||
*-h*::
|
||||
Output syntax and command line options.
|
||||
@ -46,6 +44,4 @@ Options
|
||||
See Also
|
||||
--------
|
||||
|
||||
linkman:pacman[8]
|
||||
|
||||
include::footer.asciidoc[]
|
||||
linkman:chroot[1], linkman:proc[5], linkman:sysfs[5]
|
||||
|
@ -1,18 +0,0 @@
|
||||
|
||||
Bugs
|
||||
----
|
||||
Bugs can be reported on the bug tracker 'https://bugs.archlinux.org' in the Arch
|
||||
Linux category and title prefixed with [arch-install-scripts] or via
|
||||
mailto:arch-projects@archlinux.org[].
|
||||
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
Maintainers:
|
||||
|
||||
* Dave Reisner <dreisner@archlinux.org>
|
||||
* Eli Schwartz <eschwartz@archlinux.org>
|
||||
|
||||
For additional contributors, use `git shortlog -s` on the arch-install-scripts.git
|
||||
repository.
|
@ -26,12 +26,12 @@ Options
|
||||
*-L*::
|
||||
Use labels for source identifiers (shortcut for '-t LABEL').
|
||||
|
||||
*-p*::
|
||||
Exclude pseudofs mounts (default behavior).
|
||||
|
||||
*-P*::
|
||||
Include pseudofs mounts.
|
||||
|
||||
*-p*::
|
||||
Exclude pseudofs mounts (default behavior).
|
||||
|
||||
*-t* <tag>::
|
||||
Use 'tag' for source identifiers (should be one of: 'LABEL', 'UUID',
|
||||
'PARTLABEL', 'PARTUUID').
|
||||
@ -45,6 +45,4 @@ Options
|
||||
See Also
|
||||
--------
|
||||
|
||||
linkman:pacman[8]
|
||||
|
||||
include::footer.asciidoc[]
|
||||
linkman:fstab[5], linkman:file-hierarchy[7], linkman:filesystems[5]
|
||||
|
@ -62,5 +62,3 @@ See Also
|
||||
--------
|
||||
|
||||
linkman:pacman[8]
|
||||
|
||||
include::footer.asciidoc[]
|
||||
|
168
genfstab.in
168
genfstab.in
@ -2,59 +2,59 @@
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
# m4_include() is recognized as a function definition
|
||||
# shellcheck source=common disable=SC1073,SC1065,SC1064,SC1072
|
||||
m4_include(common)
|
||||
|
||||
bytag=src
|
||||
pseudofs=0
|
||||
|
||||
write_source() {
|
||||
local src=$1 spec= label= uuid= comment=()
|
||||
local src spec comments=()
|
||||
|
||||
label=$(lsblk -rno LABEL "$1" 2>/dev/null)
|
||||
uuid=$(lsblk -rno UUID "$1" 2>/dev/null)
|
||||
src="$(mangle "$1")"
|
||||
local -A sources=([LABEL]="$(mangle "$(lsblk -rno LABEL "$1" 2>/dev/null)")"
|
||||
[UUID]="$(lsblk -rno UUID "$1" 2>/dev/null)"
|
||||
[PARTLABEL]="$(mangle "$(lsblk -rno PARTLABEL "$1" 2>/dev/null)")"
|
||||
[PARTUUID]="$(lsblk -rno PARTUUID "$1" 2>/dev/null)"
|
||||
)
|
||||
|
||||
# bind mounts do not have a UUID!
|
||||
|
||||
case $bytag in
|
||||
'')
|
||||
[[ $uuid ]] && comment=("UUID=$uuid")
|
||||
[[ $label ]] && comment+=("LABEL=$(mangle "$label")")
|
||||
;;
|
||||
LABEL)
|
||||
spec=$label
|
||||
[[ $uuid ]] && comment=("$src" "UUID=$uuid")
|
||||
;;
|
||||
UUID)
|
||||
spec=$uuid
|
||||
comment=("$src")
|
||||
[[ $label ]] && comment+=("LABEL=$(mangle "$label")")
|
||||
;;
|
||||
*)
|
||||
[[ $uuid ]] && comment=("$1" "UUID=$uuid")
|
||||
[[ $label ]] && comment+=("LABEL=$(mangle "$label")")
|
||||
[[ $bytag ]] && spec=$(lsblk -rno "$bytag" "$1" 2>/dev/null)
|
||||
;;
|
||||
esac
|
||||
|
||||
[[ $comment ]] && printf '# %s\n' "${comment[*]}"
|
||||
|
||||
if [[ $spec ]]; then
|
||||
printf '%-20s' "$bytag=$(mangle "$spec")"
|
||||
if [[ ${sources["$bytag"]} ]]; then
|
||||
spec="$bytag"
|
||||
comments+=("$src")
|
||||
else
|
||||
printf '%-20s' "$(mangle "$src")"
|
||||
spec=src
|
||||
fi
|
||||
|
||||
for tag in "${!sources[@]}"; do
|
||||
if [[ ${sources["$tag"]} && "$tag" != "$spec" ]]; then
|
||||
comments+=("$tag=${sources["$tag"]}")
|
||||
fi
|
||||
done
|
||||
|
||||
(( ${#comments[@]} )) && printf '# %s\n' "${comments[*]}"
|
||||
|
||||
if [[ "$spec" != "src" ]]; then
|
||||
printf '%-20s' "$spec=${sources["$spec"]}"
|
||||
else
|
||||
printf '%-20s' "$src"
|
||||
fi
|
||||
}
|
||||
|
||||
optstring_apply_quirks() {
|
||||
local varname=$1 fstype=$2
|
||||
local fstype="$2"
|
||||
local -n _optstring="$1"
|
||||
|
||||
# SELinux displays a 'seclabel' option in /proc/self/mountinfo. We can't know
|
||||
# if the system we're generating the fstab for has any support for SELinux (as
|
||||
# one might install Arch from a Fedora environment), so let's remove it.
|
||||
optstring_remove_option "$varname" seclabel
|
||||
optstring_remove_options _optstring seclabel
|
||||
|
||||
# Prune 'relatime' option for any pseudofs. This seems to be a rampant
|
||||
# default which the kernel often exports even if the underlying filesystem
|
||||
# doesn't support it. Example: https://bugs.archlinux.org/task/54554.
|
||||
if awk -v fstype="$fstype" '$1 == fstype { exit 1 }' /proc/filesystems; then
|
||||
optstring_remove_option "$varname" relatime
|
||||
if fstype_is_pseudofs "$fstype"; then
|
||||
optstring_remove_options _optstring relatime
|
||||
fi
|
||||
|
||||
case $fstype in
|
||||
@ -62,22 +62,15 @@ optstring_apply_quirks() {
|
||||
# Having only one of subvol= and subvolid= is enough for mounting a btrfs subvolume
|
||||
# And having subvolid= set prevents things like 'snapper rollback' to work, as it
|
||||
# updates the subvolume in-place, leaving subvol= unchanged with a different subvolid.
|
||||
if optstring_has_option "$varname" subvol; then
|
||||
optstring_remove_option "$varname" subvolid
|
||||
if optstring_match_one_option _optstring subvol; then
|
||||
optstring_remove_options _optstring subvolid
|
||||
fi
|
||||
;;
|
||||
f2fs)
|
||||
# These are Kconfig options for f2fs. Kernels supporting the options will
|
||||
# only provide the negative versions of these (e.g. noacl), and vice versa
|
||||
# for kernels without support.
|
||||
optstring_remove_option "$varname" noacl,acl,nouser_xattr,user_xattr
|
||||
;;
|
||||
vfat)
|
||||
# Before Linux v3.8, "cp" is prepended to the value of the codepage.
|
||||
if optstring_get_option "$varname" codepage && [[ $codepage = cp* ]]; then
|
||||
optstring_remove_option "$varname" codepage
|
||||
optstring_append_option "$varname" "codepage=${codepage#cp}"
|
||||
fi
|
||||
optstring_remove_options _optstring noacl,acl,nouser_xattr,user_xattr
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@ -92,13 +85,13 @@ usage: ${0##*/} [options] root
|
||||
-p Exclude pseudofs mounts (default behavior)
|
||||
-P Include pseudofs mounts
|
||||
-t <tag> Use TAG for source identifiers (TAG should be one of: LABEL,
|
||||
UUID, PARTLABEL, PARTUUID)
|
||||
UUID, PARTLABEL, PARTUUID)
|
||||
-U Use UUIDs for source identifiers (shortcut for -t UUID)
|
||||
|
||||
-h Print this help message
|
||||
|
||||
genfstab generates output suitable for addition to an fstab file based on the
|
||||
devices mounted under the mountpoint specified by the given root.
|
||||
genfstab generates output suitable for addition to an fstab file based on
|
||||
the devices mounted under the mountpoint specified by the given root.
|
||||
|
||||
EOF
|
||||
}
|
||||
@ -110,15 +103,12 @@ fi
|
||||
|
||||
while getopts ':f:LPpt:U' flag; do
|
||||
case $flag in
|
||||
f)
|
||||
prefixfilter="$OPTARG"
|
||||
;;
|
||||
L)
|
||||
bytag=LABEL
|
||||
;;
|
||||
U)
|
||||
bytag=UUID
|
||||
;;
|
||||
f)
|
||||
prefixfilter=$OPTARG
|
||||
;;
|
||||
P)
|
||||
pseudofs=1
|
||||
;;
|
||||
@ -126,42 +116,46 @@ while getopts ':f:LPpt:U' flag; do
|
||||
pseudofs=0
|
||||
;;
|
||||
t)
|
||||
bytag=${OPTARG^^}
|
||||
bytag="${OPTARG^^}"
|
||||
;;
|
||||
U)
|
||||
bytag=UUID
|
||||
;;
|
||||
:)
|
||||
die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
die "%s: option requires an argument -- '%s'" "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
?)
|
||||
die '%s: invalid option -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
die "%s: invalid option -- '%s'" "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(( OPTIND - 1 ))
|
||||
|
||||
(( $# )) || die "No root directory specified"
|
||||
root=$(realpath -mL "$1"); shift
|
||||
(( $# )) || die 'No root directory specified'
|
||||
root="$(realpath -mL "$1")"; shift
|
||||
|
||||
if ! mountpoint -q "$root"; then
|
||||
die "$root is not a mountpoint"
|
||||
die '%s: not a mountpoint' "$root"
|
||||
fi
|
||||
|
||||
# handle block devices
|
||||
findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root" |
|
||||
while read -r src target fstype opts fsroot; do
|
||||
if (( !pseudofs )) && fstype_is_pseudofs "$fstype"; then
|
||||
if (( ! pseudofs )) && fstype_is_pseudofs "$fstype"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
[[ $target = "$prefixfilter"* ]] || continue
|
||||
[[ "$target" = "$prefixfilter"* ]] || continue
|
||||
|
||||
# default 5th and 6th columns
|
||||
dump=0 pass=2
|
||||
|
||||
src=$(unmangle "$src")
|
||||
target=$(unmangle "$target")
|
||||
target=${target#$root}
|
||||
src="$(unmangle "$src")"
|
||||
target="$(unmangle "$target")"
|
||||
target="${target#"$root/"}"
|
||||
[[ "$target" != '/'* ]] && target="/$target"
|
||||
|
||||
if (( !foundroot )) && findmnt "$src" "$root" >/dev/null; then
|
||||
if (( ! foundroot )) && findmnt "$src" "$root" >/dev/null; then
|
||||
# this is root. we can't possibly have more than one...
|
||||
pass=1 foundroot=1
|
||||
fi
|
||||
@ -171,19 +165,29 @@ findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root" |
|
||||
pass=0
|
||||
fi
|
||||
|
||||
if [[ $fsroot != / && $fstype != btrfs ]]; then
|
||||
if [[ "$fsroot" != "/" && "$fstype" != "btrfs" ]]; then
|
||||
# it's a bind mount
|
||||
src=$(findmnt -funcevo TARGET "$src")$fsroot
|
||||
src="/${src#$root/}"
|
||||
if [[ $src -ef $target ]]; then
|
||||
mapfile -t bind_srcs < <(findmnt -uncevo TARGET "$src")
|
||||
for bind_src in "${bind_srcs[@]}"; do
|
||||
if [[ -d "$bind_src$fsroot" ]]; then
|
||||
src="$bind_src$fsroot"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
src="${src#"$root/"}"
|
||||
[[ "$src" != '/'* ]] && src="/$src"
|
||||
|
||||
if [[ "$src" = "$target" ]]; then
|
||||
# hrmm, this is weird. we're probably looking at a file or directory
|
||||
# that was bound into a chroot from the host machine. Ignore it,
|
||||
# because this won't actually be a valid mount. Worst case, the user
|
||||
# just re-adds it.
|
||||
continue
|
||||
fi
|
||||
|
||||
fstype=none
|
||||
opts+=,bind
|
||||
opts+=',bind'
|
||||
pass=0
|
||||
fi
|
||||
|
||||
@ -192,20 +196,20 @@ findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root" |
|
||||
fuseblk)
|
||||
# well-behaved FUSE filesystems will report themselves as fuse.$fstype.
|
||||
# this is probably NTFS-3g, but let's just make sure.
|
||||
if ! newtype=$(lsblk -no FSTYPE "$src") || [[ -z $newtype ]]; then
|
||||
if ! newtype="$(lsblk -no FSTYPE "$src")" || [[ -z $newtype ]]; then
|
||||
# avoid blanking out fstype, leading to an invalid fstab
|
||||
error 'Failed to derive real filesystem type for FUSE device on %s' "$target"
|
||||
else
|
||||
fstype=$newtype
|
||||
fstype="$newtype"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
optstring_apply_quirks "opts" "$fstype"
|
||||
optstring_apply_quirks opts "$fstype"
|
||||
|
||||
# write one line
|
||||
write_source "$src"
|
||||
printf '\t%-10s' "/$(mangle "${target#/}")" "$fstype" "$opts"
|
||||
printf '\t%-10s' "$(mangle "$target")" "$fstype" "$opts"
|
||||
printf '\t%s %s' "$dump" "$pass"
|
||||
printf '\n\n'
|
||||
done
|
||||
@ -213,20 +217,20 @@ done
|
||||
# handle swaps devices
|
||||
{
|
||||
# ignore header
|
||||
read
|
||||
read -r
|
||||
|
||||
while read -r device type _ _ prio; do
|
||||
options=defaults
|
||||
if (( prio >= 0 )); then
|
||||
options+=,pri=$prio
|
||||
options+=",pri=$prio"
|
||||
fi
|
||||
|
||||
# skip files marked deleted by the kernel
|
||||
[[ $device = *'\040(deleted)' ]] && continue
|
||||
[[ "$device" = *'\040(deleted)' ]] && continue
|
||||
|
||||
if [[ $type = file ]]; then
|
||||
printf '%-20s' "${device#${root%/}}"
|
||||
elif [[ $device = /dev/dm-+([0-9]) ]]; then
|
||||
if [[ "$type" = "file" ]]; then
|
||||
printf '%-20s' "${device#"${root%/}"}"
|
||||
elif [[ "$device" = "/dev/dm-"+([0-9]) ]]; then
|
||||
# device mapper doesn't allow characters we need to worry
|
||||
# about being mangled, and it does the escaping of dashes
|
||||
# for us in sysfs.
|
||||
@ -235,7 +239,7 @@ done
|
||||
write_source "$(unmangle "$device")"
|
||||
fi
|
||||
|
||||
printf '\t%-10s\t%-10s\t%-10s\t0 0\n\n' 'none' 'swap' "$options"
|
||||
printf '\t%-10s\t%-10s\t%-10s\t0 0\n\n' none swap "$options"
|
||||
done
|
||||
} </proc/swaps
|
||||
|
||||
|
91
pacstrap.in
91
pacstrap.in
@ -10,18 +10,20 @@
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
# m4_include() is recognized as a function definition
|
||||
# shellcheck source=common disable=SC1073,SC1065,SC1064,SC1072
|
||||
m4_include(common)
|
||||
|
||||
hostcache=0
|
||||
copykeyring=1
|
||||
initkeyring=0
|
||||
copymirrorlist=1
|
||||
copyconfig=0
|
||||
pacman_config=/etc/pacman.conf
|
||||
pacman_args=()
|
||||
pacmode=-Sy
|
||||
pacmode='-Sy'
|
||||
setup=chroot_setup
|
||||
unshare=0
|
||||
copyconf=0
|
||||
pacman_config=/etc/pacman.conf
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
@ -37,12 +39,12 @@ usage: ${0##*/} [options] root [packages...]
|
||||
-M Avoid copying the host's mirrorlist to the target
|
||||
-N Run in unshare mode as a regular user
|
||||
-P Copy the host's pacman config to the target
|
||||
-U Use pacman -U to install packages
|
||||
-U Use 'pacman -U' to install packages
|
||||
|
||||
-h Print this help message
|
||||
|
||||
pacstrap installs packages to the specified new root directory. If no packages
|
||||
are given, pacstrap defaults to the "base" group.
|
||||
pacstrap installs packages to the specified new root directory.
|
||||
If no packages are given, pacstrap defaults to the 'base' metapackage.
|
||||
|
||||
EOF
|
||||
}
|
||||
@ -55,20 +57,20 @@ fi
|
||||
while getopts ':C:cDGiKMNPU' flag; do
|
||||
case $flag in
|
||||
C)
|
||||
pacman_config=$OPTARG
|
||||
;;
|
||||
D)
|
||||
pacman_args+=(-dd)
|
||||
pacman_config="$OPTARG"
|
||||
;;
|
||||
c)
|
||||
hostcache=1
|
||||
;;
|
||||
i)
|
||||
interactive=1
|
||||
D)
|
||||
pacman_args+=(-dd)
|
||||
;;
|
||||
G)
|
||||
copykeyring=0
|
||||
;;
|
||||
i)
|
||||
interactive=1
|
||||
;;
|
||||
K)
|
||||
initkeyring=1
|
||||
;;
|
||||
@ -80,68 +82,85 @@ while getopts ':C:cDGiKMNPU' flag; do
|
||||
unshare=1
|
||||
;;
|
||||
P)
|
||||
copyconf=1
|
||||
copyconfig=1
|
||||
;;
|
||||
U)
|
||||
pacmode=-U
|
||||
pacmode='-U'
|
||||
;;
|
||||
:)
|
||||
die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
die "%s: option requires an argument -- '%s'" "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
?)
|
||||
die '%s: invalid option -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
die "%s: invalid option -- '%s'" "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(( OPTIND - 1 ))
|
||||
|
||||
(( $# )) || die "No root directory specified"
|
||||
newroot=$1; shift
|
||||
pacman_args+=("$pacmode" "${@:-base}" --config="$pacman_config")
|
||||
(( $# )) || die 'No root directory specified'
|
||||
newroot="$1"; shift
|
||||
[[ -d "$newroot" ]] || die '%s: not a directory' "$newroot"
|
||||
|
||||
if (( ! hostcache )); then
|
||||
pacman_args+=(--cachedir="$newroot/var/cache/pacman/pkg")
|
||||
fi
|
||||
pacman_args+=("$pacmode" "${@:-base}" --config="$pacman_config")
|
||||
|
||||
if (( ! interactive )); then
|
||||
pacman_args+=(--noconfirm)
|
||||
fi
|
||||
|
||||
[[ -d $newroot ]] || die "%s is not a directory" "$newroot"
|
||||
set -e
|
||||
gpgdir="$(pacman-conf --config="$pacman_config" GPGDir)"
|
||||
cachedir="$(pacman-conf --config="$pacman_config" CacheDir)"
|
||||
if (( copyconfig )); then
|
||||
dbpath="$newroot$(pacman-conf --config="$pacman_config" DBPath)"
|
||||
logfile="$newroot$(pacman-conf --config="$pacman_config" LogFile)"
|
||||
cachedir_target="$newroot$cachedir"
|
||||
gpgdir_target="$newroot$gpgdir"
|
||||
|
||||
pacman_args+=(--dbpath="$dbpath" --logfile="$logfile")
|
||||
else
|
||||
dbpath="$newroot"/var/lib/pacman
|
||||
logfile="$newroot"/var/log/pacman.log
|
||||
cachedir_target="$newroot"/var/cache/pacman/pkg
|
||||
gpgdir_target="$newroot"/etc/pacman.d/gnupg
|
||||
fi
|
||||
|
||||
if (( ! hostcache )); then
|
||||
cachedir="$cachedir_target"
|
||||
fi
|
||||
pacman_args+=(--cachedir="$cachedir")
|
||||
set +e
|
||||
|
||||
pacstrap() {
|
||||
(( EUID == 0 )) || die 'This script must be run with root privileges'
|
||||
check_root
|
||||
|
||||
# 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 0755 -p "$dbpath" "$cachedir_target" "$(dirname "$logfile")" "$newroot"/{dev,run,etc/pacman.d}
|
||||
mkdir -m 1777 -p "$newroot"/tmp
|
||||
mkdir -m 0555 -p "$newroot"/{sys,proc}
|
||||
|
||||
# mount API filesystems
|
||||
$setup "$newroot" || die "failed to setup chroot %s" "$newroot"
|
||||
$setup "$newroot"
|
||||
|
||||
if [[ ! -d $newroot/etc/pacman.d/gnupg ]]; then
|
||||
if [[ ! -d "$gpgdir_target" ]]; then
|
||||
if (( initkeyring )); then
|
||||
pacman-key --gpgdir "$newroot"/etc/pacman.d/gnupg --init
|
||||
elif (( copykeyring )) && [[ -d /etc/pacman.d/gnupg ]]; then
|
||||
pacman-key --gpgdir "$gpgdir_target" --init
|
||||
elif (( copykeyring )) && [[ -d "$gpgdir" ]]; then
|
||||
# if there's a keyring on the host, copy it into the new root
|
||||
cp -a --no-preserve=ownership /etc/pacman.d/gnupg "$newroot/etc/pacman.d/"
|
||||
cp -aT --no-preserve=ownership "$gpgdir" "$gpgdir_target"
|
||||
fi
|
||||
fi
|
||||
|
||||
msg 'Installing packages to %s' "$newroot"
|
||||
if ! $pid_unshare pacman -r "$newroot" "${pacman_args[@]}"; then
|
||||
die 'Failed to install packages to new root'
|
||||
fi
|
||||
$pid_unshare pacman --root "$newroot" "${pacman_args[@]}" || die 'Failed to install packages to new root'
|
||||
|
||||
if (( copymirrorlist )); then
|
||||
# install the host's mirrorlist onto the new root
|
||||
cp -a /etc/pacman.d/mirrorlist "$newroot/etc/pacman.d/"
|
||||
cp -a /etc/pacman.d/mirrorlist "$newroot"/etc/pacman.d/ || warning "Failed to copy the host's mirrorlist to new root"
|
||||
fi
|
||||
|
||||
if (( copyconf )); then
|
||||
cp -a "$pacman_config" "$newroot/etc/pacman.conf"
|
||||
if (( copyconfig )); then
|
||||
cp -a "$pacman_config" "$newroot"/etc/pacman.conf
|
||||
fi
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user