FSA/spravuj_sdileni.sh
mxnticek 6152fced71 CRITICAL FIX: Add safety checks to prevent breaking system files
ISSUE: Previous version could break sudo and system files
The recursive chown/chmod commands in configure_user_shares() and
create_share() could modify system directories like /usr, /etc, /home
causing critical system breakage including sudo permissions.

ROOT CAUSE:
- No validation of paths before recursive operations
- Could modify /, /usr, /home and other system directories
- Broke /usr/bin/sudo permissions (needs uid 0 and setuid bit)

SOLUTION: Added comprehensive path safety checks

New function: is_safe_path_for_permissions()
- Blacklists ALL dangerous system paths: /, /usr, /etc, /bin, /var, etc.
- Only allows /mnt/* (external disk mounts)
- Only allows /home/user/subdir (not /home or /home/user itself)
- Returns error for any system directory

Protection applied to:
1. create_share() - disk share creation (line 326)
2. configure_user_shares() - user access configuration (line 869)

Behavior:
- Safe paths (/mnt/*): Permissions applied normally
- Unsafe paths: Prints warning, skips permission changes
- Users must manually set permissions for system directories

Emergency fix instructions added to README:
- How to fix broken sudo (chown root:root /usr/bin/sudo && chmod 4755)
- Multiple recovery methods (root shell, su, recovery mode)
- Clear warning about older versions

This prevents catastrophic system breakage while still allowing
proper multi-user access for external disk shares.

APOLOGIES TO USERS: If you were affected by the previous version,
I'm deeply sorry for breaking your system. Please follow the
recovery instructions in the README.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 21:09:53 +01:00

1059 lines
36 KiB
Bash
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# =============================================================================
# Universal Samba Share Manager - FSA (Fucking Samba Ass)
# =============================================================================
# Works on: Debian, Ubuntu, Arch Linux, RHEL/CentOS, Fedora
# Auto-detects distribution and installs Samba if needed
# =============================================================================
# Zkontrolujte, zda je skript spuštěn jako root
if [ "$EUID" -ne 0 ]; then
echo "Tento skript je nutné spustit s právy roota (sudo)."
exit 1
fi
# --- Detekce distribuce a správce balíčků ---
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO_ID="$ID"
DISTRO_NAME="$NAME"
elif [ -f /etc/arch-release ]; then
DISTRO_ID="arch"
DISTRO_NAME="Arch Linux"
elif [ -f /etc/debian_version ]; then
DISTRO_ID="debian"
DISTRO_NAME="Debian"
else
DISTRO_ID="unknown"
DISTRO_NAME="Unknown Linux"
fi
echo "Detected distribution: $DISTRO_NAME"
}
# --- Instalace Samby pokud není nainstalovaná ---
install_samba() {
echo "Kontroluji instalaci Samby..."
# Zkontroluj jestli už není Samba nainstalovaná
if command -v smbd &> /dev/null; then
echo "✅ Samba je již nainstalována."
return 0
fi
echo "⚠️ Samba není nainstalována. Instaluji..."
case "$DISTRO_ID" in
arch|manjaro)
pacman -Sy --noconfirm samba
;;
ubuntu|debian|linuxmint|pop)
apt-get update
apt-get install -y samba samba-common-bin
;;
fedora)
dnf install -y samba samba-common
;;
rhel|centos|rocky|almalinux)
yum install -y samba samba-common
;;
opensuse*|sles)
zypper install -y samba
;;
*)
echo "❌ CHYBA: Nepodařilo se detekovat distribuci pro automatickou instalaci."
echo "Prosím nainstalujte Sambu manuálně a spusťte skript znovu."
exit 1
;;
esac
if [ $? -eq 0 ]; then
echo "✅ Samba byla úspěšně nainstalována."
else
echo "❌ Instalace Samby selhala!"
exit 1
fi
}
# --- Auto-detekce uživatele a síťových rozhraní ---
DETECTED_USER="${SUDO_USER:-$(whoami)}"
DETECTED_INTERFACES=$(ip -o link show | awk -F': ' '{print $2}' | grep -v '^lo$' | tr '\n' ' ')
# --- Nastavení sdílení ---
declare -A SHARES=(
["${DETECTED_USER}-home"]="/home/${DETECTED_USER}|Domovska slozka uzivatele ${DETECTED_USER}|home"
)
CONFIG_FILE="/etc/samba/smb.conf"
BACKUP_FILE="/etc/samba/smb.conf.bak.$(date +%F_%T)"
# Spusť detekci distribuce a instalaci Samby
detect_distro
install_samba
# Funkce pro kontrolu existence smb.conf
check_config_exists() {
if [ ! -f "$CONFIG_FILE" ]; then
echo "Chyba: Konfigurační soubor $CONFIG_FILE neexistuje!"
echo "Pokud je Samba nově nainstalovaná, vytvořte ho z šablony příkazem:"
echo "sudo cp /etc/samba/smb.conf.default /etc/samba/smb.conf"
exit 1
fi
}
# VYLEPŠENÁ FUNKCE: Najde všechny diskové oddíly a správně očistí jejich názvy
discover_mounts() {
local verbose="$1"
if [ "$verbose" = "verbose" ]; then
echo "=== HLEDÁNÍ DISKOVÝCH ODDÍLŮ ==="
fi
# Vyčisti předchozí disky z SHARES
for key in "${!SHARES[@]}"; do
if [[ $key == disk-* ]]; then
unset SHARES[$key]
fi
done
local disk_count=0
while read -r name label uuid mountpoint; do
# Odstranění stromových znaků (└─, ├─) z názvu zařízení
name=$(echo "$name" | sed 's/^[├└]─//g' | sed 's/ //g')
# Přeskoč prázdné řádky nebo loopback zařízení
if [ -z "$name" ] || [[ "$name" == loop* ]]; then
continue
fi
local identifier="${label:-$name}"
local clean_identifier
clean_identifier=$(echo "$identifier" | sed 's/[^a-zA-Z0-9_-]//g')
# Záložní varianta pro prázdné popisky
if [ -z "$clean_identifier" ]; then
clean_identifier="$name"
fi
local share_name="disk-$clean_identifier"
local share_path=""
local status_info=""
if [ -n "$mountpoint" ]; then
share_path="$mountpoint"
local disk_info=$(df -h "$mountpoint" 2>/dev/null | tail -1 | awk '{print "("$2" celkem, "$4" volné)"}')
status_info="✅ Připojeno na $mountpoint $disk_info"
else
share_path="/mnt/$clean_identifier"
status_info="❌ Nepřipojeno (navrhovaná cesta: $share_path)"
fi
if [ -n "$clean_identifier" ]; then
SHARES["$share_name"]="$share_path|Oddíl $identifier ($name)|disk|$name|$uuid"
disk_count=$((disk_count + 1))
if [ "$verbose" = "verbose" ]; then
echo " [$disk_count] $name (Label: $identifier) - $status_info"
fi
fi
done < <(lsblk -no NAME,LABEL,UUID,MOUNTPOINT -e7,1 | grep -v '^$')
if [ "$verbose" = "verbose" ]; then
if [ $disk_count -eq 0 ]; then
echo " Žádné diskové oddíly nebyly nalezeny."
else
echo ""
echo "Celkem nalezeno: $disk_count oddílů"
fi
fi
}
# Funkce pro vytvoření dfree skriptu pro konkrétní disk
create_dfree_script() {
local mount_point="$1"
local script_name="samba-dfree-$(basename "$mount_point").sh"
local script_path="/usr/local/bin/$script_name"
cat > "$script_path" << EOT
#!/bin/bash
# Auto-generated dfree script for $mount_point
MOUNT_PATH="$mount_point"
if [ -d "\$MOUNT_PATH" ]; then
df -k --output=size,avail "\$MOUNT_PATH" | tail -n 1
else
echo "0 0"
fi
EOT
chmod +x "$script_path"
echo "$script_path"
}
# Funkce pro vytvoření kompletní smb.conf s [global] sekcí
create_complete_smb_config() {
echo "Vytvářím kompletní SMB konfiguraci..."
local HOSTNAME=$(hostname | tr '[:lower:]' '[:upper:]')
cat > "$CONFIG_FILE" <<EOT
[global]
workgroup = WORKGROUP
server string = $DISTRO_NAME Server (%v)
netbios name = ${HOSTNAME}-SAMBA
security = user
encrypt passwords = yes
map to guest = Bad User
guest account = nobody
wins support = yes
local master = yes
domain master = yes
preferred master = yes
dns proxy = yes
min protocol = SMB2
max protocol = SMB3
server min protocol = SMB2
bind interfaces only = no
interfaces = lo $DETECTED_INTERFACES
hosts allow = 127.0.0.1 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 100.64.0.0/10
smb ports = 445 8445
socket options = TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=131072 SO_SNDBUF=131072
use sendfile = yes
aio read size = 16384
aio write size = 16384
log file = /var/log/samba/log.%m
max log size = 1000
log level = 1
load printers = no
printcap name = /dev/null
disable spoolss = yes
EOT
echo "Základní konfigurace vytvořena."
}
# Funkce pro zajištění existence skupiny sambashare
ensure_sambashare_group() {
if ! getent group sambashare > /dev/null 2>&1; then
groupadd sambashare 2>/dev/null || true
fi
}
# CRITICAL SAFETY: Check if path is safe to modify recursively
is_safe_path_for_permissions() {
local path="$1"
# Blacklist of DANGEROUS paths that should NEVER be modified
local dangerous_paths=(
"/"
"/bin"
"/boot"
"/dev"
"/etc"
"/lib"
"/lib64"
"/opt"
"/proc"
"/root"
"/run"
"/sbin"
"/sys"
"/tmp"
"/usr"
"/var"
)
# Check if path matches any dangerous path
for dangerous in "${dangerous_paths[@]}"; do
if [ "$path" = "$dangerous" ] || [[ "$path" == "$dangerous"/* ]]; then
echo "⚠️ NEBEZPEČNÉ: Odmítám měnit oprávnění pro $path (systémový adresář)"
return 1
fi
done
# Only allow /mnt/* and /home/*/specific-dirs (but not /home itself)
if [[ "$path" == /mnt/* ]]; then
return 0
elif [[ "$path" == /home/*/* ]]; then
# Allow /home/user/something but not /home or /home/user
return 0
else
echo "⚠️ VAROVÁNÍ: $path není v bezpečné cestě (/mnt/* nebo /home/user/dir)"
return 1
fi
}
# OPRAVENÁ FUNKCE: Vytvoří sdílení se správným formátováním a `force user`
create_share() {
local share_name="$1"
if [ -z "$share_name" ]; then echo "Chybí název sdílení."; return 1; fi
if [ -z "${SHARES[$share_name]}" ]; then echo "Neznámé sdílení: $share_name"; return 1; fi
check_config_exists
IFS='|' read -r share_path comment share_type device_name uuid <<< "${SHARES[$share_name]}"
if grep -Fq "[$share_name]" "$CONFIG_FILE"; then echo "Chyba: Sdílení s názvem [$share_name] již existuje."; return 1; fi
if [ ! -d "$share_path" ]; then
echo "Varování: Cesta '$share_path' neexistuje."
read -p "Chcete ji vytvořit? (Y/n): " -n 1 -r; echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
if ! mkdir -p "$share_path"; then echo "Chyba: Nepodařilo se vytvořit adresář."; return 1; fi
echo "Adresář vytvořen."
else
echo "Operace zrušena."; return 1
fi
fi
echo "Přidávám nové sdílení '[$share_name]' do $CONFIG_FILE..."
case "$share_type" in
"home") cat <<EOT >> "$CONFIG_FILE"
[$share_name]
path = $share_path
writable = yes
guest ok = yes
hosts allow = 127.0.0.1 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 100.64.0.0/10
comment = $comment
EOT
;;
"disk")
local dfree_script=$(create_dfree_script "$share_path")
# Zajisti existenci skupiny sambashare
ensure_sambashare_group
# Přidej detekovaného uživatele do sambashare
if ! groups "$DETECTED_USER" 2>/dev/null | grep -q "sambashare"; then
usermod -a -G sambashare "$DETECTED_USER" 2>/dev/null || true
fi
# Nastav filesystem permissions - ONLY FOR SAFE PATHS
if [ -d "$share_path" ] && is_safe_path_for_permissions "$share_path"; then
echo " - Nastavuji oprávnění pro $share_path"
chown -R :sambashare "$share_path" 2>/dev/null || true
chmod -R g+rw "$share_path" 2>/dev/null || true
chmod g+s "$share_path" 2>/dev/null || true
elif [ -d "$share_path" ]; then
echo " - ⚠️ Přeskakuji nastavení oprávnění (systémový adresář)"
echo " - Pro přístup nastavte oprávnění manuálně"
fi
cat <<EOT >> "$CONFIG_FILE"
[$share_name]
path = $share_path
writable = yes
guest ok = no
valid users = $DETECTED_USER
force group = sambashare
create mask = 0664
directory mask = 0775
dfree command = $dfree_script
hosts allow = 127.0.0.1 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 100.64.0.0/10
comment = $comment
EOT
echo "Vytvořen dfree skript: $dfree_script"
;;
*) cat <<EOT >> "$CONFIG_FILE"
[$share_name]
path = $share_path
writable = yes
guest ok = yes
hosts allow = 127.0.0.1 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 100.64.0.0/10
comment = $comment
EOT
;;
esac
if [ $? -eq 0 ]; then echo "Nové sdílení '$share_name' bylo přidáno."; else echo "Chyba při přidávání sdílení."; return 1; fi
}
# Funkce pro smazání sdílení
delete_share() {
local share_name="$1"
if [ -z "$share_name" ]; then echo "Použití: sudo $0 delete <název-sdílení>"; exit 1; fi
check_config_exists
if ! grep -Fq "[$share_name]" "$CONFIG_FILE"; then echo "Chyba: Sdílení [$share_name] nebylo nalezeno."; exit 1; fi
echo "Vytvářím zálohu..."; cp "$CONFIG_FILE" "$BACKUP_FILE"; echo "Mažu sdílení '[$share_name]'..."
awk -v section="[$share_name]" 'BEGIN{p=1} /^\[/{p=($0!=section)} p' "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
local dfree_script="/usr/local/bin/samba-dfree-${share_name#disk-}.sh"
if [ -f "$dfree_script" ]; then echo "Mažu dfree skript: $dfree_script"; rm -f "$dfree_script"; fi
echo "Sdílení '$share_name' bylo smazáno."
}
# Funkce pro zobrazení existujících sdílení
list_shares() {
check_config_exists
echo "Existující sdílení v $CONFIG_FILE:"; echo "=================================="
grep -E "^\[.*\]" "$CONFIG_FILE" | grep -v "\[global\]"
}
# Funkce pro vytvoření všech sdílení včetně automatického mountování disků
create_all() {
echo "=== KOMPLETNÍ NASTAVENÍ VŠECH SDÍLENÍ ==="
echo ""
# 1. Vytvoř statická sdílení
echo "Krok 1/4: Vytvářím statická sdílení..."
create_share "${DETECTED_USER}-home"
echo ""
echo "Krok 2/4: Hledám diskové oddíly..."
discover_mounts verbose
# 2. Zpracuj všechny nalezené disky
local unmounted_disks=()
local mounted_disks=()
for share_name in "${!SHARES[@]}"; do
IFS='|' read -r path comment share_type device_name uuid <<< "${SHARES[$share_name]}"
if [ "$share_type" = "disk" ]; then
if mountpoint -q "$path" 2>/dev/null; then
mounted_disks+=("$share_name|$path|$device_name|$uuid")
else
unmounted_disks+=("$share_name|$path|$device_name|$uuid")
fi
fi
done
echo ""
echo "Krok 3/4: Připojuji nepřipojené disky..."
if [ ${#unmounted_disks[@]} -gt 0 ]; then
for disk_info in "${unmounted_disks[@]}"; do
IFS='|' read -r share_name path device_name uuid <<< "$disk_info"
if [ -z "$uuid" ]; then
echo " ⚠️ Přeskakuji $device_name - nemá UUID"
continue
fi
echo " Připojuji /dev/$device_name na $path..."
# Vytvoř mount point
if [ ! -d "$path" ]; then
mkdir -p "$path"
fi
# Připoj disk
if mount "/dev/$device_name" "$path" 2>/dev/null; then
echo " ✅ Úspěšně připojeno"
# Přidej do fstab
if ! grep -q "$uuid" /etc/fstab 2>/dev/null; then
local fstype=$(lsblk -no FSTYPE "/dev/$device_name" 2>/dev/null)
if [ -n "$fstype" ]; then
echo " Přidávám do /etc/fstab..."
echo "" >> /etc/fstab
echo "# Auto-added by FSA on $(date)" >> /etc/fstab
echo "UUID=$uuid $path $fstype defaults,nofail 0 2" >> /etc/fstab
echo " ✅ Přidáno do /etc/fstab"
fi
fi
mounted_disks+=("$share_name|$path|$device_name|$uuid")
else
echo " ❌ Nepodařilo se připojit"
fi
done
else
echo " Žádné nepřipojené disky k připojení."
fi
echo ""
echo "Krok 4/4: Vytvářím Samba sdílení pro připojené disky..."
if [ ${#mounted_disks[@]} -gt 0 ]; then
for disk_info in "${mounted_disks[@]}"; do
IFS='|' read -r share_name path device_name uuid <<< "$disk_info"
echo " Vytvářím sdílení: $share_name"
create_share "$share_name"
done
else
echo " Žádné připojené disky k nasdílení."
fi
echo ""
echo "✅ HOTOVO! Všechna sdílení byla vytvořena."
}
# Funkce pro automatické vytvoření sdílení pro všechny disky
auto_discover_and_create() {
echo "=== AUTOMATICKÉ VYTVÁŘENÍ SDÍLENÍ PRO PŘIPOJENÉ DISKY ==="
discover_mounts verbose
echo ""
local created_count=0
for share_name in "${!SHARES[@]}"; do
IFS='|' read -r path comment share_type device_name uuid <<< "${SHARES[$share_name]}"
if [ "$share_type" = "disk" ]; then
if mountpoint -q "$path" 2>/dev/null; then
echo "--- Vytvářím sdílení pro připojený disk: $share_name ---"
create_share "$share_name"
created_count=$((created_count + 1))
fi
fi
done
echo ""
if [ $created_count -eq 0 ]; then
echo "Žádné připojené disky k nasdílení."
else
echo "✅ Vytvořeno $created_count sdílení pro připojené disky."
fi
}
# Funkce pro přidání [global] sekce do existující konfigurace
add_global_section() {
echo "=== PŘIDÁVÁNÍ [GLOBAL] SEKCE ==="
if grep -q "^\[global\]" "$CONFIG_FILE"; then echo "Sekce [global] již existuje."; return; fi
echo "Vytvářím zálohu..."; cp "$CONFIG_FILE" "$BACKUP_FILE"; echo "Přidávám [global] na začátek..."
local HOSTNAME=$(hostname | tr '[:lower:]' '[:upper:]')
local GLOBAL_CONFIG=$(cat <<EOT
[global]
workgroup = WORKGROUP
server string = $DISTRO_NAME Server (%v)
netbios name = ${HOSTNAME}-SAMBA
security = user
encrypt passwords = yes
map to guest = Bad User
guest account = nobody
wins support = yes
local master = yes
domain master = yes
preferred master = yes
dns proxy = yes
min protocol = SMB2
max protocol = SMB3
server min protocol = SMB2
bind interfaces only = no
interfaces = lo $DETECTED_INTERFACES
hosts allow = 127.0.0.1 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 100.64.0.0/10
smb ports = 445 8445
socket options = TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=131072 SO_SNDBUF=131072
use sendfile = yes
aio read size = 16384
aio write size = 16384
log file = /var/log/samba/log.%m
max log size = 1000
log level = 1
load printers = no
printcap name = /dev/null
disable spoolss = yes
EOT
)
echo "$GLOBAL_CONFIG" | cat - "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
echo "Sekce [global] byla přidána."
}
# Funkce pro inicializaci kompletní konfigurace
init_config() {
echo "=== INICIALIZACE NOVÉ SMB KONFIGURACE ==="
if [ -f "$CONFIG_FILE" ]; then echo "Zálohuji stávající konfiguraci..."; mv "$CONFIG_FILE" "${CONFIG_FILE}.backup-$(date +%F_%T)"; fi
create_complete_smb_config
create_share "${DETECTED_USER}-home"
read -p "Přidat automaticky všechny nalezené PŘIPOJENÉ disky? (Y/n): " -n 1 -r; echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
auto_discover_and_create
fi
echo "Inicializace dokončena!"
}
# Funkce pro testování a restart
test_and_restart() {
echo ""; echo "Kontroluji syntaxi...";
if ! testparm -s > /dev/null 2>&1; then
echo "CHYBA: Konfigurace Samby je neplatná! Změny nebyly aplikovány."; testparm -s; exit 1
fi
echo "Restartuji služby SMB a NMB..."
# Detekuj správné názvy služeb pro různé distribuce
local SMB_SERVICE=""
local NMB_SERVICE=""
if systemctl list-unit-files | grep -q "^smbd.service"; then
SMB_SERVICE="smbd.service"
NMB_SERVICE="nmbd.service"
elif systemctl list-unit-files | grep -q "^smb.service"; then
SMB_SERVICE="smb.service"
NMB_SERVICE="nmb.service"
elif systemctl list-unit-files | grep -q "^samba.service"; then
SMB_SERVICE="samba.service"
NMB_SERVICE=""
fi
if [ -z "$SMB_SERVICE" ]; then
echo "CHYBA: Nepodařilo se najít Samba službu!"
exit 1
fi
# Aktivuj a restartuj služby
systemctl enable "$SMB_SERVICE" 2>/dev/null
if [ -n "$NMB_SERVICE" ]; then
systemctl enable "$NMB_SERVICE" 2>/dev/null
if systemctl restart "$SMB_SERVICE" "$NMB_SERVICE"; then
echo "Služby SMB úspěšně restartovány."
else
echo "CHYBA: Restart služeb selhal. Zkontrolujte logy pomocí 'journalctl -u $SMB_SERVICE'."
exit 1
fi
else
if systemctl restart "$SMB_SERVICE"; then
echo "Služby SMB úspěšně restartovány."
else
echo "CHYBA: Restart služeb selhal. Zkontrolujte logy pomocí 'journalctl -u $SMB_SERVICE'."
exit 1
fi
fi
echo "Hotovo! ✅"
}
# NOVÁ FUNKCE: Přidá záznam do /etc/fstab
add_to_fstab() {
local device_name="$1"; local mount_path="$2"
local device_info; device_info=$(lsblk -no UUID,FSTYPE "/dev/$device_name"); local uuid; uuid=$(echo "$device_info" | awk '{print $1}'); local fstype; fstype=$(echo "$device_info" | awk '{print $2}')
if [ -z "$uuid" ] || [ -z "$fstype" ]; then echo "Chyba: Nepodařilo se zjistit UUID."; return 1; fi
echo " - UUID: $uuid"; echo " - Typ: $fstype"
if grep -q "$uuid" /etc/fstab; then echo "Varování: Záznam pro UUID již existuje."; return 0; fi
local fstab_line="UUID=$uuid $mount_path $fstype defaults,nofail 0 2"
echo "Přidávám do /etc/fstab:"; echo " $fstab_line"
echo "" >> /etc/fstab; echo "# Přidáno skriptem spravuj_sdileni.sh dne $(date)" >> /etc/fstab; echo "$fstab_line" >> /etc/fstab
if [ $? -eq 0 ]; then echo "✅ Záznam úspěšně přidán."; else echo "Chyba: Zápis do /etc/fstab selhal."; return 1; fi
}
# NOVÁ FUNKCE: Interaktivně připojí a nasdílí disk
interactive_mount_and_create() {
local mode="$1"
echo "=== INTERAKTIVNÍ PŘIPOJENÍ A SDÍLENÍ DISKU ==="
local unmounted_partitions=()
while read -r name label uuid mountpoint; do
if [ -z "$mountpoint" ]; then
name=$(echo "$name" | sed 's/^[├└]─//g' | sed 's/ //g')
local identifier="${label:-$name}"
unmounted_partitions+=("$name|$identifier")
fi
done < <(lsblk -no NAME,LABEL,UUID,MOUNTPOINT -e7,1)
if [ ${#unmounted_partitions[@]} -eq 0 ]; then echo "Nenalezeny žádné nepřipojené diskové oddíly."; exit 0; fi
echo "Který oddíl chcete připojit a sdílet?"
local i=1
for info in "${unmounted_partitions[@]}"; do
IFS='|' read -r name identifier <<< "$info"; echo " $i) $name (Popisek: $identifier)"; i=$((i+1));
done
echo " 0) Zrušit"
local choice
read -p "Zadejte číslo: " choice
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 0 ] || [ "$choice" -gt ${#unmounted_partitions[@]} ]; then echo "Chyba: Neplatná volba."; exit 1; fi
if [ "$choice" -eq 0 ]; then echo "Operace zrušena."; exit 0; fi
IFS='|' read -r selected_device selected_identifier <<< "${unmounted_partitions[$((choice-1))]}"
local clean_identifier; clean_identifier=$(echo "$selected_identifier" | sed 's/[^a-zA-Z0-9_-]//g')
if [ -z "$clean_identifier" ]; then
clean_identifier="$selected_device"
fi
local default_mount_path="/mnt/$clean_identifier"
read -p "Zadejte cestu pro připojení [navrženo: $default_mount_path]: " mount_path
mount_path="${mount_path:-$default_mount_path}"
if [ ! -d "$mount_path" ]; then echo "Vytvářím adresář $mount_path..."; if ! mkdir -p "$mount_path"; then echo "Chyba: Nelze vytvořit adresář."; exit 1; fi; fi
echo "Připojuji /dev/$selected_device na $mount_path..."
if ! mount "/dev/$selected_device" "$mount_path"; then echo "CHYBA: Nepodařilo se připojit disk! (mount /dev/$selected_device)"; exit 1; fi
echo "✅ Disk úspěšně připojen."
if [ "$mode" == "always" ]; then
echo ""; add_to_fstab "$selected_device" "$mount_path"
else
echo "INFO: Disk je připojen pouze dočasně."
fi
local share_name="disk-$clean_identifier"
echo ""; echo "Nyní vytvářím Samba sdílení s názvem '$share_name'..."
# Získej UUID pro správnou identifikaci
local device_uuid=$(lsblk -no UUID "/dev/$selected_device" 2>/dev/null)
SHARES["$share_name"]="$mount_path|Oddíl $selected_identifier|disk|$selected_device|$device_uuid"
create_share "$share_name"
}
# =============================================================================
# USER MANAGEMENT FUNCTIONS
# =============================================================================
# Funkce pro zobrazení všech Samba uživatelů
list_samba_users() {
echo "=== SEZNAM SAMBA UŽIVATELŮ ==="
if ! command -v pdbedit &> /dev/null; then
echo "Příkaz 'pdbedit' není k dispozici. Nemohu zobrazit uživatele."
return 1
fi
local users=$(pdbedit -L 2>/dev/null)
if [ -z "$users" ]; then
echo "Žádní Samba uživatelé nejsou nastaveni."
else
echo "$users"
fi
}
# Funkce pro vytvoření nového Samba uživatele
create_samba_user() {
local username="$1"
if [ -z "$username" ]; then
echo -n "Zadejte uživatelské jméno: "
read username
fi
if [ -z "$username" ]; then
echo "Chyba: Uživatelské jméno je povinné."
return 1
fi
# Zkontroluj jestli systémový uživatel existuje
if ! id "$username" &>/dev/null; then
echo "Systémový uživatel '$username' neexistuje."
read -p "Chcete vytvořit systémového uživatele? (Y/n): " -n 1 -r; echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
useradd -M -s /sbin/nologin "$username"
if [ $? -ne 0 ]; then
echo "Chyba: Nepodařilo se vytvořit systémového uživatele."
return 1
fi
echo "✅ Systémový uživatel '$username' byl vytvořen."
else
echo "Operace zrušena."
return 1
fi
fi
# Přidej uživatele do Samby
echo "Nastavte Samba heslo pro uživatele '$username':"
if smbpasswd -a "$username"; then
echo "✅ Samba uživatel '$username' byl úspěšně vytvořen."
# Nabídni možnost přiřadit přístup ke sdílením
echo ""
read -p "Chcete nastavit přístup ke sdílením? (Y/n): " -n 1 -r; echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
configure_user_shares "$username"
fi
else
echo "Chyba: Vytvoření Samba uživatele selhalo."
return 1
fi
}
# Funkce pro smazání Samba uživatele
delete_samba_user() {
local username="$1"
if [ -z "$username" ]; then
echo -n "Zadejte uživatelské jméno k odstranění: "
read username
fi
if [ -z "$username" ]; then
echo "Chyba: Uživatelské jméno je povinné."
return 1
fi
# Zkontroluj jestli Samba uživatel existuje
if ! pdbedit -L 2>/dev/null | grep -q "^$username:"; then
echo "Chyba: Samba uživatel '$username' neexistuje."
return 1
fi
echo "VAROVÁNÍ: Chystáte se odstranit Samba uživatele '$username'"
read -p "Jste si jistí? (y/N): " -n 1 -r; echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
if smbpasswd -x "$username"; then
echo "✅ Samba uživatel '$username' byl odstraněn."
read -p "Odstranit také systémového uživatele? (y/N): " -n 1 -r; echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
userdel "$username"
echo "✅ Systémový uživatel '$username' byl odstraněn."
fi
else
echo "Chyba: Odstranění Samba uživatele selhalo."
return 1
fi
else
echo "Operace zrušena."
fi
}
# Funkce pro nastavení přístupu uživatele ke sdílením
configure_user_shares() {
local username="$1"
if [ -z "$username" ]; then
echo -n "Zadejte uživatelské jméno: "
read username
fi
if [ -z "$username" ]; then
echo "Chyba: Uživatelské jméno je povinné."
return 1
fi
# Zkontroluj jestli Samba uživatel existuje
if ! pdbedit -L 2>/dev/null | grep -q "^$username:"; then
echo "Chyba: Samba uživatel '$username' neexistuje."
echo "Použijte 'user-create' pro vytvoření uživatele."
return 1
fi
check_config_exists
# Zajisti existenci skupiny sambashare
ensure_sambashare_group
# Přidej uživatele do skupiny sambashare
if ! groups "$username" 2>/dev/null | grep -q "sambashare"; then
echo "Přidávám uživatele '$username' do skupiny 'sambashare'..."
usermod -a -G sambashare "$username"
fi
# Získej seznam sdílení
local shares=($(grep -E "^\[.*\]" "$CONFIG_FILE" | grep -v "\[global\]" | sed 's/\[\(.*\)\]/\1/'))
if [ ${#shares[@]} -eq 0 ]; then
echo "Žádná sdílení nejsou nakonfigurována."
return 1
fi
echo "=== NASTAVENÍ PŘÍSTUPU PRO UŽIVATELE '$username' ==="
echo "Dostupná sdílení:"
local i=1
for share in "${shares[@]}"; do
echo " $i) $share"
i=$((i+1))
done
echo ""
echo "Zadejte čísla sdílení oddělená mezerou (např: 1 3 4)"
echo -n "Nebo stiskněte Enter pro přístup ke všem: "
read selection
local selected_shares=()
if [ -z "$selection" ]; then
selected_shares=("${shares[@]}")
else
for num in $selection; do
if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#shares[@]} ]; then
selected_shares+=("${shares[$((num-1))]}")
fi
done
fi
if [ ${#selected_shares[@]} -eq 0 ]; then
echo "Žádná sdílení nebyla vybrána."
return 1
fi
echo ""
echo "Vytvářím zálohu..."
cp "$CONFIG_FILE" "$BACKUP_FILE"
# Přidej uživatele do valid users pro vybraná sdílení
for share in "${selected_shares[@]}"; do
echo "Nastavuji přístup k sdílení: [$share]"
# Získej cestu ke sdílení
local share_path=$(grep -A 10 "^\[$share\]" "$CONFIG_FILE" | grep "^ path = " | head -1 | sed 's/^ path = //')
if [ -n "$share_path" ] && [ -d "$share_path" ]; then
# CRITICAL SAFETY: Only modify permissions on safe paths
if is_safe_path_for_permissions "$share_path"; then
echo " - Nastavuji filesystem oprávnění pro $share_path"
chown -R :sambashare "$share_path" 2>/dev/null || true
chmod -R g+rw "$share_path" 2>/dev/null || true
chmod g+s "$share_path" 2>/dev/null || true # SetGID bit
else
echo " - ⚠️ PŘESKAKUJI: $share_path je systémový adresář"
echo " - Nastavte oprávnění manuálně pokud je potřeba"
fi
fi
# Zkontroluj jestli sdílení má valid users
if grep -A 10 "^\[$share\]" "$CONFIG_FILE" | grep -q "^ valid users ="; then
# Přidej uživatele k existujícímu valid users
sed -i "/^\[$share\]/,/^\[/ s/^ valid users = \(.*\)/ valid users = \1 $username/" "$CONFIG_FILE"
else
# Přidej nový řádek valid users
sed -i "/^\[$share\]/a\ valid users = $username" "$CONFIG_FILE"
fi
# Odstraň force user pokud existuje (pro multi-user přístup)
if grep -A 10 "^\[$share\]" "$CONFIG_FILE" | grep -q "^ force user ="; then
echo " - Odstraňuji 'force user' pro podporu více uživatelů"
sed -i "/^\[$share\]/,/^\[/ s/^ force user = .*$/ \# force user removed for multi-user access/" "$CONFIG_FILE"
fi
# Nastav force group na sambashare
if grep -A 10 "^\[$share\]" "$CONFIG_FILE" | grep -q "^ force group ="; then
sed -i "/^\[$share\]/,/^\[/ s/^ force group = .*/ force group = sambashare/" "$CONFIG_FILE"
else
sed -i "/^\[$share\]/a\ force group = sambashare" "$CONFIG_FILE"
fi
# Vypni guest ok pokud je zapnutý
sed -i "/^\[$share\]/,/^\[/ s/^ guest ok = yes/ guest ok = no/" "$CONFIG_FILE"
echo " ✅ Přístup nastaven"
done
echo ""
echo "✅ Konfigurace dokončena pro sdílení: ${selected_shares[*]}"
echo " Uživatel '$username' byl přidán do skupiny 'sambashare'"
}
# Funkce pro odebrání přístupu uživatele ke sdílení
revoke_share_access() {
local username="$1"
local share_name="$2"
if [ -z "$username" ]; then
echo -n "Zadejte uživatelské jméno: "
read username
fi
if [ -z "$share_name" ]; then
echo -n "Zadejte název sdílení: "
read share_name
fi
if [ -z "$username" ] || [ -z "$share_name" ]; then
echo "Chyba: Uživatelské jméno a název sdílení jsou povinné."
return 1
fi
check_config_exists
if ! grep -Fq "[$share_name]" "$CONFIG_FILE"; then
echo "Chyba: Sdílení [$share_name] nebylo nalezeno."
return 1
fi
echo "Vytvářím zálohu..."
cp "$CONFIG_FILE" "$BACKUP_FILE"
# Odeber uživatele z valid users
sed -i "/^\[$share_name\]/,/^\[/ s/\(valid users = .*\) $username/\1/" "$CONFIG_FILE"
sed -i "/^\[$share_name\]/,/^\[/ s/valid users = $username \(.*\)/valid users = \1/" "$CONFIG_FILE"
sed -i "/^\[$share_name\]/,/^\[/ s/valid users = $username$//" "$CONFIG_FILE"
echo "✅ Přístup uživatele '$username' ke sdílení '$share_name' byl odebrán."
}
# --- Hlavní logika skriptu (Router) ---
COMMAND="$1"
ARGUMENT1="$2"
ARGUMENT2="$3"
# Před spuštěním hlavního příkazu vždy aktualizujeme seznam disků
discover_mounts
# Zpracování parametru --mode=always, který může být na druhé nebo třetí pozici
MODE=""
if [ "$ARGUMENT1" == "--mode=always" ] || [ "$ARGUMENT2" == "--mode=always" ]; then
MODE="always"
fi
# Hlavní argument je ten, který není --mode=always
MAIN_ARG="$ARGUMENT1"
if [ "$ARGUMENT1" == "--mode=always" ]; then
MAIN_ARG="$ARGUMENT2"
fi
case "$COMMAND" in
mount-share)
interactive_mount_and_create "$MODE"
test_and_restart
;;
create)
create_share "$MAIN_ARG"
test_and_restart
;;
delete)
delete_share "$MAIN_ARG"
test_and_restart
;;
list)
list_shares
;;
create-all)
create_all
test_and_restart
;;
auto-disks)
auto_discover_and_create
test_and_restart
;;
discover)
discover_mounts verbose
echo ""
echo "Dostupná sdílení pro 'create':"
for name in "${!SHARES[@]}"; do
IFS='|' read -r path comment type device_name uuid <<< "${SHARES[$name]}"
if [ "$type" = "disk" ]; then
echo " - $name (cesta: $path, zařízení: /dev/$device_name)"
else
echo " - $name (cesta: $path)"
fi
done
;;
init)
init_config
test_and_restart
;;
add-global)
add_global_section
test_and_restart
;;
user-create|user-add)
create_samba_user "$MAIN_ARG"
test_and_restart
;;
user-delete|user-remove)
delete_samba_user "$MAIN_ARG"
;;
user-list)
list_samba_users
;;
user-access)
configure_user_shares "$MAIN_ARG"
test_and_restart
;;
user-revoke)
revoke_share_access "$MAIN_ARG" "$ARGUMENT2"
test_and_restart
;;
*)
echo "FSA - Universal Samba Share Manager"
echo "===================================="
echo "Použití: sudo $0 {příkaz} [argument]"
echo ""
echo "Hlavní příkazy:"
echo " mount-share [--mode=always] - Interaktivně připojí a nasdílí nepřipojený disk."
echo " init - Smaže starou a vytvoří novou, čistou konfiguraci."
echo " add-global - Přidá [global] sekci do existující konfigurace."
echo ""
echo "Správa sdílení:"
echo " discover - Najde a zobrazí všechny dostupné disky."
echo " auto-disks - Vytvoří sdílení pro všechny AKTUÁLNĚ PŘIPOJENÉ disky."
echo " create <název> - Vytvoří sdílení."
echo " create-all - Vytvoří statická sdílení (home, root)."
echo " delete <název> - Smaže existující sdílení."
echo " list - Vypíše nakonfigurovaná sdílení."
echo ""
echo "Správa uživatelů:"
echo " user-create [jméno] - Vytvoří nového Samba uživatele s heslem."
echo " user-delete [jméno] - Odstraní Samba uživatele."
echo " user-list - Vypíše všechny Samba uživatele."
echo " user-access [jméno] - Nastaví přístup uživatele ke sdílením."
echo " user-revoke <jméno> <share> - Odebere přístup uživatele ke sdílení."
exit 1
;;
esac