Exercicis Bash 03: Còpies de seguretat amb tar

Preparatius

Abans de començar, crea una estructura de directoris de prova:

# Crear directori de prova
mkdir -p ~/practica_backups/documents
cd ~/practica_backups/documents

# Crear alguns fitxers de prova
echo "Document 1" > doc1.txt
echo "Document 2" > doc2.txt
echo "Informe important" > informe.txt
mkdir logs
echo "Log del sistema" > logs/sistema.log
echo "Log d'errors" > logs/errors.log

# Crear directori per guardar backups
mkdir -p ~/practica_backups/backups

Exercici 1: Sistema de backup progressiu (60 minuts)

Crea un script sistema_backup.sh que implementi un sistema complet de backups amb suport per a complets, incrementals i diferencials.

Requisits funcionals

El script ha de tenir tres modes d'operació:

  1. Backup complet (-c o --complet)

    • Fa un backup de tots els fitxers
    • Crea un nou fitxer snapshot
    • Elimina snapshots anteriors
    • Format: backup_complet_YYYYMMDD_HHMMSS.tar.gz
  2. Backup incremental (-i o --incremental)

    • Només copia fitxers modificats des de l'últim backup (sigui quin sigui)
    • Usa el snapshot existent
    • Format: backup_incremental_YYYYMMDD_HHMMSS.tar.gz
    • Si no existeix backup complet, crea'n un
  3. Backup diferencial (-d o --diferencial)

    • Copia fitxers modificats des de l'últim backup complet
    • Restaura el snapshot base abans de fer el backup
    • Format: backup_diferencial_YYYYMMDD_HHMMSS.tar.gz
    • Si no existeix backup complet, crea'n un

Funcionalitats addicionals

Exemple d'ús

# Backup complet inicial
./sistema_backup.sh -c ~/practica_backups/documents ~/practica_backups/backups

=== Sistema de Backup ===
Tipus: COMPLET
Origen: /home/usuari/practica_backups/documents
Destí: /home/usuari/practica_backups/backups
Data: 2024-01-08 16:30:45

[1/5] Preparant backup complet...
✓ Creant nou snapshot

[2/5] Creant arxiu...
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 100%
✓ Arxiu creat: backup_complet_20240108_163045.tar.gz

[3/5] Calculant checksum...
✓ SHA256: a3d5f8e2...

[4/5] Verificant integritat...
✓ Verificació OK

[5/5] Estadístiques:
  - Fitxers: 5
  - Directoris: 1
  - Mida: 2.3 KB
  - Temps: 0.5s
  - Compressió: 45%

=== Backup completat amb èxit ===
Log: /home/usuari/practica_backups/backups/backup.log


# Modificar alguns fitxers
echo "Nova línia" >> ~/practica_backups/documents/doc1.txt
echo "Document nou" > ~/practica_backups/documents/doc3.txt

# Backup incremental
./sistema_backup.sh -i ~/practica_backups/documents ~/practica_backups/backups

=== Sistema de Backup ===
Tipus: INCREMENTAL
Origen: /home/usuari/practica_backups/documents
Destí: /home/usuari/practica_backups/backups
Data: 2024-01-08 16:35:12

[1/5] Preparant backup incremental...
✓ Snapshot trobat: snapshot.snar
✓ Últim backup: backup_complet_20240108_163045.tar.gz

[2/5] Creant arxiu...
  Afegint: doc1.txt (modificat)
  Afegint: doc3.txt (nou)
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 100%
✓ Arxiu creat: backup_incremental_20240108_163512.tar.gz

[3/5] Calculant checksum...
✓ SHA256: f7b3c4d1...

[4/5] Verificant integritat...
✓ Verificació OK

[5/5] Estadístiques:
  - Fitxers nous/modificats: 2
  - Mida: 0.8 KB
  - Temps: 0.2s
  - Estalvi vs complet: 65%

=== Backup completat amb èxit ===


# Backup diferencial
./sistema_backup.sh -d ~/practica_backups/documents ~/practica_backups/backups

=== Sistema de Backup ===
Tipus: DIFERENCIAL
Origen: /home/usuari/practica_backups/documents
Destí: /home/usuari/practica_backups/backups
Data: 2024-01-08 16:40:23

[1/5] Preparant backup diferencial...
✓ Snapshot base trobat
✓ Restaurat a estat del backup complet

