Skip to content

Instantly share code, notes, and snippets.

@agrif
Last active August 7, 2023 06:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agrif/bc20474136a99ddaf627166f6178feca to your computer and use it in GitHub Desktop.
Save agrif/bc20474136a99ddaf627166f6178feca to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
# this is mostly copied from arch-chroot
BASE="$(realpath $(dirname "$0"))"
LFS="$BASE/root"
die() {
printf "ERROR: "
echo "$@"
exit 1
}
add_mount() {
mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}")
}
add_mount_lazy() {
mount "$@" && CHROOT_ACTIVE_LAZY=("$2" "${CHROOT_ACTIVE_LAZY[@]}")
}
bind_device() {
touch "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
add_mount "$1" "$2" --bind
}
add_link() {
ln -sf "$1" "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
}
pid_unshare="unshare --fork --pid"
mount_unshare="$pid_unshare --mount --map-auto --map-root-user --setuid 0 --setgid 0"
setup() {
CHROOT_ACTIVE_MOUNTS=()
CHROOT_ACTIVE_LAZY=()
CHROOT_ACTIVE_FILES=()
[[ $(trap -p EXIT) ]] && die cannot set EXIT trap
trap 'cleanup' EXIT
add_mount_lazy "$1" "$1" --bind
add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev
add_mount_lazy /sys "$1/sys" --rbind
add_link "/proc/self/fd" "$1/dev/fd"
add_link "/proc/self/fd/0" "$1/dev/stdin"
add_link "/proc/self/fd/1" "$1/dev/stdout"
add_link "/proc/self/fd/2" "$1/dev/stderr"
bind_device /dev/full "$1/dev/full"
bind_device /dev/null "$1/dev/null"
bind_device /dev/random "$1/dev/random"
bind_device /dev/tty "$1/dev/tty"
bind_device /dev/urandom "$1/dev/urandom"
bind_device /dev/zero "$1/dev/zero"
add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755
add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid
add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev
add_mount "$BASE/sources" "$1/sources" --bind
add_mount "$BASE/packages" "$1/packages" --bind
add_mount "$BASE/pkgspecs" "$1/pkgspecs" --bind
}
cleanup() {
if (( ${#CHROOT_ACTIVE_MOUNTS[@]} )); then
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
fi
unset CHROOT_ACTIVE_MOUNTS
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
}
declare_all() {
declare -p | grep -Fvf <(declare -rp)
declare -pf
}
do_chroot() {
(( EUID == 0 )) || die do_chroot must be run as root
[[ -d $chrootdir ]] || die path $chrootdir is not a directory
setup "$chrootdir"
chroot_args=()
[[ $userspec ]] && chroot_args+=(--userspec "$userspec")
user=$(echo "$userspec" | cut -d: -f1)
[[ "$user" ]] || user="root"
# FIXME uids don't work here
if [[ "$user" == "root" ]]; then
homedir="/root"
else
homedir="/home/$user"
fi
$pid_unshare /sbin/chroot "${chroot_args[@]}" -- "$chrootdir" /usr/bin/env -i HOME="$homedir" TERM="$TERM" PS1='(lfs chroot) \u:\w\$ ' PATH=/usr/bin:/usr/sbin "${args[@]}"
}
while getopts ':hu:' flag; do
case $flag in
h)
cat <<EOF
usage: $(basename "$0") [newroot] [command ...]
-h Print this help message
-u <user>[:group] Specify non-root user and optional group to use
If 'command' is unspecified, $(basename "$0") will launch /bin/bash.
If 'newroot' is unspecified, chroot.sh will use:
$LFS
EOF
exit 0
;;
u)
userspec=$OPTARG
;;
:)
die option requires an argument -- $OPTARG
;;
?)
die invalid option -- $OPTARG
;;
esac
done
shift $(( OPTIND - 1 ))
chrootdir="$LFS"
(( $# )) && chrootdir=$1 && shift
args=("/bin/bash" "--login" "+h")
[[ "$@" ]] && args=("$@")
$mount_unshare bash -c "$(declare_all); do_chroot"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment