Předchozí kapitola se zabývala základními funkcemi pro operace vstupu a výstupu. Nyní se budeme zabývat daląími moľnostmi systému souborů. Hlavně se budeme zabývat celkovou strukturou, symbolickými linky a metodami procházení hierarchií stromu.
#include <sys/types.h> #include <sys/stat.h> |
int stat (const char *pathname, struct stat *buf); int fstat (int filedes, struct stat *buf); int lstat (const char *pathname, struct stat *buf); |
Vąechny tři vrací: 0 kdyľ OK, -1 při chybě |
Funkce vracejí informační strukturu o daném souboru. stat získá informace o souboru daném cestou, fstat získá informace o jiľ otevřeném souboru, lstat je podobná stat, ale kdyľ se jedná o symbolický link, získá informace o tomto linku, nikoli o souboru, na který link ukazuje.
První argument pathname nebo filedes specifikuje soubor. Druhý argument je ukazatel na informační strukturu, kterou funkce vyplní.
struct stat{ mode_t st_mode; /* typ souboru & přístupová práva */ ino_t st_ino; /* číslo i-nodu */ dev_t st_dev; /* číslo zařízení (filesystem) */ dev_t st_rdev; /* číslo zařízení pro spec. soubory */ nlink_t st_nlink; /* počet odkazů (linků) */ uid_t st_uid; /* user ID */ gid_t st_gid; /* group ID */ off_t st_size; /* velikost v bajtech */ time_t st_atime; /* čas posledního přístupu */ time_t st_mtime; /* čas poslední modifikace */ time_t st_ctime; /* čas poslední změny statutu souboru */ long st_blksize; /* nejlepąí velikost I/O bloku */ long st_blocks; /* počet alokovaných 512B bloků */ };
V unixu jsou vlastně vąechna zařízení mapována jako speciální soubory -- z toho vyplývá i velké mnoľství typů souborů.
Typ souboru se nejlépe zjistí pouľitím maker z tabulky 8.
Typ souboru | makro |
---|---|
regulární soubor | S_ISREG() |
adresář | S_ISDIR() |
znakový speciální soubor | S_ISCHR() |
blokový speciální soubor | S_ISBLK() |
FIFO | S_ISFIFO() |
symbolický link | S_ISLNK() |
soket | S_ISSOCK() |
Příklad:
Uvedený příklad tiskne informace o souborech zadaných z příkazového řádku.
#include <sys/types.h> #include <sys/stat.h> int main(int argc, char *argv[]) { int i; struct stat buf; char *ptr; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < 0) { err_ret("lstat error"); continue; } if (S_ISREG(buf.st_mode)) ptr = "regular"; else if (S_ISDIR(buf.st_mode)) ptr = "directory"; else if (S_ISCHR(buf.st_mode)) ptr = "character special"; else if (S_ISBLK(buf.st_mode)) ptr = "block special"; else if (S_ISFIFO(buf.st_mode)) ptr = "fifo"; #ifdef S_ISLNK else if (S_ISLNK(buf.st_mode)) ptr = "symbolic link"; #endif #ifdef S_ISSOCK else if (S_ISSOCK(buf.st_mode)) ptr = "socket"; #endif else ptr = "** unknown mode **"; printf("%s\n", ptr); } exit(0); }
Kaľdý proces má ąest nebo více identifikačních čísel (viz tab. 9).
real user ID real group ID |
kdo skutečně jsme |
effective user ID effective group ID supplementary group ID |
pouľívané pro test přístupových práv |
saved set-user-ID saved set-group-ID |
uschované funkcí exec |
Hodnota st_mode určuje přístupová práva k souboru. K dispozici jsou opět makra pro test těchto podmínek.
Pro přístup k souborům platí určitá pravidla:
Jádro testuje práva vľdy při otevření, smazání nebo vytvoření souboru. Podmínky, za kterých jádro připustí operaci se souborem, jsou následující:
Jak jsme popsali dříve, jádro vykonává testy pro přístup k souboru. Tento test můľeme spustit sami, vyuľitím funkce access.
#include <unistd.h> |
int access (const char *pathname, int *mode); |
Vrací: 0 kdyľ OK, -1 při chybě |
První parametr určuje soubor. Druhý parametr specifikuje druh testu práv. Můľete pouľít něco (nebo pomocí | i vąe) z tabulky 10.
mode | Popis |
---|---|
R_OK | test čtení |
W_OK | test pro zápis |
X_OK | test pro spuątění |
F_OK | test existence souboru |
Příklad:
Tento program dostatečně ilustruje funkci access.
#include <sys/types.h> #include <fcntl.h> int main(int argc, char *argv[]) { if (argc != 2) err_quit("usage: a.out <pathname>"); if (access(argv[1], R_OK) < 0) err_ret("access error for %s", argv[1]); else printf("read access OK\n"); if (open(argv[1], O_RDONLY) < 0) err_ret("open error for %s", argv[1]); else printf("open for reading OK\n"); exit(0); }
Funkce umask je obdobná funkci umask shellu. Jedná se o nastavení masky vytvářených souborů. Funkce umask tedy nastaví masku vytvářených souborů a vrátí její předchozí hodnotu.
#include <sys/types.h> #include <sys/stat.h> |
mode_t umask (mode_t *cmask); |
Vrací: předchozí masku |
Větąinou se maska nastaví jen jednou při přihláąení a pak se nemění.
Příklad:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { umask(0); if (creat("foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) < 0) err_sys("creat error for foo"); umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (creat("bar", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) < 0) err_sys("creat error for bar"); exit(0); }
Pomocí těchto funkcí můľeme změnit přístupová práva existujících souborů. Rozdíl mezi funkcemi je jen ve stavu souboru (otevřený/uzavřený).
#include <sys/types.h> #include <sys/stat.h> |
int chmod (const char *pathname, mode_t mode); int fchmod (int filedes, mode_t mode); |
Obě vrací: 0 kdyľ OK, -1 při chybě |
K dispozici jsou opět makra definovaná v <sys/stat.h>.
Příklad:
#include <sys/types.h> #include <sys/stat.h> int main(void) { struct stat statbuf; /* turn on set-group-ID and turn off group-execute */ if (stat("foo", &statbuf) < 0) err_sys("stat error for foo"); if (chmod("foo", (statbuf.st_mode & S_IXGRP) | S_ISGID) < 0) err_sys("chmod error for foo"); /* set absolute mode to "rw-r--r--" */ if (chmod("bar", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) err_sys("chmod error for bar"); exit(0); }
Tento příznak má zajímavou historii. V prvních verzích Unixu slouľil k uchovávání programu ve swapovací oblasti po ukončení -- daląí spuątění bylo rychlejąí. Proto také zkratka S_ISVTX (save-text). Dneąní systémy ho jiľ v tomto významu nepotřebují. Proto dnes slouľí k něčemu jinému. Jestliľe je sticky bit nastaven nad adresářem, je moľné, aby vąichni pracovali se soubory adresáře i bez odpovídajících oprávnění.
Tyto funkce slouľí ke změně vlastníka souboru (změně UID, GID)
#include <sys/types.h> #include <unistd.h> |
int chown (const char *pathname, uid_t *owner, gid_t
*group); int fchown (int filedes, uid_t *owner, gid_t *group); int lchown (const char *pathname, uid_t *owner, gid_t *group); |
Vąechny tři vrací: 0 kdyľ OK, -1 při chybě |
Člen st_size struktury stat udává velikost souboru v bajtech. Pro regulární soubor je přípustná i délka 0 -- prvním znakem je znak konce souboru. Unix také podporuje předávání informací o fyzické velikosti souboru, tj. kolik a jaké bloky obsahuje (st_blksize, st_blocks). Pozor na praktické pouľití -- velikosti bloků mohou být rozdílné.
V určitých případech můľeme poľadovat zaříznutí souboru na určitou délku pomocí funkce truncate.
#include <sys/types.h> #include <unistd.h> |
int truncate (const char *pathname, off_t length); int ftruncate (int filedes, off_t length); |
Obě vrací: 0 kdyľ OK, -1 při chybě |
Pro pochopení filozofie linků musíme být obeznámeni se strukturou systému souborů. Různé současné systémy pouľívají různý způsob reprezentace -- pro sjednocení se vrátíme zpět k Verzi 7.
Předpokládejme, ľe disk je rozdělen do různých partitions, na kaľdé partition je nějaký systém souborů (filesystem), který mj. obsahuje seznam i-nodů a data souborů a adresářů, jak ukazauje obr. 4.
Obrázek 4:
Struktura systému souborů
Na jeden fyzický soubor (tj. na stejný i-node) můľe ukazovat více adresářových poloľek. Tyto se vytvoří pomocí tzv. pevného linku.
#include <unistd.h> |
int link (const char *existingpath, const char newpath); |
Vrací: 0 kdyľ OK, -1 při chybě |
Funkce vytvoří novou poloľku v adresáři (newpath), která odkazuje na stávající poloľku (existingpath). Pouze superuľivatel můľe provést link na adresář.
Pro zruąení odkazu slouľí unlink.
#include <unistd.h> |
int unlink (const char *pathname); |
Vrací: 0 kdyľ OK, -1 při chybě |
Funkce odstraní poloľku v adresáři a dekrementuje počítadlo odkazů na daný fyzický soubor.
Příklad:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { if (open("tempfile", O_RDWR) < 0) err_sys("open error"); if (unlink("tempfile") < 0) err_sys("unlink error"); printf("file unlinked\n"); sleep(15); printf("done\n"); exit(0); }
Daląí funkce uľ jen stručně:
#include <stdio.h> |
int remove (const char *pathname); |
Vrací: 0 kdyľ OK, -1 při chybě |
Funkce remove se pro soubor chová jako unlink, pro adresář jako rmdir (viz rmdir).
#include <stdio.h> |
int rename (const char *oldname, const char *newname); |
Vrací: 0 kdyľ OK, -1 při chybě |
Funkce rename funguje jako příkaz rm.
Symbolický link vytvoříme funkcí symlink.
#include <unistd.h> |
int symlink (const char *actualpath, const char *sympath); |
Vrací: 0 kdyľ OK, -1 při chybě |
Vytvoří se poloľka sympath, která bude odkazovat na actualpath. Při vytváření nemusí actualpath existovat.
Protoľe funkce open následuje symbolický link, potřebuje způsob, jak otevřít link samotný. Na to je funkce readlink.
#include <unistd.h> |
int readlink (const char *pathname, char *buf, int bufsize); |
Vrací: počet přečtených bajtů kdyľ OK, -1 při chybě |
Adresáře lze vytvořit pomocí mkdir, zruąit pomocí rmdir.
#include <sys/types.h> #include <sys/stat.h> |
int mkdir (const char *pathname, mode_t *mode); |
Vrací: 0 kdyľ OK, -1 při chybě |
Tato funkce vytvoří prázdný adresář. Automaticky se vytvoří poloľky . (tečka) a .. (tečka-tečka).
Prázdný adresář můľeme zruąit pomocí funkce rmdir.
#include <unistd.h> |
int rmdir (const char *pathname); |
Vrací: 0 kdyľ OK, -1 při chybě |
Pro získání základních informací o souborech musíme vědět, jaké soubory se v adresářích nacházejí. K tomu slouľí funkce pro čtení adresářů. Konkrétní struktura adresářů je implementačně závislá, ale způsob práce s nimi je obecný.
#include <sys/types.h> #include <dirent.h> |
DIR *opendir (const char *pathname); |
Vrací: ukazatel kdyľ OK, jinak NULL |
struct dirent *readdir (DIR *dp); |
Vrací: ukazatel kdyľ OK, jinak NULL |
void rewinddir (DIR *dp); |
int closedir (DIR *dp); |
Vrací: 0 kdyľ OK, -1 při chybě |
Struktura, ve které jsou uloľeny informace vypadá následovně:
struct dirent { ino_t d_ino; /* číslo i-nodu */ char d_name[NAME_MAX + 1]; /* jméno souboru ukončené NULL */ }
Tyto operace jsou sice dostatečné pro zjiątění obsahu adresáře, ale potřebujeme také prostředky pro procházení stromovou strukturou. V Systemu V existuje funkce ftw (file-tree-walking), která právě toto provádí. Tato funkce rekurzivně volá uľivatelem definovanou funkci pro kaľdou poloľku v adresáři. Nedostatek této funkce spočívá v tom, ľe na kaľdou poloľku aplikuje stat, a tudíľ následuje i symbolické linky. Opravená verze této funkce se jmenuje nftw.
Pouľití nejlépe osvětlí příklad. Mohli bychom sice pouľít nftw, ale pouľijeme radąi vlastní algoritmus.
Příklad:
#include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <limits.h> typedef int Myfunc(const char *, const struct stat *, int); /* function type that's called for each filename */ static Myfunc myfunc; static int myftw(char *, Myfunc *); static int dopath(Myfunc *); static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot; int main(int argc, char *argv[]) { int ret; if (argc != 2) err_quit("usage: ftw <starting-pathname>"); ret = myftw(argv[1], myfunc); /* does it all */ if ( (ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock) == 0) ntot = 1; /* avoid divide by 0; print 0 for all counts */ printf("regular files = %7ld, %5.2f %%\n", nreg, nreg*100.0/ntot); printf("directories = %7ld, %5.2f %%\n", ndir, ndir*100.0/ntot); printf("block special = %7ld, %5.2f %%\n", nblk, nblk*100.0/ntot); printf("char special = %7ld, %5.2f %%\n", nchr, nchr*100.0/ntot); printf("FIFOs = %7ld, %5.2f %%\n", nfifo, nfifo*100.0/ntot); printf("symbolic links = %7ld, %5.2f %%\n", nslink,nslink*100.0/ntot); printf("sockets = %7ld, %5.2f %%\n", nsock, nsock*100.0/ntot); exit(ret); } /* * Descend through the hierarchy, starting at "pathname". * The caller's func() is called for every file. */ #define FTW_F 1 /* file other than directory */ #define FTW_D 2 /* directory */ #define FTW_DNR 3 /* directory that can't be read */ #define FTW_NS 4 /* file that we can't stat */ static char *fullpath; /* contains full pathname for every file */ static int /* we return whatever func() returns */ myftw(char *pathname, Myfunc *func) { fullpath = path_alloc(NULL); /* malloc's for PATH_MAX+1 bytes */ /* ({Prog pathalloc}) */ strcpy(fullpath, pathname); /* initialize fullpath */ return(dopath(func)); } /* * Descend through the hierarchy, starting at "fullpath". * If "fullpath" is anything other than a directory, we lstat() it, * call func(), and return. For a directory, we call ourself * recursively for each name in the directory. */ static int /* we return whatever func() returns */ dopath(Myfunc* func) { struct stat statbuf; struct dirent *dirp; DIR *dp; int ret; char *ptr; if (lstat(fullpath, &statbuf) < 0) return(func(fullpath, &statbuf, FTW_NS)); /* stat error */ if (S_ISDIR(statbuf.st_mode) == 0) return(func(fullpath, &statbuf, FTW_F)); /* not a directory */ /* * It's a directory. First call func() for the directory, * then process each filename in the directory. */ if ( (ret = func(fullpath, &statbuf, FTW_D)) != 0) return(ret); ptr = fullpath + strlen(fullpath); /* point to end of fullpath */ *ptr++ = '/'; *ptr = 0; if ( (dp = opendir(fullpath)) == NULL) return(func(fullpath, &statbuf, FTW_DNR)); /* can't read directory */ while ( (dirp = readdir(dp)) != NULL) { if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) continue; /* ignore dot and dot-dot */ strcpy(ptr, dirp->d_name); /* append name after slash */ if ( (ret = dopath(func)) != 0) /* recursive */ break; /* time to leave */ } ptr[-1] = 0; /* erase everything from slash onwards */ if (closedir(dp) < 0) err_ret("can't close directory %s", fullpath); return(ret); } static int myfunc(const char *pathname, const struct stat *statptr, int type) { switch (type) { case FTW_F: switch (statptr->st_mode & S_IFMT) { case S_IFREG: nreg++; break; case S_IFBLK: nblk++; break; case S_IFCHR: nchr++; break; case S_IFIFO: nfifo++; break; case S_IFLNK: nslink++; break; case S_IFSOCK: nsock++; break; case S_IFDIR: err_dump("for S_IFDIR for %s", pathname); /* directories should have type = FTW_D */ } break; case FTW_D: ndir++; break; case FTW_DNR: err_ret("can't read directory %s", pathname); break; case FTW_NS: err_ret("stat error for %s", pathname); break; default: err_dump("unknown type %d for pathname %s", type, pathname); } return(0); }
Kaľdý proces má pracovní adresář, který se větąinou při přihláąení čte ze souboru /etc/passwd. Pracovní adresář měníme následujícími funkcemi:
#include <unistd.h> |
int chdir (const char *pathname); int fchdir (int filedes); |
Vrací: 0 kdyľ OK, -1 při chybě |
Ke zjiątění aktuálního pracovního adresáře slouľí getcwd.
#include <unistd.h> |
int *getcwd (char *buf, size_t size); |
Vrací: buf kdyľ OK, NULL při chybě |
Před voláním funkce je nutné alokovat buffer buf.
Klasická implementace unixu má pro větąinu I/O operací v jádře vyrovnávací pamě» (cache). V praxi to probíhá tak, ľe data z příkazu write se zapíąí do vyrovnávací paměti. Vyrovnávací pamě» pak kaľdých max. 30 sekund démon zapíąe na disk. Potřebujeme-li synchronizovat obsah souboru apod., musíme tohoto démona vyvolat sami. K tomu slouľí funkce sync.
#include <unistd.h> |
void sync (void); int fsync (int filedes); |
Vrací: 0 kdyľ OK, -1 při chybě |
Ladislav Dobias