Predchozi kapitola se zabyvala zakladnimi funkcemi pro operace vstupu a vystupu. Nyni se budeme zabyvat dalsimi moznostmi systemu souboru. Hlavne se budeme zabyvat celkovou strukturou, symbolickymi linky a metodami prochazeni hierarchii 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); |
Vsechny tri vraci: 0 kdyz OK, -1 pri chybe |
Funkce vraceji informacni strukturu o danem souboru. stat ziska informace o souboru danem cestou, fstat ziska informace o jiz otevrenem souboru, lstat je podobna stat, ale kdyz se jedna o symbolicky link, ziska informace o tomto linku, nikoli o souboru, na ktery link ukazuje.
Prvni argument pathname nebo filedes specifikuje soubor. Druhy argument je ukazatel na informacni strukturu, kterou funkce vyplni.
struct stat{ mode_t st_mode; /* typ souboru & pristupova prava */ ino_t st_ino; /* cislo i-nodu */ dev_t st_dev; /* cislo zarizeni (filesystem) */ dev_t st_rdev; /* cislo zarizeni pro spec. soubory */ nlink_t st_nlink; /* pocet odkazu (linku) */ uid_t st_uid; /* user ID */ gid_t st_gid; /* group ID */ off_t st_size; /* velikost v bajtech */ time_t st_atime; /* cas posledniho pristupu */ time_t st_mtime; /* cas posledni modifikace */ time_t st_ctime; /* cas posledni zmeny statutu souboru */ long st_blksize; /* nejlepsi velikost I/O bloku */ long st_blocks; /* pocet alokovanych 512B bloku */ };
V unixu jsou vlastne vsechna zarizeni mapovana jako specialni soubory -- z toho vyplyva i velke mnozstvi typu souboru.
Typ souboru se nejlepe zjisti pouzitim maker z tabulky 8.
Typ souboru | makro |
---|---|
regularni soubor | S_ISREG() |
adresar | S_ISDIR() |
znakovy specialni soubor | S_ISCHR() |
blokovy specialni soubor | S_ISBLK() |
FIFO | S_ISFIFO() |
symbolicky link | S_ISLNK() |
soket | S_ISSOCK() |
Priklad:
Uvedeny priklad tiskne informace o souborech zadanych z prikazoveho radku.
#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); }
Kazdy proces ma sest nebo vice identifikacnich cisel (viz tab. 9).
real user ID real group ID |
kdo skutecne jsme |
effective user ID effective group ID supplementary group ID |
pouzivane pro test pristupovych prav |
saved set-user-ID saved set-group-ID |
uschovane funkci exec |
Hodnota st_mode urcuje pristupova prava k souboru. K dispozici jsou opet makra pro test techto podminek.
Pro pristup k souborum plati urcita pravidla:
Jadro testuje prava vzdy pri otevreni, smazani nebo vytvoreni souboru. Podminky, za kterych jadro pripusti operaci se souborem, jsou nasledujici:
Jak jsme popsali drive, jadro vykonava testy pro pristup k souboru. Tento test muzeme spustit sami, vyuzitim funkce access.
#include <unistd.h> |
int access (const char *pathname, int *mode); |
Vraci: 0 kdyz OK, -1 pri chybe |
Prvni parametr urcuje soubor. Druhy parametr specifikuje druh testu prav. Muzete pouzit neco (nebo pomoci | i vse) z tabulky 10.
mode | Popis |
---|---|
R_OK | test cteni |
W_OK | test pro zapis |
X_OK | test pro spusteni |
F_OK | test existence souboru |
Priklad:
Tento program dostatecne 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 obdobna funkci umask shellu. Jedna se o nastaveni masky vytvarenych souboru. Funkce umask tedy nastavi masku vytvarenych souboru a vrati jeji predchozi hodnotu.
#include <sys/types.h> #include <sys/stat.h> |
mode_t umask (mode_t *cmask); |
Vraci: predchozi masku |
Vetsinou se maska nastavi jen jednou pri prihlaseni a pak se nemeni.
Priklad:
#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); }
Pomoci techto funkci muzeme zmenit pristupova prava existujicich souboru. Rozdil mezi funkcemi je jen ve stavu souboru (otevreny/uzavreny).
#include <sys/types.h> #include <sys/stat.h> |
int chmod (const char *pathname, mode_t mode); int fchmod (int filedes, mode_t mode); |
Obe vraci: 0 kdyz OK, -1 pri chybe |
K dispozici jsou opet makra definovana v <sys/stat.h>.
Priklad:
#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 priznak ma zajimavou historii. V prvnich verzich Unixu slouzil k uchovavani programu ve swapovaci oblasti po ukonceni -- dalsi spusteni bylo rychlejsi. Proto take zkratka S_ISVTX (save-text). Dnesni systemy ho jiz v tomto vyznamu nepotrebuji. Proto dnes slouzi k necemu jinemu. Jestlize je sticky bit nastaven nad adresarem, je mozne, aby vsichni pracovali se soubory adresare i bez odpovidajicich opravneni.
Tyto funkce slouzi ke zmene vlastnika souboru (zmene 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); |
Vsechny tri vraci: 0 kdyz OK, -1 pri chybe |
Clen st_size struktury stat udava velikost souboru v bajtech. Pro regularni soubor je pripustna i delka 0 -- prvnim znakem je znak konce souboru. Unix take podporuje predavani informaci o fyzicke velikosti souboru, tj. kolik a jake bloky obsahuje (st_blksize, st_blocks). Pozor na prakticke pouziti -- velikosti bloku mohou byt rozdilne.
V urcitych pripadech muzeme pozadovat zariznuti souboru na urcitou delku pomoci funkce truncate.
#include <sys/types.h> #include <unistd.h> |
int truncate (const char *pathname, off_t length); int ftruncate (int filedes, off_t length); |
Obe vraci: 0 kdyz OK, -1 pri chybe |
Pro pochopeni filozofie linku musime byt obeznameni se strukturou systemu souboru. Ruzne soucasne systemy pouzivaji ruzny zpusob reprezentace -- pro sjednoceni se vratime zpet k Verzi 7.
Predpokladejme, ze disk je rozdelen do ruznych partitions, na kazde partition je nejaky system souboru (filesystem), ktery mj. obsahuje seznam i-nodu a data souboru a adresaru, jak ukazauje obr. 4.
Obrazek 4:
Struktura systemu souboru
Na jeden fyzicky soubor (tj. na stejny i-node) muze ukazovat vice adresarovych polozek. Tyto se vytvori pomoci tzv. pevneho linku.
#include <unistd.h> |
int link (const char *existingpath, const char newpath); |
Vraci: 0 kdyz OK, -1 pri chybe |
Funkce vytvori novou polozku v adresari (newpath), ktera odkazuje na stavajici polozku (existingpath). Pouze superuzivatel muze provest link na adresar.
Pro zruseni odkazu slouzi unlink.
#include <unistd.h> |
int unlink (const char *pathname); |
Vraci: 0 kdyz OK, -1 pri chybe |
Funkce odstrani polozku v adresari a dekrementuje pocitadlo odkazu na dany fyzicky soubor.
Priklad:
#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); }
Dalsi funkce uz jen strucne:
#include <stdio.h> |
int remove (const char *pathname); |
Vraci: 0 kdyz OK, -1 pri chybe |
Funkce remove se pro soubor chova jako unlink, pro adresar jako rmdir (viz rmdir).
#include <stdio.h> |
int rename (const char *oldname, const char *newname); |
Vraci: 0 kdyz OK, -1 pri chybe |
Funkce rename funguje jako prikaz rm.
Symbolicky link vytvorime funkci symlink.
#include <unistd.h> |
int symlink (const char *actualpath, const char *sympath); |
Vraci: 0 kdyz OK, -1 pri chybe |
Vytvori se polozka sympath, ktera bude odkazovat na actualpath. Pri vytvareni nemusi actualpath existovat.
Protoze funkce open nasleduje symbolicky link, potrebuje zpusob, jak otevrit link samotny. Na to je funkce readlink.
#include <unistd.h> |
int readlink (const char *pathname, char *buf, int bufsize); |
Vraci: pocet prectenych bajtu kdyz OK, -1 pri chybe |
Adresare lze vytvorit pomoci mkdir, zrusit pomoci rmdir.
#include <sys/types.h> #include <sys/stat.h> |
int mkdir (const char *pathname, mode_t *mode); |
Vraci: 0 kdyz OK, -1 pri chybe |
Tato funkce vytvori prazdny adresar. Automaticky se vytvori polozky . (tecka) a .. (tecka-tecka).
Prazdny adresar muzeme zrusit pomoci funkce rmdir.
#include <unistd.h> |
int rmdir (const char *pathname); |
Vraci: 0 kdyz OK, -1 pri chybe |
Pro ziskani zakladnich informaci o souborech musime vedet, jake soubory se v adresarich nachazeji. K tomu slouzi funkce pro cteni adresaru. Konkretni struktura adresaru je implementacne zavisla, ale zpusob prace s nimi je obecny.
#include <sys/types.h> #include <dirent.h> |
DIR *opendir (const char *pathname); |
Vraci: ukazatel kdyz OK, jinak NULL |
struct dirent *readdir (DIR *dp); |
Vraci: ukazatel kdyz OK, jinak NULL |
void rewinddir (DIR *dp); |
int closedir (DIR *dp); |
Vraci: 0 kdyz OK, -1 pri chybe |
Struktura, ve ktere jsou ulozeny informace vypada nasledovne:
struct dirent { ino_t d_ino; /* cislo i-nodu */ char d_name[NAME_MAX + 1]; /* jmeno souboru ukoncene NULL */ }
Tyto operace jsou sice dostatecne pro zjisteni obsahu adresare, ale potrebujeme take prostredky pro prochazeni stromovou strukturou. V Systemu V existuje funkce ftw (file-tree-walking), ktera prave toto provadi. Tato funkce rekurzivne vola uzivatelem definovanou funkci pro kazdou polozku v adresari. Nedostatek teto funkce spociva v tom, ze na kazdou polozku aplikuje stat, a tudiz nasleduje i symbolicke linky. Opravena verze teto funkce se jmenuje nftw.
Pouziti nejlepe osvetli priklad. Mohli bychom sice pouzit nftw, ale pouzijeme radsi vlastni algoritmus.
Priklad:
#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;