Zottels Zeug
 Mon, 21 Mar 2016 23:43:27 +0100 zuletzt bearbeitet: Tue, 22 Mar 2016 02:42:06 +0100  
As this is a post intended for a worldwide audience, I'll write it in English, not in German like the rest of my blog.

So my 2008 iMac broke down last weekend, and this was the time to get rid of the last piece of Apple hardware in my household and return back to Linux.
I had backups, of course, done with Time Machine. "No problem!" I thought. I had looked at the files in OS X often and thought it was just a matter of copying back the files from the latest backup to Linux after mounting the HD. I thought wrong.
When I browsed through the files, I found that in many cases, where there should have been directories, I only found files with a size of 0 bytes instead. Huh? What's going on?

Hard links for directories
Some searching on the net revealed this hint from the former macosxhints site (which was always excellent!) that nowadays belongs to Macworld.
Bottom line: HFS+ supports hard links for directories, but this is unsupported under Linux.
But boy, what do they do with those hard-linked directories?! It's really absolutely ridiculous.
In the root directory of the disk, there is a directory called ".HFS+ Private Directory Data\r". And yes, that's a carriage return at the end of this directory name. You'll never see this directory on a Mac, it's hidden even if you choose to see hidden files. It contains large numbers of directories, all named dir_xxx, where xxx is a number.
Now, if you do an ls -l on a directory where directories have mysteriously turned into files, you'll see something like this:
drwxr-xr-x        1  503      503  26 23. Jun 2012  nicole
-r--r--r-- 17909650 root 20523400   0 28. Aug 2010  rozottel
-r--r--r-- 19911125 root 20523401   0 28. Aug 2010  Shared
drwxr-xr-x        1  501      501 114 18. Dez 02:22 zottel

rozottel and Shared should be directories, they aren't. But the number you see after the permission is the number of dir_xxx you'll find at the ".HFS+ Private Directory Data\r" directory. Example: The contents of rozottel above can be found in ".HFS+ Private Directory Data^M/dir_17909650". Of course, subdirectories there can contain other directories crippled in the same way.
Gah! Trying to restore even a few directories manually would take ages!

