Files
ProxMenux/scripts/share/local-shared-manager.sh
T
2026-04-12 20:32:34 +02:00

248 lines
8.8 KiB
Bash

#!/bin/bash
# ==========================================================
# ProxMenux - Local Shared Directory Manager
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT
# Version : 1.0
# Last Updated: 08/04/2026
# ==========================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCAL_SCRIPTS_LOCAL="$(cd "$SCRIPT_DIR/.." && pwd)"
LOCAL_SCRIPTS_DEFAULT="/usr/local/share/proxmenux/scripts"
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_DEFAULT"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
if [[ -f "$LOCAL_SCRIPTS_LOCAL/utils.sh" ]]; then
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_LOCAL"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
elif [[ ! -f "$UTILS_FILE" ]]; then
UTILS_FILE="$BASE_DIR/utils.sh"
fi
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
exit 1
fi
load_language
initialize_cache
if ! command -v pveversion >/dev/null 2>&1; then
dialog --backtitle "ProxMenux" --title "$(translate "Error")" \
--msgbox "$(translate "This script must be run on a Proxmox host.")" 8 60
exit 1
fi
# ==========================================================
lsm_apply_multi_unpriv_permissions() {
local dir="$1"
[[ -z "$dir" || ! -d "$dir" ]] && return 1
# root:root ownership — no new group needed.
chown root:root "$dir" 2>/dev/null || true
# 1777 = sticky bit (prevents cross-container file deletion) + world-rwx.
# Unprivileged LXC UIDs (100000+) appear as 'others' on the host,
# so 'o+rwx' is what grants them read+write access.
chmod 1777 "$dir" 2>/dev/null || true
# Ensure existing content is readable/writable regardless of UID mapping.
chmod -R a+rwX "$dir" 2>/dev/null || true
find "$dir" -type d -exec chmod 1777 {} + 2>/dev/null || true
if command -v setfacl >/dev/null 2>&1; then
# Remove restrictive ACLs and enforce permissive inheritance for new files.
setfacl -b -R "$dir" 2>/dev/null || true
setfacl -R -m u::rwx,g::rwx,o::rwx,m::rwx "$dir" 2>/dev/null || true
setfacl -R -m d:u::rwx,d:g::rwx,d:o::rwx,d:m::rwx "$dir" 2>/dev/null || true
fi
return 0
}
# Returns a free name like /mnt/shared, /mnt/shared2, /mnt/shared3 …
lsm_next_free_name() {
local base="${1:-shared}"
local candidate="/mnt/$base"
[[ ! -d "$candidate" ]] && echo "$candidate" && return
local n=2
while [[ -d "/mnt/${base}${n}" ]]; do
((n++))
done
echo "/mnt/${base}${n}"
}
lsm_list_mnt_folders() {
show_proxmenux_logo
msg_title "$(translate "Folders in /mnt")"
echo "=================================================="
if [[ ! -d /mnt ]] || [[ -z "$(ls -A /mnt 2>/dev/null)" ]]; then
echo ""
echo -e "${TAB}$(translate "No folders found in /mnt.")"
else
local found=false
while IFS= read -r dir; do
[[ ! -d "$dir" ]] && continue
found=true
local perms owner
perms=$(stat -c "%a" "$dir" 2>/dev/null)
owner=$(stat -c "%U:%G" "$dir" 2>/dev/null)
echo ""
echo -e "${TAB}${BGN}$(translate "Directory:")${CL} ${BL}$dir${CL}"
echo -e "${TAB}${BGN}$(translate "Permissions:")${CL} ${BL}${perms} $(stat -c "(%A)" "$dir" 2>/dev/null)${CL}"
echo -e "${TAB}${BGN}$(translate "Owner:")${CL} ${BL}${owner}${CL}"
done < <(find /mnt -mindepth 1 -maxdepth 1 -type d | sort)
if [[ "$found" = false ]]; then
echo ""
echo -e "${TAB}$(translate "No folders found in /mnt.")"
fi
fi
echo ""
echo "=================================================="
echo ""
# Summary of /mnt available space
if mountpoint -q /mnt 2>/dev/null || [[ -d /mnt ]]; then
local mnt_avail mnt_total
mnt_avail=$(df -h /mnt 2>/dev/null | awk 'NR==2{print $4}')
mnt_total=$(df -h /mnt 2>/dev/null | awk 'NR==2{print $2}')
if [[ -n "$mnt_avail" ]]; then
echo -e "${TAB}${BGN}$(translate "Available space in /mnt:")${CL} ${BL}${mnt_avail} $(translate "of") ${mnt_total}${CL}"
echo ""
fi
fi
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
# Result is stored in LSM_SELECTED_MOUNT_POINT (not echoed) to avoid subshell issues
LSM_SELECTED_MOUNT_POINT=""
lsm_select_host_mount_point_dialog() {
local title="${1:-$(translate "Select Shared Directory Location")}"
local base_name="${2:-shared}"
local choice folder_name result mount_point
LSM_SELECTED_MOUNT_POINT=""
# Auto-suggest a free name in /mnt
local suggested
suggested=$(lsm_next_free_name "$base_name")
while true; do
choice=$(dialog --backtitle "ProxMenux" \
--title "$title" \
--menu "\n$(translate "Where do you want the host folder?")" 16 72 4 \
"1" "$(translate "Create new folder in /mnt")" \
"2" "$(translate "Enter custom path")" \
"3" "$(translate "View existing folders in /mnt")" \
"4" "$(translate "Cancel")" \
3>&1 1>&2 2>&3) || return 1
case "$choice" in
1)
folder_name=$(dialog --backtitle "ProxMenux" \
--title "$(translate "Folder Name")" \
--inputbox "\n$(translate "Enter folder name for /mnt:")" 10 70 "$(basename "$suggested")" \
3>&1 1>&2 2>&3) || continue
[[ -z "$folder_name" ]] && continue
mount_point="/mnt/$folder_name"
# Only warn if the user manually typed an existing name
if [[ -d "$mount_point" ]]; then
if ! dialog --backtitle "ProxMenux" --title "$(translate "Directory Exists")" \
--yesno "\n$(translate "Directory already exists. Continue with permission setup?")" 8 70; then
continue
fi
fi
;;
2)
result=$(dialog --backtitle "ProxMenux" \
--title "$(translate "Custom Path")" \
--inputbox "\n$(translate "Enter full path:")" 10 80 "" \
3>&1 1>&2 2>&3) || continue
[[ -z "$result" ]] && continue
mount_point="$result"
if [[ -d "$mount_point" ]]; then
if ! dialog --backtitle "ProxMenux" --title "$(translate "Directory Exists")" \
--yesno "\n$(translate "Directory already exists. Continue with permission setup?")" 8 70; then
continue
fi
fi
;;
3)
lsm_list_mnt_folders
# Refresh suggestion after viewing
suggested=$(lsm_next_free_name "$base_name")
continue
;;
4) return 1 ;;
*) continue ;;
esac
if [[ ! "$mount_point" =~ ^/ ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "Invalid Path")" \
--msgbox "\n$(translate "Path must be absolute (start with /).")" 8 60
continue
fi
LSM_SELECTED_MOUNT_POINT="$mount_point"
return 0
done
}
create_shared_directory() {
lsm_select_host_mount_point_dialog "$(translate "Select Shared Directory Location")" "shared"
[[ -z "$LSM_SELECTED_MOUNT_POINT" ]] && return
SHARED_DIR="$LSM_SELECTED_MOUNT_POINT"
show_proxmenux_logo
msg_title "$(translate "Create Shared Directory")"
if ! mkdir -p "$SHARED_DIR" 2>/dev/null; then
msg_error "$(translate "Failed to create directory:") $SHARED_DIR"
echo ""
msg_success "$(translate "Press Enter to continue...")"
read -r
return 1
fi
msg_ok "$(translate "Directory created:") $SHARED_DIR"
lsm_apply_multi_unpriv_permissions "$SHARED_DIR"
pmx_share_map_set "$SHARED_DIR" "open"
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Shared Directory Ready:")${CL}"
echo -e "${TAB}${BGN}$(translate "Directory:")${CL} ${BL}$SHARED_DIR${CL}"
echo -e "${TAB}${BGN}$(translate "Permissions:")${CL} ${BL}1777 (rwxrwxrwt)${CL}"
echo -e "${TAB}${BGN}$(translate "Owner:")${CL} ${BL}root:root${CL}"
echo -e "${TAB}${BGN}$(translate "Access profile:")${CL} ${BL}$(translate "Compatible with privileged and unprivileged LXC containers")${CL}"
echo -e "${TAB}${BGN}$(translate "ACL Status:")${CL} ${BL}$(translate "Open rwx + default inheritance for new files")${CL}"
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
create_shared_directory