BLog

ImprintImpressum
PrivacyDatenschutz
DisclaimerHaftung
Downloads 

Time-Machine-Client-Backups auf dem FreeBSD-Home-Server

Die Einrichtung der File-Services FTP, AFP, SMB und NFS auf dem FreeBSD-Home-Server wurde im vorausgegangenen Artikel beschrieben. Es wurde auch bereits unter AFP und SMB ein Netzlaufwerk für die Client-Backups eingerichtet. Im Grunde könnte man es dabei bewenden lassen, denn um das Backups-Netzlaufwerk für Time-Machine-Backups eines Macs zu benutzen, verbindet man sich einmal manuell über das Finder Menü Gehe Zu und dort Mit Server verbinden ..., und in den Time-Machine-Systemeinstellungen wählt man dann das Backups-Netzlaufwerk als Backup-Volume aus.

Mit wenigen zusätzlichen Handgriffen am Server können die Mac's das dort verfügbare Backups-Netzlaufwerk über Bonjour = Zeroconf = mDNS automatisch erkennen. Hierzu müssen wir nur ein zusätzliches Software-Paket, nämlich net/mDNSResponder aus den FreeBSD-Ports heraus installieren und einrichten. Die aktuelle Version 544 erkennt IPv6-Adressen nicht zuverlässig, und ferner läßt sich der mDNS-Dienst nicht einfach auf das lokale Netzwerk beschränken. Um diese beiden Kleinigkeiten zu beheben kann man einen von mir erstellten Patch einspielen. Hierzu lädt man sich die folgende Patch-Datei - mDNSResponder-IPv6+bind.patch - herunter (s. den folgenden Inhalt):

