NAS account is read-only on the libraries at the Synology side; the ro mount option is belt-and-braces. media_pc is this box's one write share (PMS backup, Calibre, Nikola iPad). Per Mike 2026-07-31.
212 lines
8.3 KiB
Bash
Executable File
212 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =====================================================================================
|
|
# 20-cifs — NAS mounts, named to match the Plex database exactly.
|
|
#
|
|
# SEVEN shares. The six Plex library shares are mounted READ-ONLY; media_pc is the
|
|
# single share this box may write to. The six were confirmed live from the Windows
|
|
# box and cross-checked against the Plex database 2026-07-27; media_pc added
|
|
# 2026-07-31 as the box's write location (PMS backup, Calibre, "Nikola iPad"):
|
|
#
|
|
# media 1,360 movies + 23,493 episodes + all audio (~26.5 TB) [ro]
|
|
# Radio Shows 236 files 1.6 GB [ro]
|
|
# Education Videos 215 files 50.0 GB [ro]
|
|
# Health 30 files 14.4 GB [ro]
|
|
# Home Movies 51 files 28.4 GB [ro]
|
|
# Pictures 385 files 0.9 GB [ro]
|
|
# media_pc this box's write share [rw]
|
|
#
|
|
# The NAS account (mediabox) is read-only on the six library shares AT THE SYNOLOGY
|
|
# TOO — the ro mount option here is belt-and-braces, not the enforcement.
|
|
#
|
|
# NOT mounted, deliberately:
|
|
# Share not a Plex library root
|
|
# Audio Books a library root, but EMPTY — 0 files, 0 bytes.
|
|
# Music Organized a library root, but EMPTY — 0 files, 0 bytes.
|
|
# All audio actually lives under media/audiobooks and media/music. The two
|
|
# empty roots are vestigial and are deleted by migration/remap.sql.
|
|
#
|
|
# -----------------------------------------------------------------------------------
|
|
# MOUNT POINTS MIRROR THE SHARE NAMES, VERBATIM.
|
|
#
|
|
# /mnt/nas/Home Movies — capital letters, and yes, a space in the path.
|
|
#
|
|
# This is not cosmetic. The Plex database stores \\korval\Home Movies\...,
|
|
# so mounting at the identically-named path reduces the entire migration to
|
|
# one uniform prefix substitution: \\korval\ -> /mnt/nas/
|
|
# Rename the mount points to something tidier and every path becomes its own
|
|
# special case in the remap, which is how migrations get silently wrong.
|
|
#
|
|
# The same paths are bind-mounted into the Plex container at the same
|
|
# location, so the database, the host and the container all agree with no
|
|
# further translation anywhere.
|
|
# -----------------------------------------------------------------------------------
|
|
#
|
|
# WHY EACH OPTION IS THERE — none of these are decoration:
|
|
# nofail an unreachable NAS must NOT drop a headless
|
|
# box to an emergency shell. There is no
|
|
# keyboard attached to type a root password.
|
|
# _netdev tells systemd this needs the network up.
|
|
# x-systemd.automount mount on first access, so a slow NAS never
|
|
# stretches boot.
|
|
# x-systemd.mount-timeout=30 bounded failure instead of an endless hang.
|
|
# vers=3.1.1 dialect negotiated by the Windows box. If a
|
|
# mount fails on build night, try vers=3.0 —
|
|
# arrsstack talks to this same NAS at 3.0.
|
|
# uid/gid=3000 MUST equal Plex's PUID/PGID or every file is
|
|
# permission-denied.
|
|
# ro (libraries only) belt-and-braces with the Synology-side
|
|
# read-only permission. media_pc omits it.
|
|
# \040 fstab splits fields on whitespace, so spaces
|
|
# must be escaped in BOTH the share name and
|
|
# the mount point.
|
|
# =====================================================================================
|
|
set -uo pipefail
|
|
|
|
CRED="/etc/cifs/korval.cred"
|
|
NAS="10.0.1.254"
|
|
MEDIA_UID=3000
|
|
MEDIA_GID=3000
|
|
MARK_BEGIN="# >>> mediabox NAS mounts (managed by 20-cifs.sh) >>>"
|
|
MARK_END="# <<< mediabox NAS mounts <<<"
|
|
|
|
OPTS="credentials=${CRED},vers=3.1.1,uid=${MEDIA_UID},gid=${MEDIA_GID},file_mode=0664,dir_mode=0775,iocharset=utf8,nofail,_netdev,x-systemd.automount,x-systemd.mount-timeout=30,x-systemd.idle-timeout=600"
|
|
|
|
# Share name on the NAS == directory name under /mnt/nas. Do not "tidy" these.
|
|
# Six Plex library shares — mounted read-only.
|
|
SHARES_RO=(
|
|
"media"
|
|
"Radio Shows"
|
|
"Education Videos"
|
|
"Health"
|
|
"Home Movies"
|
|
"Pictures"
|
|
)
|
|
# The one write share: PMS backup, Calibre and "Nikola iPad" live here.
|
|
SHARES_RW=(
|
|
"media_pc"
|
|
)
|
|
SHARES=( "${SHARES_RO[@]}" "${SHARES_RW[@]}" )
|
|
|
|
mp_for() { printf '/mnt/nas/%s' "$1"; }
|
|
|
|
opts_for() {
|
|
local s="$1" rw
|
|
for rw in "${SHARES_RW[@]}"; do
|
|
if [ "$s" = "$rw" ]; then printf '%s' "$OPTS"; return; fi
|
|
done
|
|
printf '%s,ro' "$OPTS"
|
|
}
|
|
|
|
# --- --verify mode: check mounts, change nothing ----------------------------------
|
|
if [ "${1:-}" = "--verify" ]; then
|
|
echo " verifying NAS mounts"
|
|
rc=0
|
|
if [ ! -s "$CRED" ]; then
|
|
echo " [FAIL] $CRED is empty — write the NAS credentials first"
|
|
exit 1
|
|
fi
|
|
for s in "${SHARES[@]}"; do
|
|
mp="$(mp_for "$s")"
|
|
if ls "$mp" >/dev/null 2>&1 && mountpoint -q "$mp"; then
|
|
printf ' [ok] %-20s %s\n' "$s" "$(df -h --output=size "$mp" 2>/dev/null | tail -1 | tr -d ' ')"
|
|
else
|
|
printf ' [FAIL] %-20s not mounted\n' "$s"
|
|
rc=1
|
|
fi
|
|
done
|
|
# Write-permission sanity: libraries must refuse writes, media_pc must accept.
|
|
if mountpoint -q "/mnt/nas/media"; then
|
|
if touch "/mnt/nas/media/.wtest-mediabox" 2>/dev/null; then
|
|
rm -f "/mnt/nas/media/.wtest-mediabox"
|
|
printf ' [FAIL] %s\n' "media accepted a write — the account or mount should be read-only"
|
|
rc=1
|
|
else
|
|
printf ' [ok] %s\n' "media refused a write (read-only confirmed)"
|
|
fi
|
|
fi
|
|
if mountpoint -q "/mnt/nas/media_pc"; then
|
|
if touch "/mnt/nas/media_pc/.wtest-mediabox" 2>/dev/null; then
|
|
rm -f "/mnt/nas/media_pc/.wtest-mediabox"
|
|
printf ' [ok] %s\n' "media_pc write test passed"
|
|
else
|
|
printf ' [FAIL] %s\n' "media_pc refused a write — check share permissions"
|
|
rc=1
|
|
fi
|
|
fi
|
|
exit $rc
|
|
fi
|
|
|
|
echo " writing fstab entries for ${#SHARES[@]} shares"
|
|
|
|
install -d -m 0700 /etc/cifs
|
|
[ -f "$CRED" ] || install -m 0600 /dev/null "$CRED"
|
|
chmod 600 "$CRED"
|
|
|
|
if [ ! -s "$CRED" ]; then
|
|
cat <<'CREDNOTE'
|
|
NOTE: /etc/cifs/korval.cred is empty. That is expected at this stage — the
|
|
autoinstall creates it empty on purpose so the NAS password never
|
|
rides on removable media. The mounts fail harmlessly (nofail) until:
|
|
printf 'username=<user>\npassword=<pass>\ndomain=WORKGROUP\n' \
|
|
> /etc/cifs/korval.cred
|
|
chmod 600 /etc/cifs/korval.cred
|
|
No quotes. No spaces around '='. Trailing newline required.
|
|
CREDNOTE
|
|
fi
|
|
|
|
for s in "${SHARES[@]}"; do
|
|
mp="$(mp_for "$s")"
|
|
install -d -m 0755 "$mp"
|
|
chown ${MEDIA_UID}:${MEDIA_GID} "$mp"
|
|
done
|
|
|
|
# Rebuild only our managed block; never touch the rest of fstab.
|
|
tmp="$(mktemp)"
|
|
awk -v b="$MARK_BEGIN" -v e="$MARK_END" '
|
|
$0 == b { skip=1 }
|
|
!skip { print }
|
|
$0 == e { skip=0 }
|
|
' /etc/fstab > "$tmp"
|
|
|
|
{
|
|
printf '%s\n' "$MARK_BEGIN"
|
|
for s in "${SHARES[@]}"; do
|
|
mp="$(mp_for "$s")"
|
|
esc_share="${s// /\\040}"
|
|
esc_mp="${mp// /\\040}"
|
|
printf '//%s/%s %s cifs %s 0 0\n' "$NAS" "$esc_share" "$esc_mp" "$(opts_for "$s")"
|
|
done
|
|
printf '%s\n' "$MARK_END"
|
|
} >> "$tmp"
|
|
|
|
# Sanity: never install an fstab that lost the root entry.
|
|
if ! awk '$2=="/" && $1 !~ /^#/' "$tmp" | grep -q .; then
|
|
echo " [FAIL] refusing to write fstab — root entry missing from generated file"
|
|
rm -f "$tmp"; exit 1
|
|
fi
|
|
|
|
cp -a /etc/fstab "/etc/fstab.bak.$(date +%Y%m%d%H%M%S)"
|
|
install -m 0644 "$tmp" /etc/fstab
|
|
rm -f "$tmp"
|
|
|
|
systemctl daemon-reload
|
|
|
|
echo " fstab updated (backup written alongside). Managed block:"
|
|
sed -n "/>>> mediabox NAS mounts/,/<<< mediabox NAS mounts/p" /etc/fstab | cut -c1-120 | sed 's/^/ /'
|
|
|
|
if [ -s "$CRED" ]; then
|
|
echo " credentials present — attempting mounts"
|
|
for s in "${SHARES[@]}"; do
|
|
mp="$(mp_for "$s")"
|
|
if timeout 40 mount "$mp" 2>/dev/null || mountpoint -q "$mp"; then
|
|
printf ' [ok] %s\n' "$mp"
|
|
else
|
|
printf ' [warn] %s did not mount (check credentials / share name)\n' "$mp"
|
|
fi
|
|
done
|
|
else
|
|
echo " skipping mount attempts until credentials are written"
|
|
fi
|
|
|
|
exit 0
|