Christian Seiler: Erkennen von Änderungen an Dateien

Beitrag lesen

Hallo Tom,

Ich drei große Probleme mit einem zentralen Punkt Deines Postings:

Mit den Methoden, die Christian Seiler beschreiben hat, kommst Du nicht zum Ziel. Dies insbesondere deshalb, weil er touch() benutzt. Das ist hier für eine einfache Lösung aber schädlich.

a) touch() könnte man in diesem Fall auch weglassen, da man HIER beim Schreiben in jedem (!) Fall davon ausgeht, dass die Datei bereits existiert (und das ist auch jedem klar, der den Artikel gelesen und verstanden hat). Für meine Lösung (s.u.) wäre touch() aber auch nicht schädlich.

b) Deine Pauschalisierung "kommt man nicht zum Ziel" - natürlich kommt man damit nicht ALLEINE zum Ziel, aber File Locking an sich braucht man weiterhin, Dein Posting dagegen liest sich so, als ob mein Artikel hier völlig fehl am Platze ist.

c) Das Dateimodifikationsdatum ist in meinen Augen ein sehr schlechtes Kriterium, weil es nur eine informative Angabe ist, die das Betriebsystem liefert, man sich auf sie jedoch bei kritischen Dingen NICHT darauf verlassen sollte und - wie Du selbst bemerktest - nur sekundengenau ist.
Eine viel sinnvollere Methode ist, mit Checksummen des Inhalts der Datei zu arbeiten (wenn sie sowieso komplett ausgelesen wird - warum nicht?).

Folgendes Vorgehen wäre für diesen Fall am sinnvollsten (nochmal alles zusammengefasst, halt mit Prüfsummen statt Modifikationsdatum):

1. Beim Anzeigen des Formulars wird die gesamte Datei eingelesen, dabei auch Locking betrieben, um parallelen Schreibzugriffen einen Riegel vorzuschieben (GANZ WICHTIG: Fehlerbehandlung ist hier noch nachzutragen! Der Code hier unten geht der Einfachheit wegen davon aus, dass die Funktionen immer erfolgreich sind!):

$datei = 'datei.txt'; // oder sonstwoher  
$fd = fopen ($datei, 'rb');  
flock ($fp, LOCK_SH);  
$inhalt = fread ($fp, filesize ($datei));  
fclose ($fp);

1a. Danach wird eine Prüfsumme des Inhalts berechnet. Nachdem es hier nicht um kryptographische Sicherheit geht, sondern nur um Konsistenzprüfung, dürfte MD5 vollkommen ausreichend sein:

$pruefsumme = md5 ($inhalt);

1b. Nun wird der Inhalt zeilenweise aufgespalten:

$zeilen = preg_split ("/\r\n|\n|\r/", $inhalt);

Danach enthält $zeilen ein Array mit allen Zeilen der Datei - allerdings OHNE die Zeilentrennzeichen - das ist der Unterschied zu file().

1c. Nun wird das Formular erzeugt, dort gibt es zum einen die Felder, die sich an Hand von $zeilen ergeben (wie Du's halt bisher erzeugst). ZUSÄTZLICH gibt es ein Hidden-Feld <input type="hidden" name="pruefsumme" value="...">, wobei das '...' durch den Inhalt der Variable $pruefsumme zu ersetzen ist.

2. Wenn das Formular ankommt, wird es zuerst validiert, damit die Benutzeringaben gültig sind. Wenn nicht, bekommt es der User wieder vorgesetzt. Ist dies geschehen, wird die Datei wieder geöffnet (diesmal zum Lesen & Schreiben), die Prüfsumme erneut generiert und dann der Dateizeiger zurückgesetzt (wir gehen davon aus, dass die Datei in der Zwischenzeit NICHT gelöscht wurde, auch hier wieder: FEHLERBEHANDLUNG SELBST NACHTRAGEN!):

$datei = 'datei.txt'; // oder sonstwoher  
$fd = fopen ($datei, 'rb+'); // zum lesen UND schreiben  
flock ($fp, LOCK_EX); // EXKLUSIVE sperre  
$inhalt = fread ($fp, filesize ($datei));  
$pruefsumme = md5 ($inhalt);  
fseek ($fp, 0, SEEK_SET);  
// DATEI NICHT SCHLIESSEN!

Nun wird $pruefsumme mit der über das Formular übermittelten Prüfsumme verglichen ($pruefsumme == $_POST['pruefsumme']). Wenn sie übereinstimmen, wurde die Datei NICHT verändert seit dem Anzeigen des Formulars, dann kann der Inhalt problemlos zurückgeschrieben werden. Dies geschieht, indem die Datei "zurechtgestutzt" wird und danach ganz Normal fwrite() genutzt wird:

if ($pruefsumme == $_POST['pruefsumme']) {  
  ftruncate ($fp, 0);  
  fwrite ($fp, $neuerInhalt);  
  fclose ($fp);  
} else {  
  fclose ($fp);  
  // siehe unten...  
}

Wie Du an $neuerInhalt kommst, bleibt Dir (= dem mit dem Problem) überlassen. Du kannst an dieser Stelle zum Beispiel den alten Inhalt wieder zeilenweise aufteilen und die POST-Daten an die entsprechenden Zeilen einfügen und dann per join ("\n", $zeilen) die Zeilen wieder zu einem String zusammenfügen oder Du kannst nur aus den POST-Daten den neuen Inhalt erstellen - das bleibt ganz Dir überlassen. Sobald die Datei geschlossen ist, war alles erfolgreich und Du kannst Dich zurücklehnen.

Nun zur else-Bedingung, die auftritt, wenn die Prüfsumme NICHT übereinstimmt. Dann ist folgender Fall eingetreten: Jemand hat die Datei modifiziert, NACHDEM Du das Formular an den Client ausgeliefert hast, BEVOR es jedoch zurückgekommen ist. Je nachdem wie komfortabel es für den Benutzer machen willst, kannst Du Dir überlegen, wie Du darauf reagierst. Du könntest eine Fehlermeldung ausgeben. Du könntest die Unterschiede zwischen den verschiedenen Versionen anzeigen. Und so weiter, und so fort.

PS: wenn einer fragt, woher der Name vom "Academic Locking" kommt. Das ist Slang und kommt von der Viertelstunde Unterschied. Man speichert erst CT

Das ist - mit Verlaub - Quatsch. "Academic Locking" ist eindeutig eine Wortschöpfung von Dir (d.h. wenn schon nennst DU es so, nicht MAN!), denn die einzigen relevanten Google-Suchtreffer sind ein paar weitere Postings in diesem Forum von Dir.

Zudem: "Akademische Viertelstunde" ist eine rein deutsche Redewendung - im Englischen spricht man ausschließlich von 'c.t.'.

Und schließlich ist hier "Locking" fehl am Platze, weil das, was hier gemacht wird, kein Locking ist. Hier wird zwar gesperrt beim Zugriff auf die Datei - dort aber ganz normal, dafür braucht man keinen zusätzlichen Begriff erfinden. Und die Abfrage, ob sich in der Zwischenzeit etwas an der Datei geändert hat, ist mit SICHERHEIT kein Locking, da sie ja nur dazu dient, Veränderungen zu ERKENNEN, nicht zu VERHINDERN.

Viele Grüße,
Christian