update
This commit is contained in:
176
ma-sync
176
ma-sync
@@ -1,58 +1,146 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
remove=0
|
||||
if [[ "$1" == "--remove" ]]; then
|
||||
remove=1
|
||||
fi
|
||||
SRC_ROOT="$HOME/Documents/2026/projects"
|
||||
DST_ROOT="$HOME/projects"
|
||||
SUBDIR="doc"
|
||||
FSTAB="/etc/fstab"
|
||||
|
||||
source="$(pwd)"
|
||||
name="$(basename "$source")"
|
||||
target="$HOME/projects/$name/doc"
|
||||
die() { echo "Fehler: $*" >&2; exit 1; }
|
||||
|
||||
if [[ "$source" == "$HOME" ]]; then
|
||||
echo "Fehler: \$HOME selbst kann nicht gemountet werden." >&2
|
||||
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
|
||||
fi
|
||||
if [[ -z "$name" || "$name" == "/" ]]; then
|
||||
echo "Fehler: ungültiger Ordnername." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$source" == "$target" ]]; then
|
||||
echo "Fehler: Source und Target sind identisch." >&2
|
||||
exit 1
|
||||
fi
|
||||
case "$target/" in "$source"/*) echo "Fehler: Target liegt unter Source (Rekursionsgefahr)." >&2; exit 1;; esac
|
||||
case "$source/" in "$target"/*) echo "Fehler: Source liegt unter Target (Rekursionsgefahr)." >&2; exit 1;; esac
|
||||
}
|
||||
|
||||
fstab_line="$source $target none bind,nofail 0 0"
|
||||
fstab_pattern="\\# $target #d"
|
||||
safe_umount() {
|
||||
local target="$1"
|
||||
is_mounted "$target" || return 0
|
||||
sudo umount "$target" 2>/dev/null || abort_busy "$target"
|
||||
}
|
||||
|
||||
if (( remove )); then
|
||||
if mountpoint -q "$target"; then
|
||||
sudo umount "$target"
|
||||
cp -a "$source/." "$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
|
||||
sudo sed -i "$fstab_pattern" /etc/fstab
|
||||
|
||||
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 "Entfernt: $target"
|
||||
exit 0
|
||||
fi
|
||||
echo "✓ $removed Mount(s) entfernt, Inhalte kopiert, fstab bereinigt."
|
||||
}
|
||||
|
||||
mkdir -p "$target"
|
||||
mode_mount() {
|
||||
[[ -d "$SRC_ROOT" ]] || die "Source-Root '$SRC_ROOT' existiert nicht."
|
||||
|
||||
if ! mountpoint -q "$target" && [[ -n "$(ls -A "$target" 2>/dev/null)" ]]; then
|
||||
echo "Target nicht leer — wird durch Source überschrieben: $target" >&2
|
||||
find "$target" -mindepth 1 -delete
|
||||
fi
|
||||
local mounted=0 skipped=0 remounted=0
|
||||
|
||||
if mountpoint -q "$target"; then
|
||||
sudo umount "$target"
|
||||
fi
|
||||
sudo sed -i "$fstab_pattern" /etc/fstab
|
||||
shopt -s nullglob
|
||||
local src name target
|
||||
for src in "$SRC_ROOT"/*/; do
|
||||
src="${src%/}"
|
||||
name=$(basename "$src")
|
||||
target="$DST_ROOT/$name/$SUBDIR"
|
||||
|
||||
sudo mount --bind "$source" "$target"
|
||||
echo "$fstab_line" | sudo tee -a /etc/fstab > /dev/null
|
||||
sudo systemctl daemon-reload
|
||||
# Sicherheitschecks
|
||||
[[ -z "$name" || "$name" == "/" ]] && continue
|
||||
[[ "$src" == "$HOME" ]] && continue
|
||||
[[ "$src" == "$target" ]] && continue
|
||||
case "$target/" in "$src"/*) continue;; esac
|
||||
case "$src/" in "$target"/*) continue;; esac
|
||||
|
||||
echo "Gespiegelt: $source ↔ $target"
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user