Merge pull request #70 from outis1one/claude/zealous-feynman-odDrh
tools: add mount-network-drive.sh for SMB/CIFS and NFS shares
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env bash
|
||||
# mount-network-drive.sh — Mount a network share (SMB/CIFS or NFS) and add
|
||||
# it to /etc/fstab so it survives reboots.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash mount-network-drive.sh
|
||||
#
|
||||
# Supports:
|
||||
# - SMB/CIFS (Windows shares, Samba, NAS)
|
||||
# - NFS (Linux/NAS exports)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
info() { printf '\033[0;34m[INFO]\033[0m %s\n' "$*"; }
|
||||
ok() { printf '\033[0;32m[OK]\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
|
||||
err() { printf '\033[0;31m[ERROR]\033[0m %s\n' "$*" >&2; }
|
||||
|
||||
[[ "$(id -u)" == "0" ]] || { err "Run with sudo: sudo bash $0"; exit 1; }
|
||||
|
||||
# Actual user who invoked sudo
|
||||
ACTUAL_USER="${SUDO_USER:-$USER}"
|
||||
ACTUAL_HOME="$(getent passwd "$ACTUAL_USER" | cut -d: -f6)"
|
||||
ACTUAL_UID="$(id -u "$ACTUAL_USER")"
|
||||
ACTUAL_GID="$(id -g "$ACTUAL_USER")"
|
||||
|
||||
# ── Install packages ──────────────────────────────────────────────────────────
|
||||
ensure_packages() {
|
||||
local pkgs=()
|
||||
case "$1" in
|
||||
smb) command -v mount.cifs &>/dev/null || pkgs+=(cifs-utils) ;;
|
||||
nfs) command -v mount.nfs &>/dev/null || pkgs+=(nfs-common) ;;
|
||||
esac
|
||||
if [[ ${#pkgs[@]} -gt 0 ]]; then
|
||||
info "Installing: ${pkgs[*]}"
|
||||
apt-get install -y "${pkgs[@]}" -qq
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Already in fstab? ─────────────────────────────────────────────────────────
|
||||
fstab_has() {
|
||||
grep -qs "$1" /etc/fstab
|
||||
}
|
||||
|
||||
# ── Show existing network mounts ──────────────────────────────────────────────
|
||||
show_existing() {
|
||||
local entries
|
||||
entries=$(grep -E 'cifs|nfs' /etc/fstab 2>/dev/null || true)
|
||||
if [[ -n "$entries" ]]; then
|
||||
info "Existing network mounts in /etc/fstab:"
|
||||
echo "$entries" | sed 's/^/ /'
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ── SMB/CIFS ──────────────────────────────────────────────────────────────────
|
||||
mount_smb() {
|
||||
ensure_packages smb
|
||||
|
||||
echo ""
|
||||
echo " Enter the share details."
|
||||
echo " Format: //server/share (e.g. //192.168.1.10/media or //nas/homes)"
|
||||
echo ""
|
||||
|
||||
local share=""
|
||||
while [[ -z "$share" ]]; do
|
||||
read -r -p " Share path (//server/share): " share
|
||||
done
|
||||
|
||||
local default_mount
|
||||
default_mount="/mnt/$(echo "$share" | sed 's|//[^/]*/||; s|/|-|g')"
|
||||
local mount_point=""
|
||||
read -r -p " Mount point [$default_mount]: " mount_point
|
||||
mount_point="${mount_point:-$default_mount}"
|
||||
|
||||
# Credentials
|
||||
echo ""
|
||||
local use_creds=""
|
||||
read -r -p " Does this share require a username/password? [Y/n]: " use_creds
|
||||
local creds_file="" creds_opt=""
|
||||
|
||||
if [[ "${use_creds,,}" != "n" ]]; then
|
||||
local smb_user="" smb_pass="" smb_domain=""
|
||||
read -r -p " Username: " smb_user
|
||||
read -r -s -p " Password: " smb_pass; echo ""
|
||||
read -r -p " Domain (leave blank if none): " smb_domain
|
||||
|
||||
# Store credentials file — root-owned, root-readable only
|
||||
creds_file="/etc/samba/credentials.$(echo "$share" | sed 's|[^a-zA-Z0-9]|_|g')"
|
||||
mkdir -p /etc/samba
|
||||
cat > "$creds_file" << CREDS
|
||||
username=${smb_user}
|
||||
password=${smb_pass}
|
||||
${smb_domain:+domain=${smb_domain}}
|
||||
CREDS
|
||||
chmod 600 "$creds_file"
|
||||
chown root:root "$creds_file"
|
||||
ok "Credentials saved to $creds_file (root-only, 600)"
|
||||
creds_opt="credentials=${creds_file}"
|
||||
else
|
||||
creds_opt="guest"
|
||||
fi
|
||||
|
||||
# SMB version
|
||||
echo ""
|
||||
echo " SMB version (leave blank to let the system negotiate):"
|
||||
echo " 3.0 — modern NAS, Windows 2012+, Samba 4+"
|
||||
echo " 2.1 — older NAS, Windows 7/2008 R2"
|
||||
echo " 1.0 — legacy only (insecure, not recommended)"
|
||||
local smb_ver=""
|
||||
read -r -p " SMB version [auto]: " smb_ver
|
||||
|
||||
local ver_opt=""
|
||||
[[ -n "$smb_ver" ]] && ver_opt=",vers=${smb_ver}"
|
||||
|
||||
local opts="uid=${ACTUAL_UID},gid=${ACTUAL_GID},${creds_opt}${ver_opt},iocharset=utf8,nofail,_netdev"
|
||||
|
||||
_do_mount "cifs" "$share" "$mount_point" "$opts"
|
||||
}
|
||||
|
||||
# ── NFS ───────────────────────────────────────────────────────────────────────
|
||||
mount_nfs() {
|
||||
ensure_packages nfs
|
||||
|
||||
echo ""
|
||||
echo " Format: server:/export (e.g. 192.168.1.10:/mnt/data or nas:/homes)"
|
||||
echo ""
|
||||
|
||||
local share=""
|
||||
while [[ -z "$share" ]]; do
|
||||
read -r -p " NFS export (server:/path): " share
|
||||
done
|
||||
|
||||
local default_mount
|
||||
default_mount="/mnt/$(echo "$share" | sed 's|[:/]|-|g; s|^-||')"
|
||||
local mount_point=""
|
||||
read -r -p " Mount point [$default_mount]: " mount_point
|
||||
mount_point="${mount_point:-$default_mount}"
|
||||
|
||||
echo ""
|
||||
echo " NFS version:"
|
||||
echo " 4 — recommended (NFSv4, supports Kerberos, ACLs)"
|
||||
echo " 3 — older servers/NAS"
|
||||
local nfs_ver=""
|
||||
read -r -p " NFS version [4]: " nfs_ver
|
||||
nfs_ver="${nfs_ver:-4}"
|
||||
|
||||
local opts="nfsvers=${nfs_ver},rw,soft,intr,nofail,_netdev"
|
||||
|
||||
_do_mount "nfs" "$share" "$mount_point" "$opts"
|
||||
}
|
||||
|
||||
# ── Common mount + fstab logic ────────────────────────────────────────────────
|
||||
_do_mount() {
|
||||
local fstype="$1" share="$2" mount_point="$3" opts="$4"
|
||||
|
||||
# Create mount point
|
||||
if [[ ! -d "$mount_point" ]]; then
|
||||
mkdir -p "$mount_point"
|
||||
ok "Created mount point: $mount_point"
|
||||
fi
|
||||
|
||||
# Test mount
|
||||
echo ""
|
||||
info "Testing mount..."
|
||||
if mount -t "$fstype" -o "$opts" "$share" "$mount_point"; then
|
||||
ok "Mounted successfully at $mount_point"
|
||||
else
|
||||
err "Mount failed. Check the share path, credentials, and network connectivity."
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Show what's there
|
||||
echo ""
|
||||
info "Contents of $mount_point:"
|
||||
ls "$mount_point" | head -20 | sed 's/^/ /' || true
|
||||
echo ""
|
||||
|
||||
# fstab
|
||||
local add_fstab=""
|
||||
read -r -p " Add to /etc/fstab so it mounts on boot? [Y/n]: " add_fstab
|
||||
if [[ "${add_fstab,,}" != "n" ]]; then
|
||||
if fstab_has "$mount_point"; then
|
||||
warn "$mount_point already in /etc/fstab — skipping."
|
||||
else
|
||||
# Backup fstab
|
||||
local bk="/etc/fstab.backup.$(date +%Y%m%d-%H%M%S)"
|
||||
cp /etc/fstab "$bk"
|
||||
info "Backed up /etc/fstab → $bk"
|
||||
|
||||
printf '\n# %s — added by mount-network-drive.sh %s\n' \
|
||||
"$share" "$(date +%Y-%m-%d)" >> /etc/fstab
|
||||
printf '%-40s %-25s %-6s %s 0 0\n' \
|
||||
"$share" "$mount_point" "$fstype" "$opts" >> /etc/fstab
|
||||
|
||||
ok "Added to /etc/fstab"
|
||||
info "Verify with: sudo mount -a"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ownership hint
|
||||
echo ""
|
||||
info "To make this drive writable by $ACTUAL_USER:"
|
||||
echo " sudo chown -R ${ACTUAL_USER}:${ACTUAL_USER} $mount_point"
|
||||
echo ""
|
||||
ok "Done. $share is mounted at $mount_point"
|
||||
}
|
||||
|
||||
# ── Remove a mount ────────────────────────────────────────────────────────────
|
||||
remove_mount() {
|
||||
echo ""
|
||||
local entries=()
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" || "$line" == \#* ]] && continue
|
||||
entries+=("$line")
|
||||
done < <(grep -E '\bcifs\b|\bnfs\b' /etc/fstab 2>/dev/null || true)
|
||||
|
||||
if [[ ${#entries[@]} -eq 0 ]]; then
|
||||
warn "No network mounts found in /etc/fstab."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo " Network mounts in /etc/fstab:"
|
||||
echo ""
|
||||
local i=1
|
||||
for e in "${entries[@]}"; do
|
||||
local mp; mp=$(echo "$e" | awk '{print $2}')
|
||||
local src; src=$(echo "$e" | awk '{print $1}')
|
||||
printf " %2d) %-30s %s\n" "$i" "$src" "$mp"
|
||||
i=$(( i + 1 ))
|
||||
done
|
||||
echo ""
|
||||
|
||||
local choice=""
|
||||
read -r -p " Remove entry number [cancel]: " choice
|
||||
[[ -z "$choice" || ! "$choice" =~ ^[0-9]+$ ]] && { info "Cancelled."; return 0; }
|
||||
|
||||
local idx=$(( choice - 1 ))
|
||||
[[ "$idx" -lt 0 || "$idx" -ge ${#entries[@]} ]] && { warn "Invalid selection."; return 0; }
|
||||
|
||||
local target_entry="${entries[$idx]}"
|
||||
local target_mp; target_mp=$(echo "$target_entry" | awk '{print $2}')
|
||||
local target_src; target_src=$(echo "$target_entry" | awk '{print $1}')
|
||||
|
||||
warn "Will unmount $target_mp and remove from /etc/fstab."
|
||||
local confirm=""
|
||||
read -r -p " Confirm? [y/N]: " confirm
|
||||
[[ "${confirm,,}" == "y" ]] || { info "Cancelled."; return 0; }
|
||||
|
||||
# Unmount
|
||||
if mountpoint -q "$target_mp" 2>/dev/null; then
|
||||
umount "$target_mp" && ok "Unmounted $target_mp" || warn "umount failed — may still be in use"
|
||||
fi
|
||||
|
||||
# Remove from fstab (match the mount point field)
|
||||
local bk="/etc/fstab.backup.$(date +%Y%m%d-%H%M%S)"
|
||||
cp /etc/fstab "$bk"
|
||||
info "Backed up /etc/fstab → $bk"
|
||||
# Remove the entry line and its preceding comment if added by this script
|
||||
sed -i "\|${target_src}|d" /etc/fstab
|
||||
sed -i "\|# ${target_src} — added by mount-network-drive.sh|d" /etc/fstab
|
||||
ok "Removed from /etc/fstab"
|
||||
|
||||
# Remove credentials file if it exists
|
||||
local creds_file
|
||||
creds_file="/etc/samba/credentials.$(echo "$target_src" | sed 's|[^a-zA-Z0-9]|_|g')"
|
||||
if [[ -f "$creds_file" ]]; then
|
||||
local rm_creds=""
|
||||
read -r -p " Remove credentials file $creds_file? [Y/n]: " rm_creds
|
||||
[[ "${rm_creds,,}" != "n" ]] && rm -f "$creds_file" && ok "Removed $creds_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Show current mounts ───────────────────────────────────────────────────────
|
||||
show_mounts() {
|
||||
echo ""
|
||||
echo " Currently mounted network shares:"
|
||||
echo ""
|
||||
mount | grep -E 'type (cifs|nfs)' | awk '{print " " $1 " → " $3}' || echo " (none)"
|
||||
echo ""
|
||||
echo " /etc/fstab network entries:"
|
||||
echo ""
|
||||
grep -E '\bcifs\b|\bnfs\b' /etc/fstab | sed 's/^/ /' || echo " (none)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||
main() {
|
||||
echo ""
|
||||
echo "┌──────────────────────────────────────────────────────────┐"
|
||||
echo "│ Network Drive Mount Manager │"
|
||||
echo "│ SMB/CIFS (Windows/Samba) and NFS shares │"
|
||||
echo "└──────────────────────────────────────────────────────────┘"
|
||||
echo ""
|
||||
|
||||
show_existing
|
||||
|
||||
while true; do
|
||||
echo " What would you like to do?"
|
||||
echo " 1) Mount an SMB/CIFS share (Windows, Samba, NAS)"
|
||||
echo " 2) Mount an NFS share"
|
||||
echo " 3) Show current network mounts"
|
||||
echo " 4) Remove a mount"
|
||||
echo " 0) Quit"
|
||||
echo ""
|
||||
read -r -p " Choice [1]: " action
|
||||
action="${action:-1}"
|
||||
echo ""
|
||||
|
||||
case "$action" in
|
||||
1) mount_smb ;;
|
||||
2) mount_nfs ;;
|
||||
3) show_mounts ;;
|
||||
4) remove_mount ;;
|
||||
0|q|Q) break ;;
|
||||
*) warn "Invalid choice." ;;
|
||||
esac
|
||||
echo ""
|
||||
done
|
||||
|
||||
ok "Done."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user