147 lines
4.2 KiB
Bash
Executable File
147 lines
4.2 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
IFS=$'\n\t'
|
|
|
|
SRC_ROOT="$HOME/Documents/2026/projects"
|
|
DST_ROOT="$HOME/projects"
|
|
SUBDIR="doc"
|
|
FSTAB="/etc/fstab"
|
|
|
|
die() { echo "Fehler: $*" >&2; exit 1; }
|
|
|
|
is_mounted() { findmnt -n "$1" >/dev/null 2>&1; }
|
|
|
|
bind_source_of() {
|
|
awk -v t="$1" '$5 == t {print $4; exit}' /proc/self/mountinfo
|
|
}
|
|
|
|
abort_busy() {
|
|
local target="$1"
|
|
echo "Fehler: '$target' ist belegt — kann nicht ausgehängt werden." >&2
|
|
echo "Folgende Prozesse halten das Verzeichnis offen:" >&2
|
|
lsof +D "$target" 2>/dev/null | head -20 >&2 || true
|
|
exit 1
|
|
}
|
|
|
|
safe_umount() {
|
|
local target="$1"
|
|
is_mounted "$target" || return 0
|
|
sudo umount "$target" 2>/dev/null || abort_busy "$target"
|
|
}
|
|
|
|
remove_fstab_targets() {
|
|
(( $# > 0 )) || return 0
|
|
local tmp list
|
|
tmp=$(mktemp)
|
|
list=$(printf '%s\n' "$@")
|
|
awk -v targets="$list" '
|
|
BEGIN { n = split(targets, arr, "\n"); for (i in arr) if (arr[i] != "") skip[arr[i]] = 1 }
|
|
!($2 in skip) { print }
|
|
' "$FSTAB" > "$tmp"
|
|
sudo install -m 644 -o root -g root "$tmp" "$FSTAB"
|
|
rm -f "$tmp"
|
|
}
|
|
|
|
append_fstab() {
|
|
echo "$1 $2 none bind,nofail 0 0" | sudo tee -a "$FSTAB" > /dev/null
|
|
}
|
|
|
|
fstab_bind_targets_under() {
|
|
local prefix="$1"
|
|
awk -v p="$prefix/" '
|
|
$3 == "none" && $4 ~ /(^|,)bind(,|$)/ && index($2, p) == 1 { print $1 "\t" $2 }
|
|
' "$FSTAB"
|
|
}
|
|
|
|
mode_remove() {
|
|
local entries=()
|
|
mapfile -t entries < <(fstab_bind_targets_under "$DST_ROOT")
|
|
if (( ${#entries[@]} == 0 )); then
|
|
echo "Keine ma-sync Mounts in fstab gefunden."
|
|
return 0
|
|
fi
|
|
|
|
local targets=() removed=0 src target entry
|
|
for entry in "${entries[@]}"; do
|
|
IFS=$'\t' read -r src target <<< "$entry"
|
|
echo "→ Entferne: $target (Source: $src)"
|
|
safe_umount "$target"
|
|
if [[ -d "$src" ]]; then
|
|
cp -a "$src/." "$target/"
|
|
else
|
|
echo " Warnung: Source '$src' existiert nicht mehr — Target bleibt leer." >&2
|
|
fi
|
|
targets+=("$target")
|
|
removed=$((removed + 1))
|
|
done
|
|
|
|
remove_fstab_targets "${targets[@]}"
|
|
sudo systemctl daemon-reload
|
|
echo "✓ $removed Mount(s) entfernt, Inhalte kopiert, fstab bereinigt."
|
|
}
|
|
|
|
mode_mount() {
|
|
[[ -d "$SRC_ROOT" ]] || die "Source-Root '$SRC_ROOT' existiert nicht."
|
|
|
|
local mounted=0 skipped=0 remounted=0
|
|
|
|
shopt -s nullglob
|
|
local src name target
|
|
for src in "$SRC_ROOT"/*/; do
|
|
src="${src%/}"
|
|
name=$(basename "$src")
|
|
target="$DST_ROOT/$name/$SUBDIR"
|
|
|
|
# Sicherheitschecks
|
|
[[ -z "$name" || "$name" == "/" ]] && continue
|
|
[[ "$src" == "$HOME" ]] && continue
|
|
[[ "$src" == "$target" ]] && continue
|
|
case "$target/" in "$src"/*) continue;; esac
|
|
case "$src/" in "$target"/*) continue;; esac
|
|
|
|
# Nur mounten, wenn das Target-Parent-Verzeichnis (mit gleichem Namen) bereits existiert
|
|
if [[ ! -d "$DST_ROOT/$name" ]]; then
|
|
echo "↷ kein Target-Container: $DST_ROOT/$name fehlt → übersprungen"
|
|
skipped=$((skipped + 1))
|
|
continue
|
|
fi
|
|
|
|
if is_mounted "$target"; then
|
|
local cur
|
|
cur=$(bind_source_of "$target")
|
|
if [[ "$cur" == "$src" ]]; then
|
|
echo "↻ skip (bereits korrekt): $target"
|
|
skipped=$((skipped + 1))
|
|
continue
|
|
fi
|
|
echo "↺ Re-mount: $target (war: $cur, neu: $src)"
|
|
safe_umount "$target"
|
|
remounted=$((remounted + 1))
|
|
fi
|
|
|
|
mkdir -p "$target"
|
|
if [[ -n "$(ls -A "$target" 2>/dev/null)" ]]; then
|
|
echo " Target '$target' nicht leer — wird geleert." >&2
|
|
find "$target" -mindepth 1 -delete
|
|
fi
|
|
|
|
remove_fstab_targets "$target"
|
|
sudo mount --bind "$src" "$target"
|
|
append_fstab "$src" "$target"
|
|
echo "✓ gemountet: $src → $target"
|
|
mounted=$((mounted + 1))
|
|
done
|
|
shopt -u nullglob
|
|
|
|
sudo systemctl daemon-reload
|
|
|
|
echo ""
|
|
echo "Zusammenfassung: $mounted gemountet (davon $remounted Re-mount), $skipped übersprungen."
|
|
}
|
|
|
|
case "${1:-}" in
|
|
--remove) mode_remove ;;
|
|
"") mode_mount ;;
|
|
*) die "Unbekanntes Argument: '$1' (erwartet: --remove oder leer)" ;;
|
|
esac
|