A script to solve the problem
So I wrote a short Perl script to solve the problem.
BTW, that number from above is actually a number that would normally represent the number of hard links that point to the file. It doesn't make sense at all to use this value for names in this private directory, and so, of course, it doesn't mean what it should mean. I'm not sure if this is Linux not supporting all aspects of HFS+ or Apple abusing the specs of that file system in completely braindead ways, but either way, I'm not sure if such a Time Machine backup would survive a file system check with repair under Linux, so better not try that. (The same probably happens with other HFS+ disks where someone has used hard links for directories, but as nobody in their right mind normally does that, it's only a problem for Time Machine backup media.)
The script takes a directory on the Time Machine file system as first parameter, and a directory where you want to copy the backup directory to as second parameter.
Side note: It assumes that your current working directory is a directory on the backup disk. It won't work if you are somewhere else and use absolute paths.
Example:
time_machine.pl foo/ /home/bar/
In this example, foo/ will be created under /home/bar/, and all the contents of foo/ will be recursively copied.
If you omit the second parameter, time_machine.pl will just traverse all files in all subdirectories and show you the complete size of what would be copied. (I needed this because, of course, a simple du won't work on such structures.)
Files that already exist at the destination are not overwritten. Thus, you can cancel the job by pressing Ctrl-C and resume later. Just be sure to delete the last file the script was working on (it shows them all on the console) at the destination after pressing Ctrl-C, because it has probably not been copied completely.
You'll find time_machine.pl as an attachment to this post (click on the paper clip at the end of this post).
It's by no means done well or sophisticatedly, it's just a quick hack I did to solve the problem for myself. Edit it to your needs if you want it to do something else.
And, of course, use it at your own risk.
 Linux
Zottels Zeug
 Sat, 28 Mar 2015 02:45:11 +0100 zuletzt bearbeitet: Sat, 28 Mar 2015 03:44:30 +0100  
Seit ich die Möglichkeiten von rsync kennengelernt habe, mag ich Incremental Backups nicht mehr. Sie sind mir vergleichsweise zu eingeschränkt: Es wird immer ein Full Backup, also ein komplettes Backup aller Daten, benötigt, auf das sich die Incremental Backups beziehen, und ein Incremental Backup baut auf dem vorherigen auf.
Für ein Restore muss also immer eine Reihe von korrekten Incremental Backups vorhanden sein, und das Full Backup, auf das sie sich beziehen, muss ebenfalls ok sein, sonst ist man verloren. Mit anderen Worten: Man sollte mindestens einmal pro Woche ein Full Backup ziehen, sonst wird das ganze zu unsicher.
Und: Mit allen Backup-Lösungen, die Incremental Backups nutzen und normalerweise komprimierte Backup-Archive abspeichern, ist es relativ kompliziert, einzelne Dateien aus dem Backup wiederherzustellen.

Rsync-Backups mit Hardlinks


Ein Rsync-Backup mir Hardlinks ist da viel schöner: Alle zu speichernden Verzeichnisbäume werden einfach so, wie sie sind, auf dem Backup-Server repliziert. Ein Restore einer Datei beschränkt sich also darauf, diese Datei per scp vom Backup-Server auf den Hauptserver zurückzukopieren.
Und jetzt kommt das wirklich geniale:
Über entsprechende Kommandozeilen-Optionen kann man rsync anweisen, die neue Kopie der Verzeichnisbäume beim Backup als Hardlinks anzulegen, sofern die Dateien sich nicht geändert haben.

Hardlinks


Das zu erklären erfordert einen kleinen Exkurs: Was sind eigentlich Hardlinks?
Zur Abgrenzung seien zunächst Softlinks erklärt: Das ist ähnlich dem, was Windows mit .lnk-Dateien tut: Ein Softlink enthält den Pfad zu einer anderen Datei (oder einem anderen Verzeichnis), auf die er verweist. Wird ein Softlink geöffnet, folgt das Betriebssystem dem angegebenen Pfad und öffnet dann diese Datei. Ist die Datei nicht mehr vorhanden, zeigt der Link ins Leere („dangling softlink“), und ein File not Found Error wird erzeugt.
Softlinks funktionieren auch über Filesystem-Grenzen hinweg, d.h. ein Softlink auf einer Festplatte kann auf eine Datei auf einer anderen Festplatte zeigen.
Anders Hardlinks.
Hardlinks kann es nur auf dem gleichen Filesystem geben, denn es sind mehrere Verzeichniseinträge, die auf die gleichen Daten verweisen.
Das können verschiedene Namen innerhalb des gleichen Verzeichnisses sein (so gibt es viele Kommandozeilen-Tools unter Linux, die sich, je nachdem, mit welchem Namen sie aufgerufen werden, unterschiedlich verhalten), aber auch Verzeichniseinträge in ganz unterschiedlichen Verzeichnissen.
Mit dem Befehl ln lassen sich sowohl Hard- wie auch Softlinks erzeugen. Ohne Parameter außer Quell- und Zieldatei werden Hardlinks erzeugt, mit dem Parameter -s Softlinks.
Das Windows-Filesystem NTFS unterstützt grundsätzlich Hardlinks, es gibt aber in der Windows-Oberfläche wie auch auf der Kommandozeile keine Möglichkeit, sie auch zu nutzen.
Wenn es mehrere Hardlinks auf die gleichen Daten gibt und eine dieser „Dateien“ wird gelöscht, löscht das Dateisystem nur diesen Verzeichniseintrag – die Daten selbst bleiben auf der Platte erhalten, und über die anderen Hardlinks kann noch auf sie zugegriffen werden. Erst wenn der letzte Hardlink gelöscht wird, werden die Daten auf der Platte als frei markiert und können durch andere Dateien überschrieben werden.

Rsync und Hardlinks


Genau dieses Verhalten kann sich rsync zunutze machen: Beim ersten Backup-Lauf werden einfach alle Dateien auf den Zielserver kopiert. Beim zweiten Backup-Lauf werden alle Dateien, die sich nicht geändert haben, auf dem Zielserver als Hardlinks zum ersten Backup erstellt und brauchen dadurch keinen zusätzlichen Plattenplatz. Nur die Dateien, die sich tatsächlich geändert haben oder neu erstellt wurden, werden auf den Zielserver kopiert und brauchen dort zusätzlichen Platz.
Wird eine Datei auf dem Hauptserver gelöscht, findet sie keinen Eingang mehr ins neue Backup. Sie ist aber beispielsweise noch in sieben alten Backups vorhanden – alles Hardlinks auf die gleichen Daten.
Wird das erste Backup gelöscht, weil es zu alt geworden ist, zeigen noch sechs Hardlinks auf die Daten – und so weiter, bis das siebte Backup gelöscht wird und damit dann tatsächlich der Plattenplatz freigegeben wird.
Rsync ist sehr effizient in der Erkennung von geänderten Dateien und überträgt nur die Differenz zwischen alter und neuer Version einer Datei über das Netzwerk. Dadurch ist es – nachdem das erste, komplette Backup einmal erstellt wurde – sehr schnell.
Auf diese Weise erhält man Backups, die sehr viel besser sind als das, was die üblichen Backup-Tools so anbieten: Zwar ist das Backup nicht komprimiert, da aber nur Änderungen tatsächlich Platz brauchen, kann man sehr viele Backups des gleichen Servers unterbringen, ohne groß zusätzlichen Plattenplatz zu benötigen.
Die Backups sind direkt ohne Entpacken lesbar, und eine Datei, die man wiederherstellen will, kann man einfach per scp auf den Hauptserver zurückkopieren. Mit entsprechenden Skripten kann man zum Beispiel 5 tägliche Backups behalten, zwei wöchentliche und ein monatliches. Alles andere wird gelöscht. Das ist übrigens genau das, was auch Time Machine, das Backup-System von Mac OS X, tut.

Rsync und Backupninja


Das alles geht – aber es ist nicht ganz einfach, sich rsync selbst per Cronjob so zu konfigurieren, dass es all das tut. Rsync glänzt mit extrem vielen Optionen, es tut per Default nicht das, was wir hier wollen, und es muss um zusätzliche Skripte ergänzt werden, um nicht mehr benötigte Backups zu löschen.
Da kommt Backupninja gerade recht. Backupninja ist eine Sammlung von Skripten, die Backups von verschiedenen Quellen (z.B. Dateisysteme, Datenbanken) mit einfachen Config-Files ermöglicht, ohne dass man sich Gedanken darum machen muss, welche Einstellungen bei den verschiedenen genutzten Backup-Tools sinnvoll sind.
Backupninja kann mit vielen Tools zusammenarbeiten, hier soll es aber ausschließlich um rsync gehen.

Backupninja fixen


Nach der Installation von Backupninja kann man im Verzeichnis /etc/backup.d/ Dateien anlegen, die die verschiedenen Backups steuern, die man nutzen will. Die Dateierweiterung bestimmt dabei jeweils, welches Tool genutzt werden soll.
Für rsync ist, wie könnte es anders sein, die Erweiterung .rsync vorgesehen.
Da gibt es aber ein Problem:
Die aktuelle Version von Backupninja, die bei Debian und Ubuntu LTS mitgeliefert wird, hat ein Problem mit dem su -c Befehl, der auf dem Zielserver ausgeführt wird, und funktioniert somit nicht korrekt.
Das wurde in Backupninja 1.0.2 bereits gefixt (siehe hier), aber bei beiden Distributionen wird aktuell noch 1.0.1 ausgliefert. Wir müssen das also selbst fixen.
Dazu gehen wir nach /usr/share/backupninja/ (als root) und kopieren die Datei rsync (die für alle Dateien in /etc/backup.d/ mit der Endung .rsync zuständig ist), zum Beispiel nach rsyncf (wie rsync fixed).
Dort ersetzen wir die Zeilen
  debug $nice $rsync ${rsync_options[@]} $filelist_flag $excludes $batch_option $orig $dest_path
  set_pipefail
  $nice su -c "$rsync ${rsync_options[@]} --delete-excluded $filelist_flag $excludes $batch_option $orig $dest_path" | tee -a $log


durch

  command="$rsync ${rsync_options[@]} --delete-excluded $filelist_flag $excludes $batch_option $orig $dest_path"
  debug $nice su -c "$command"
  set_pipefail
  $nice su -c "$command" | tee -a $log


Jetzt können wir in /etc/backup.d/ eine Datei mit der Erweiterung .rsyncf anlegen, die unser gefixtes rsync-Skript für Backupninja benutzt.

Die Datei in /etc/backup.d/


Für alle anderen Backup-Möglichkeiten in Backupninja (zumindest die, die ich mir angesehen habe) gilt: Schau Dir die Datei in /usr/share/backupninja an, das ist in den Kommentaren gut dokumentiert, da ist das Erstellen einer entsprechenden Backup-Anweisung Pipifax.
Nicht ganz so bei rsync, leider.
Hier die wichtigen Optionen:

Ganz oben ohne Abschnitt:
when: Ausführungszeit, z.B. „everyday at 05“ (täglich um 5:00 Uhr)

Abschnitt [general]:
mountpoint: Backup-Verzeichnis auf dem Remote-Server
backupdir: Verzeichnis auf dem Remote-Server unter mountpoint, in dem die Backups für diesen Server gespeichert werden sollen
format: short = Eine Anzahl von täglichen Backups behalten, long = eine Anzahl von täglichen, wöchentlichen und monatlichen Backups behalten (s.u.), mirror = gar keine Backups behalten, nur spiegeln
days: für format = short: so viele tägliche Backups behalten (Default 7)
keepdaily: für format = long: so viele tägliche Backups behalten (Default 5)
keepweekly: für format = long: so viele wöchentliche Backups behalten (Default 3)
keepmonthly: für format = long: so viele monatliche Backups behalten (Default 1)

Abschnitt [source]:
include: Kann mehrmals vorkommen; Verzeichnisse, die gesichert werden sollen
exclude: Kann mehrmals vorkommen; Verzeichnisse, die nicht gesichert werden sollen
rsync_options: Optionen für rsync (Default -av --delete --recursive. Ich habe -avzHAXS --delete gesetzt, damit mit allen möglichen Dateien korrekt umgegangen wird, speziell mit dem großen sparse file auf meinem Zimbra- Server. --recursive is nicht nötig, weil -a das impliziert.)

Abschnitt [dest]:
dest: local oder remote, hier remote
host: Server, auf den kopiert werden soll
user: User auf dem Zielsystem
id_file: SSH-Keyfile für die Authentifizierung. Der Default ist hier $HOME/.ssh/id_dsa, heutzutage braucht man aber normalerweise $HOME/.ssh/id_rsa
Nebenbemerkung: Remote rsync läuft heutzutage via ssh. Damit das automatisiert funktionieren kann, muss natürlich eine passwortlose Authentifizierung am Zielrechner mit entsprechenden Schlüsseln möglich sein. Wie man das konfiguriert beschreibe ich hier nicht, gebe aber auf Nachfrage gerne Hilfestellung.

Hier eine Beispiel-Konfiguration, wie ich sie verwende (/etc/backup.d/20-rsync.rsyncf):
when = everyday at 05

[general]
format = long
mountpoint = /
backupdir = var/backups/main

[source]
include = /
exclude = /dev
exclude = /proc
exclude = /run
exclude = /sys
exclude = /tmp

[dest]
dest = remote
host = foobar.backupserver.com
user = root
id_file = /root/.ssh/id_rsa


Allgemeine Überlegungen


Meiner Meinung nach ist diese Art von Backup allen anderen überlegen. Das Problem: Backup-Space, den man vom VPS-Provider bekommt, ist meist nur per FTP erreichbar, nicht per ssh/rsync.
Ich habe deshalb die Entscheidung getroffen, mir statt dessen einen eigenen VPS nur für Backups zu mieten. Das kostet bei meinem Hoster € 8,– im Monat und beinhaltet 500 GB Plattenplatz. Damit mache ich Backups von meinen zwei Servern, und ich habe vor, in Zukunft auch von zu Hause aus per rsync Backups auf diesen Server zu machen.

#Administration #Linux