[2/5] Creant arxiu...
  Afegint: doc1.txt (modificat des del complet)
  Afegint: doc3.txt (nou des del complet)
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 100%
✓ Arxiu creat: backup_diferencial_20240108_164023.tar.gz

...

Esquelet del script

#!/bin/bash

###############################################
# Sistema de Backup amb tar
# Suporta backups: complets, incrementals, diferencials
###############################################

# Variables globals
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TIPUS_BACKUP=""
ORIGEN=""
DESTI=""
LOG_FILE=""
SNAPSHOT_FILE=""
SNAPSHOT_BASE=""

# Colors
VERD='\033[0;32m'
VERMELL='\033[0;31m'
BLAU='\033[0;34m'
NC='\033[0m'

###############################################
# Funcions de log
###############################################

log() {
    local missatge="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
    echo -e "$missatge" | tee -a "$LOG_FILE"
}

log_info() {
    echo -e "${BLAU}$1${NC}"
    log "$1"
}

log_ok() {
    echo -e "${VERD}✓ $1${NC}"
    log "OK: $1"
}

log_error() {
    echo -e "${VERMELL}✗ $1${NC}" >&2
    log "ERROR: $1"
}

###############################################
# Funcions de validació
###############################################

validar_parametres() {
    if [ -z "$ORIGEN" ] || [ -z "$DESTI" ]; then
        log_error "Cal especificar origen i destí"
        mostrar_ajuda
        exit 1
    fi
    
    if [ ! -d "$ORIGEN" ]; then
        log_error "El directori origen no existeix: $ORIGEN"
        exit 1
    fi
    
    # Crear directori destinació si no existeix
    mkdir -p "$DESTI"
}

###############################################
# Funcions de backup
###############################################

backup_complet() {
    log_info "[1/5] Preparant backup complet..."
    
    # Eliminar snapshots antics
    rm -f "$SNAPSHOT_FILE" "$SNAPSHOT_BASE"
    log_ok "Creant nou snapshot"
    
    log_info "[2/5] Creant arxiu..."
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local arxiu="$DESTI/backup_complet_$timestamp.tar.gz"
    
    # Crear backup
    tar -czf "$arxiu" \
        --listed-incremental="$SNAPSHOT_FILE" \
        -C "$(dirname "$ORIGEN")" \
        "$(basename "$ORIGEN")" 2>&1 | tee -a "$LOG_FILE"
    
    if [ ${PIPESTATUS[0]} -ne 0 ]; then
        log_error "Error creant el backup"
        return 1
    fi
    
    log_ok "Arxiu creat: $(basename "$arxiu")"
    
    # Guardar snapshot base per a diferencials
    cp "$SNAPSHOT_FILE" "$SNAPSHOT_BASE"
    
    # Verificar i estadístiques
    verificar_backup "$arxiu"
    estadistiques "$arxiu"
}

backup_incremental() {
    # Comprovar si existeix snapshot
    if [ ! -f "$SNAPSHOT_FILE" ]; then
        log_info "No existeix snapshot. Creant backup complet..."
        backup_complet
        return $?
    fi
    
    log_info "[1/5] Preparant backup incremental..."
    log_ok "Snapshot trobat: $(basename "$SNAPSHOT_FILE")"
    
    # Trobar últim backup
    local ultim_backup=$(ls -t "$DESTI"/backup_*.tar.gz 2>/dev/null | head -1)
    if [ -n "$ultim_backup" ]; then
        log_ok "Últim backup: $(basename "$ultim_backup")"
    fi
    
    log_info "[2/5] Creant arxiu incremental..."
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local arxiu="$DESTI/backup_incremental_$timestamp.tar.gz"
    
    # Crear backup incremental
    tar -czf "$arxiu" \
        --listed-incremental="$SNAPSHOT_FILE" \
        -C "$(dirname "$ORIGEN")" \
        "$(basename "$ORIGEN")" 2>&1 | tee -a "$LOG_FILE"
    
    if [ ${PIPESTATUS[0]} -ne 0 ]; then
        log_error "Error creant el backup incremental"
        return 1
    fi
    
    log_ok "Arxiu creat: $(basename "$arxiu")"
    
    verificar_backup "$arxiu"
    estadistiques "$arxiu"
}

