Qualche pensierino sul tar

Per farmi i backup ho sempre usato il classico tar perché le varie soluzioni proposte da altri e disponibili in internet non sempre soddifacevano i semplici requisiti che mi ero posto. Alcuni progetti utilizzano altri "impacchettatori" diversi da tar adducendo a migliorie varie, ma dato che quello che ho funziona bene non ho mai approfondito la faccenda; anche l'abbondante uso di find per ricercare esplicitamente file e directory più recenti di una data prefissata e successivamente costruire "a mano" la lista dei file da archiviare non mi ha mai convinto più di tanto.

Prima di proseguire è meglio chiarire subito che, sebbene tar riesca ad effettuare backup incrementali senza alcuna fatica, la procedura di restore presenta il problema che tar non è in grado di rimuovere le directory. Cerco di spiegarmi meglio con un esempio: ipotizziamo di voler fare il backup di /home, quindi abbiamo

Nel fare il restore, partendo dal contenuto del primo backup si ritrovano le directory pippo/ e pluto/, ma proseguendo con il contenuto del secondo backup, pur ripristinando correttamente tutti i file (eliminando eventualmente quelli che l'utente ha rimosso tra un backup ed il successivo) tar non rimuoverà la directory /home/pluto (perché? perché per rimuovere gli elementi del filesystem che non devono sopravvivere utilizza la stessa chiamata di sistema indifferentemente per file e directory).

Si tratta di un problema non di poco conto, ma l'ho risolto in maniera soddisfacente (quanto meno per me) rimuovendo "a mano" le directory che dovrebbero essere soppresse (in realtà non lo faccio "a mano": una piccola funzione per la shell si occupa di tutta la noiosa faccenda in una manciata di righe scritte con una generosa spaziatura; vedi oltre per il codice).

Passando alle opzioni per i backup: ci sono due modi differenti per effettuare il backup incrementale (quanto meno fino alla versione 1.13.25), quello vecchio (opzione -G, --incremental) e quello nuovo (opzione -g, --listed-incremental). La documentazione ufficiale consiglia caldamente di utilizzare il nuovo formato e nel seguito mi limito a quello.

Questo nuovo formato utilizza un file "di appoggio" (la documentazione ufficiale lo chiama snapshot-file) per ogni archivio per tenere traccia dell'epoca di modifica di file e directory tra un backup incrementale ed il successivo; il nome di questo file non è ristretto da alcuna convenzione e puoi quindi chiamarlo come ti pare (per semplicità nel seguito lo chiamerò <nome archivio>.inc). La presenza di questo file è fondamentale per la creazione di un backup incrementale: se il file dovesse essere cancellato, tar procederà ad eseguire un bel backup completo.

La prima volta che si esegue un backup lo snaptshot-file non esiste, quindi questo sarà sempre completo; è necessario, però, utilizzare fin da subito l'opzione -g per creare il file di appoggio. Esempio:

cd /home
tar -cvj -f /tmp/emanuele.00.tar.bz2 -g /tmp/emanuele.inc \
	emanuele/

Inciso: solitamente preferisco fare il backup partendo dalla directory immediatamente superiore a quella da archiviare in modo da poter effettuare l'eventuale ripristino senza avere fra i piedi tutta la gerarchia di directory.

Per i successivi backup è necessario indicare nuovamente lo stesso file di appoggio in modo che tar possa utilizzarlo per decidere quali file/directory devono essere archiviati:

cd /home
tar -cvj -f /tmp/emanuele.01.tar.bz2 -g /tmp/emanuele.inc \
	emanuele/

Per i ripristino è nuovamente necessario indicare il file di appoggio in modo che tar possa eliminare quelli che devono essere eliminati e ripristinare correttamente (quasi) tutto:

cd /home
tar -xvj -f /tmp/emanuele00.tar.bz2 -g /tmp/emanuele.inc
tar -xvj -f /tmp/emanuele01.tar.bz2 -g /tmp/emanuele.inc

Dopo queste due operazioni /home/emanuele conterrà ancora le directory che tra i due backup avevo rimosso (e sullo stderr ti ritrovi qualche messaggio di errore per le directory che tar ha provato a rimuovere ma non è riuscito ad eliminare).

Un piccolo appunto: dato che gli snapshot-file sono così importanti conviene averne sempre qualche copia di scorta.

Prima di passare oltre, una piccola deviazione su un'opzione interessante: --exclude. Permette di escludere dagli archivi creati da tar determinate entità del filesystem; per chiarire

--exclude=.netscape/*/Cache

permette di evitare il backup della cache di netscape; analogamente per mozilla:

--exclude=.mozilla/*/Cache

KDE (e mi pare anche GNOME) opzionalmente visualizza le anteprime delle immagini e memorizza queste anteprime in ~/.thumbnails oppure ~/.kde/share/thumbnails; per togliere di mezzo tutta 'sta roba senza pensarci più basta usare

--exclude=.*thumbnails

L'opzione --exclude può essere utilizzata più volte nello stesso comando, ma se i pattern sono molti torna comodo metterli in un file (uno per riga) ed utilizzare -X <nomefile> oppure --exclude-from=<nomefile> per abbreviare il tutto.

Gli archivi possono essere "verificati": con l'opzione --verify (-W) tar controlla il contenuto dell'archivio appena creato confrontandolo con quanto si trova ancora sul disco, quindi in effetti legge il disco ben due volte; in caso di modifiche ai dati sul disco rispetto a quelli che sono finiti nell'archivio, tar emette un avvertimento.

Come ben sai gli archivi possono essere compressi (-j, --bzip2 per bzip2 o -z, --gzip per gzip). Non è possibile verificare la correttezza di archivi compressi.

Veniamo al codice per rimuovere le directory "dimenticate" da tar durante il ripristino; ho scritto questa funzione per il piccolo programma che uso per automatizzare il backup a casa e a XXX; funziona, ma come sempre non si garantisce nulla :-)

