summaryrefslogtreecommitdiff
path: root/bin/enter-chroot
blob: a151002e251a708c87fbea3284d9eb0812e1aa2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/bin/sh
#
# enter-chroot - Bind mount and chroot into installed system
# rawnix.org - https://rawnix.org
#
# Usage: enter-chroot <mountpoint>
#        enter-chroot /mnt

set -e

die() { echo "ERROR: $*" >&2; exit 1; }

[ "$(id -u)" = 0 ] || die "Must root"
[ -n "$1" ]        || die "Usage: enter-chroot <mountpoint>"
[ -d "$1" ]        || die "Directory not found: $1"

MNT="${1%/}"
[ -n "$MNT" ] || MNT="/"

mountpoint -q "$MNT" || die "$MNT is not mounted"
[ -x "$MNT/bin/bash" ] || die "No /bin/bash in $MNT — install the core group first"

# ── Unmount on the way out ────────────────────────────────────
# Also on failure.  Without a trap, a mount that fails partway
# through the list leaves the earlier ones in place and $MNT busy,
# with nothing to tell you which ones they were.
cleanup() {
    umount "$MNT/sys/firmware/efi/efivars" 2>/dev/null || true
    umount "$MNT/dev/pts" 2>/dev/null || true
    umount "$MNT/dev/shm" 2>/dev/null || true
    umount "$MNT/sys"     2>/dev/null || true
    umount "$MNT/proc"    2>/dev/null || true
    umount "$MNT/run"     2>/dev/null || true
    umount "$MNT/tmp"     2>/dev/null || true
    umount "$MNT/dev"     2>/dev/null || true
    return 0
}
trap cleanup EXIT INT TERM

echo ">>> Mounting virtual filesystems..."

mount --bind /dev     "$MNT/dev"
mount --bind /tmp     "$MNT/tmp"
mount --bind /run     "$MNT/run"
mount -t proc    proc    "$MNT/proc"
mount -t sysfs   none    "$MNT/sys"
mount -t devpts  devpts  "$MNT/dev/pts" -o noexec,nosuid,gid=tty,mode=0620

# `mount --bind` is not recursive, so /dev/shm does not come along
# with /dev and anything using POSIX shared memory inside the
# chroot fails with ENOENT.  Mount it explicitly rather than
# switching to --rbind, which would also drag in the host's devpts
# and fight the line above.
mount -t tmpfs -o mode=1777,nosuid,nodev tmpfs "$MNT/dev/shm"

if [ -d /sys/firmware/efi ]; then
    if mount -t efivarfs efivarfs "$MNT/sys/firmware/efi/efivars" 2>/dev/null; then
        echo "    + efivars"
    else
        echo "    ! efivarfs unavailable - efibootmgr will not work" >&2
    fi
fi

echo ">>> Entering chroot..."
echo ""

# -l, so /etc/profile is read.  A non-login bash sources ~/.bashrc
# and nothing else, which means PATH, PKG_CONFIG_PATH and the rest
# of the target's /etc/profile do not apply and you silently get
# the installer's environment instead of the installed system's.
chroot "$MNT" /bin/bash -l

echo ""
echo ">>> Exited chroot. Unmounting..."

cleanup
trap - EXIT INT TERM

echo ">>> Done."