backup_diferencial() {
    # Comprovar si existeix snapshot base
    if [ ! -f "$SNAPSHOT_BASE" ]; then
        log_info "No existeix snapshot base. Creant backup complet..."
        backup_complet
        return $?
    fi
    
    log_info "[1/5] Preparant backup diferencial..."
    log_ok "Snapshot base trobat"
    
    # Restaurar snapshot a l'estat del backup complet
    cp "$SNAPSHOT_BASE" "$SNAPSHOT_FILE"
    log_ok "Restaurat a estat del backup complet"
    
    log_info "[2/5] Creant arxiu diferencial..."
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local arxiu="$DESTI/backup_diferencial_$timestamp.tar.gz"
    
    # Crear backup diferencial
    tar -czf "$arxiu" \
        --listed-incremental="$SNAPSHOT_FILE" \
        -C "$(dirname "$ORIGEN")" \
        "$(basename "$ORIGEN")" 2>&1 | tee -a "$LOG_FILE"
    
    if [ ${PIPESTATUS[0]} -ne 0 ]; then
        log_error "Error creant el backup diferencial"
        return 1
    fi
    
    log_ok "Arxiu creat: $(basename "$arxiu")"
    
    # Tornar a restaurar snapshot base (per al proper diferencial)
    cp "$SNAPSHOT_BASE" "$SNAPSHOT_FILE"
    
    verificar_backup "$arxiu"
    estadistiques "$arxiu"
}

###############################################
# Funcions de verificació
###############################################

verificar_backup() {
    local arxiu=$1
    
    log_info "[3/5] Calculant checksum..."
    sha256sum "$arxiu" > "$arxiu.sha256"
    local checksum=$(cut -d' ' -f1 "$arxiu.sha256")
    log_ok "SHA256: ${checksum:0:8}..."
    
    log_info "[4/5] Verificant integritat..."
    if tar -tzf "$arxiu" > /dev/null 2>&1; then
        log_ok "Verificació OK"
        return 0
    else
        log_error "Arxiu corrupte!"
        return 1
    fi
}

estadistiques() {
    local arxiu=$1
    
    log_info "[5/5] Estadístiques:"
    
    local mida=$(du -h "$arxiu" | cut -f1)
    local num_fitxers=$(tar -tzf "$arxiu" 2>/dev/null | grep -v '/$' | wc -l)
    local num_dirs=$(tar -tzf "$arxiu" 2>/dev/null | grep '/$' | wc -l)
    
    echo "  - Fitxers: $num_fitxers"
    echo "  - Directoris: $num_dirs"
    echo "  - Mida: $mida"
    # Afegir més estadístiques...
}

###############################################
# Programa principal
###############################################

mostrar_ajuda() {
    cat << EOF
Ús: $(basename $0) [OPCIÓ] ORIGEN DESTÍ

Opcions:
  -c, --complet       Backup complet
  -i, --incremental   Backup incremental
  -d, --diferencial   Backup diferencial
  -h, --help          Mostra aquesta ajuda

Exemples:
  $(basename $0) -c /home/usuari/documents /backups
  $(basename $0) -i /home/usuari/documents /backups
  $(basename $0) -d /home/usuari/documents /backups
EOF
}

main() {
    # Parsejar arguments
    case "$1" in
        -c|--complet)
            TIPUS_BACKUP="complet"
            shift
            ;;
        -i|--incremental)
            TIPUS_BACKUP="incremental"
            shift
            ;;
        -d|--diferencial)
            TIPUS_BACKUP="diferencial"
            shift
            ;;
        -h|--help)
            mostrar_ajuda
            exit 0
            ;;
        *)
            log_error "Opció desconeguda: $1"
            mostrar_ajuda
            exit 1
            ;;
    esac
    
    ORIGEN="$1"
    DESTI="$2"
    LOG_FILE="$DESTI/backup.log"
    SNAPSHOT_FILE="$DESTI/.snapshot.snar"
    SNAPSHOT_BASE="$DESTI/.snapshot_base.snar"
    
    # Validar
    validar_parametres
    
    # Capçalera
    echo ""
    echo "=== Sistema de Backup ==="
    echo "Tipus: ${TIPUS_BACKUP^^}"
    echo "Origen: $ORIGEN"
    echo "Destí: $DESTI"
    echo "Data: $(date '+%Y-%m-%d %H:%M:%S')"
    echo ""
    
    # Executar backup segons tipus
    case "$TIPUS_BACKUP" in
        complet)
            backup_complet
            ;;
        incremental)
            backup_incremental
            ;;
        diferencial)
            backup_diferencial
            ;;
    esac
    
    local resultat=$?
    
    echo ""
    if [ $resultat -eq 0 ]; then
        echo "=== Backup completat amb èxit ==="
        echo "Log: $LOG_FILE"
    else
        echo "=== Backup fallit ==="
        exit $resultat
    fi
}

