1 Theorie | |
→ | 1.2 Häufig auftretende Problemstellungen |
Informationen über eine Datei werden unter Linux/Unix in eine
Struktur vom Typ struct stat geschrieben.
Auf Windows-Systemen existiert eine ganze Familie von Datentypen,
ich empfehle derzeit die Anwendung von
struct __stat64.
Nicht alle Elemente des POSIX-Datentyps struct stat sind auch unter Windows in struct __stat64 verfügbar. Die nachfolgende Tabelle führt die Elemente auf.
Element | Datentyp auf POSIX- Systemen |
Datentyp auf Windows- Systemen |
Inhalt |
---|---|---|---|
st_mode | mode_t | unsigned short | Dateityp und Zugriffsrechte (unterschiedliche Bedeutung und unterschiedlicher Inhalt unter Windows und Linux/Unix) |
st_size | off_t | _off_t | Dateigröße in Bytes |
st_ctime | time_t | __time64_t | Zeitpunkt, an dem die Datei erzeugt wurde |
st_mtime | time_t | __time64_t | Zeitpunkt, wann die Datei zuletzt modifiziert wurde |
st_atime | time_t | __time64_t | Zeitpunkt, wann zuletzt auf die Datei zugegriffen wurde |
st_nlink | nlink_t | short | Anzahl harter Links (nur unter Linux/Unix relevant, unter Windows i.d.R. 1) |
st_uid | uid_t | short | Nutzer-ID des Eigentümers (unter Windows nicht benutzt) |
st_gid | gid_t | short | Gruppen-ID der Eingentümer-Gruppe (unter Windows nicht benutzt) |
st_dev | dev_t | _dev_t | ID des Gerätes (z.B. Festplatte oder Festplattenpartition), auf dem sich die Datei befindet |
st_rdev | dev_t | _dev_t | Relative Geräte-ID |
st_ino | ino_t | _ino_t | Inode-Number (nur von Bedeutung für Dateisysteme, die Inodes verwenden, z.B. unter Linux/Unix) |
st_blksize | blksize_t | — | Blockgröße des Dateisystems |
st_blocks | blkcnt_t | — | Anzahl belegter 512-Byte-Blöcke |
Im Element st_mode sind sowohl der Dateityp als auch die Zugriffsrechte gespeichert.
Um den Dateityp zu ermitteln, wird das Element st_mode
mit der Bit-Maske _S_IFMT und-verknüpft. Dadurch werden alle
sonstigen Bits des Elements st_mode (z.B. die Bits für die
Zugriffsrechte) auf 0 gesetzt, nur die Bits für die Angaben des
Dateityps bleiben unverändert.
Das Ergebnis ist einer der folgenden Werte:
Konstante | Bedeutung |
---|---|
_S_IFDIR | Verzeichnis |
_S_IFREG | reguläre (normale) Datei |
_S_IFCHR | byteorientiertes Gerät |
_S_IFIFO | Pipe für die Interprozess-Kommunikation |
Die Information, welche Zugriffsrechte der aktuelle Benutzer
hat, ist in den Bits _S_IREAD, _S_IWRITE bzw. S_IEXEC
gespeichert.
Ergibt die und-Verknüpfung des Elementes st_mode mit der
entsprechenden Konstante einen Wert ungleich 0, so ist das
entsprechende Bit in st_mode gesetzt und der Zugriff ist
erlaubt.
Konstante | Bedeutung |
---|---|
_S_IREAD | der aktuelle Benutzer darf lesen |
_S_IWRITE | der aktuelle Benutzer darf schreiben |
_S_IEXEC | der aktuelle Benutzer darf die Datei ausführen bzw. ein Verzeichnis durchsuchen |
Die Dateigröße ist in einem Element vom Typ _off_t gespeichert. Die Online-Hilfe zu _stat64() besagt, dass es sich um eine 64-Bit-Zahl handelt. Wir konvertieren die Zahl zu uintmax_t und geben Sie mit dem Platzhalter "%I64u" aus.
In der Struktur struct __stat64 werden die
Zeitpunkte von Dateierzeugung, letzter Änderung und letzen Zugriff
in Elementen vom Typ __time64_t gespeichert.
Um diesen Zeitpunkt-Datentyp in Komponenten zu zerlegen, muss die
Funktion _localtime64_s() verwendet werden.
/** @file ex040.c Demo-Programm fuer Datei-Attribute auf Windows-Systmen.
ex040 <Dateiname>
*/
/* Copyright (C) 2014-2017 - HS Schmalkalden. All rights reserved. */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
/** Hauptprogramm.
@param argc Anzahl der Kommandozeilenargumente.
@param argv Feld mit Kommandozeilenargumenten.
@return 0 bei Erfolg, alle anderen Werte deuten auf Fehler hin.
*/
int
main(int argc, char *argv[])
{
struct __stat64 stbuf;
struct tm timebuf;
int exc = EXIT_FAILURE;
/* Es muss ein Argument angegeben sein: Dateiname */
if (2 != argc) {
/* !!! Falsche Argumente-Anzahl */
goto finished;
}
/* Versuche, Datei-Attribute abzufragen */
if (0 != _stat64(argv[1], &stbuf)) {
/* !!! Abfrage der Dateiattribute fehlgeschlagen */
goto finished;
}
/* Bis jetzt erfolgreich */
exc = EXIT_SUCCESS;
/* Allgemeine Informationen ueber Datei ausgeben */
printf("Datei-Name: %s\n", argv[1]);
printf("Groesse: %I64u\n", (uintmax_t)(stbuf.st_size));
/* Dateityp ermitteln und ausgeben */
switch(stbuf.st_mode & _S_IFMT) {
case _S_IFREG: {
printf("Dateityp: Regulaere Datei\n");
} break;
case _S_IFDIR: {
printf("Dateityp: Verzeichnis\n");
} break;
case _S_IFCHR: {
printf("Dateityp: Byteorientiertes Geraet\n");
} break;
case _S_IFIFO: {
printf("Dateityp: FIFO (benannte Pipe)\n");
} break;
default: {
printf("Dateityp: Unbekannt\n");
exc = EXIT_FAILURE;
} break;
}
/* Eigentuemer ausgeben */
printf("Eigentuemer-UID: %d\n", (int)(stbuf.st_uid));
printf("Eigentuemer-GID: %d\n", (int)(stbuf.st_gid));
printf(
"Berechtigungen: %c%c%c\n",
((0 != (stbuf.st_mode & _S_IREAD)) ? 'r' : '-'),
((0 != (stbuf.st_mode & _S_IWRITE)) ? 'w' : '-'),
((0 != (stbuf.st_mode & _S_IEXEC)) ? 'x' : '-')
);
/* Weitere Informationen ausgeben */
printf("Link-Anzahl: %d\n", (int)(stbuf.st_nlink));
printf("Device-ID: %I64u\n", (uintmax_t)(stbuf.st_dev));
printf("Rel. Device-ID: %I64u\n", (uintmax_t)(stbuf.st_rdev));
printf("Inode-Nummer: %I64u\n", (uintmax_t)(stbuf.st_ino));
/* Zeitpunkt ausgeben, wann Datei angelegt wurde */
if (0 == _localtime64_s(&timebuf, &(stbuf.st_ctime))) {
printf(
"Erzeugungszeitpunkt: %04d-%02d-%02d %02d:%02d:%02d\n",
(1900 + timebuf.tm_year),
(1 + timebuf.tm_mon),
timebuf.tm_mday,
timebuf.tm_hour,
timebuf.tm_min,
timebuf.tm_sec
);
}
else {
exc = EXIT_FAILURE;
}
/* Zeitpunkt der letzten Aenderung ausgeben */
if (0 == _localtime64_s(&timebuf, &(stbuf.st_mtime))) {
printf("Modifikationszeitpunkt: %04d-%02d-%02d %02d:%02d:%02d\n",
(1900 + timebuf.tm_year),
(1 + timebuf.tm_mon),
timebuf.tm_mday,
timebuf.tm_hour,
timebuf.tm_min,
timebuf.tm_sec
);
}
else {
exc = EXIT_FAILURE;
}
/* Zeitpunkt des letzten Zugriffes auf Datei ausgeben */
if (0 == _localtime64_s(&timebuf, &(stbuf.st_atime))) {
printf("Zugriffszeitpunkt: %04d-%02d-%02d %02d:%02d:%02d\n",
(1900 + timebuf.tm_year),
(1 + timebuf.tm_mon),
timebuf.tm_mday,
timebuf.tm_hour,
timebuf.tm_min,
timebuf.tm_sec
);
}
else {
exc = EXIT_FAILURE;
}
finished:
exit(exc); return exc;
}
/* vim: set ai sw=4 ts=4 expandtab : */
Das Kommando
ex040 ex040.c
ergibt die Ausgabe:
Datei-Name: ex040.c Groesse: 3610 Dateityp: Regulaere Datei Eigentuemer-UID: 0 Eigentuemer-GID: 0 Berechtigungen: rw- Link-Anzahl: 1 Device-ID: 2 Rel. Device-ID: 2 Inode-Nummer: 0 Erzeugungszeitpunkt: 2014-09-19 15:19:48 Modifikationszeitpunkt: 2014-09-19 16:26:27 Zugriffszeitpunkt: 2014-09-19 15:37:55
Liegt der Dateiname als WCHAR- bzw. TCHAR-String vor, muss anstelle von _stat64() die Funktion _wstat64() bzw. _tstat64() verwendet werden.
Im Element st_mode sind sowohl der Dateityp als auch die Zugriffsrechte gespeichert.
Um den Dateityp zu ermitteln, wird das Element st_mode
mit der Bit-Maske S_IFMT und-verknüpft. Dadurch werden alle
sonstigen Bits des Elements st_mode (z.B. die Bits für die
Zugriffsrechte) auf 0 gesetzt, nur die Bits für die Angaben des
Dateityps bleiben unverändert.
Das Ergebnis ist einer der folgenden Werte:
Konstante | Bedeutung |
---|---|
S_IFDIR | Verzeichnis |
S_IFREG | reguläre (normale) Datei |
S_IFLNK | symbolischer Link |
S_IFBLK | blockorientiertes Gerät |
S_IFCHR | byteorientiertes Gerät |
S_IFIFO | Pipe für die Interprozess-Kommunikation |
S_IFSOCK | Socket für Interprozess-Kommunikation |
Die Information, welche Zugriffsrechte der Datei-Eigentümer, die
Eigentümer-Gruppe bzw. alle anderen Benutzer haben, ist in den
Zugriffsrechte-Bits gespeichert.
Ergibt die und-Verknüpfung des Elementes st_mode mit der
entsprechenden Konstante einen Wert ungleich 0, so ist das
entsprechende Bit in st_mode gesetzt und der Zugriff ist
erlaubt.
Konstante | Bedeutung |
---|---|
S_IRUSR | Eigentümer darf lesen |
S_IWUSR | Eigentümer darf schreiben |
S_IXUSR | Eigentümer darf Datei ausführen bzw. Verzeichnis durchsuchen |
S_IRGRP | Eigentümer-Gruppe darf lesen |
S_IWGRP | Eigentümer-Gruppe darf schreiben |
S_IXGRP | Eigentümer-Gruppe darf Datei ausführen bzw. Verzeichnis durchsuchen |
S_IROTH | andere Nutzer dürfen lesen |
S_IWOTH | andere Nutzer dürfen schreiben |
S_IXOTH | andere Nutzer dürfen die Datei ausführen bzw. das Verzeichnis durchsuchen |
S_ISUID | Setze UID-Bit. Wenn diese Datei ausgeführt wird, läuft der Prozess immer unter der Kennung und mit den Berechtigungen des Dateieigentümers, unabhängig davon, wer das Programm gestartet hat. |
S_ISGID | Setze GID-Bit.
|
S_ISVTX | Sticky-Bit.
|
Für die Datentypen off_t, nlink_t, uid_t, gid_t, dev_t, ino_t,
blksize_t und blkcnt_t gibt es keine Festlegung für die Breite.
Sie varrieren je nach Betriebssystem und Version.
Um entsprechende Daten auszugeben, nehmen wir einen Typcast zum Typ
uintmax_t vor und verwenden den Platzhalter "%ju"
/** @file ex039.c Demo-Programm fuer Datei-Attribute auf POSIX-Systmen.
ex039 <Dateiname>
*/
/* Copyright (C) 2014-2017 - HS Schmalkalden. All rights reserved. */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/** Hauptprogramm.
@param argc Anzahl der Kommandozeilenargumente.
@param argv Feld mit Kommandozeilenargumenten.
@return 0 bei Erfolg, alle anderen Werte deuten auf Fehler hin.
*/
int
main(int argc, char *argv[])
{
struct stat stbuf;
struct tm timebuf;
int exc = EXIT_FAILURE;
/* Es muss ein Argument angegeben sein: Dateiname */
if (2 != argc) {
/* !!! Falsche Argumente-Anzahl */
goto finished;
}
/* Versuche, Datei-Attribute abzufragen */
if (0 != stat(argv[1], &stbuf)) {
/* !!! Abfrage der Dateiattribute fehlgeschlagen */
goto finished;
}
/* Bis jetzt erfolgreich */
exc = EXIT_SUCCESS;
/* Allgemeine Informationen ueber Datei ausgeben */
printf("Datei-Name: %s\n", argv[1]);
printf("Groesse: %ju\n", (uintmax_t)(stbuf.st_size));
/* Dateityp ermitteln und ausgeben */
switch(stbuf.st_mode & S_IFMT) {
case S_IFREG: {
printf("Dateityp: Regulaere Datei\n");
} break;
case S_IFDIR: {
printf("Dateityp: Directory\n");
} break;
case S_IFSOCK: {
printf("Dateityp: Socket\n");
} break;
case S_IFLNK: {
printf("Dateityp: Symbolischer Link\n");
} break;
case S_IFBLK: {
printf("Dateityp: Blockorientiertes Geraet\n");
} break;
case S_IFCHR: {
printf("Dateityp: Byteorientiertes Geraet\n");
} break;
case S_IFIFO: {
printf("Dateityp: FIFO (benannte Pipe)\n");
} break;
default: {
printf("Dateityp: Unbekannt\n");
exc = EXIT_FAILURE;
} break;
}
/* Eigentuemer ausgeben */
printf("Eigentuemer-UID: %ju\n", (uintmax_t)(stbuf.st_uid));
printf("Eigentuemer-GID: %ju\n", (uintmax_t)(stbuf.st_gid));
printf(
"Berechtigungen: %c%c%c%c%c%c%c%c%c%c%c%c\n",
((0 != (stbuf.st_mode & S_ISUID)) ? 'u' : '-'),
((0 != (stbuf.st_mode & S_ISGID)) ? 'g' : '-'),
((0 != (stbuf.st_mode & S_ISVTX)) ? 's' : '-'),
((0 != (stbuf.st_mode & S_IRUSR)) ? 'r' : '-'),
((0 != (stbuf.st_mode & S_IWUSR)) ? 'w' : '-'),
((0 != (stbuf.st_mode & S_IXUSR)) ? 'x' : '-'),
((0 != (stbuf.st_mode & S_IRGRP)) ? 'r' : '-'),
((0 != (stbuf.st_mode & S_IWGRP)) ? 'w' : '-'),
((0 != (stbuf.st_mode & S_IXGRP)) ? 'x' : '-'),
((0 != (stbuf.st_mode & S_IROTH)) ? 'r' : '-'),
((0 != (stbuf.st_mode & S_IWOTH)) ? 'w' : '-'),
((0 != (stbuf.st_mode & S_IXOTH)) ? 'x' : '-')
);
/* Weitere Informationen ausgeben */
printf("Link-Anzahl: %ju\n", (uintmax_t)(stbuf.st_nlink));
printf("Device-ID: %ju\n", (uintmax_t)(stbuf.st_dev));
printf("Rel. Device-ID: %ju\n", (uintmax_t)(stbuf.st_rdev));
printf("Inode-Nummer: %ju\n", (uintmax_t)(stbuf.st_ino));
printf("Blockgroesse: %ju\n", (uintmax_t)(stbuf.st_blksize));
printf("Block-Anzahl: %ju\n", (uintmax_t)(stbuf.st_blocks));
/* Zeitpunkt ausgeben, wann Datei angelegt wurde */
if (NULL != localtime_r(&(stbuf.st_ctime), &timebuf)) {
printf(
"Erzeugungszeitpunkt: %04d-%02d-%02d %02d:%02d:%02d\n",
(1900 + timebuf.tm_year),
(1 + timebuf.tm_mon),
timebuf.tm_mday,
timebuf.tm_hour,
timebuf.tm_min,
timebuf.tm_sec
);
}
else {
exc = EXIT_FAILURE;
}
/* Zeitpunkt der letzten Aenderung ausgeben */
if (NULL != localtime_r(&(stbuf.st_mtime), &timebuf)) {
printf("Modifikationszeitpunkt: %04d-%02d-%02d %02d:%02d:%02d\n",
(1900 + timebuf.tm_year),
(1 + timebuf.tm_mon),
timebuf.tm_mday,
timebuf.tm_hour,
timebuf.tm_min,
timebuf.tm_sec
);
}
else {
exc = EXIT_FAILURE;
}
/* Zeitpunkt des letzten Zugriffes auf Datei ausgeben */
if (NULL != localtime_r(&(stbuf.st_atime), &timebuf)) {
printf("Zugriffszeitpunkt: %04d-%02d-%02d %02d:%02d:%02d\n",
(1900 + timebuf.tm_year),
(1 + timebuf.tm_mon),
timebuf.tm_mday,
timebuf.tm_hour,
timebuf.tm_min,
timebuf.tm_sec
);
}
else {
exc = EXIT_FAILURE;
}
finished:
exit(exc); return exc;
}
/* vim: set ai sw=4 ts=4 expandtab : */
Das Kommando
ex039 ex039.c
ergibt folgende Ausgabe:
Datei-Name: ex039.c Groesse: 4509 Dateityp: Regulaere Datei Eigentuemer-UID: 3023 Eigentuemer-GID: 310 Berechtigungen: ---rw-r--r-- Link-Anzahl: 3 Device-ID: 2053 Rel. Device-ID: 0 Inode-Nummer: 14312320 Blockgroesse: 4096 Block-Anzahl: 16 Erzeugungszeitpunkt: 2014-09-19 16:53:28 Modifikationszeitpunkt: 2014-09-19 15:36:03 Zugriffszeitpunkt: 2014-09-19 16:53:28
Ein häufig benötigtes Datei-Attribut ist die Datei-Größe, diese wird mitunter benötigt, um einen Puffer zum Einlesen der gesamten Datei bereitzustellen.
Da Code zur Ermittelung der Dateigröße unter Windows bzw.
Linux/Unix variiert, wird der entsprechende Code an einer Stelle
gebündelt.
Ein Beispiel für eine solche Funktion ist
/** Dateigroesse ermitteln. @param szptr Zeiger auf Variable, in der Dateigroesse gespeichert wird. @param name Dateiname. @param ec Zeiger auf Fehlercode-Variable, darf NULL sein. @return 1 bei Erfolg, 0 bei Fehler. */ int hsm_et_file_size(uintmax_t *szptr, const char *name, int *ec);
Diese Funktion ist in der Datei he-file.h deklariert und in he-file.c implementiert.
Anstelle der Datentypen _off_t bzw. off_t wird
hier der Datentyp uintmax_t verwendet.
In he-file.c wird die bedingte Compilierung benutzt, um für Windows
und Linux/Unix jeweils den passenden Code zu verwenden.