#!/bin/bashshopt-s extglob
# generated from util-linux source: libmount/src/utils.cdeclare-Apseudofs_types=([anon_inodefs]=1[autofs]=1[bdev]=1[bpf]=1[binfmt_misc]=1[cgroup]=1[cgroup2]=1[configfs]=1[cpuset]=1[debugfs]=1[devfs]=1[devpts]=1[devtmpfs]=1[dlmfs]=1[efivarfs]=1[fuse.gvfs-fuse-daemon]=1[fusectl]=1[hugetlbfs]=1[mqueue]=1[nfsd]=1[none]=1[pipefs]=1[proc]=1[pstore]=1[ramfs]=1[rootfs]=1[rpc_pipefs]=1[securityfs]=1[sockfs]=1[spufs]=1[sysfs]=1[tmpfs]=1)# generated from: pkgfile -vbr '/fsck\..+' | awk -F. '{ print $NF }' | sortdeclare-Afsck_types=([cramfs]=1[exfat]=1[ext2]=1[ext3]=1[ext4]=1[ext4dev]=1[jfs]=1[minix]=1[msdos]=1[reiserfs]=1[vfat]=1[xfs]=1)out(){printf"$1$2\n""${@:3}";}error(){ out "==> ERROR:""$@";}>&2warning(){ out "==> WARNING:""$@";}>&2msg(){ out "==>""$@";}msg2(){ out " ->""$@";}die(){ error "$@";exit1;}ignore_error(){"$@"2>/dev/null
return0}in_array(){local i
foriin"${@:2}";do[[$1="$i"]]&&return0donereturn1}chroot_add_mount(){mount"$@"&&CHROOT_ACTIVE_MOUNTS=("$2""${CHROOT_ACTIVE_MOUNTS[@]}")}chroot_maybe_add_mount(){localcond=$1;shiftifeval"$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 -omode=0755,nosuid &&
chroot_add_mount devpts "$1/dev/pts"-t devpts -omode=0620,gid=5,nosuid,noexec &&
chroot_add_mount shm "$1/dev/shm"-t tmpfs -omode=1777,nosuid,nodev &&
chroot_add_mount /run "$1/run"--bind&&
chroot_add_mount tmp "$1/tmp"-t tmpfs -omode=1777,strictatime,nodev,nosuid
}chroot_teardown(){if(( ${#CHROOT_ACTIVE_MOUNTS[@]} ));thenumount"${CHROOT_ACTIVE_MOUNTS[@]}"fiunset CHROOT_ACTIVE_MOUNTS
}
try_cast()(_=$(( $1#$2)))2>/dev/null
valid_number_of_base(){localbase=$1len=${#2}i=for(( i =0; i < len; i++));do
try_cast "$base""${2:i:1}"||return1donereturn0}mangle(){locali=chr=out=local{a..f}={A..F}=for(( i =0; i < ${#1}; i++));dochr=${1:i:1}case$chrin[[:space:]\\])printf-v chr '%03o'"'$chr"out+=\\;;esacout+=$chrdoneprintf'%s'"$out"}unmangle(){locali=chr=out=len=$(( ${#1} -4))local{a..f}={A..F}=for(( i =0; i < len; i++));dochr=${1:i:1}case$chrin\\)if valid_number_of_base 8"${1:i+1:3}"||
valid_number_of_base 16"${1:i+1:3}";thenprintf-v chr '%b'"${1:i:4}"(( i +=3))fi;;esacout+=$chrdoneprintf'%s'"$out${1:i}"}optstring_match_option(){local candidate pat patterns
IFS=, read-ra patterns <<<"$1"forpatin"${patterns[@]}";doif[[$pat= *=* ]];then# "key=val" will only ever match "key=val"candidate=$2else# "key" will match "key", but also "key=anyval"candidate=${2%%=*}fi[[$pat="$candidate"]]&&return0donereturn1}optstring_remove_option(){local o options_ remove=$2IFS=,
read-ra options_ <<<"${!1}"foroin"${!options_[@]}";do
optstring_match_option "$remove""${options_[o]}"&&unset'options_[o]'donedeclare-g"$1=${options_[*]}"}optstring_normalize(){local o options_ norm IFS=,
read-ra options_ <<<"${!1}"# remove empty fieldsforoin"${options_[@]}";do[[$o]]&&norm+=("$o")done# avoid empty strings, reset to "defaults"declare-g"$1=${norm[*]:-defaults}"}optstring_append_option(){if! optstring_has_option "$1""$2";thendeclare-g"$1=${!1},$2"fi
optstring_normalize "$1"}optstring_prepend_option(){localoptions_=$1if! optstring_has_option "$1""$2";thendeclare-g"$1=$2,${!1}"fi
optstring_normalize "$1"}optstring_get_option(){local opts o
IFS=, read-ra opts <<<"${!1}"foroin"${opts[@]}";doif optstring_match_option "$2""$o";thendeclare-g"$o"return0fidonereturn1}optstring_has_option(){local"${2%%=*}"
optstring_get_option "$1""$2"}dm_name_for_devnode(){read dm_name <"/sys/class/block/${1#/dev/}/dm/name"if[[$dm_name]];thenprintf'/dev/mapper/%s'"$dm_name"else# don't leave the caller hanging, just print the original name# along with the failure.
error 'Failed to resolve device mapper name for: %s'"$1"fi}fstype_is_pseudofs(){(( pseudofs_types["$1"] ))}fstype_has_fsck(){(( fsck_types["$1"] ))}write_source(){localsrc=$1spec=label=uuid=comment=()label=$(lsblk -rno LABEL "$1"2>/dev/null)uuid=$(lsblk -rno UUID "$1"2>/dev/null)# bind mounts do not have a UUID!case$bytagin'')[[$uuid]]&&comment=("UUID=$uuid")[[$label]]&&comment+=("LABEL=$(mangle "$label")");;
LABEL)spec=$label[[$uuid]]&&comment=("$src""UUID=$uuid");;
UUID)spec=$uuidcomment=("$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]];thenprintf'%-20s'"$bytag=$(mangle "$spec")"elseprintf'%-20s'"$(mangle "$src")"fi}optstring_apply_quirks(){localvarname=$1fstype=$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.ifawk-vfstype="$fstype"'$1 == fstype { exit 1 }' /proc/filesystems;then
optstring_remove_option "$varname" relatime
ficase$fstypein
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;;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))fiwhilegetopts':f:LPpt:U' flag;docase$flagin
L)bytag=LABEL
;;
U)bytag=UUID
;;
f)prefixfilter=$OPTARG;;
P)pseudofs=1;;
p)pseudofs=0;;
t)bytag=${OPTARG^^};;:)
die '%s: option requires an argument -- '\''%s'\' "${0##*/}""$OPTARG";;
?)
die '%s: invalid option -- '\''%s'\' "${0##*/}""$OPTARG";;esacdoneshift$(( OPTIND -1))(( $# ))|| die "No root directory specified"root=$(realpath -mL"$1");shiftif! mountpoint -q"$root";then
die "$root is not a mountpoint"fi# handle block devices
findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root"|whileread-r src target fstype opts fsroot;doif((!pseudofs ))&& fstype_is_pseudofs "$fstype";thencontinuefi[[$target="$prefixfilter"* ]]||continue# default 5th and 6th columnsdump=0pass=2src=$(unmangle "$src")target=$(unmangle "$target")target=${target#$root}if((!foundroot ))&& findmnt "$src""$root">/dev/null;then# this is root. we can't possibly have more than one...pass=1foundroot=1fi# if there's no fsck tool available, then only pass=0 makes sense.if! fstype_has_fsck "$fstype";thenpass=0fiif[[$fsroot!= / &&$fstype!= btrfs ]];then# it's a bind mountsrc=$(findmnt -funcevo TARGET "$src")$fsrootif[[$src-ef$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.continuefifstype=none
opts+=,bind
pass=0fi# filesystem quirkscase$fstypein
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"elsefstype=$newtypefi;;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 headerreadwhileread-r device type _ _ prio;dooptions=defaults
if(( prio >=0));thenoptions+=,pri=$priofi# skip files marked deleted by the kernel[[$device= *'\040(deleted)']]&&continueif[[$type=file]];thenprintf'%-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")"fiprintf'\t%-10s\t%-10s\t%-10s\t0 0\n\n''none''swap'"$options"done}</proc/swaps
# vim: et ts=2 sw=2 ft=sh: