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

Siehe auch

Weitere Posts