genfstab (6094B)
1 #!/bin/sh 2 # fstab generator similar in function to the Arch Linux tool that can be found 3 # on arch-install-scripts. 4 # 5 # Copyright (C) 2020 Cem Keylan <cem@ckyln.com> 6 7 out() { printf '%s\n' "$@" ;} 8 die() { out "$@" >&2; exit 1 ;} 9 10 find_tag() { 11 # Function to find the tag of the given device. This eliminates the 12 # requirement for programs such as lsblk/blkid. 13 # 14 # usage: find_tag /dev/sdx1 15 16 # If no identifiers are given, return with the plain partition name. 17 [ "$ident" ] || { out "$1"; return 0 ;} 18 19 [ -d "${tagdir:=/dev/disk/by-$(printf %s "$ident" | tr '[:upper:]' '[:lower:]')}" ] || 20 die "Directory '$dir' doesn't exist" 21 22 for file in "$tagdir/"*; do 23 [ "$(_readlinkf "$file")" = "$1" ] || continue 24 tag="$ident=${file##*/}" 25 break 26 done 27 28 # If the tag couldn't be found for some reason, fallback to the default 29 # name instead of printing empty. 30 out "${tag:-$1}" 31 } 32 33 print_mnt() { 34 # Print fstab entry 35 printf '%-23s %-15s %-15s %-15s %s %s\n\n' "$@" 36 } 37 38 _readlinkf() { 39 # https://github.com/ko1nksm/readlinkf 40 [ "${1:-}" ] || return 1 41 max_symlinks=40 42 CDPATH='' # to avoid changing to an unexpected directory 43 44 target=$1 45 [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes 46 [ -d "${target:-/}" ] && target="$target/" 47 48 cd -P . 2>/dev/null || return 1 49 while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do 50 if [ ! "$target" = "${target%/*}" ]; then 51 case $target in 52 /*) cd -P "${target%/*}/" 2>/dev/null || break ;; 53 *) cd -P "./${target%/*}" 2>/dev/null || break ;; 54 esac 55 target=${target##*/} 56 fi 57 58 if [ ! -L "$target" ]; then 59 target="${PWD%/}${target:+/}${target}" 60 printf '%s\n' "${target:-/}" 61 return 0 62 fi 63 64 # `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n", 65 # <file mode>, <number of links>, <owner name>, <group name>, 66 # <size>, <date and time>, <pathname of link>, <contents of link> 67 # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html 68 link=$(ls -dl -- "$target" 2>/dev/null) || break 69 target=${link#*" $target -> "} 70 done 71 return 1 72 } 73 74 ispseudo() { 75 # This function checks for pseudo-filesystems and returns 0 if it is. 76 # There is no definitive way to know every single pseudo-filesystems, 77 # there might be some issues with this function. 78 # 79 # If the filesystem is not a directory or a tag, we will assume that 80 # it is a pseudofs. The only exception I have noticed so far are loop 81 # devices. 82 case "$fs" in /dev/loop*) return 0 ;; /*|*=*) ;; *) return 0; esac 83 84 # These are the most common types of pseudo filesystems. 85 case "$type" in squashfs|proc|sysfs|tmpfs|devtmpfs|devpts|debugfs) return 0; esac 86 87 # As for the last check, we will look up /proc/filesystems which are 88 # filesystems known by the running kernel. 89 while read -r ps filesystem; do 90 [ "$filesystem" = "$type" ] || continue 91 [ "$ps" ] && return 0 92 return 1 93 done < /proc/filesystems 94 95 # If we still haven't come up with an answer, let's assume that this is NOT 96 # a pseudo-filesystem. 97 return 1 98 } 99 100 usage() { 101 out "usage: ${0##*/} [-f filter] [-t tag] [-LpPU] [root]" "" \ 102 " Options:" \ 103 " -f <filter> Restrict output to mountpoints matching the prefix FILTER" \ 104 " -L Use labels for source identifiers (Shortcut for -t LABEL)" \ 105 " -p Exclude pseudofs mounts (default behaviour)" \ 106 " -P Include pseudofs mounts" \ 107 " -t <tag> Use tag for source identifiers (should be one of: LABEL," \ 108 " UUID, PARTLABEL, PARTUUID)" \ 109 " -U Use UUIDs for source identifiers (Shortcut for -t UUID)" "" \ 110 " -h Print this help message" "" \ 111 "${0##*/} generates output suitable for addition to an fstab file based on the" \ 112 "devices mounted under the mountpoint specified by the given root." "" \ 113 "This implementation of genfstab is from <https://git.ckyln.com/genfstab>" 114 exit "${1:-0}" 115 } 116 117 118 # Print help information 119 case " $* " in *' --help '*|*' -h '*) usage; esac 120 121 ident='' filter='' pseudo=0 122 while getopts hPpLUf:t: flag; do 123 case $flag in 124 h) usage ;; 125 P) pseudo=1 ;; 126 p) pseudo=0 ;; 127 L) ident=LABEL ;; 128 U) ident=UUID ;; 129 f) [ -d "$OPTARG" ] || die "Not a directory '$OPTARG'" 130 filter=$(_readlinkf "$OPTARG") ;; 131 t) case "$OPTARG" in 132 LABEL|UUID|PARTLABEL|PARTUUID) ident=$OPTARG ;; 133 *) die "Unknown identifier '$OPTARG'"; esac 134 ;; 135 ?) usage 1 136 esac 137 done 138 shift $((OPTIND - 1)) 139 140 [ "$1" ] && { 141 [ -d "$1" ] || die "Not a directory '$1'" 142 root="$(_readlinkf "$1")" 143 } 144 145 # It's a good thing that /proc/mounts and fstab share the same syntax. We will 146 # always be defining dump and pass ourselves since they will always be '0 0' on 147 # /proc/mounts. 148 while read -r fs dir type options _; do 149 if ispseudo; then 150 pass=0 151 [ "$pseudo" != 1 ] && continue 152 [ "$ident" ] && out "# $fs" 153 else 154 case "$dir" in "${root:-/}"*) ;; *) continue; esac 155 [ "$ident" ] && out "# $fs" 156 fs="$(find_tag "$fs")" 157 [ "$root" ] && dir=/${dir#$root} 158 159 case "$dir" in /) pass=1 ;; *) pass=2; esac 160 fi 161 [ "$filter" ] && case "$dir" in ${filter%/}*) ;; *) continue; esac 162 163 print_mnt "$fs" "${dir:-/}" "$type" "$options" "${dump:=0}" "$pass" 164 done < /proc/mounts 165 166 # Now print out mounted swaps. I am really not quite sure how swaps work as I 167 # don't personally use them. If you have a better approach to this, please let 168 # me know. 169 while read -r file _; do 170 [ -f "/$file" ] || continue 171 [ "$root" ] && { 172 case "$file" in "$root/"*) ;; *) continue; esac 173 file=/${file#$root} 174 } 175 print_mnt "$file" none swap sw 0 0 176 done < /proc/swaps