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