# Argomenti: $1 = il nome dell'archivio
#            $2 = il nome dello snapshot file
rimuovi_directory(){
  if [ ! -d "$1/" ]; then
    return
  fi
  find $1/ -type d | while read i; do
    if [ "$1/" = "$i" ]; then
      true
    else
      grep $i $2 2>/dev/null | cut -d ' ' -f 3- | while read j; do
        if [ "$i" = "$j" ]; then
          return 1
        fi
      done
      if [ $? -ne 1 ]; then
        echo "Removing directory $i and its contents" && rm -rf "$i"
      fi
    fi
  done
}

Come vedi sono stato molto largo. In sintesi significa: controllo tutte le sottodirectory di quella che sto per ripristinare confrontandole con quelle elencate nello snapshot-file; se una directory non è elencata nello snapshot-file la butto via.

Negli script per automatizzare l'eventuale ripristino la funzione viene usata in questo modo:

echo Ripristino di /home/emanuele in /home
(cd /home && rimuovi_directory emanuele /var/lib/backup/emanuele.inc
 tar --extract --listed-incremental=/var/lib/backup/emanuele.inc \
   --file /var/lib/backup/emanuele-incr-cortirion-20040221-101935.tar \
   2>/dev/null)

Come combinare tutto questo guazzabuglio in una forma a te utile è un'altra faccenda: dal farlo completamente a mano ad automatizzare tutto fin nei minimi particolari c'è un abisso. Se ti interessa ti posso spedire lo script che uso io: c'è del codice per te superfluo per automatizzare la creazione delle iso e non è molto commentato, ma funziona bene senza dare problemi, è controllato da un piccolo file di configurazione e potresti trarne "ispirazione" (oppure, semplicemente evitando di creare le iso potresti usarlo per avrere una manciata di archivi di backup pronti per essere sbattuti sul dat).

Ammetto che la faccenda possa sembrare più complicata di quanto in realtà non sia e capirei se a questo punto decidessi di optare per qualche cosa di "già pronto" :-)

Ultima modifica: Wed Oct 20 11:24:39 CEST 2004

Valid HTML 4.01! Valid CSS!