1+ #! /usr/bin/env bash
2+ # generate-mount-persistence.sh
3+ #
4+ # Prints:
5+ # 1. udev rules that bind each currently mounted real block device (under /dev)
6+ # to a stable symlink under /dev/disk/by-mountpoint/<sanitized-mount-path>.
7+ # 2. Equivalent /etc/fstab entries with UUID=<uuid>.
8+ #
9+ # Supported distros: CentOS 6/7/8, Rocky Linux, Ubuntu, SUSE, RHEL.
10+ #
11+ # Usage:
12+ # sudo bash generate-mount-persistence.sh > output.txt
13+ # # Review output.txt for udev rules + suggested fstab entries.
14+
15+ set -euo pipefail
16+
17+ command -v blkid > /dev/null 2>&1 || {
18+ echo " Error: blkid not found. Please install util-linux." >&2
19+ exit 1
20+ }
21+
22+ SKIP_FS_TYPES=" ^(autofs|bpf|cgroup|cgroup2|configfs|debugfs|devpts|devtmpfs|efivarfs|fusectl|fuse\.overlayfs|hugetlbfs|mqueue|nsfs|overlay|proc|pstore|ramfs|rpc_pipefs|securityfs|selinuxfs|smb3?|squashfs|sysfs|tmpfs|tracefs)$"
23+ SKIP_DEVICES_REGEX=" ^/dev/(ram|loop|fd|sr|zram|nbd|md|pmem)[0-9]"
24+
25+ UDEV_RULES=" "
26+ FSTAB_LINES=" "
27+
28+ awk '
29+ {
30+ src=$1; tgt=$2; fstype=$3;
31+ gsub(/\\040/, " ", tgt);
32+ gsub(/\\011/, "\t", tgt);
33+ gsub(/\\012/, "\n", tgt);
34+ print src "\t" tgt "\t" fstype;
35+ }
36+ ' /proc/mounts | while IFS=$' \t ' read -r SRC TGT FST; do
37+ [[ " $SRC " == /dev/* ]] || continue
38+ if [[ " $FST " =~ $SKIP_FS_TYPES ]]; then
39+ continue
40+ fi
41+ if [[ " $SRC " =~ $SKIP_DEVICES_REGEX ]]; then
42+ continue
43+ fi
44+
45+ UUID=" $( blkid -s UUID -o value -- " $SRC " 2> /dev/null || true) "
46+ if [[ -z " $UUID " ]] && command -v lsblk > /dev/null 2>&1 ; then
47+ UUID=" $( lsblk -no UUID -- " $SRC " 2> /dev/null | head -n1 || true) "
48+ fi
49+ [[ -z " $UUID " ]] && continue
50+
51+ SAFE_NAME=" $TGT "
52+ if [[ " $SAFE_NAME " == " /" ]]; then
53+ SAFE_NAME=" root"
54+ else
55+ SAFE_NAME=" ${SAFE_NAME#/ } "
56+ SAFE_NAME=" ${SAFE_NAME// \/ / _} "
57+ SAFE_NAME=" ${SAFE_NAME// / _} "
58+ fi
59+
60+ # Udev rules
61+ echo " ACTION==\" add\" , SUBSYSTEM==\" block\" , ENV{ID_FS_UUID}==\" $UUID \" , SYMLINK+=\" disk/by-mountpoint/$SAFE_NAME \" "
62+ echo " ACTION==\" change\" , SUBSYSTEM==\" block\" , ENV{ID_FS_UUID}==\" $UUID \" , SYMLINK+=\" disk/by-mountpoint/$SAFE_NAME \" "
63+
64+ # /etc/fstab entry suggestion
65+ # Extract mount options from /proc/mounts
66+ OPTIONS=" $( awk -v src=" $SRC " ' $1==src {print $4}' /proc/mounts | head -n1) "
67+ OPTIONS=" ${OPTIONS:- defaults} "
68+
69+ # Pass and dump values: root usually 1 1, others 0 2
70+ if [[ " $TGT " == " /" ]]; then
71+ DUMP_PASS=" 1 1"
72+ else
73+ DUMP_PASS=" 0 2"
74+ fi
75+
76+ echo " FSTAB: UUID=$UUID $TGT $FST $OPTIONS $DUMP_PASS "
77+ done | awk '
78+ BEGIN {
79+ print "# === UDEV RULES (save to /etc/udev/rules.d/99-by-mountpoint.rules) ==="
80+ }
81+ /^ACTION/ { print; next }
82+ /^FSTAB:/ {
83+ fstab_line = substr($0, 7)
84+ if (!printed_fstab) {
85+ print ""
86+ print "# === /etc/fstab entries (append to /etc/fstab) ==="
87+ printed_fstab=1
88+ }
89+ print fstab_line
90+ }
91+ '
0 commit comments