2.4 Standard-E/A-Funktionen
Um mit der Linux-Programmierung zu beginnen, habe ich Sie zu Beginn des Buches bereits darauf hingewiesen, dass die Voraussetzung in der Programmiersprache C gegeben sein muss. Und jedes gute Einsteiger-Buch zur C-Programmierung behandelt die grundlegenden Standard-E/A-Funktionen, und somit setze ich voraus, dass Sie damit schon vertraut sind. Daher werden Sie auf den folgenden Seiten nur eine Schnellübersicht zu den einzelnen Funktionen erhalten.
Die Funktionen, die hierbei beschrieben werden, sind in der Headerdatei <stdio.h> definiert und größtenteils von ANSI C vorgeschrieben. Der Vorteil dieser Funktionen gegenüber den elementaren ist, dass diese auch auf Betriebssystemen jenseits von Linux/UNIX vorhanden sind und Sie sich daher relativ wenig um Low-Level-E/A kümmern müssen, da mit optimal eingestellten Puffern gearbeitet wird.
2.4.1 Der FILE-Zeiger
Der große Unterschied zwischen den elementaren und den Standard-E/A-Funktionen liegt darin, dass die Standardfunktionen einen Zeiger vom Typ FILE statt eines Filedeskriptors verwenden. Dabei handelt es sich um eine Struktur, die in etwa wie folgt aussieht:
typedef struct _iobuf {
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
Darin sind Informationen enthalten wie:
|
Anfangsadresse des Puffers |
|
Puffergröße |
|
aktueller Pufferzeiger |
|
Filedeskriptor |
|
Position des Schreib-/Lesezeigers |
|
Fehlerflag |
|
EOF-Flag |
Wie Sie sicherlich bemerkt haben, verwenden die Standard-E/A-Funktionen auch einen Filedeskriptor. Daraus lässt sich schließen (Sie wissen es bereits), dass die Standardfunktionen auf den elementaren Funktionen aufgebaut sind.
Hinweis Bei dem FILE-Zeiger spricht man auch von einem Datenstrom (englisch Stream).
|
Natürlich gibt es auch hierbei die drei vordefinierten Standardstreams, die jeder Prozess automatisch bereitstellt:
|
FILE* stdin – Standard-engabestrom Iist voreingestellt auf die Tastatur. |
|
FILE* stdout – Standardausgabestrom ist voreingestellt auf den Monitor. |
|
FILE* stderr – Standardausgabestrom für Fehlermeldungen ist voreingestellt auf den Monitor. |
2.4.2 Öffnen und Schließen von Dateien
Hier die Funktionen zum Öffnen und Schließen unter der Verwendung des FILE-Zeigers:
FILE *fopen(const char *pfad, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *pfad, const char *mode, FILE *stream);
int fclose(FILE *stream);
Abgesehen von der Funktion fdopen() (es wurde bereits darüber geschrieben) sind alle Funktionen Teil des ANSI C-Standards. Bei allen Funktionen, mit Ausnahme von fclose(), wird im Falle eines Fehlers NULL, ansonsten der FILE-Zeiger zurückgeben.
Die Funktion fopen() öffnet eine Datei, die mit pfad angegeben wurde, im Modus mode. Die einzelnen Modi finden Sie in der gleich folgenden Tabelle aufgelistet.
Mit der Funktion freopen() können Sie eine Datei mit einem bereits existierenden Stream öffnen. Dabei wird versucht, erst diesen Stream (drittes Argument) zu schließen. Fehler beim Schließen werden dabei ignoriert! Anschließend wird versucht, diesen Stream der Datei pfad zuzuordnen. freopen() wird vorwiegend eingesetzt, um die Streams stdin, stdout und stderr umzuleiten.
Mit fclose() schließen Sie die Datei wieder. Zuvor werden alle noch nicht geschriebenen Daten im Ausgabepuffer in die entsprechende Datei geschrieben. Der Eingabepuffer hingegen wird geleert.
Hierzu jetzt die versprochene Tabelle mit den entsprechendegin Modi, die bei den eben erwähnten Funktionen für den Parameter mode verwendet werden können.
Tabelle 2.9
Modi zum Öffnen für den FILE-Zeiger
mode
|
Lesen
|
Schreiben
|
Position
|
Inhalt gelöscht
|
Datei erzeugen
|
Binär/Text
|
r
|
ja
|
nein
|
Anfang
|
nein
|
nein
|
Text
|
r+
|
ja
|
ja
|
Anfang
|
nein
|
nein
|
Text
|
w
|
nein
|
ja
|
Anfang
|
ja
|
ja
|
Text
|
w+
|
ja
|
ja
|
Anfang
|
ja
|
ja
|
Text
|
a
|
nein
|
ja
|
Ende
|
nein
|
ja
|
Text
|
a+
|
ja
|
ja
|
Ende
|
nein
|
ja
|
Text
|
rb
|
ja
|
nein
|
Anfang
|
nein
|
nein
|
Binär
|
rb+,
r+b
|
ja
|
ja
|
Anfang
|
nein
|
nein
|
Binär
|
wb
|
nein
|
ja
|
Anfang
|
ja
|
ja
|
Binär
|
wb+,
w+b
|
ja
|
ja
|
Anfang
|
ja
|
ja
|
Binär
|
ab
|
nein
|
ja
|
Ende
|
nein
|
ja
|
Binär
|
ab+,
a+b
|
ja
|
ja
|
Ende
|
nein
|
ja
|
Binär
|
2.4.3 Formatierte Ausgabe
Zur formatierten Ausgabe auf einem Stream steht Ihnen die ganze printf()-Familie zur Verfügung. Mit diesen Funktionen dürfte auch niemand Probleme haben, wenn er nur geringe Erfahrungen in C hat. Hier der Überblick zu den nötigsten printf-Varianten und deren Bedeutung:
Tabelle 2.10
printf()-Varianten zur formatierten Ausgabe
Syntax
|
Bedeutung
|
int printf(
const char *format, ... );
|
Formatiert das Schreiben auf stdout.
|
int fprintf( FILE *stream,
const char *format, ... );
|
Formatiert das Schreiben in einem Stream.
|
int sprintf( char *string,
const char *format, ... );
|
Formatiert das Schreiben in einen String.
|
int snprintf( char *string,
size_t size,
const char format, ... );
|
Formatiert das Schreiben in einen String mit Längenbegrenzung (size).
|
Alle Funktionen geben die Anzahl der erfolgreich geschriebenen Zeichen oder bei einem Fehler einen negativen Wert zurück. Außerdem wird empfohlen, sprintf() zu vermeiden, da diese Funktion keine Längenüberprüfung vornimmt und somit anfällig für Buffer Overflows (und dadurch Stack Smashes) ist. Verwenden Sie stattdessen die n-Version snprintf(), sofern diese vorhanden ist. Sollte dies nicht der Fall sein, so empfiehlt sich ein »portables printf«, das Sie Ihrem Projekt hinzufügen können.
Neben diesen printf-Versionen gibt es noch die v-Versionen, womit der Funktion eine in der Länge variable Argumentenliste übergeben wird. Hier also die v-Versionen von printf:
Tabelle 2.11
Weitere printf()-Versionen mit <stdarg.h>
Syntax
|
Bedeutung
|
int vprintf( const char *format,
va_list ap );
|
Formatiert das Schreiben auf stdout mit Argumentenzeiger.
|
int vfprintf( FILE *stream,
const char *format,
va_list ap );
|
Formatiert das Schreiben in einem Stream mit Argumentenzeiger.
|
int vsprintf( char *string,
const char *format,
va_list ap );
|
Formatiert das Schreiben in einen String mit Argumentenzeiger.
|
int vsnprintf( char *string,
size_t size,
const char format,
va_list ap );
|
Formatiert das Schreiben in einen String mit Argumentenzeiger inklusive einer Längenbegrenzung (size).
|
Bei den v-Versionen der printf-Familie müssen Sie die Headerdatei <stdarg.h> mit einbinden. Auch bei diesen printf-Versionen wird die Anzahl der erfolgreich geschriebenen Zeichen oder bei einem Fehler ein negativer Wert zurückgegeben.
Hinweis Es ist noch anzumerken, dass es außerhalb des ANSI C-Standards noch eine Reihe weiterer printf() gibt wie z. B. asprintf(), vasprintf(), asnprintf(), vasnprintf().
|
2.4.4 Formatierte Eingabe
Das Gegenstück zur printf-Familie ist die scanf-Familie, die zur formatierten Eingabe aus einem Stream konzipiert wurde. Auch hierbei gibt es verschiedene Versionen, die normale, die f-Versionen und die s-Verisonen.
Hinweis Funktionen, die mit f beginnen, beziehen sich auf einen beliebigen Stream. Die Funktionen mit s stehen für die String-Versionen, und v steht für den Argumentenlistenzeiger. Die Versionen ohne eine Bezeichnung verwenden in der Regel den Standardstream, was bei scanf() stdin und bei printf() stdout wäre. Man sieht also, die Leute vom ANSI C-Komitee haben sich schon etwas bei der Namenskonvention gedacht. Natürlich können f-Versionen auch für die Standardstreams verwendet werden.
|
Hier ein Überblick zu den scanf-Versionen zur formatierten Eingabe:
Tabelle 2.12
scanf()-Varianten zur formatierten Eingabe
Syntax
|
Bedeutung
|
int scanf(
const char *format, ... );
|
Formatiertes Lesen von stdin
|
int fscanf( FILE *stream,
const char *format,... );
|
Formatiertes Lesen aus einem beliebigen Stream
|
int sscanf(
const char *string,
const char *format, ... );
|
Formatiertes Lesen aus einem String
|
Alle Funktionen geben die Anzahl der gelesenen Zeichen oder bei einem Fehler EOF zurück.
2.4.5 Binäres Lesen und Schreiben
Als wahrer Zwitter lassen sich die Funktionen fread() und fwrite() bezeichnen, da diese Funktionen weder zu 100 % in die höhere noch in die niedrigere Ebene zu passen scheinen. Die Daten werden dabei im Binärmodus mit einer festen Satzstruktur bearbeitet. Damit können Sie mit fread() und fwrite() ganze Speicherblöcke lesen bzw. schreiben. Hier die Syntax dazu:
#include <stdio.h>
size_t fread(void *ptr, size_t blkgroesse,
size_t blkanzahl, FILE *stream);
size_t fwrite(const void *ptr, size_t blkgroesse,
size_t blkanzahl, FILE *stream);
Beide Funktionen geben bei Erfolg die Anzahl der gelesenen bzw. geschriebenen Blöcke (nicht Bytes) zurück. Das erste Argument ist der Puffer, aus dem gelesen oder in dem geschrieben werden soll. Mit dem zweiten Argument geben Sie die Blockgröße an, die gelesen bzw. geschrieben werden soll. Das dritte Argument erwartet die Anzahl der Blöcke mit Blockgröße, und das letzte Argument ist der Stream, woraus gelesen bzw. wohin geschrieben werden soll. Eine Fehlerüberprüfung sollten Sie mit den Funktionen feof() und ferror() einrichten, um zu erfahren, was schief gelaufen ist.
2.4.6 Zeichen- und zeilenweise Ein-/Ausgabe
Zur zeichen- und zeilenweisen Ein-/Ausgabe ist eine Menge Funktionen vorhanden. Diese Funktionen gibt es in zwei Geschmacksrichtungen, eine Sorte, die mit Zeichen handelt, und die andere, die mit Strings arbeitet. Hier die Syntax dieser Funktionen:
#include <stdio.h>
int fgetc( FILE *stream );
char *fgets(char *s, int size, FILE *stream );
int getc( FILE *stream );
int getchar(void );
char *gets( char *s );
int ungetc( int c, FILE *stream );
int fputc( int c, FILE *stream );
int fputs( const char *s, FILE *stream );
int putc( int c, FILE *stream );
int putchar( int c );
int puts( char *s );
Jede Funktion, die ein int liefert, gibt bei einem Fehler einen negativen Wert (EOF) zurück. Die Funktionen, die einen Zeiger auf ein char liefert, geben entweder die Anfangsadresse eines Strings zurück oder bei einem Fehler NULL. In der folgenden Tabelle sind diese Funktionen und ihre Eigenschaften nochmals zusammengefasst.
Tabelle 2.13
Zeichen- und zeilenweise E/A-Funktionen
Funktion
|
E/A
|
Größe
|
Stream
|
Newline
|
fgetc
|
Eingabe
|
Zeichen
|
alle
|
–
|
fgets
|
Eingabe
|
Zeile
|
alle
|
behalten
|
getc
|
Eingabe
|
Zeichen
|
alle
|
–
|
getchar
|
Eingabe
|
Zeichen
|
stdin
|
–
|
gets
|
Eingabe
|
Zeile
|
stdin
|
entfernt
|
ungetc
|
weder noch
|
Zeichen
|
alle
|
–
|
fputc
|
Ausgabe
|
Zeichen
|
alle
|
–
|
fputs
|
Ausgabe
|
Zeile
|
alle
|
ohne
|
putc
|
Ausgabe
|
Zeichen
|
alle
|
–
|
putchar
|
Ausgabe
|
Zeichen
|
stdout
|
–
|
puts
|
Ausgabe
|
Zeile
|
stdout
|
mit
|
Es wird außerdem empfohlen, ganz auf die Funktion gets() zu verzichten und stattdessen die Funktion fgets() zu verwenden. gets() hat keine Längenbegrenzung und ist somit auch für den Buffer Overflows anfällig. Beachten Sie außerdem, dass fgets() das Newline-Zeichen anders behandelt als gets(). Weil beim Einlesen von der Standardeingabe mit fgets() auch das '\n'-Zeichen mit eingelesen wird, verwenden einige Programmierer, sei es aus Faulheit oder mangelndem Wissen, die Funktion gets() – die kein Newline-Zeichen anfügt.
2.4.7 Status der Ein-/Ausgabe überprüfen
Mit den Funktionen feof() und ferror() können Sie den aktuellen Status eines Streams überprüfen. Hier die Syntax dazu:
#include <stdio.h>
int feof( FILE *stream );
int ferror( FILE *stream );
void clearerr( FILE *stream );
Die Funktion feof() gibt einen Wert ungleich 0 zurück, wenn das EOF-Flag gesetzt ist. Ebenso gibt die Funktion ferror() einen Wert ungleich 0 zurück, wenn das Fehlerflag gesetzt ist. Wollen Sie einen der beiden Funktionen nochmals auf einem Stream verwenden, müssen Sie das EOF-Flag bzw. das Fehlerflag mit der Funktion clearerr() wieder löschen.
2.4.8 Stream positionieren
Das Positionieren des Dateizeigers funktioniert in der Regel ähnlich wie bei der niedrigeren Ebene. Im Prinzip verwendet man dabei auch die niedrigere Ebene, um den Schreib-/Lesezeiger zu versetzen. Hier ein Überblick über die Funktionen.
Tabelle 2.14
Dateiposition
Syntax
|
Bedeutung
|
int fseek( FILE *stream,
long offset, int wie );
|
Schreib-/Lesezeiger positionieren
|
long ftell( FILE *stream );
|
Position des Schreib-/Lesezeigers abfragen
|
void rewind( FILE *stream );
|
Schreib-/Lesezeiger wieder zurück an den Anfang positionieren
|
int fgetpos( FILE *stream,
fpos_t *pos );
|
Position des Schreib-/Lesezeigers abfragen (2. Möglichkeit)
|
int fsetpos(FILE *stream,
fpos_t *pos );
|
Schreib-/Lesezeiger positionieren (2. Möglichkeit)
|
Die Funktion fseek() funktioniert analog wie die Funktion lseek() der niedrigeren Ebene, nur dass hierbei statt eines Filedeskriptors ein FILE-Zeiger verwendet wird. Auch hierbei hängt es vom dritten Parameter ab, wie der zweite Parameter interpretiert werden soll. Dabei sind die gleichen Konstanten zu verwenden wie schon bei der Funktion lseek():
Tabelle 2.15
Konstanten für »wie«
Konstante
|
Bedeutung
|
SEEK_SET
|
Den Schreib-/Lesezeiger vom Anfang der Datei um abstand Bytes versetzen. abstand darf dabei keine negative Zahl sein.
|
SEEK_CUR
|
Den Schreib-/Lesezeiger von der aktuellen Position der Datei um abstand Bytes versetzen. abstand darf dabei sowohl eine positive als auch eine negative Zahl sein.
|
SEEK_END
|
Den Schreib-/Lesezeiger vom Ende der Datei um abstand Bytes versetzen. abstand darf dabei sowohl eine positive als auch eine negative Zahl sein.
|
Hinweis Auch wenn fseek() hier dieselben Konstanten verwendet wie die Low-Level-Funktion lseek(), sollten Sie niemals Low-Level und High-Level-Funktionen in einer Anwendung vermischen, sofern man nicht Vorkehrungen dazu getroffen hat (z. B. durch fflush()), denn wie vorher erläutert, positioniert fseek() aufgrund der Pufferung nicht sofort.
|
Der erste Parameter ist der FILE-Zeiger der geöffneten Datei, wo der Schreib-/Lesezeiger versetzt werden soll. Bei Erfolg gibt fseek() 0, ansonsten bei einem Fehler ungleich 0 zurück.
Mit ftell() können Sie die aktuelle Position des Schreib-/Lesezeigers abfragen. Zurückgegeben wird dabei entweder die aktuelle Position in Bytes oder bei einem Fehler -1. Die aktuelle Position wird dabei vom Dateianfang zurückgegeben. fseek() und ftell() unterstützen trotz des o. g. LARGEFILE-Supports nur 32 Bit (wegen ihrer Prototypen). Dagegen kann ftello() / fseeko() helfen.
Mit der Funktion rewind() können Sie den Schreib-/Lesezeiger wieder auf den Dateianfang zurücksetzen, aber fseek(fp, 0, SEEK_SET) macht dasselbe und passt besser zu den anderen fseek() in Ihrer Anwendung.
Eine zweite Möglichkeit zur Positionierung des FILE-Zeigers haben Sie mit fsetpos(). Dabei können Sie den Schreib-/Lesezeiger auf die Position des Zeigers pos setzen. Diese Adresse sollten Sie allerdings zuvor mit der Funktion fgetpos() ermittelt haben. Diese Funktion setzt ebenfalls die EOF- und Fehlerflags zurück.
Mit fgetpos() ermitteln Sie die Position des aktuellen Schreib-/Lesezeigers. Dieser Wert befindet sich anschließend in der Adresse des Zeigers pos. Diesen Zeiger sollten Sie dann nur noch verwenden, um mit der Funktion fsetpos() den Schreib-/Lesezeiger wieder auf seinen ursprünglichen Wert zurückzusetzen.
Beide Funktionen, fgetpos() und fsetpos(), geben bei Erfolg 0 und bei einem Fehler ungleich 0 zurück.
2.4.9 Puffer kontrollieren
Dem Programmierer stehen drei grundlegende Möglichkeiten zur Einstellung des Puffers der Standard-E/A-Funktionen zur Verfügung, ungepuffert, zeilengepuffert und voll gepuffert. Des Weiteren gibt es noch die Möglichkeit, alle Inhalte eines nicht geleerten Puffers in eine Datei zu übertragen. Hier die Funktionen, womit Sie den Puffer kontrollieren können:
#include <stdio.h>
int fflush(FILE *stream);
int setbuf(FILE *stream, char *buf);
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
Die Funktion setbuf() wird nur noch aus Kompatibilitätsgründen erhalten und soll daher hier nicht näher erläutert werden. Mit der Funktion setvbuf() können Sie einen expliziten Puffer einstellen. Dazu müssen Sie nur für den Parameter mode eine der folgenden Konstanten einsetzen:
Tabelle 2.16
Konstanten für die Einstellung des Puffers mit setvbuf()
Puffertyp (modus)
|
Bedeutung
|
_IOLBF
|
Datei wird zeilenweise gepuffert .
|
_IONBF
|
Ein-/Ausgabe wird gar nicht gepuffert .
|
_IOFBF
|
Ein-/Ausgabe wird voll gepuffert.
|
Natürlich gilt, wenn für mode die Konstante _IONBF gesetzt wird, werden die Argumente buf und size ignoriert. Ansonsten geben Sie bei den Konstanten _IOLBF und _IOFBF mit buf die Pufferadresse und mit size die Puffergröße an. Verwenden Sie für buf den NULL-Zeiger, stellt die Funktion einen eigenen Puffer mit entsprechender Größe zur Verfügung. Diese Größe kann entweder der Wert von st_blksize der Struktur stat oder die Konstante BUFSIZ der Headerdatei <stdio.h> sein.
Bei alten BSD-Programmen kann es vorkommen, dass Sie die Funktionen setlinebuf() und setbuffer() sichten. Diese Funktionen gelten als veraltet, und es wird ebenfalls geraten, die Funktion setvbuf() zu verwenden.
Voreingestellte Puffer
Der ANSI C-Standard schreibt Folgendes für die Pufferung vor:
|
stderr darf niemals voll gepuffert werden. |
|
stdin und stdout dürfen nur dann voll gepuffert sein, wenn diese nicht auf ein interaktives Gerät (z. B. Tastatur) zeigen. |
In SVR 4 hingegen sind noch folgende Vorschriften implementiert:
|
Normale Streams, die auf ein Terminal eingestellt sind, sind immer zeilengepuffert. Wenn sich diese Streams auf kein Terminal beziehen, wird Vollpufferung verwendet. |
|
stderr ist immer ungepuffert (siehe »ANSI C« oben, Punkt 1). |
Flushing
Um den Inhalt von einem (möglicherweise nicht geleertem) Puffer zu leeren, können Sie die Funktion fflush() verwenden. Wenden Sie fflush() auf einen Stream an, aus dem gelesen wird, liegt ein undefiniertes Verhalten vor. Ebenfalls können Sie fflush() mit dem NULL-Zeiger als Argument aufrufen. In diesem Fall werden alle Ausgabepuffer, woraus nicht gelesen wird, übertragen.
Es gibt außerdem folgende Möglichkeiten, wie ein Puffer automatisch entleert wird. Wenn:
|
der Puffer voll ist, |
|
der Stream geschlossen wird (fclose), |
|
das Programm normal beendet wird, |
|
eine neue Zeile geschrieben wird und der Stream zeilengepuffert ist. |
2.4.10 Datei löschen und umbenennen
Mit der Funktion remove() können Sie eine Datei entfernen, und mit rename() können Sie eine Datei umbenennen. Hier die Syntax der beiden Funktionen:.
#include <stdio.h>
int remove( const char *pfad );
int rename( const char *pfad_alt, const char *pfad_neu );
Beide Funktionen erwarten als erstes Argument den Pfadnamen einer existierenden Datei. Bei der Funktion rename() wird als zweites Argument angegeben, durch welchen Namen pfad_alt ersetzt werden soll. Bei Erfolg geben beide Funktionen 0 und bei einem Fehler -1 zurück.
Hinweis Auf Dateien verhält sich remove() identisch zur Funktion unlink(), und auf Verzeichnissen ist remove() mit rmdir() äquivalent.
|
2.4.11 Temporäre Dateien erstellen
Zum Erstellen von temporären Dateien können Sie entweder die ANSI C-Funktionen tmpfile() und tmpnam() verwenden oder die in UNIX implementierten Funktionen mkstemp() und mktemp().
#include <stdio.h>
FILE *tmpfile(void);
char *tmpnam(char *s);
#include <unistd. h.>
int mkstemp(char *template) ;
char *mktemp(char *template) ;
Die Funktion tmpfile() öffnet eine temporäre Datei. Mit tmpnam() kann ein Dateiname erstellt werden, der als Name für die temporäre Datei verwendet wird. Wird die Funktion tmpnam() mit dem NULL-Zeiger als Argument verwendet, wird die Datei in einem static-Speicherbereich untergebracht (was nicht thread-safe ist), und die Adresse wird als Funktionswert wiedergegeben. Das bedeutet, dass nachfolgende Aufrufe der Funktion tmpnam() dieselbe Adresse einnehmen. Aus diesem Grund sollte zuerst umkopiert werden, um die alte Adresse nicht zu überschreiben. Wird tmpnam() ohne den NULL-Zeiger aufgerufen, wird für s ein Adressbereich adressiert, der L_tmpnam Zeichen aufnehmen kann. L_tmpnam ist ebenso in der Headerdatei <stdio.h> deklariert.
Die Funktion mktemp() erzeugt einen eindeutigen, temporären Dateinamen. Dieser Name wird aus template erzeugt. Dazu müssen die letzten sechs Buchstaben des Parameters template XXXXXX sein, diese werden dann durch eine Zeichenfolge ersetzt, die diesen Dateinamen eindeutig macht. Die Funktion mktemp() gibt bei Erfolg einen Zeiger auf template zurück. Bei einem Fehler ist der Rückgabewert NULL. Der Fehlercode EINVAL wird zurückgegeben, wenn die letzten sechs Buchstaben von template nicht XXXXXX waren.
Es besteht allerdings ein (und nicht nur dieses) Sicherheitsloch bei dieser Funktion. Zwischen der Zeit, wo der Pfadname konstruiert und die Datei mit open() geöffnet wird, kann ein anderer Prozess eine Datei oder einen Link mit demselben Namen erzeugen. Um dieses Problem zu umgehen, sollten Sie den so erzeugten temporären Dateinamen beim Öffnen mit open() mit dem Flag O_EXCL öffnen oder gleich die Funktion mkstemp() verwenden. Das Flag O_EXCL garantiert Ihnen, dass diese Datei nur dann erzeugt wird, wenn der Name noch nicht existiert.
Die Funktion mkstemp() – die einzige sicherere Alternative – erzeugt einen temporären Dateinamen und öffnet diese Datei mittels open() und dem Flag 0 EXCL gleich für Sie. Bei Erfolg gibt Ihnen diese Funktion einen Filedeskriptor zum Lesen und Schreiben auf diese temporäre Datei zurück. Dank des Flags O_EXCL ist sichergestellt, dass die so erzeugte temporäre Datei wirklich einzigartig ist. Die geöffnete Datei verwendet den Modus 0600 und ist standardmäßig nur dem Eigentümer vorbehalten. Wenn andere User darauf zugreifen sollen (dürfen), müssen Sie das ändern (z. B. mit fchmod(), fchown()). mkstemp() liefert bei einem Fehler -1 zurück, wenn die Datei nicht erzeugt oder geöffnet werden kann.
|