18.26 Datei öffnen – open
 
Öffnen Sie eine Datei mit der Funktion open(), so wird der Datei ein Deskriptor – auch File-Deskriptor genannt – zugeordnet. Ein File-Deskriptor ist eine positive kleine Zahl, die vom Betriebssystem vergeben wird. Über diesen Deskriptor geben Sie an, ob gelesen oder geschrieben werden soll. Mit der Funktion open() kann zudem auch gleich eine Datei angelegt werden. Hier die Syntax mit den dazugehörenden Headerdateien, die mit eingebunden werden müssen, für Linux/UNIX:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int open(const char *pfad, int modus);
int open(const char *pfadname, int flags, mode_t zugriffsrechte);
und die Syntax mit Headerdateien für Windows/MS-DOS:
#include <fcntl.h>
#include <io.h>
#include <sys\stat.h>
int open(const char *pfad, int modus);
int open(const char *pfadname, int flags, mode_t zugriffsrechte);
Als Pfadangabe müssen Sie den absoluten oder relativen Pfad der Datei angeben, die geöffnet werden soll. Da der Prototyp (unter Linux/UNIX zumindest) in Wirklichkeit korrekt open(const char *, int, ...) lautet, kann je nach Situation das dritte Argument auch wegfallen.
Beim Modus gibt es mehrere Varianten, aber diese sind systemabhängig. Einer von den folgenden drei Modi muss immer angegeben werden:
Tabelle 18.11
Bearbeitungsmodi für open()
Modus
|
Bedeutung
|
O_WRONLY
|
Nur zum Schreiben öffnen
|
O_RDWR
|
Zum Lesen und Schreiben öffnen
|
O_RDONLY
|
Nur zum Lesen öffnen
|
Dies sieht dann bei Linux/UNIX folgendermaßen aus:
int fh;
if((fh = open("/home/programmname", O_RDONLY)) != –1)
oder mit Windows/MS-DOS:
if((fh = open("c:\\config.sys", O_RDONLY)) != –1)
In den beiden Beispielen wird eine Datei nur zum Lesen geöffnet. Zu einem dieser drei genannten Modi können Sie mit dem bitweisen ODER-Zeichen (|) weitere Aktionen verknüpfen. Dies sieht dann so aus:
if((fh = open("c:\\test.sys", O_WRONLY|O_CREAT)) != –1)
Hiermit wird eine Datei zum Schreiben geöffnet. Existiert diese Datei nicht, so wird sie erzeugt (O_CREAT). Existiert diese Datei, so wird der zusätzliche Modus O_CREATE ignoriert. Folgende zusätzliche Modi stehen für Linux/UNIX und Windows/MS-DOS darüber hinaus zur Verfügung:
Tabelle 18.12
Weitere Bearbeitungsmodi für open()
Modus
|
Bedeutung
|
O_CREAT
|
Falls Datei nicht existiert, wird sie neu angelegt. Falls die Datei existiert, ist O_CREAT ohne Wirkung.
|
O_APPEND
|
Datei öffnen zum Schreiben am Ende.
|
O_EXCL
|
O_EXCL kombiniert mit O_CREAT bedeutet, dass die Datei nicht geöffnet werden kann, wenn sie bereits existiert und open() den Wert –1 zurückliefert (-1 == Fehler).
|
O_TRUNC
|
Datei, die zum Schreiben geöffnet wird, wird geleert. Darauf folgendes Schreiben bewirkt erneutes Beschreiben der Datei von Anfang an. Die Attribute der Datei bleiben erhalten.
|
Beispielsweise wird mit
if((fh = open("/home/Name.txt", O_WRONLY | O_TRUNC)) != –1)
erreicht, dass die Datei »Name.txt« zum Schreiben geöffnet wird. Der Inhalt dieser Datei wird gelöscht, und sie kann neu beschrieben werden. Weitere Modi, die nur für Linux/UNIX zur Verfügung stehen, sind:
Tabelle 18.13
Bearbeitungsmodi nur für Linux/UNIX
Modus
|
Bedeutung
|
O_NOCTTY
|
Falls der Pfadname der Name eines Terminals ist, so sollte dieser nicht der neue Kontrollterminal des Prozesses werden, sofern der aktuelle Prozess kein Kontrollterminal besitzt.
|
O_NONBLOCK
|
Falls der Pfadname der Name eines FIFOs oder einer Gerätedatei ist, wird der Prozess beim Öffnen und bei nachfolgenden I/O-Operationen nicht blockiert. Dieses Flag zeigt seine Wirkung erst bei einer Pipe oder nichtblockierenden Sockets.
|
O_SYNC
|
Jeder Schreibvorgang auf das Medium wird direkt ausgeführt und es wird gewartet, bis der Schreibvorgang komplett beendet wurde. Dieses Flag setzt den Pufferungsmechanismus außer Kraft. O_SYNC wird nicht von POSIX.1 unterstützt, wohl aber von SVR4.
|
Für Windows/MS-DOS gibt es folgende extra Modi:
Tabelle 18.14
Bearbeitungsmodi nur für MS-DOS/Windows
Modus
|
Bedeutung
|
O_BINARY
|
Legt den Binärmodus der Datei fest
|
O_TEXT
|
Legt den Textmodus der Datei fest
|
Jetzt folgen noch einige Modi, mit denen die Zugriffsrechte auf eine Datei erteilt werden können. Zuerst für Windows/MS-DOS:
Tabelle 18.15
Zugriffsrechte auf eine Datei erteilen (MS-DOS/Windows)
Modus für Zugriffe
|
Bedeutung
|
S_IWRITE
|
Schreiben erlaubt
|
S_IREAD
|
Lesen erlaubt
|
S_IREAD | SIWRITE
|
Lesen und Schreiben erlaubt
|
Mit folgender Zeile wird z.B. eine Datei erzeugt, die nur gelesen werden darf:
if((fh=open("new.xxx", O_CREAT , S_IREAD)) == –1)
Wenn Lesen und Schreiben erlaubt sein soll, sieht dies so aus:
if((fh=open("new.xxx",O_CREAT, S_IREAD|S_IWRITE)) == –1)
Diese drei Zugriffsrechte für Windows/MS-DOS stehen in der Headerdatei <sys\stat.h>.
Für Linux/UNIX können Sie folgende Zugriffsrechte erteilen, welche sich in der Headerdatei <sys/stat.h> befinden. Hier die Modi für Zugriffsrechte unter Linux/UNIX:
Tabelle 18.16
Zugriffsrechte auf eine Datei erteilen (Linux/UNIX)
Modus für Zugriffe
|
Bedeutung
|
S_ISUID
|
set-user-ID Bit
|
S_ISGID
|
set-group-ID Bit
|
S_ISVTX
|
sticky Bit (saved-text Bit)
|
S_IRUSR
|
read (user; Leserecht für Eigentümer)
|
S_IWUSR
|
write (user; Schreibrecht für Eigentümer)
|
S_IXUSR
|
execute (user; Ausführungsrecht für Eigentümer)
|
S_IRWXU
|
read, write, execute (user; Lese-, Schreib-, Ausführungsrecht für Eigentümer)
|
S_IRGRP
|
read (group; Leserecht für Gruppe)
|
S_IWGRP
|
write (group; Schreibrecht für Gruppe)
|
S_IXGRP
|
execute (group; Ausführungsrecht für Gruppe)
|
S_IRWXG
|
read, write, execute (group; Lese-, Schreib-, Ausführungsrecht für Eigentümer)
|
S_IROTH
|
read (other; Leserecht für alle anderen Benutzer)
|
S_IWOTH
|
write (other; Schreibrecht für alle anderen Benutzer)
|
S_IXOTH
|
execute (other; Ausführungsrecht für alle anderen Benutzer)
|
S_IRWXO
|
read, write, execute (other; Lese-, Schreib-, Ausführungsrecht für alle anderen Benutzer)
|
Hinweis Sofern Sie mit der oktalen Schreibweise der Rechtevergabe vertraut sind, können Sie natürlich diese als Alternative verwenden. Bspw.:
fd = open( new_file, O_WRONLY | O_EXCL | O_CREAT, 0644);
|
Außerdem sollten Sie noch wissen, dass bei Verwendung des Flags O_CREAT die Zugriffsrechte unter Linux/UNIX nicht unbedingt gewährt werden müssen, da die Einschränkungsmaske die Vergabe von Rechten verhindern kann (wird) – und somit die Rechte selbst. Aus diesem Grund wurde mithilfe der Funktion umask() die Maske zur Wegnahme von Rechtebits auf 0 gesetzt, womit alle Zugriffsrechte in dieser Maske erlaubt werden.
Tipp Standardmäßig wird meistens die Einschränkungsmaske 022 vergeben. Es ist aber auch möglich, mit dem Shell-Builtin-Kommando umask die eigene Einschränkungsmaske zu ändern. Innerhalb eines Listings z.B. würde die neu gesetzte umask von 0 nur während der Ausführung des Programms (und der Unterprozesse) gültig. Dazu kann man bspw. einen entsprechenden umask-Aufruf in einer Startup-Datei wie .profile eintragen, so dass beim Start einer entsprechenden Shell die Einschränkungsmaske automatisch gesetzt wird.
|
Im folgenden Listing soll eine Datei zum Lesen und Schreiben geöffnet werden. Existiert diese nicht, wird eine neue Datei erzeugt. Falls sie existiert, wird der Inhalt gelöscht und neu beschrieben. Hier der Quellcode, der portabel gehalten wurde:
/* open1.c */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __unix__
#include <unistd.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
#include <io.h>
#endif
int main(void) {
int fh;
if((fh=open("adress.txt", O_RDWR|O_CREAT|O_TRUNC))==-1) {
perror("Fehler bei open()");
return EXIT_FAILURE;
}
close(fh);
return EXIT_SUCCESS;
}
Falls Sie jetzt noch die Zugriffsrechte auf diese Datei vergeben wollen, muss für Linux/UNIX die Headerdatei <sys/stat.h> und für MS-DOS/Windows <sys\stat.h> eingebunden werden (beachten Sie den Slash und Backslash). Soll bspw. der User unter Linux diese Datei nur lesen dürfen, so muss nur mithilfe des ODER-Operators der Modus S_IRUSR hinzugefügt werden:
// Alle Zugriffsrechte der Einschränkungsmaske erlauben
umask(0);
open("adress.txt", O_RDWR|O_CREAT|O_TRUNC, S_IRUSR)) == –1)
Ein weiteres Beispiel mit open() ist z.B. das Aufrufen von Programmen aus der Kommandozeile:
/* open2.c */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __unix__
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
#include <io.h>
#include <sys\stat.h>
#endif
int main(int argc, char **argv) {
int fh;
if( (fh=open(*++argv,O_RDONLY)) == –1)
perror(*argv);
else
close(fh);
return EXIT_SUCCESS;
}
In diesem Beispiel wird eine Datei zum Lesen geöffnet, deren Name als zweites Argument in der Kommandozeile angegeben wurde. Falls die Datei nicht existiert, wird eine entsprechende Fehlermeldung wie
Programmname : No such File in Directory
ausgegeben. Danach wird der File-Deskriptor wieder geschlossen. Falls die Datei, die eben aufgerufen wurde, nicht existiert, aber anschließend erzeugt werden soll, dann ist dies so möglich:
/* open3.c */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __unix__
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
#include <io.h>
#include <sys\stat.h>
#endif
int main(int argc,char **argv) {
int fh;
if(*++argv == NULL)
return EXIT_FAILURE; /* Keine Argumente vorhanden */
if( (fh = open(*argv, O_RDONLY)) == –1)
if( (fh = open(*argv,O_RDWR|O_CREAT)) == –1)
perror(*argv);
close(fh);
return EXIT_SUCCESS;
}
Hiermit wird zuerst versucht, die Datei zu öffnen. Falls die Datei nicht existiert, wird diese gleich zum Lesen und Schreiben erzeugt.
Bei der Erläuterung der Low-Level-Datei-E/A-Funktionen konnten Sie schon erkennen, warum eher auf die höhere Ebene zurückgegriffen wird. Wer auf mehreren Systemen programmiert, kann schnell durcheinander kommen. Was bei dem einen System gelingt, ist bei dem anderen nicht machbar. Andererseits ist es durchaus hilfreich, beim Erzeugen einer Datei die Zugriffsrechte von Beginn an festzulegen (gilt speziell unter Linux/UNIX). Daraus ergibt sich, dass Low-Level-Datei-E/A vorwiegend zur Systemprogrammierung eingesetzt werden.
Vor allem für Linux/UNIX-Programmierer ist diese Art, eine Datei (auch Gerätedateien) zu öffnen, eine sehr wichtige Schnittstelle. Bspw. kann man hier mit folgendem einfachen Code-Konstrukt etwas auf dem Drucker ausgeben lassen:
int fd;
// Drucker auf /dev/lp0 zum Schreiben öffnen
fd = open("/dev/lp0", O_WRONLY);
if(fd >= 0)
// Drucken ...
write(fd, buf, buf_size);
close(fd);
|