Sicheres Öffnen von Dateien mit sysopen() und flock()
Veröffentlicht von Thomas Fahle am (Permalink)
sysopen() und flock() ermöglichen den sicheren Zugriff auf Dateien aus Perlprogrammen.
Sie finden hier eine ausführliche Beschreibung der Funktionen und ein Beispiel.
Race Conditions beim Öffnen von Dateien vermeiden
Etwas wie
if (-e $file) { open(FH,">$file") or die $!; }
ist praktisch immer ein Programmierfehler. Hier werden zwei Dinge nacheinander getan, die gleichzeitig getan werden müssen. Weiterhin wird der unsichere Aufruf von open() mit zwei Argumenten statt der sichereren Form mit drei Argumenten verwendet.
Eine sichere Methode zum Öffnen von Dateien ist sysopen().
Verwenden Sie also:
use Fcntl; sysopen(FILEHANDLE, $FileName, $Modus);
oder
sysopen(FILEHANDLE, $FileName, $Modus, $permission);
statt open().
Fcntl importiert u.a. folgende Modi:
- O_RDONLY
- Lesezugriff
- O_WRONLY
- Schreibzugriff
- O_RDWR
- Lesen und Schreiben
Diese Modi können mit folgenden flags durch ein bitweises OR verknüpft werden.
- O_CREAT
- Datei erzeugen, falls nicht vorhanden
- O_EXCL
- In Verbindung mit O_CREAT: Fehler zurückgeben, falls Datei bereits existiert
- O_APPEND
- Append, "Anhängen": der Datei-Pointer wird auf das Ende der Datei gesetzt
- O_TRUNC
- Truncate: wenn die Datei besteht, wird sie überschrieben
- O_NONBLOCK
- Non-blocking access
- O_NDELAY
- Nicht warten
- O_SYNC
- Die Datei wird im "synchron I/O Modus" geöffnet.
Jeder Schreibzugriff wird den aufrufenden Prozess solange anhalten, bis die Daten auf die angesprochene Hardware geschrieben sind.
open() und sysopen() im Vergleich
- Datei zum Lesen öffnen
-
open(FH, "<", $file) or die $!; sysopen(FH, $file, O_RDONLY) or die $!;
- Datei zum Schreiben öffnen, erzeugen falls nötig, ansonsten trunkieren
-
open(FH, ">", $file) or die $!; sysopen(FH, $file, O_WRONLY|O_TRUNC|O_CREAT) or die $!; sysopen(FH, $file, O_WRONLY|O_TRUNC|O_CREAT, 0600) or die $!;
- Datei zum Schreiben öffnen, neue Datei erzeugen, Datei darf noch nicht existieren
-
sysopen(FH, $file, O_WRONLY|O_EXCL|O_CREAT) or die $!; sysopen(FH, $file, O_WRONLY|O_EXCL|O_CREAT, 0600) or die $!;
- Datei Öffen zum Anhängen, ggf. erzeugen
-
open(FH, ">>", $file) or die $!; sysopen(FH, $file, O_WRONLY|O_APPEND|O_CREAT) or die $!; sysopen(FH, $file, O_WRONLY|O_APPEND|O_CREAT, 0600) or die $!;
- Datei Öffen zum Anhängen, Datei muss existieren
-
sysopen(FH, $file, O_WRONLY|O_APPEND) or die $!;
- Datei öffnen für Aktualisierungen (Lesen und Schreiben), Datei muss existieren
-
open(FH, "+<", $file) or die $!; sysopen(FH, $file, O_RDWR) or die $!;
- Datei öffnen für Aktualisierungen, Datei darf noch nicht existieren
-
sysopen(FH, $file, O_RDWR|O_EXCL|O_CREAT) or die $!; sysopen(FH, $file, O_RDWR|O_EXCL|O_CREAT, 0600) or die $!;
- Datei öffnen für Aktualisierungen, Datei ggf. erzeugen
-
sysopen(FH, $file, O_RDWR|O_CREAT) or die $!; sysopen(FH, $file, O_RDWR|O_CREAT, 0600) or die $!;
Sperren der Datei für andere Instanzen Ihres Programms
Verwenden Sie einfach sysopen() in Kombination mit flock().
use Fcntl qw/:DEFAULT :flock/;
Fcntl importiert nun zusätzlich folgende Konstanten:
- LOCK_SH
- Shared Lock (Lesezugriff)
- LOCK_EX
- Exclusive Lock (Schreibzugriff)
- LOCK_NB
- Non-Blocking Request( don't stall)
- LOCK_UN
- Lock wieder freigeben
Beispiel:
use Fcntl qw/:DEFAULT :flock/; # importiert die Konstanten für sysopen() und flock() ... flock(FH,LOCK_EX); # Exklusiver Lock ... flock(FH,LOCK_UN); # Lock wieder freigeben
Der Lock verfällt, wenn Sie ihn explizit wieder freigeben oder wenn Sie die Datei mit close() schliessen.
Innerhalb einer kritischen Operation darf die Datei also keinesfalls, auch nur kurzfristig, geschlossen werden.
flock() wird Schwierigkeiten bereiten, wenn Sie versuchen Dateien über ein Netzwerk, z.B. NFS, zu flocken.
Permissions
Wenn Sie eine Datei neu anlegen und keinen Wert für $permission angeben verwendet Perl 0666 als Datei-Permission. Der Wert für die Dateirechte wird von der aktuellen umask Ihres Prozesses beeinflusst. umask() gibt daher an, welche Rechte nicht gesetzt werden sollen.
Beispiel Logdatei:
Schauen wir uns das Ganze am Beispiel einer Logdatei an.
Die Datei soll zum Schreiben geöffnet werden, neue Daten am Ende der Datei hinzugefügt werden. Falls die Datei nicht existiert, soll sie erstellt werden. Die Datei soll nur für den Besitzer Lese- und Schreibrechte besitzen (0600). Gleichzeitig darf immer nur eine Instanz des Programmes auf die Logdatei zugreifen (flock).
use Fcntl qw/:DEFAULT :flock/; local *LOG; my $file = '/pfad/zur/LogDatei'; my $old_umask = umask(0); # umask zurücksetzen und alten Wert speichern # Sicheres Öffnen der Datei sysopen(LOG, $file, O_WRONLY|O_APPEND|O_CREAT, 0600) or die $!; # Exlusives Locking flock(LOG, LOCK_EX) || die "flock failed $file $!"; print LOG ".........\n"; close(LOG) or warn "close failed $file $!"; # Seit Perl 5.004 wird mit close() der I/O Puffer geleert. # Vorher mussten Sie den Puffer selbst leeren. # Das Locking wird ebenfalls aufgehoben. umask($old_umask); # umask wieder auf ursprünglichen Wert zurücksetzen