alpinux.presentations/slides/sauvegardes/script/tester-sans-sudo.sh
Cédrix a04a72054a
Ajouter la sauvegarde automatique sur clé USB
Dès qu'on branche la clé déclarée (reconnue par son UUID), udev demande
à systemd de lancer une sauvegarde rsync des dossiers choisis. Ce qui a
disparu de l'ordinateur est retiré de la clé, mais conservé 30 jours dans
une corbeille datée sur la clé. Notifications de bureau, journal systemd,
garde-fous contre une destination erronée. Installateur, tests sans sudo
et mode d'emploi fournis.

Refs #1
2026-11-05 17:44:54 +01:00

167 lines
8.1 KiB
Bash
Executable file

#!/usr/bin/env bash
# =============================================================================
# tester-sans-sudo.sh — vérifie la logique de sauvegarde, sans clé ni sudo
# -----------------------------------------------------------------------------
# Utilise le mode test de sauvegarde-cle-usb (SAUVEGARDE_TEST_DESTINATION) :
# un dossier temporaire joue le rôle de la clé. Rien n'est monté, rien n'est
# écrit dans /etc. Usage : ./tester-sans-sudo.sh [dossier-de-travail]
# =============================================================================
# shellcheck disable=SC2016 # les bash -c reçoivent leurs arguments via $1, voulu
set -euo pipefail
ICI="$(cd "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT="$ICI/sauvegarde-cle-usb"
BASE="${1:-$(mktemp -d)}"
TRAVAIL="$BASE/essai-$$"
SRC="$TRAVAIL/maison/marie"
CLE="$TRAVAIL/cle"
CONF="$TRAVAIL/test.conf"
export TMPDIR="$TRAVAIL"
REUSSIS=0
RATES=0
ok() { echo " OK $*"; REUSSIS=$((REUSSIS + 1)); }
rate() { echo " RATÉ $*"; RATES=$((RATES + 1)); }
verifier() { local desc="$1"; shift; if "$@"; then ok "$desc"; else rate "$desc"; fi; }
ecrire_conf() { # $1 = CORBEILLE_JOURS, $2 = DOSSIER_DESTINATION, $3.. = sources
local jours="$1" dest="$2"; shift 2
{
echo "UUID=\"TEST\""
echo "UTILISATEUR=\"$(id -un)\""
printf 'SOURCES=('; printf ' "%s"' "$@"; echo ' )'
echo "DOSSIER_DESTINATION=\"$dest\""
echo "CORBEILLE_JOURS=$jours"
echo 'EXCLUSIONS=( ".cache/" ".local/share/Trash/" "*.tmp" )'
echo 'VERIFIER_ESPACE="oui"'
} > "$CONF"
}
lancer() { # lance le script en mode test ; sortie dans $TRAVAIL/sortie.txt
sleep 1 # horodatage à la seconde : une corbeille par lancement
set +e
SAUVEGARDE_CONF="$CONF" SAUVEGARDE_TEST_DESTINATION="${DEST_TEST:-$CLE}" \
"$SCRIPT" "$@" > "$TRAVAIL/sortie.txt" 2>&1
CODE=$?
set -e
}
nb_corbeilles() { find "$CLE/.corbeille" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l; }
mkdir -p "$SRC/Documents" "$SRC/Images" "$SRC/.cache/firefox" "$SRC/.local/share/Trash/files" "$CLE"
echo "lettre v1" > "$SRC/Documents/lettre.txt"
echo "impôts 2026" > "$SRC/Documents/impots.txt"
echo "photo" > "$SRC/Images/vacances.jpg"
echo "cache" > "$SRC/.cache/firefox/gros-cache"
echo "jeté" > "$SRC/.local/share/Trash/files/vieux.txt"
echo "temporaire" > "$SRC/Documents/brouillon.tmp"
ecrire_conf 30 Sauvegarde "$SRC"
COPIE="$CLE/Sauvegarde/marie"
echo "Dossier de test : $TRAVAIL"
echo "1. Première sauvegarde"
lancer
verifier "code de sortie 0" test "$CODE" -eq 0
verifier "lettre.txt copiée" test -f "$COPIE/Documents/lettre.txt"
verifier "vacances.jpg copiée" test -f "$COPIE/Images/vacances.jpg"
verifier ".cache exclu" test ! -e "$COPIE/.cache"
verifier "corbeille locale exclue" test ! -e "$COPIE/.local/share/Trash"
verifier "*.tmp exclu" test ! -e "$COPIE/Documents/brouillon.tmp"
verifier "derniere-sauvegarde.txt = OK" grep -q 'Résultat *: OK' "$CLE/derniere-sauvegarde.txt"
echo "2. Simulation (--simulation) après suppression de impots.txt"
rm "$SRC/Documents/impots.txt"
lancer --simulation
verifier "code de sortie 0" test "$CODE" -eq 0
verifier "annonce la suppression" grep -q 'deleting.*impots.txt' "$TRAVAIL/sortie.txt"
verifier "impots.txt toujours sur la clé" test -f "$COPIE/Documents/impots.txt"
verifier "aucune corbeille créée" test "$(nb_corbeilles)" -eq 0
echo "3. Suppression côté ordinateur → retiré de la copie, gardé en corbeille"
lancer
verifier "code de sortie 0" test "$CODE" -eq 0
verifier "impots.txt absent de la copie" test ! -e "$COPIE/Documents/impots.txt"
CORB1="$(find "$CLE/.corbeille" -mindepth 1 -maxdepth 1 -type d | head -n1)"
verifier "nom de corbeille AAAA-MM-JJ_HHMMSS" bash -c '[[ "$(basename "$1")" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}$ ]]' _ "$CORB1"
verifier "impots.txt dans .corbeille/<date>" test -f "$CORB1/marie/Documents/impots.txt"
echo "4. Modification d'un fichier → nouvelle version copiée, ancienne en corbeille"
echo "lettre v2, bien plus longue" > "$SRC/Documents/lettre.txt"
lancer
verifier "code de sortie 0" test "$CODE" -eq 0
verifier "copie = v2" grep -q 'v2' "$COPIE/Documents/lettre.txt"
verifier "ancienne v1 en corbeille" bash -c 'grep -rqs "lettre v1" "$1"/.corbeille/*/marie/Documents/lettre.txt' _ "$CLE"
verifier "deux corbeilles" test "$(nb_corbeilles)" -eq 2
echo "5. Purge des corbeilles de plus de 30 jours"
mkdir -p "$CLE/.corbeille/2026-09-01_120000/marie" "$CLE/.corbeille/2026-10-30_120000"
echo vieux > "$CLE/.corbeille/2026-09-01_120000/marie/vieux.txt"
touch -d '40 days ago' "$CLE/.corbeille/2026-09-01_120000"
touch -d '6 days ago' "$CLE/.corbeille/2026-10-30_120000"
mkdir -p "$CLE/.corbeille/a-moi"; touch -d '90 days ago' "$CLE/.corbeille/a-moi"
lancer
verifier "code de sortie 0" test "$CODE" -eq 0
verifier "corbeille de 40 jours purgée" test ! -e "$CLE/.corbeille/2026-09-01_120000"
verifier "corbeille de 6 jours gardée" test -d "$CLE/.corbeille/2026-10-30_120000"
verifier "dossier au nom inattendu épargné" test -d "$CLE/.corbeille/a-moi"
verifier "purge annoncée dans le journal" grep -q 'Purge.*2026-09-01_120000' "$TRAVAIL/sortie.txt"
echo "6. CORBEILLE_JOURS=0 : miroir pur, sans corbeille"
ecrire_conf 0 Sauvegarde "$SRC"
avant="$(nb_corbeilles)"
rm "$SRC/Images/vacances.jpg"
lancer
verifier "code de sortie 0" test "$CODE" -eq 0
verifier "vacances.jpg absent de la copie" test ! -e "$COPIE/Images/vacances.jpg"
verifier "aucune nouvelle corbeille" test "$(nb_corbeilles)" -eq "$avant"
verifier "vacances.jpg nulle part sur la clé" test -z "$(find "$CLE" -name vacances.jpg)"
ecrire_conf 30 Sauvegarde "$SRC"
echo "7. Clé FAT simulée : lien symbolique ignoré sans erreur"
ln -s Documents/lettre.txt "$SRC/raccourci"
SAUVEGARDE_TEST_FSTYPE=vfat lancer
verifier "code de sortie 0" test "$CODE" -eq 0
verifier "lien non copié" test ! -e "$COPIE/raccourci" -a ! -L "$COPIE/raccourci"
rm "$SRC/raccourci"
echo "8. Garde-fous (tous doivent REFUSER, sans rien toucher)"
empreinte() { find "$CLE" "$SRC" -printf '%p %s\n' | sort | md5sum; }
E0="$(empreinte)"
DEST_TEST="$TRAVAIL/nexiste-pas" lancer
verifier "destination de test absente → refus" test "$CODE" -ne 0
DEST_TEST="/" lancer
verifier "destination « / » → refus" test "$CODE" -ne 0
for d in "../evasion" "a/../../evasion" "/tmp/x" "." ".corbeille" ""; do
ecrire_conf 30 "$d" "$SRC"; lancer
verifier "DOSSIER_DESTINATION=« $d » → refus" test "$CODE" -ne 0
done
verifier "rien n'a été créé hors de la clé" test ! -e "$TRAVAIL/evasion" -a ! -e "$TRAVAIL/x"
ln -s "$TRAVAIL/maison" "$CLE/piege"
ecrire_conf 30 piege "$SRC"; lancer
verifier "lien sur la clé vers l'extérieur → refus" test "$CODE" -ne 0
rm "$CLE/piege"
ecrire_conf 30 Sauvegarde; lancer
verifier "SOURCES vide → refus" test "$CODE" -ne 0
mkdir -p "$TRAVAIL/vide"
ecrire_conf 30 Sauvegarde "$TRAVAIL/vide"; lancer
verifier "source vide → refus" test "$CODE" -ne 0
ecrire_conf 30 Sauvegarde "$TRAVAIL/absent"; lancer
verifier "source inexistante → refus" test "$CODE" -ne 0
ecrire_conf 30 Sauvegarde "$CLE"; lancer
verifier "source contenant la clé → refus" test "$CODE" -ne 0
ecrire_conf 30 Sauvegarde "$SRC/Documents" "$TRAVAIL/maison/marie/../marie/Documents"; lancer
verifier "deux sources au même nom → refus" test "$CODE" -ne 0
verifier "clé et sources inchangées" test "$(empreinte)" = "$E0"
ecrire_conf 30 Sauvegarde "$SRC"
echo "9. Verrou : deux sauvegardes simultanées impossibles"
exec 8>"$TRAVAIL/sauvegarde-cle-usb-test.lock"; flock -n 8
lancer
verifier "2e exécution s'arrête sans rien faire" grep -q 'déjà en cours' "$TRAVAIL/sortie.txt"
exec 8>&-
echo
echo "Bilan : $REUSSIS réussis, $RATES ratés."
[[ "$RATES" -eq 0 ]] && rm -rf "$TRAVAIL"
[[ "$RATES" -eq 0 ]]