dalsi predchozi obsah
Dalsi: Systemove soubory Predchozi: Souborovy vstup a vystup

  Soubory a adresare





Uvod

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.



Funkce stat, fstat, lstat

#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       */
};



Typy souboru

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()
Tabulka 8: Zjisteni typu souboru

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);
}



Pristupova prava

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
Tabulka 9: UID a GID procesu

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:

  1. Efektivni UID je 0 (superuzivatel)
  2. Efektivni UID je shodne s UID vlastnika souboru a je nastaven odpovidajici typ masky.
  3. Efektivni GID je shodne s GID vlastnika souboru a je nastaven odpovidajici bit masky.
  4. Je povolen pristup ostatnim uzivatelum.


  Funkce access

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
Tabulka 10: Konstanty mode pro funkci access

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

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);
}



Funkce chmod a fchmod

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);
}

POZOR! Pri nastavovani S_SVTX (sticky bitu) musite mit privilegia superuzivatele.



Sticky bit

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.



Funkce chown, fchown a lchown

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



Velikosti souboru

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.



Zariznuti souboru

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



System souboru podrobneji

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.

Zde ma byt moc hezky obrazek 'filesyst', skoda, ze ho nevidite

Obrazek 4: Struktura systemu souboru

Adresar
je specialni soubor, ktery obsahuje jmeno souboru a cislo i-nodu daneho souboru.

I-node
(mozna bychom mohli rikat informacni uzel) obsahuje dalsi informace o souboru, jako velikost, casy vytvoreni, modifikace, pristupu, .... Take zde nalezneme jedno cislo, ktere ukazuje, kolik odkazu na tento soubor existuje. Temto odkazum se rika pevne linky. Kdyz toto cislo dosahne nuly, je soubor pak smazan.



Funkce link, unlink, remove a rename

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.



Funkce symlink a readlink

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



  Funkce mkdir a rmdir

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



Cteni adresaru

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;