#!/bin/bash ####################################################################### ### ### D at V e r s e ### Data Integrity Verification And Replication Tool ### "DatVerse-DIVART" ### ### Die Software "DatVerse-DIVART" wird zur verifizierenden ### 1:1-Datenreplikation eingesetzt. ### ####################################################################### ### ### © 2008 ### Joachim Simon ### ### DatVerse - ### Die Internetgalerie für Digitalbild- und Computerkunst ### ### Email: Joachim.Simon@DatVerse.com ### Internet: www.DatVerse.com ### ####################################################################### ### ### L I Z E N Z B E S T I M M U N G: ### ### Jegliche kommerzielle Nutzung oder Weiterverwertung der ### Software "DatVerse-DIVART" oder deren Bestandteilen, auch ### in Zusammenhang mit einer Dienstleistung, ist untersagt. ### Ausgenommen davon ist alleine der Autor der Software ### "DatVerse-DIVART", Joachim Simon. ### ### Die Software "DatVerse-DIVART" darf nur als Ganzes und in ### unveränderter Originalform weitergegeben werden. ### ### Der Autor der Software "DatVerse-DIVART", Joachim Simon, ### gewährt jeder Einzelperson - im folgenden Benutzer genannt - ### das kostenlose Nutzungsrecht an der Software "DatVerse-DIVART" ### für die Datenreplikation bzw. Datensicherung seiner Daten. ### ### Als Gegenleistung besucht der Benutzer bitte die Webseiten ### "www.DatVerse.com" und gibt dem Autor eine Rückmeldung dazu, ### wie ihm die Software "DatVerse-DIVART" gefällt. ### ### Der Benutzer darf für seinen persönlichen Gebrauch eine ### Anpassung der Pfadvariablen an seine Betriebssystemumgebung ### vornehmen. Weiteres darf der Benutzer an der Software ### "DatVerse-DIVART" nicht verändern. Anspruch auf Support hat ### der Benutzer nicht. ### ### Es wird keine Garantie oder Gewährleistung für die ### Fehlerfreiheit der Software "DatVerse-DIVART" oder der ### Erläuterungen im Quelltext übernommen. Für jegliche Schäden, ### die im Zusammenhang mit der Benutzung der Software ### "DatVerse-DIVART" entstehen, wird keinerlei Haftung ### übernommen. Die Benutzung der Software "DatVerse-DIVART" ### erfolgt ausschließlich auf eigene Gefahr. ### ### Bei einem Verstoß gegen die Lizenzbestimmung behält sich der ### Autor rechtliche Schritte vor. ### ####################################################################### ### ### M E R K M A L E D E R S O F T W A R E "DatVerse-DIVART": ### ### 1. Portabilität: ### Die Software ist in einer Linux-Bash oder unter ### Windows in einer Cygwin-Bash einsetzbar. Der gesamte ### Quelltext ist nur ca. 32kB kurz. Die Datensicherung kann ### z.B. durch einen Cronjob oder durch ein Shutdownscript ### automatisiert ausgeführt werden oder manuell erfolgen. ### 2. Sicherheit: ### Mit erzeugten Prüfsummen kann die Kopie unabhängig ### vom Original auf Integrität geprüft werden und ### die kopierten Daten eines Sicherungslaufs werden ### abschließend Bit für Bit auf Konsistenz mit dem ### Original geprüft. Die Verwendung mehrerer externer ### Medien ermöglicht eine höhere Sicherheitsstufe durch ### Medienrotation nach dem Generationenprinzip. ### 3. Zuverlässigkeit: ### Die Software arbeitet als Skript aus einer Shell ### heraus mit elementaren Befehlen des jeweiligen ### Betriebssystems. Pfadnamen mit Leerzeichen werden ### auch gesichert. ### 4. Robustheit: ### Falls die Sicherungsprozedur vom Benutzer oder durch ### einen Fehler abgebrochen wird, wird das Programm nach ### der Behebung des Problems einfach erneut gestartet. ### 5. Effizienz: ### Ein gemessener Wert ist z.B. 12MB/s via USB2.0 und ### und 25MB/s via eSATA mit allen Berechnungen bzw. Proben ### bzgl. der Prüfsumme und dem nachfolgenden Bitvergleich. ### Durch Mengenoperationen von Dateien und Verzeichnissen ### werden stets nur geänderte Daten oder neu erzeugte ### Daten bearbeitet und übertragen. Durch die Verwendung ### von elementaren Kommandos des Betriebssystems ### wird eine hohe Geschwindigkeit erreicht. ### 6. Einfachheit: ### Das Filesystem wird als natürliche Datenbank genutzt, ### welche die Prüfsummen verwaltet, die gleichzeitig der ### Zeitstempel für die Daten sind. Die Sicherung der ### Daten erfolgt auf lokale Ressourcen, z.B. auf eine ### 2. Festplatte, wodurch kein Serverdienst oder Netzwerk ### benötigt wird. ### 7. Transparenz: ### Es wird eine 1:1-Replikation von der Datenquelle zum ### Ziel durchgeführt. Gepackte Archive oder proprietäre ### Datenformate werden nicht verwendet. ### 8. Information: ### Nach dem Sicherungslauf werden Kennzahlen zum Status ### der Datensicherung ermittelt und dem Benutzer ### angezeigt. Falls ein Problem auftaucht, wird de ### Benutzer darüber informiert. Während der Sicherung ### erhält der Benutzer Informationen über den Status der ### Datensicherung. ### 9. Systemchecks: ### Häufungen von Fehlern bei der Datensicherung können ### auf einen Hardwaredefekt hinweisen, z.B. auf eine ### fehlerhafte Festplatte, und können helfen, einen ### Ausfall rechtzeitig zu erkennen. ### 10. Benchmarking: ### Die Zeitnahme für einen Sicherungslauf kann ein ### einfacher Anhaltspunkt für die Performance ### des Computersystems sein. ### ####################################################################### ### ### A B L A U F D E R D A T E N S I C H E R U N G: ### ### Die Datensicherung könnte z.B. auf Benutzeranforderung, durch ### ein Shutdownscript, oder als Cronjob erfolgen. ### ### Ändern sich die Quelldaten während der Datensicherung, können ### Fehlermeldungen bzgl. der Prüfsumme oder des Bitvergleichs ### auftreten. ### ### 1. Zeitlich getrennte Quelldaten: Aktualisierung der Prüfsummen ### und Integritätsprüfung. ### 1.1. Die Prüfsummen der gelöschten Quelldaten (DEL) werden auch ### gelöscht. ### 1.2. Die Prüfsummen der inzwischen erzeugten Quelldaten (CRE) ### werden berechnet. ### 1.3. Vergleich der Änderungszeit zwischen den Quelldaten und den ### Prüfsummendaten: Falls die Quelldaten jünger als die ### Prüfsummen sind (NEW) werden diese neu berechnet. ### 1.4. Für die konstanten Quelldaten (FIX) ist nichts zu tun. ### 1.5. Probe: Alle neu berechneten Prüfsummen werden mit den Daten ### auf Integrität geprüft. ### ### 2. Zeitlich und räumlich getrennte Quell- und Zieldaten: ### Replikation der Quelldaten ins Ziel und Konsistenzprüfung. ### 2.1. Für die inzwischen gelöschten Quelldaten (DEL) werden die ### korrespondierenden Zieldaten in einen speziellen Ordner ### verschoben (delta) und ihre Prüfsummen im Ziel gelöscht. ### 2.2. Die inzwischen erzeugten Quelldaten und ihre berechneten ### Prüfsummen (CRE) werden ins Ziel kopiert. ### 2.3. Die veränderten Quelldaten und ihre berechneten Prüfsummen ### (NEW) werden ins Ziel kopiert. ### 2.4. Für die konstanten Quelldaten (FIX) ist bzgl. der Zieldaten ### nichts zu tun. ### 2.5. Probe: Alle kopierten Daten und ihre Prüfsummen werden Bit ### für Bit auf Konsistenz geprüft. ### ##################################################################### ### ### A N P A S S U N G D E R P F A D E: ### ### Als Volume wird eine Partition auf der Festplatte verstanden. ### Das darauf befindliche Dateisytem kann mit einem sog. ### Mountpoint oder Aufsetzpunkt in das System eingehängt werden, ### was erst den Zugriff auf die darauf vorhandenen Daten über ### einen Pfadnamen ermöglicht. Der vollständige Pfad einer Datei ### setzt sich aus dem Verzeichnisnamen und dem Dateinamen zusammen ### und reicht von der Wurzel des Dateisystems bis zur Datei selbst. ### Der Mountpoint beginnt stets im Wurzelverzeichnis und ist ein ### Teil des Verzeichnisnamens im Pfad. Unter Linux findet man z.B. ### "/net/host27/export/home3" oder "/daten" als Mountpoints. Der ### Pfadname einer Datei könnte dann z.B. "/daten/info.txt" oder ### "/daten/quelle/texte/beruf/bewerbung/firma.txt" lauten. Unter ### Windows verwendet man z.B. "f:" oder "g:/backup/extdisk" als ### Mountpoints. Darunter können weitere Verzeichnisse mit Daten ### liegen. Die Software "DatVerse-DIVART" benötigt für die ### Teilpfade folgende Angaben: ### ### 1. Definierbare Mountpoints: ### VC = Volume Control, hier sind Prüfsummen und ### Kontrolldateien erreichbar ### VS = Volume Source, hier ist die Datenquelle erreichbar ### VT = Volume Target, hier sind die Datenziele erreichbar ### VD = Volume Delta, die in der Datenquelle gelöschten ### Dateien sind im Ziel hier zunächst noch erreichbar ### ### 2. Definierbare Verzeichnisse: ### DC = Directory Control, die Kontrolldateien liegen hier ### DS = Directory Source, die Datenquellen liegen hier ### DT = Directory Target, die Zieldateien liegen hier ### DD = Directory Delta, die inzwischen gelöschten ### Quelldaten werden zunächst hierher verschoben ### ### Die verwendeten Funktionen zur Datensicherung gliedern sich nach ### folgenden Bereichen: Hilfsfunktionen, Initialisierung, Suchen ## bzw. Finden, Mengenoperationen, Löschen, Erzeugen, Verschieben, ### Kopieren, Prüfen bzw. Vergleichen, Unterprogramme für Prüfsumme/ ### Replikation etc., Hauptprogramm. ### ########################################################################## ### ### A N L E G E N V O N V E R K N U E P F U N G E N A U F D E M D E S K T O P ### ### 1. Linux: ### 1.1. Als Benutzer "jos" Auf Desktop neu erstellen - ### Verknüpfung zu Programm, z.B.: ### Allgemein: Backup Ziel 1 ### Berechtigungen: Ausführbar ### Programm/Befehl: xterm -e /home/jos/DatVerse-DIVART/DatVerse-DIVART.sh /externePlatte ### Programm/Arbeitsordner: /home/jos/DatVerse-DIVART ### 1.2. Für den Benutzer"jos" das Replikations-Skript ### "DatVerse-DIVART.sh" im Verzeichnis "/home/jos/DatVerse-DIVART" ### einrichten und alle Pfade im Skript richtig anpassen! ### 1.3. Beim Klicken auf "Backup Ziel 1" erfolgt die 1:1-Replikation auf das unter ### "/externePlatte" gemountete Laufwerk in die angepassten Verzeichnisse. ### ### 2. Windows: ### 2.1. Cygnus installieren nach "C:\cygwin", siehe Homepage von Cygwin (cygwin.com) ### 2.2. Batch-Datei "C:\cygwin\CygwinMy.bat" anlegen mit folgenden Inhalt: ### @echo off ### C: ### chdir C:\cygwin\bin ### bash --login -i %1 %2 ### PAUSE ### 2.3. Für den Benutzer (hier: "jos") das Replikations-Skript ### "DatVerse-DIVART.sh" im Verzeichnis "C:\cygwin\home\jos\DatVerse-DIVART\" ### von Windows bzw. im Verzeichnis von Cygwin unter "/home/jos/DatVerse-DIVART/" ### einrichten und alle Pfade im Skript richtig anpassen! ### 2.4. Auf dem Desktop eine Verknüpfung "Backup Ziel 1" anlegen und Pfad bzgl. ### einer Sicherung nach "f:" eintragen, wobei durch den 2. Parameter (%2) ### "f:" der Mountpoint des Ziels angegeben wird. Für "Target:" und "Start in:" ### werden eingetragen: ### C:\cygwin\CygwinMy.bat "/home/jos/DatVerse-DIVART/DatVerse-DIVART.sh" "f:" ### C:\cygwin\bin ### 2.5. Beim Klicken auf "Backup Ziel 1" erfolgt die 1:1-Replikation auf das unter ### "f:" gemountete Laufwerk in die angepassten Verzeichnisse. ### ####################################################################### ### ### B E G I N N "A N P A S S U N G D E R P F A D V A R I A B L E N": ### ####################################################################### ### ### - - - W I N D O W S - - - W I N D O W S - - - W I N D O W S - - - ### HIER ANPASSEN ODER AUSKOMMENTIEREN FALLS LINUX BENUTZT WERDEN SOLL! ### ### - - - FILESYSTEME - - - FILESYSTEME - - - FILESYSTEME - - - VC=d: VS=d: VT="$1" ### VT="$1" entspricht dem Mountpoint-Parameter des Sicherungsziels VD="${VT}" ### Es gilt stets DV := VT !!! ### - - - VERZEICHNISSE - - - VERZEICHNISSE - - - VERZEICHNISSE - - - DC=DatVerse-DIVART ### Muss angelegt sein, beinhaltet dieses Skript DS=DatVerse-QUELLE ### Muss angelegt sein, darf zunächst auch leer sein DT=DatVerse-ZIEL DD=DatVerse-DELTA ### ### - - - L I N U X - - - L I N U X - - - L I N U X - - - L I N U X - - - ### HIER ANPASSEN ODER AUSKOMMENTIEREN FALLS WINDOWS BENUTZT WERDEN SOLL! ### ### - - - FILESYSTEME - - - FILESYSTEME - - - FILESYSTEME - - - #VC=/net/host123/export/home3 #VS=/net/host123/export/home3 #VT=/users1 #VD=${VT} ### Es gilt stets DV := VT !!! ### - - - VERZEICHNISSE - - - VERZEICHNISSE - - - VERZEICHNISSE - - - #DC=userid/DatVerse-DIVART ### Muss angelegt sein, beinhaltet dieses Skript #DS=userid/DatVerse-QUELLE ### Muss angelegt sein, darf zunächst auch leer sein #DT=DatVerse-ZIEL #DD=DatVerse-DELTA ### ####################################################################### ### ### E N D E "A N P A S S U N G D E R P F A D V A R I A B L E N". ### ####################################################################### ### K O N S T A N T E N: ####################################################################### PROG="\"DatVerse-DIVART\"" PROGRAMM="DatVerse Data Integrity Verification And Replication Tool ${PROG}" VERSION="V1.01/2008-03-28" COPYRIGHT="© 2008 Joachim Simon" KONTAKT="DatVerse.com" ### Vollständige Pfadnamen: PC="${VC}/${DC}" PS="${VS}/${DS}" PT="${VT}/${DT}" PD="${PT}/${DD}" ### Ist ein Unterverzeichnis der Lokation VT/DT !!! ### Quellenrelevante Kontrolldateinamen: DIRLIST="${PC}/dirlist" ### Quelldateien FILELIST="${PC}/filelist" CHKDIRLIST="${PC}/chkdirlist" ### Prüfsummen CHKFILELIST="${PC}/chkfilelist" DELDIRLIST="${PC}/deldirlist" ### Gelöschte Quelldateien bzgl. der Prüfsummen DELFILELIST="${PC}/delfilelist" SHRDIRLIST="${PC}/shrdirlist" ### Gemeinsame Quelldateien und Prüfsummen SHRFILELIST="${PC}/shrfilelist" NEWDIRLIST="${PC}/newdirlist" ### Veränderte Quelldateien bzgl. der Prüfsummen NEWFILELIST="${PC}/newfilelist" CREDIRLIST="${PC}/credirlist" ### Erzeugte Quelldateien CREFILELIST="${PC}/crefilelist" ### Zielrelevante Kontrolldateinamen: TARDIRLIST="${PC}/tardirlist" ### Zieldateien TARFILELIST="${PC}/tarfilelist" DELTARDIRLIST="${PC}/deltardirlist" ### Gelöschte Quelldateien bzgl. der Zieldateien DELTARFILELIST="${PC}/deltarfilelist" SHRTARDIRLIST="${PC}/shrtardirlist" ### Gemeinsame Dateien zw. Quelle und Ziel SHRTARFILELIST="${PC}/shrtarfilelist" NEWTARDIRLIST="${PC}/newtardirlist" ### Veränderte Quelldateien bzgl. der Zieldateien NEWTARFILELIST="${PC}/newtarfilelist" CRETARDIRLIST="${PC}/cretardirlist" ### Erzeugte Quelldateien bzgl. der Zieldateien CRETARFILELIST="${PC}/cretarfilelist" ### Integritätstest-Dateien: INTEGRITY="${PC}/integrity" ### Alle Daten NEWINTEGRITY="${PC}/newintegrity" ### Veränderte Daten CREINTEGRITY="${PC}/creintegrity" ### Erzeugte Daten ### Fehlermeldungen: MESSAGES="FAILED|FAIL|ERR|MIS|NOT|WARN|WRONG" ### Datums- und Zeitformat: DATER=`date +%Y-%m-%d` ; TIMER=`date +%H:%M:%S` PUTL=0 ; PUTG=0 ; PUTN=0 ; HOR=0 ; MIN=0 ; SEK=0 ### Plattenplatz und Dateigröße: DISKNEED=1000000 ; DISKFREE=0 ; FILESIZE=0 ### Hash-Funktion: CHECKSUM=md5sum ####################################################################### ### I N I T I A L I S I E R U N G UND H I L F S F U N K T I O N E N: ####################################################################### function ErrorMessage () { echo -e "\e[2;31m${1}: \"${2}\"\e[0m" return 1 } function PathExist () { if [ -d "${1}" ] ; then echo -e "\e[2;36mPfad existiert: \"${1}\"\e[0m" else echo -e "\e[2;31mFehler - Pfad nicht gefunden: \"${1}\"\e[0m" exit 1 fi } function DirectoryExist () { if [ -d "${1}" ] ; then return 1 else return 0 fi } function TimerStartL () { PUTL=`date +%s` echo -e "\e[2;36mLokale Zeitnahme gestartet.\e[0m" } function TimerStartG () { PUTG=`date +%s` echo -e "\e[2;36mGlobale Zeitnahme gestartet.\e[0m" } function TimerStopL () { PUTN=`date +%s` HLP=$((PUTN-PUTL)) HOR=$((HLP/3600)) MIN=$(((HLP-60*HOR)/60)) SEK=$((HLP-60*(60*HOR+MIN))) echo -e "\e[2;36mLokaler Zeitbedarf [h]:[m]:[s] für ${1} : ${HOR}:${MIN}:${SEK}\e[0m" } function TimerStopG () { PUTN=`date +%s` HLP=$((PUTN-PUTG)) HOR=$((HLP/3600)) MIN=$(((HLP-60*HOR)/60)) SEK=$((HLP-60*(60*HOR+MIN))) echo -e "\e[2;36mGlobaler Zeitbedarf [h]:[m]:[s] insgesamt: ${HOR}:${MIN}:${SEK}\e[0m" } function NumOfDirs () { echo "$((`find "${1}" -type d -print | wc -l`))" } function NumOfFiles () { echo "$((`find "${1}" -type f -print | wc -l`))" } function NumOfLines () { echo "`cat < "${1}" | wc -l`" } function NumOfBytes () { echo "`du -bsx "${1}"`" } function FileSizeZero () { FILESIZE=$(ls -l "${1}" | tr -s " " | cut -d " " -f 5) if [ "${FILESIZE}" -eq 0 ] ; then return 1; else return 0; fi } function DiskSpace () { DISKNEED=`du -ms "${1}" | cut -f 1` DISKNEED=$((DISKNEED+1)) echo "Benutzter Plattenplatz der Quelldaten: ${DISKNEED} [MB]" DISKFREE=`df -m | grep "${2}" | tr -s " " | cut -d " " -f 4` DISKFREE=$((DISKFREE-1)) echo "Freier Plattenplatz im Zielbereich: ${DISKFREE} [MB]" if [ "${DISKFREE}" -gt "${DISKNEED}" ] ; then echo "Anscheinend ist genug Speicherplatz für eine Datenreplikation vorhanden." else echo "Es könnte an Speicherplatz für eine Datenreplikation mangeln!" fi DirectoryExist "${PT}/${DD}" if [ 1 -eq $? ] ; then echo -n "Verzeichnisse von ${DD}: " ; NumOfDirs "${PT}/${DD}" echo -n "Dateien von ${DD}: " ; NumOfFiles "${PT}/${DD}" echo -n "Bytes von ${DD}: " ; du -hsx "${PT}/${DD}" echo "Eventuell sollten zunächst Daten aus \""${DD}"\" gelöscht werden?" fi } function InitStep () { echo "Initialisierung ..." echo -n "Steuerverzeichnis -> " ; PathExist "${PC}" cd "${PC}" rm -f *.act *.act0 echo -n "Quellverzeichnis -> " ; PathExist "${PS}" echo -n "Zielfilesystem -> " ; PathExist "${VT}" echo -n "Zielverzeichnis -> " ; DirectoryExist "${PT}" if [ 0 -eq $? ] ; then echo -e "\e[0;31mWarnung - Zielverzeichnis nicht gefunden: \"${PT}\"\e[0m" echo -e "\e[0;31mLege Zielverzeichnis an.\e[0m" mkdir -p "${PT}" ### touch -t "200203302355" "${PT}" else echo -e "\e[2;36mPfad existiert: \"${PT}\"\e[0m" fi if [ "${DC}" = "${DS}" ] ; then ErrorMessage "Fehler durch identische Definitionen: DC = DS" "${DC}" exit 1 fi if [ "${DS}" = "${DT}" ] ; then ErrorMessage "Fehler durch identische Definitionen: DS = DT" "${DS}" exit 1 fi if [ "${DC}" = "${DT}" ] ; then ErrorMessage "Fehler durch identische Definitionen: DC = DT" "${DC}" exit 1 fi DiskSpace "${PS}" "${VT}" } function StatusEcho () { echo -e "\e[2;33mStatusinformationen zur Replikation:\e[0m" echo -n "Verzeichn. der Quelldaten : " ; NumOfDirs "${PS}" echo -n "Verzeichn. der Zieldaten : " ; NumOfDirs "${PT}/${DS}" echo -n "Verzeichn. der Quellprüfsummen: " ; NumOfDirs "${PC}/${DS}" echo -n "Verzeichn. der Zielprüfsummen : " ; NumOfDirs "${PT}/${DC}/${DS}" echo "------------------------------- ------------" echo -n "Dateien der Quelldaten : " ; NumOfFiles "${PS}" echo -n "Dateien der Zieldaten : " ; NumOfFiles "${PT}/${DS}" echo -n "Dateien der Quellprüfsummen: " ; NumOfFiles "${PC}/${DS}" echo -n "Dateien der Zielprüfsummen : " ; NumOfFiles "${PT}/${DC}/${DS}" echo "------------------------------- ------------" echo -n "Bytes der Quelldaten : " ; NumOfBytes "${PS}" echo -n "Bytes der Zieldaten : " ; NumOfBytes "${PT}/${DS}" echo "------------------------------- ------------" echo -n "Bytes der Quellprüfsummen: " ; NumOfBytes "${PC}" echo -n "Bytes der Zielprüfsummen : " ; NumOfBytes "${PT}/${DC}" echo "------------------------------- ------------" DirectoryExist "${PT}/${DD}" if [ 1 -eq $? ] ; then echo -n "Verzeichnisse von ${DD}: " ; NumOfDirs "${PT}/${DD}" echo -n "Dateien von ${DD}: " ; NumOfFiles "${PT}/${DD}" echo -n "Bytes von ${DD}: " ; du -hsx "${PT}/${DD}" echo "Eventuell sollten demnächst Daten aus \""${DD}"\" gelöscht werden?" fi } ####################################################################### ### S U C H F U N K T I O N E N: ### F I N D E N V O N O B J E K T E N R E A L T I V Z U R ### A K T U E L L E N P O S I T I O N I M P F A D ####################################################################### function DirListBuild () { cd "${1}" echo "* DirListBuild: Starte in \"${1}\"." echo "Suchverzeichnis \"${2}\"." DirectoryExist "${2}" if [ 0 -eq $? ] ; then echo "Ok - Suchverzeichnis nicht vorhanden." touch "${3}".act0 "${3}".act else find "${2}" -depth -xdev -type d -print0 > "${3}".act0 sed 's/\x00/\n/g' "${3}".act0 | sort > "${3}".act fi echo "Angelegt: \"${3}.act+\"." echo -n "Anzahl der Verzeichnisse: " ; NumOfLines "${3}".act } function FileListBuild () { cd "${1}" echo "* FileListBuild: Starte in \"${1}\"." echo "Suchverzeichnis \"${2}\"." DirectoryExist "${2}" if [ 0 -eq $? ] ; then echo "Ok - Suchverzeichnis nicht vorhanden." touch "${3}".act0 "${3}".act else find "${2}" -depth -xdev -type f -print0 > "${3}".act0 sed 's/\x00/\n/g' "${3}".act0 | sort > "${3}".act fi echo "Angelegt: \"${3}.act+\"." echo -n "Anzahl der Dateien: " ; NumOfLines "${3}".act } function FindNewObjects () { cd "${PC}" echo "* FindNewObjects: Starte in \"${PC}\"." echo "Suchdatei \"${1}.act\"." rm -f "${2}".act touch "${2}".act cat "${1}".act | while read FILINE do if [ "${VS}/${FILINE}" -nt "${PC}/${FILINE}" ] ; then echo "${FILINE}" >> "${2}".act fi done tr '\n' '\0' < "${2}".act > "${2}".act0 echo "Angelegt: \"${2}.act+\"." echo -n "Anzahl der Dateien: " ; NumOfLines "${2}".act } function FindNewObjects4Target () { cd "${PC}" echo "* FindNewObjects4target: Starte in \"${PC}\"." echo "Suchdatei \"${1}.act\"." rm -f "${2}".act touch "${2}".act cat "${1}".act | while read FILINE do if [ "${VS}/${FILINE}" -nt "${PT}/${FILINE}" ] ; then echo "${FILINE}" >> "${2}".act fi done echo "Angelegt: \"${2}.act\"." echo -n "Anzahl der Dateien: " ; NumOfLines "${2}".act } ####################################################################### ### L O G I S C H E M E N G E N O P E R A T I O N E N A U F ### D A T E I L I S T E N: ### Die zu verarbeitenden Dateilisten müssen stets vorsortiert sein. ### Die Quelldaten sind stets neuer als die Prüfsummen oder die ### Zieldaten. Folgende Operationen können definiert werden ### (1 = Neue Dateiliste, 2 = Alte Dateiliste): ### Vereinigung := 1 ++ 2 (Alle Dateien zusammen, Elemente kommen ### nur einmal vor) ### Schnittmenge := 1 ** 2 (Nur die Elemente in beiden Mengen, ### einfach vorkommend) ### ab-Differenz := 1 -- 2 (Erzeugte Dateien, einfach vorkommend) ### ba-Differenz := 2 -- 1 (Gelöschte Dateien, einfach vorkommend) ### Die Vereinigung wird hier nicht benötigt. Sie kann berechnet ### werden durch: ### "sort -r ${1} ${2} | uniq > ${3}" ### oder: ### "sort -r -u ${1} ${2} > ${3}" ####################################################################### function SharedListBuild () ### Alternativ: "join -t \0 ${1} ${2} > ${3}" { cd "${PC}" echo "* SharedListBuild: Starte in \"${PC}\". Schnittmenge:" echo "\"${1}.act\" und \"${2}.act\"." comm -1 -2 "${1}".act "${2}".act | sort | uniq > "${3}".act tr '\n' '\0' < "${3}".act > "${3}".act0 echo "Angelegt: \"${3}.act+\"." echo -n "Anzahl der Dateien: " ; NumOfLines "${3}".act } function CreatedListBuild () ### Alternativ: "join -v 1 ${1} ${2} > ${3}" { cd "${PC}" echo "* CreatedListBuild: Starte in \"${PC}\". Erzeugungsmenge:" echo "\"${1}.act\" und \"${2}.act\"." comm -2 -3 "${1}".act "${2}".act | sort -r | uniq > "${3}".act tr '\n' '\0' < "${3}".act > "${3}".act0 ### \n nach \0 echo "Angelegt: \"${3}.act+\"." echo -n "Anzahl der Dateien: " ; NumOfLines "${3}".act } function DeletedListBuild () ### Alternativ: "join -v 2 ${1} ${2} > ${3}" { cd "${PC}" echo "* DeletedListBuild: Starte in \"${PC}\". Löschmenge:" echo "\"${1}.act\" und \"${2}.act\"." comm -1 -3 "${1}".act "${2}".act | sort -r | uniq > "${3}".act tr '\n' '\0' < "${3}".act > "${3}".act0 echo "Angelegt: \"${3}.act+\"." echo -n "Anzahl der Dateien: " ; NumOfLines "${3}".act } ####################################################################### ### L Ö S C H F U N K T I O N E N: ####################################################################### function DeleteFilesInList () { cd "${PC}" echo "* DeleteFilesInList: Starte in \"${PC}\"." echo "Löschdatei \"${1}.act\"." cat "${1}".act | while read FILINE do rm -f "${FILINE}" done echo -n "Anzahl der Dateien: " ; NumOfLines "${1}".act } function DeleteDirsInList () { cd "${PC}" echo "* DeleteDirsInList: Starte in \"${PC}\"." echo "Löschdatei \"${1}.act\"." cat "${1}".act | while read FILINE do rmdir "${FILINE}" done echo -n "Anzahl der Verzeichnisse: " ; NumOfLines "${1}".act } function DeleteTargetFilesInList () { cd "${PC}" echo "* DeleteTargetFilesInList: Starte in \"${PC}\"." echo "Löschdatei \"${1}.act\"." cat "${1}".act | while read FILINE do rm -f "${PT}/${DC}/${FILINE}" done echo -n "Anzahl der Dateien: " ; NumOfLines "${1}".act } function DeleteTargetDirsInList () { cd "${PC}" echo "* DeleteTargetDirsInList: Starte in \"${PC}\"." echo "Löschdatei \"${1}.act\"." cat "${1}".act | while read FILINE do rmdir "${PT}/${DC}/${FILINE}" done cat "${1}".act | while read FILINE do rmdir "${PT}/${FILINE}" done echo -n "Anzahl der Verzeichnisse: " ; NumOfLines "${1}".act } ######################################################################### ### E R Z E U G U N G S F U N K T I O N E N: ######################################################################### function CreateNewDirs () { cd "${PC}" echo "* CreateNewDirs: Starte in \"${PC}\"." FileSizeZero "${1}".act if [ $? -eq 1 ] ; then echo "Ok - keine Verzeichnisse vorhanden." return fi echo "Erzeugungsdatei \"${1}.act\"." cat "${1}".act | while read FILINE do if [ ! -d "${FILINE}" ] ; then mkdir -p "${FILINE}" fi done echo -n "Anzahl der Verzeichnisse: " ; NumOfLines "${1}".act } function CreateNewFiles () { cd "${PC}" echo "* CreateNewFiles: Starte in \"${PC}\"." FileSizeZero "${1}".act if [ $? -eq 1 ] ; then echo "Ok - keine Dateien vorhanden." return fi echo "Erzeugungsdatei \"${1}.act\"." cat "${1}".act | while read FILINE do "${CHECKSUM}" "${VS}/${FILINE}" > "${FILINE}" done echo -n "Anzahl der Dateien: " ; NumOfLines "${1}".act } function CreateTargetDirs () { cd "${1}" echo "* CreateTargetDirs: Starte in \"${1}\"." echo "Erzeugungsdatei \"${2}.act\"." cat "${2}".act | while read FILINE do if [ ! -d "${PT}/${FILINE}" ] ; then mkdir -p "${PT}/${FILINE}" fi if [ ! -d "${PT}/${DC}/${FILINE}" ] ; then mkdir -p "${PT}/${DC}/${FILINE}" fi done echo -n "Anzahl der Verzeichnisse: " ; NumOfLines "${2}".act } ####################################################################### ### V E R S C H I E B E F U N K T I O N E N: ####################################################################### function MoveDirs () { cd "${PC}" echo "* MoveDirs: Starte in \"${PC}\"." echo "Verschiebedatei \"${1}.act\"." cat "${1}".act | while read FILINE do mkdir -p "${2}/${FILINE}" done echo -n "Anzahl der Verzeichnisse: " ; NumOfLines "${1}".act } function MoveFiles () { cd "${PC}" echo "* MoveFiles: Starte von \"${PC}\"." echo "Verschiebedatei \"${1}.act\"." cat "${1}".act | while read FILINE do LEFTPART=`dirname "${FILINE}"` RIGHTPART=`basename "${FILINE}"` mkdir -p "${2}/${DD}/${LEFTPART}" mv "${2}/${FILINE}" "${2}/${DD}/${LEFTPART}/${RIGHTPART}".bak done echo -n "Anzahl der Dateien: " ; NumOfLines "${1}".act } ####################################################################### ### K O P I E R F U N K T I O N E N: ####################################################################### function CopyObjects2Target () { cd "${VS}" echo "* CopyObjects2Target: Starte in \"${VS}\"." echo "Kopieredatei (Daten)\"${2}.act\"." cat "${2}".act | while read FILINE do cp --parents -dupx "${FILINE}" "${PT}/" done cd "${PC}" echo "* CopyObjects2Target: Starte in \"${PC}\"." echo "Kopieredatei (Prüfsummen) \"${2}.act\"." cat "${2}".act | while read FILINE do cp --parents -dupx "${FILINE}" "${PT}/${DC}/" done echo "Kopiere Steuerdateien." cp --parents -dupx * "${PT}/${DC}/" &> /dev/null echo -n "Anzahl der Dateien: " ; NumOfLines "${2}".act } ####################################################################### ### V E R G L E I C H S F U N K T I O N E N: ####################################################################### function VerifyAllFilesInList () { cd "${1}" echo "* * VerifyAllFilesInList: Starte in \"${1}\"." FileSizeZero "${2}".act0 if [ $? -eq 1 ] ; then echo "Ok - keine Prüfsummen zur Verifikation vorhanden." return fi echo "Prüfdatei für Integrität \"${2}.act0\"." cat < "${2}".act0 | xargs -0 "${CHECKSUM}" -c > "${3}".act if (egrep -iw "${MESSAGES}$" "${3}".act) ; then ErrorMessage "*** Fehler bei Integritätsprüfung in" "${3}" else echo "Ok - keine Abweichungen der Prüfsummen." fi echo -n "Anzahl der Dateien: " ; NumOfLines "${2}".act } function CompareAllFilesInList () { cd "${1}" echo "* * CompareAllFilesInList: Starte in \"${1}\"." FileSizeZero "${2}".act if [ $? -eq 1 ] ; then echo "Ok - keine Dateien zur Konsistenzprüfung vorhanden." return fi echo "Prüfdatei für Konsistenz \"${2}.act\"." cat "${2}".act | while read FILINE do cmp -l "${VS}/${FILINE}" "${PT}/${FILINE}" if [ $? -ne 0 ] ; then ErrorMessage "*** Fehler bei Konsistenzprüfung für" "${VS}/${FILINE}" return 1 fi cmp -l "${PC}/${FILINE}" "${PT}/${DC}/${FILINE}" if [ $? -ne 0 ] ; then ErrorMessage "*** Fehler bei Konsistenzprüfung für" "${PC}/${FILINE}" return 1 fi done if [ $? -eq 0 ] ; then echo "Ok - keine Abweichungen im Bitstrom." else echo "ERROR STATE" ; fi echo -n "Anzahl der Dateien: " ; NumOfLines "${2}".act } ####################################################################### ### U N T E R P R O R A M M E: ####################################################################### function Checksums { echo -e "\e[2;33mAktualisierung der Prüfsummen und Integritätsprobe:\e[0m" DirListBuild "${VS}" "${DS}" "${DIRLIST}" FileListBuild "${VS}" "${DS}" "${FILELIST}" DirListBuild "${PC}" "${DS}" "${CHKDIRLIST}" FileListBuild "${PC}" "${DS}" "${CHKFILELIST}" DeletedListBuild "${FILELIST}" "${CHKFILELIST}" "${DELFILELIST}" DeletedListBuild "${DIRLIST}" "${CHKDIRLIST}" "${DELDIRLIST}" DeleteFilesInList "${DELFILELIST}" DeleteDirsInList "${DELDIRLIST}" ### Das Erneuern der Verzeichnis- und Dateiliste für Prüfsummen ### nach deren Löschung macht alles evtl. schneller: #DirListBuild ${PC} ${DS} ${CHKDIRLIST} #FileListBuild ${PC} ${DS} ${CHKFILELIST} SharedListBuild "${FILELIST}" "${CHKFILELIST}" "${SHRFILELIST}" SharedListBuild "${DIRLIST}" "${CHKDIRLIST}" "${SHRDIRLIST}" FindNewObjects "${SHRFILELIST}" "${NEWFILELIST}" CreateNewFiles "${NEWFILELIST}" CreatedListBuild "${FILELIST}" "${CHKFILELIST}" "${CREFILELIST}" CreatedListBuild "${DIRLIST}" "${CHKDIRLIST}" "${CREDIRLIST}" CreateNewDirs "${CREDIRLIST}" CreateNewFiles "${CREFILELIST}" VerifyAllFilesInList "${PC}" "${NEWFILELIST}" "${NEWINTEGRITY}" VerifyAllFilesInList "${PC}" "${CREFILELIST}" "${CREINTEGRITY}" } function Replication { echo -e "\e[2;33mReplikation der Quelldaten und Konsistenzprobe:\e[0m" ### Die Dateilisten für Verzeichnisse und Dateien existiert ### hier bereits durch die Aktualisierung der Prüfsummen! DirListBuild "${PT}" "${DS}" "${TARDIRLIST}" FileListBuild "${PT}" "${DS}" "${TARFILELIST}" DeletedListBuild "${FILELIST}" "${TARFILELIST}" "${DELTARFILELIST}" DeletedListBuild "${DIRLIST}" "${TARDIRLIST}" "${DELTARDIRLIST}" MoveDirs "${DELTARDIRLIST}" "${PT}" MoveFiles "${DELTARFILELIST}" "${PT}" DeleteTargetFilesInList "${DELTARFILELIST}" DeleteTargetDirsInList "${DELTARDIRLIST}" SharedListBuild "${FILELIST}" "${TARFILELIST}" "${SHRTARFILELIST}" FindNewObjects4Target "${SHRTARFILELIST}" "${NEWTARFILELIST}" CopyObjects2Target "${VS}" "${NEWTARFILELIST}" CreatedListBuild "${FILELIST}" "${TARFILELIST}" "${CRETARFILELIST}" CreatedListBuild "${DIRLIST}" "${TARDIRLIST}" "${CRETARDIRLIST}" CreateTargetDirs "${PC}" "${CRETARDIRLIST}" CopyObjects2Target "${VS}" "${CRETARFILELIST}" CompareAllFilesInList "${PC}" "${NEWTARFILELIST}" CompareAllFilesInList "${PC}" "${CRETARFILELIST}" } function SourceIntegrity { FileListBuild "${VS}" "${DS}" "${FILELIST}" VerifyAllFilesInList "${PC}" "${FILELIST}" "${INTEGRITY}" } function TargetIntegrity { FileListBuild "${PT}" "${DS}" "${TARFILELIST}" VerifyAllFilesInList "${PC}" "${TARFILELIST}" "${INTEGRITY}" } function TargetConsistency { FileListBuild "${PT}" "${DS}" "${TARFILELIST}" CompareAllFilesInList "${PC}" "${TARFILELIST}" } function SourceConsistency { FileListBuild "${VS}" "${DS}" "${FILELIST}" CompareAllFilesInList "${PC}" "${FILELIST}" } ####################################################################### ### H A U P T P R O R A M M: ####################################################################### echo "===========================================================================" echo -e "\e[1;36m\e[44m${PROGRAMM}\e[0m" echo -e "\e[1;36m\e[44m${VERSION} ${COPYRIGHT} ${KONTAKT}\e[0m" echo "===========================================================================" echo -e "\e[2;33mStatusinformationen zum System:\e[0m" echo "Datum: ${DATER} (... bitte überprüfen!)" echo "Zeit: ${TIMER} (... bitte überprüfen!)" InitStep while true do echo -e "\e[2;33mBitte wählen Sie aus:\e[0m" echo -e "\e[2;33m 1 = Aktualisierung durch Datenreplikation von der Quelle zum Ziel.\e[0m" echo -e "\e[2;33m 3 = Integrität aller aktuellen Quelldaten testen.\e[0m" echo -e "\e[2;33m 5 = Integrität aller aktuellen Zieldaten testen.\e[0m" echo -e "\e[2;33m 7 = Konsistenz aller aktuellen Zieldaten bzgl. der Quelldaten prüfen.\e[0m" echo -e "\e[2;33m 9 = Konsistenz aller aktuellen Quelldaten bzgl. der Zieldaten prüfen.\e[0m" echo -e "\e[2;33m x = Skript beenden.\e[0m" echo "Kennung eingeben: <#> + , Abbruch: + " read OPTION case "${OPTION}" in 1) TimerStartG ; TimerStartL ; Checksums ; TimerStopL "Prüfsummen" TimerStartL ; Replication ; TimerStopL "Replikation" ; TimerStopG ; StatusEcho ;; 3) TimerStartL ; SourceIntegrity ; TimerStopL "Integrität der Quelle" ;; 5) TimerStartL ; TargetIntegrity ; TimerStopL "Integrität des Ziels" ;; 7) TimerStartL ; TargetConsistency ; TimerStopL "Konsistenz des Ziels" ;; 9) TimerStartL ; SourceConsistency ; TimerStopL "Konsistenz der Quelle" ;; x) break ;; *) echo "Fehler - die Option ist ungültig!" ;; esac done echo "===========================================================================" echo -e "\e[1;36m\e[44m${PROG} - Tschüss!\e[0m" echo "===========================================================================" ################################################################# ### E N D E #################################################################