7881d7ebce
Kernel before 3.18 is unsupported by Arch.
247 lines
6.7 KiB
Bash
247 lines
6.7 KiB
Bash
#!/bin/bash
|
|
# shellcheck disable=SC2154 # optstring_*_option may export variables in runtime
|
|
|
|
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 spec comments=()
|
|
|
|
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)"
|
|
)
|
|
|
|
if [[ ${sources["$bytag"]} ]]; then
|
|
spec="$bytag"
|
|
comments+=("$src")
|
|
else
|
|
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"
|
|
|
|
# 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
|
|
|
|
# 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 fstype_is_pseudofs "$fstype"; then
|
|
optstring_remove_option "$varname" relatime
|
|
fi
|
|
|
|
case $fstype in
|
|
btrfs)
|
|
# 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
|
|
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
|
|
;;
|
|
esac
|
|
}
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
usage: ${0##*/} [options] root
|
|
|
|
Options:
|
|
-f <filter> Restrict output to mountpoints matching the prefix FILTER
|
|
-L Use labels for source identifiers (shortcut for -t LABEL)
|
|
-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)
|
|
-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.
|
|
|
|
EOF
|
|
}
|
|
|
|
if [[ -z $1 || $1 = @(-h|--help) ]]; then
|
|
usage
|
|
exit $(( $# ? 0 : 1 ))
|
|
fi
|
|
|
|
while getopts ':f:LPpt:U' flag; do
|
|
case $flag in
|
|
f)
|
|
prefixfilter="$OPTARG"
|
|
;;
|
|
L)
|
|
bytag=LABEL
|
|
;;
|
|
P)
|
|
pseudofs=1
|
|
;;
|
|
p)
|
|
pseudofs=0
|
|
;;
|
|
t)
|
|
bytag="${OPTARG^^}"
|
|
;;
|
|
U)
|
|
bytag=UUID
|
|
;;
|
|
:)
|
|
die "%s: option requires an argument -- '%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
|
|
|
|
if ! mountpoint -q "$root"; then
|
|
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
|
|
continue
|
|
fi
|
|
|
|
[[ "$target" = "$prefixfilter"* ]] || continue
|
|
|
|
# default 5th and 6th columns
|
|
dump=0 pass=2
|
|
|
|
src="$(unmangle "$src")"
|
|
target="$(unmangle "$target")"
|
|
target="${target#"$root/"}"
|
|
[[ "$target" != '/'* ]] && target="/$target"
|
|
|
|
if (( ! foundroot )) && findmnt "$src" "$root" >/dev/null; then
|
|
# this is root. we can't possibly have more than one...
|
|
pass=1 foundroot=1
|
|
fi
|
|
|
|
# if there's no fsck tool available, then only pass=0 makes sense.
|
|
if ! fstype_has_fsck "$fstype"; then
|
|
pass=0
|
|
fi
|
|
|
|
if [[ "$fsroot" != "/" && "$fstype" != "btrfs" ]]; then
|
|
# it's a bind mount
|
|
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'
|
|
pass=0
|
|
fi
|
|
|
|
# filesystem quirks
|
|
case $fstype in
|
|
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
|
|
# 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"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
optstring_apply_quirks opts "$fstype"
|
|
|
|
# write one line
|
|
write_source "$src"
|
|
printf '\t%-10s' "$(mangle "$target")" "$fstype" "$opts"
|
|
printf '\t%s %s' "$dump" "$pass"
|
|
printf '\n\n'
|
|
done
|
|
|
|
# handle swaps devices
|
|
{
|
|
# ignore header
|
|
read -r
|
|
|
|
while read -r device type _ _ prio; do
|
|
options=defaults
|
|
if (( prio >= 0 )); then
|
|
options+=",pri=$prio"
|
|
fi
|
|
|
|
# skip files marked deleted by the kernel
|
|
[[ "$device" = *'\040(deleted)' ]] && continue
|
|
|
|
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.
|
|
write_source "$(dm_name_for_devnode "$device")"
|
|
else
|
|
write_source "$(unmangle "$device")"
|
|
fi
|
|
|
|
printf '\t%-10s\t%-10s\t%-10s\t0 0\n\n' none swap "$options"
|
|
done
|
|
} </proc/swaps
|
|
|
|
# vim: et ts=2 sw=2 ft=sh:
|