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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
#!/bin/sh
#
# genfstab - Generate /etc/fstab from current mounts
# rawnix.org - https://rawnix.org
#
# Usage: genfstab [-d] <mountpoint>
# genfstab /mnt > /mnt/etc/fstab
#
# -d emit device paths (/dev/nvme0n1p2) instead of UUIDs
#
# UUIDs are the default. A device path in fstab is stable only
# until the next disk is plugged in: adding a USB drive can renumber
# /dev/sdX and the system boots into an emergency shell for a change
# that had nothing to do with it. -d exists for the cases where the
# device really is fixed and you want to read the file at a glance.
set -e
die() { echo "ERROR: $*" >&2; exit 1; }
USE_UUID=1
while [ $# -gt 0 ]; do
case "$1" in
-d) USE_UUID=0; shift ;;
-*) die "Unknown option: $1" ;;
*) break ;;
esac
done
[ -n "$1" ] || die "Usage: genfstab [-d] <mountpoint>"
[ -d "$1" ] || die "Mountpoint not found: $1"
# Strip a trailing slash before it reaches the sed below. With
# MNT=/mnt/ the substitution turns /mnt/boot/efi into "boot/efi"
# with no leading slash, and the resulting fstab is silently wrong.
MNT="${1%/}"
[ -n "$MNT" ] || MNT="/"
mountpoint -q "$MNT" || die "$MNT is not a mountpoint"
# Resolve a device to UUID=..., falling back to the device path if
# blkid cannot see it. Never emits a bare UUID= with nothing after.
devspec() {
_dev="$1"
if [ "$USE_UUID" -eq 1 ]; then
_uuid=$(blkid -o value -s UUID "$_dev" 2>/dev/null || true)
if [ -n "$_uuid" ]; then
printf 'UUID=%s' "$_uuid"
return 0
fi
echo "genfstab: no UUID for $_dev, using device path" >&2
fi
printf '%s' "$_dev"
}
cat << 'EOF'
# /etc/fstab: static file system information
# rawnix.org - https://rawnix.org
#
# <file system> <dir> <type> <options> <dump> <pass>
EOF
# -l is not optional: without it findmnt draws a tree, and every
# submount's TARGET arrives prefixed with box-drawing characters
# ("|-/mnt/boot/efi"). The $MNT strip below then does not match, and
# the entry lands in fstab as /mnt/boot/efi -- which mounts nothing at
# boot and drops the installed system into an emergency shell. The
# root line has no prefix, so root is correct and only the submounts
# are wrong, which is what makes it look like a UUID problem.
findmnt -l -R -n -o TARGET,SOURCE,FSTYPE "$MNT" | sort | while read target source fstype; do
case "$fstype" in
tmpfs|devtmpfs|devpts|sysfs|proc|cgroup*|efivarfs|hugetlbfs|mqueue|securityfs|pstore|bpf|fusectl|configfs) continue ;;
esac
case "$source" in
/dev/loop*) continue ;;
esac
dir=${target#"$MNT"}
[ -n "$dir" ] || dir="/"
case "$dir" in
/) opts="defaults,noatime" ; dump=1; pass=1 ;;
/boot/efi) opts="defaults,noatime,nodev,noexec" ; dump=0; pass=2 ;;
/tmp) opts="defaults,noatime,nosuid,nodev" ; dump=1; pass=2 ;;
/home) opts="defaults,noatime" ; dump=1; pass=2 ;;
/var) opts="defaults,noatime" ; dump=1; pass=2 ;;
*) opts="defaults,noatime" ; dump=1; pass=2 ;;
esac
printf "%-42s %-20s %-9s %-31s %s %s\n" \
"$(devspec "$source")" "$dir" "$fstype" "$opts" "$dump" "$pass"
done
# Every swap signature the *running* system can see, which in an
# installer is only the target's. Run from a live system that has its
# own swap, this writes that swap into the target's fstab.
for dev in $(blkid -t TYPE=swap -o device 2>/dev/null); do
printf "%-42s %-20s %-9s %-31s %s %s\n" \
"$(devspec "$dev")" "swap" "swap" "defaults" "0" "0"
done
cat << 'EOF'
devpts /dev/pts devpts noexec,nosuid,gid=tty,mode=0620 0 0
tmpfs /dev/shm tmpfs mode=1777,nosuid,nodev 0 0
EOF
# efivarfs only on a machine booted through UEFI. Under legacy BIOS
# there is no /sys/firmware/efi to mount it on: nofail covers a missing
# device, not a missing mount point, so mount -a fails at boot and
# stage 1 drops to the emergency shell. The installer runs on the
# target machine, so its own /sys/firmware/efi says which firmware the
# installed system will boot with.
if [ -d /sys/firmware/efi ]; then
echo 'efivarfs /sys/firmware/efi/efivars efivarfs nodev,noexec,nosuid,nofail 0 0'
fi
echo '# End of file'
|