main "$@"

Exercici 2: Restaurador de backups (30 minuts)

Crea un script restaurador.sh que permeti restaurar backups.

Requisits

  1. Llistar tots els backups disponibles (complets, incrementals, diferencials)
  2. Mostrar informació de cada backup:
    • Data i hora
    • Tipus
    • Mida
    • Checksum (i verificar-lo)
  3. Permetre seleccionar quin backup restaurar
  4. Restaurar automàticament en l'ordre correcte:
    • Si és complet: només aquest
    • Si és incremental: complet + tots els incrementals fins aquest
    • Si és diferencial: complet + aquest diferencial
  5. Verificar integritat abans de restaurar

Exemple d'ús

./restaurador.sh /home/usuari/practica_backups/backups

=== Restaurador de Backups ===

Backups disponibles:

1. backup_complet_20240108_163045.tar.gz
   Data: 2024-01-08 16:30:45
   Tipus: COMPLET
   Mida: 2.3 KB
   Checksum: ✓ OK

2. backup_incremental_20240108_163512.tar.gz
   Data: 2024-01-08 16:35:12
   Tipus: INCREMENTAL (depèn de #1)
   Mida: 0.8 KB
   Checksum: ✓ OK

3. backup_diferencial_20240108_164023.tar.gz
   Data: 2024-01-08 16:40:23
   Tipus: DIFERENCIAL (depèn de #1)
   Mida: 1.1 KB
   Checksum: ✓ OK

Selecciona un backup per restaurar (1-3): 2

Restauració de backup incremental #2:
  ➜ Necessita aplicar:
    1. backup_complet_20240108_163045.tar.gz (base)
    2. backup_incremental_20240108_163512.tar.gz (seleccionat)

Directori de restauració [per defecte: ./restaurat]: /tmp/restauracio

Confirmes la restauració? (s/n): s

[1/2] Verificant integritat dels arxius...
✓ backup_complet_20240108_163045.tar.gz: OK
✓ backup_incremental_20240108_163512.tar.gz: OK

[2/2] Restaurant...
✓ Aplicant backup_complet_20240108_163045.tar.gz
✓ Aplicant backup_incremental_20240108_163512.tar.gz

=== Restauració completada ===
Fitxers restaurats a: /tmp/restauracio

Exercici 3: Verificador de backups (20 minuts)

Crea un script verificador.sh que comprovi l'estat de tots els backups.

Requisits

  1. Escanejar un directori de backups
  2. Per cada backup:
    • Verificar que existeix el checksum
    • Verificar que el checksum és correcte
    • Comprovar integritat de l'arxiu tar
    • Detectar backups orfes (incrementals/diferencials sense complet base)
  3. Generar un informe amb:
    • Backups vàlids / totals
    • Backups corruptes
    • Backups orfes
    • Espai total ocupat

Exemple de sortida

=== Verificador de Backups ===
Directori: /home/usuari/practica_backups/backups

Verificant backups...
[✓] backup_complet_20240108_163045.tar.gz
[✓] backup_incremental_20240108_163512.tar.gz
[✓] backup_diferencial_20240108_164023.tar.gz
[✗] backup_incremental_20240108_170000.tar.gz - Checksum incorrecte
[⚠] backup_incremental_20240109_090000.tar.gz - Orfe (no hi ha complet base)

=== INFORME ===
Total backups: 5
Vàlids: 3 (60%)
Corruptes: 1 (20%)
Orfes: 1 (20%)

Espai total: 15.3 MB
Backup més antic: 2024-01-08 16:30:45
Backup més recent: 2024-01-09 09:00:00

⚠ ACCIONS RECOMANADES:
  - Eliminar o reparar backup_incremental_20240108_170000.tar.gz
  - Fer backup complet nou (backup_incremental_20240109_090000.tar.gz és orfe)

Criteris d'avaluació