fstabgen.in 6.48 KB
Newer Older
udeved's avatar
udeved committed
1
#!/bin/bash
artoo's avatar
artoo committed
2
#
udeved's avatar
udeved committed
3 4 5 6 7 8 9 10
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
udeved's avatar
udeved committed
11

artoo's avatar
artoo committed
12 13
version=@version@

udeved's avatar
udeved committed
14 15
shopt -s extglob

udeved's avatar
udeved committed
16 17 18 19 20
LIBDIR='@libdir@'

[[ -r ${LIBDIR}/util-msg.sh ]] && source ${LIBDIR}/util-msg.sh

import ${LIBDIR}/util-fstab.sh
udeved's avatar
udeved committed
21 22

write_source() {
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
    local src=$1 spec= 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 $bytag in
        '')
            [[ $uuid ]] && comment=("UUID=$uuid")
            [[ $label ]] && comment+=("LABEL=$(mangle "$label")")
        ;;
        LABEL)
            spec=$label
            [[ $uuid ]] && comment=("$src" "UUID=$uuid")
        ;;
        UUID)
            spec=$uuid
            comment=("$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 ]]; then
        printf '%-20s' "$bytag=$(mangle "$spec")"
    else
        printf '%-20s' "$(mangle "$src")"
    fi
udeved's avatar
udeved committed
58 59
}

udeved's avatar
udeved committed
60
optstring_apply_quirks() {
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    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

    case $fstype in
        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
udeved's avatar
udeved committed
83 84
}

udeved's avatar
udeved committed
85
usage() {
86
  cat <<EOF
udeved's avatar
udeved committed
87 88
usage: ${0##*/} [options] root

89
  Options:
udeved's avatar
udeved committed
90
    -L             Use labels for source identifiers (shortcut for -t LABEL)
udeved's avatar
udeved committed
91 92
    -p             Exclude pseudofs mounts (default behavior)
    -P             Include printing mounts
udeved's avatar
udeved committed
93 94 95 96 97
    -t TAG         Use TAG for source identifiers
    -U             Use UUIDs for source identifiers (shortcut for -t UUID)

    -h             Print this help message

udeved's avatar
udeved committed
98
fstabgen generates output suitable for addition to an fstab file based on the
udeved's avatar
udeved committed
99 100 101 102 103 104
devices mounted under the mountpoint specified by the given root.

EOF
}

if [[ -z $1 || $1 = @(-h|--help) ]]; then
105 106
  usage
  exit $(( $# ? 0 : 1 ))
udeved's avatar
udeved committed
107 108
fi

udeved's avatar
udeved committed
109
while getopts ':LPpt:U' flag; do
110 111 112 113 114 115 116 117 118
    case $flag in
        L) bytag=LABEL ;;
        U) bytag=UUID ;;
        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" ;;
    esac
udeved's avatar
udeved committed
119 120 121 122
done
shift $(( OPTIND - 1 ))

(( $# )) || die "No root directory specified"
123
root=$(realpath -mL "$1"); shift
udeved's avatar
udeved committed
124 125

if ! mountpoint -q "$root"; then
126
    die "$root is not a mountpoint"
udeved's avatar
udeved committed
127 128 129 130
fi

# handle block devices
findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root" |
131 132 133 134
while read -r src target fstype opts fsroot; do
    if (( !pseudofs )) && fstype_is_pseudofs "$fstype"; then
        continue
    fi
udeved's avatar
udeved committed
135

136 137
    # default 5th and 6th columns
    dump=0 pass=2
udeved's avatar
udeved committed
138

139 140 141
    src=$(unmangle "$src")
    target=$(unmangle "$target")
    target=${target#$root}
udeved's avatar
udeved committed
142

143 144 145
    if (( !foundroot )) && findmnt "$src" "$root" >/dev/null; then
        # this is root. we can't possibly have more than one...
        pass=1 foundroot=1
udeved's avatar
udeved committed
146 147
    fi

148 149 150
    # if there's no fsck tool available, then only pass=0 makes sense.
    if ! fstype_has_fsck "$fstype"; then
        pass=0
udeved's avatar
udeved committed
151
    fi
udeved's avatar
udeved committed
152

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    if [[ $fsroot != / ]]; then
        if [[ $fstype = btrfs ]]; then
        opts+=,subvol=${fsroot#/}
        else
        # it's a bind mount
        src=$(findmnt -funcevo TARGET "$src")$fsroot
        if [[ $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.
            continue
        fi
        fstype=none
        opts+=,bind
        pass=0
        fi
    fi
udeved's avatar
udeved committed
171

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    # 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'
udeved's avatar
udeved committed
193 194 195 196
done

# handle swaps devices
{
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
    # ignore header
    read

    while read -r device type _ _ prio; do
        options=defaults
        if [[ $prio != -1 ]]; then
        options+=,pri=$prio
        fi

        # skip files marked deleted by the kernel
        [[ $device = *'\040(deleted)' ]] && continue

        if [[ $type = file ]]; then
        printf '%-20s' "$device"
        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
udeved's avatar
udeved committed
222
} </proc/swaps