--- mDNSPosix/Responder.c.orig	2011-12-01 22:39:45.000000000 -0200
+++ mDNSPosix/Responder.c	2014-08-21 13:36:58.000000000 -0300
@@ -209,7 +209,7 @@
 static void PrintUsage()
 {
     fprintf(stderr,
-            "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
+            "Usage: %s [-v level ] [-r] [-n name] [-i iface] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
             gProgramName);
     fprintf(stderr, "          -v verbose mode, level is a number from 0 to 2\n");
     fprintf(stderr, "             0 = no debugging info (default)\n");
@@ -218,6 +218,7 @@
     fprintf(stderr, "             can be cycled kill -USR1\n");
     fprintf(stderr, "          -r also bind to port 53 (port 5353 is always bound)\n");
     fprintf(stderr, "          -n uses 'name' as the service name (required)\n");
+    fprintf(stderr, "          -i only binds to the interface 'iface'\n");
     fprintf(stderr, "          -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
     fprintf(stderr, "          -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
     fprintf(stderr, "          -p uses 'port' as the port number (default is '%d')\n",  kDefaultPortNumber);
@@ -231,6 +232,8 @@
     fprintf(stderr, "             all subsequent arguments after -x are treated as name=val pairs.\n");
 }
 
+extern const char *gInterfaceName;
+
 static mDNSBool gAvoidPort53      = mDNStrue;
 static const char *gServiceName      = "";
 static const char *gServiceType      = kDefaultServiceType;
@@ -260,7 +263,7 @@
     // Parse command line options using getopt.
 
     do {
-        ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
+        ch = getopt(argc, argv, "v:rn:i:t:d:p:f:dP:bx");
         if (ch != -1) {
             switch (ch) {
             case 'v':
@@ -281,6 +284,9 @@
                     exit(1);
                 }
                 break;
+            case 'i':
+                gInterfaceName = strcpy(malloc(strlen(optarg)+1), optarg);
+                break;
             case 't':
                 gServiceType = optarg;
                 if ( !CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
--- mDNSPosix/mDNSPosix.c.orig	2014-08-21 11:39:02.000000000 -0300
+++ mDNSPosix/mDNSPosix.c	2014-08-21 11:43:26.000000000 -0300
@@ -923,6 +923,8 @@
     return err;
 }
 
+const char *gInterfaceName = NULL;
+
 // Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one.
 mDNSlocal int SetupInterfaceList(mDNS *const m)
 {
@@ -950,7 +952,8 @@
         struct ifi_info *i = intfList;
         while (i)
         {
-            if (     ((i->ifi_addr->sa_family == AF_INET)
+            if ((!gInterfaceName || !strcmp(i->ifi_name, gInterfaceName))
+                  && ((i->ifi_addr->sa_family == AF_INET)
 #if HAVE_IPV6
                       || (i->ifi_addr->sa_family == AF_INET6)
 #endif
--- mDNSCore/mDNS.c.orig	2014-08-21 19:32:41.000000000 -0300
+++ mDNSCore/mDNS.c	2014-08-21 19:33:07.000000000 -0300
@@ -12777,7 +12777,7 @@
     if (!set->InterfaceID)
     { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
 
-    if (!mDNSAddressIsValidNonZero(&set->mask))
+    if (0 && !mDNSAddressIsValidNonZero(&set->mask))
     { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
 
     mDNS_Lock(m);

Diese Datei soll unter /root/install/mDNSResponder-IPv6+bind.patch abgelegt werden. Dazu gibt man auf dem FreeBSD-Home-Server die folgenden Befehle ein:

mkdir -p /root/install
fetch -o /root/install/mDNSResponder-IPv6+bind.patch https://obsigna.com/articles/downloads/mDNSResponder-IPv6+bind.patch
sha256 /root/install/mDNSResponder-IPv6+bind.patch
>>>>
SHA256 (...) = f949fee4aa3cbe3d8a26823633c548dafc62ca4ffa7605f844cd61448275d6b5​

Der mit dem sha256-Befehl generierte Hash-Wert muß mit dem oben gezeigten übereinstimmen, und in dem Fall legt man einen Symbolic Link auf diese Patch-Datei unter /usr/ports/net/mDNSResponder/files/patch-zz-IPv6+bind.local an:

ln -s /root/install/mDNSResponder-IPv6+bind.patch /usr/ports/net/mDNSResponder/files/patch-zz-IPv6+bind.local
chflags -h schg /usr/ports/net/mDNSResponder/files/patch-zz-IPv6+bind.local

Durch Setzen des schg-Flags mit chflags(1) wird verhindert, daß der Patch bei jedem Update des Ports-Verzeichnisses wieder entfernt wird. net/mDNSResponder wird nun mit den folgenden Kommandos installiert:

cd /usr/ports/net/mDNSResponder
make install clean

Man erstellt eine mDNS-Konfigurationsdatei unter /root/config/mDNSAnnouncements.conf, in der alle File-Services (FTP, AFP, SMB und NFS) eingetragen werden, die analog zum vorangegangenen Artikel eingerichtet wurden:

nano /root/config/mDNSAnnouncements.conf
Server
_ftp._tcp
21

Server
_afpovertcp._tcp
548

Server
_smb._tcp
445

Server
_nfs._tcp
2049

Server
_adisk._tcp
9
sys=waMa=0,adVF=0x100,adVU=c7ea66fa-30ab-11e4-9b71-00270e08c161
dk0=adVF=0xa1,adVN=Backups

Server
_device-info._tcp
548
model=Xserve

Der Eintrag ...,adVU=c7ea66fa-30ab-11e4-9b71-00270e08c161 muß angepaßt werden. Dazu erzeugt man sich mit dem folgenden Befehl seine eigene UUID, und trägt diese anstelle der oben in blau dargestellten ein:

uuidgen
>>>>
790be97e-30ac-11e4-9b71-00270e09D123

In das Systemstart-Script /etc/rc.conf fügt man zwecks Aktivierung die Einträge für den mDNSResponder hinzu, um ihn starten zu können:

...

## File Services
ftpd_enable="YES"
ftpd_flags="-8 -u 0007 -a 192.168.1.35"
netatalk_enable="YES"
samba_server_enable="YES"
nfs_server_enable="YES"
mountd_enable="YES"
mountd_flags="-r"
rpcbind_enable="YES"
rpc_lockd_enable="YES"
rpc_statd_enable="YES"
mdnsresponderposix_enable="YES"
mdnsresponderposix_flags="-i em0 -f /root/config/mDNSAnnouncements.conf"​

Hier hat das LAN-Interface die Gerätekennung em0, und an ihrer Stelle ist natürlich die tatsächliche Kennung des internen Netzwerkadapters einzutragen. Schließlich können wir die Bekanntmachung (Announcement) der auf unserem Home-Server zur Verfügung stehenden File-Services via Mulitcast-DNS in Betrieb nehmen:

service mdnsresponderposix start

Im Bonjour Browser unter Mac OS X werden nun alle File-Services des FreeBSD-Home-Servers aufgelistet:

In den Systemeinstellungen unter Time Machine betätigt man die Taste Volume auswählen ..., und das Backups-Netzlaufwerk auf unserem FreeBSD-Home-Server findet sich in der Liste der verfügbaren Volumes wieder:

Man kann es nun auswählen, und Time Machine beginnt dann auch direkt damit das erste Backup aufzuspielen.

Time Machine Integritäts-Prüfung schlägt fehl

Im allgemeinen funktioniert das stündliche automatische Backup schnell und zuverlässig. Wenn allerdings der Backup-Vorgang unvorhergesehen unterbrochen wird, z.B. durch Abschalten des betreffenden Mac-Computers, Neustart des Servers, Stromausfall, Ethernetstecker mit Wackelkontakt, WLAN-Ausfall, o.ä., dann wird das Backup-Volume in einem nicht-verifizierten Zustand hinterlassen. Das fällt der Time Machine dann beim nächsten regulären Backup-Durchlauf auf, und sie veranlaßt in dem Fall zunächst eine Integritäts-Prüfung des Backup-Volumes, die in aller Regel ohne Auffälligkeiten abgeschlossen wird, so daß das normale Backup-Prozedere fortgesetzt werden kann.

Hin und wieder schlägt diese Integritäts-Prüfung allerdings fehl:

Die Verlässlichkeit des Backup-Volumes hat selbstverständlich die oberste Priorität, und selbstverständlich ist die von Apple für den vorliegenden Fall vorgesehene Problemlösung, nämlich das fehlerhafte Backup zu löschen und ein neues Backup anzulegen, eine zielführende und aus der Sicht des Apple-Services auch reklamationsarme Maßnahme.

Leider verliert man dabei nun einmal den kompletten Backup-Verlauf, und manchmal mag man diesen schlicht nicht so einfach in den Wind schreiben - halt nur wenn es gar nicht anders geht. Heute ist es bei mir wieder einmal passiert, ich hatte meinen Mac mitten im Backup-Vorgang ausgeschaltet.

Den Backup-Verlauf habe ich dann wie folgt erfolgreich gerettet:

  1. In den Systemeinstellungen → Time Machine, Backup ausschalten.
  2. Im Finder, Menü Gehe zu → Mit Server verbinden ..., das Volume „Backups“ vom FreeBSD Server aktivieren.
  3. Die eigentlichen Backups der diversen Macs im Heim-Netzwerk befinden sich in sogenannten Sparse-Bundles, das sind virtuelle Laufwerke mit variabler Größe. Das Symbol des fehlerhaften Sparse-Bundles ist mit einem kleinen Schloß gekennzeichnet, d.h. es kann zwar noch gelesen aber ohne weiteres nicht mehr verändert werden. Technisch wird dieses „Schloß“ durch Setzen des sog. User Immutable Flags für das gesamte Bundle bewirkt. Um das Sparse-Bundle reparieren zu können muß das Immutable Flag zurückgesetzt werden. Das geschieht im Terminal mit dem folgenden Kommando - mein Bundle heißt MBPRJ.sparsebundle - in jedem Einzelfall muß natürlich der tatsächliche Bundle-Name im folgenden Kommando eingetragen werden:
    sudo chflags -R nouchg /Volumes/Backups/MBPRJ.sparsebundle

    Je nach Backup-Größe kann das Zurücksetzen des Immutable Flags einige Zigminuten dauern.

  4. Für den nächsten Schritt muß das Sparse-Bundle als Volume an seinen Mount-Point eingeklinkt werden, und das geschieht im Terminal mit dem folgenden Kommando:
    sudo hdiutil attach -nomount /Volumes/Backups/MBPRJ.sparsebundle

    Das obige Kommando zeigt als Ausgabe die Bezeichnungen der virtuellen Laufwerke, die dem eingeklinkten Sparse-Bundle zugeordnet wurden:

    /dev/disk1          	Apple_partition_scheme         	
    /dev/disk1s1        	Apple_partition_map            	
    /dev/disk1s2        	Apple_HFSX

    Die letzte Zeile muß man sich merken, denn sie gibt den Bezeichner des Laufwerks an, das überprüft/repariert werden soll, hier /dev/disk1s2. Weiter unten wird das entsprechende Raw-Device /dev/rdisk1s2 verwendet werden. Es muß noch überprüft werden, ob ein kompletter Reperatur-Durchlauf bereits automatisch gestartet wurde, denn in dem Fall muß man nur u.U. mehrere Stunden auf dessen Ende warten:

    tail /var/log/fsck_hfs.log
    >>>>
    /dev/rdisk1s2: fsck_hfs started at Tue Feb 10 09:52:55 2015
    /dev/rdisk1s2: /dev/rdisk1s2: ** /dev/rdisk1s2 (NO WRITE)
    /dev/rdisk1s2:    Executing fsck_hfs (version hfs-226.1.1).
    QUICKCHECK ONLY; FILESYSTEM CLEAN
    /dev/rdisk1s2: fsck_hfs completed at Tue Feb 10 09:52:55 2015

    Im vorliegenden Fall wurde nur ein Schnelltest durchgeführt, und es muß der komplette Reperaturvorgang manuell gestartet werden (Schritt 5). Wenn der Reperatur-Vorgang bereits laufen sollte, wiederholt man das letzte Kommando hin und wieder, bis daß die Meldung „... fsck_hfs completed at ..." angezeigt wird, und man kann sich den nächsten Schritt 5 ersparen.

  5. Die eigentliche Reparatur wird mit dem folgenden Terminal-Kommando manuell gestartet - hierbei ist wichtig, die richtige Laufwerks-Bezeichnung, die wir uns im Schritt 4 gemerkt haben, einzusetzen:
    sudo fsck_hfs -fpy /dev/rdisk1s2
    >>>>
    ** /dev/rdisk1s2
       Executing fsck_hfs (version hfs-226.1.1).
    ** Checking Journaled HFS Plus volume.
    ** Detected a case-sensitive volume.
       The volume name is Time Machine-Backups
    ** Checking extents overflow file.
    ** Checking catalog file.
    ** Checking multi-linked files.
    ** Checking catalog hierarchy.
    ** Checking extended attributes file.
    ** Checking multi-linked directories.
    ** Checking volume bitmap.
    ** Checking volume information.
    ** The volume Time Machine-Backups appears to be OK.
  6. Das überprüfte Laufwerk wird ausgeklinkt:
    sudo hdiutil detach /dev/disk1s2
    >>>>
    "disk1" unmounted.
    "disk1" ejected.
  7. Für den Fall, daß die Überprüfung/Reperatur im Schritt 5 erfolgreich war, d.h. mit „... appears to be OK" und NICHT mit „... could not be repaired" abgeschlossen wurde, muß man den OK-Status noch in das Sparse-Bundle eintragen, dazu setzt man im Terminal den folgenden Befehl ab:
    sudo sed -e 's/<integer>./<integer>0/' -i "" /Volumes/Backups/MBPRJ.sparsebundle/com.apple.TimeMachine.MachineID.plist
  8. Schließlich wirft man das Volume „Backups“ aus und schaltet in den Systemeinstellungen die Time Machine wieder ein.

Auf diese Weise konnte ich bereits 3mal in den letzten 2 Jahren den Backup-Verlauf retten. Wenn das allerdings so nicht klappen sollte, dann folgt man besser doch der System-Empfehlung von Apple und legt sein Time Machine-Backup neu an.

Copyright © Dr. Rolf Jansen - 2014-08-31 02:24:49

PROMOTION