#!/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=\npassword=\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