getdents(2)

NAZWA
getdents, getdents64 - pobiera wpisy z katalogu

BIBLIOTEKA
Standardowa biblioteka C (libc, -lc)

SKŁADNIA
#include <sys/syscall.h>           /* Definicja stałych SYS_* */
#include <unistd.h>

long syscall(SYS_getdents, unsigned int fd, struct linux_dirent *dirp,
             unsigned int count);

#define _GNU_SOURCE           /* Zob. feature_test_macros(7) */
#include <dirent.h>

ssize_t getdents64(size_t count;
                   int fd, void dirp[count], size_t count);

Uwaga: glibc nie udostępnia opakowania dla getdents(), co wymusza użycie syscall(2).

Note: W glibc brak definicji struct linux_dirent; zob. UWAGI.

OPIS
Nie są to interfejsy, które cię interesują. Opis implementacji interfejsu zgodnego z POSIX w bibliotece C znajduje się w readdir(3). Niniejsza strona opisuje nagi interfejs wywołania systemowego.

  getdents()
Wywołanie systemowe getdents() odczytuje kolejne struktury linux_dirent z katalogu wskazywanego przez przez deskryptor otwartego pliku fd do bufora wskazywanego przez dirp. Argument count określa rozmiar tego bufora.

Struktura linux_dirent jest zadeklarowana następująco:

    struct linux_dirent {
        unsigned long  d_ino;     /* Numer i-węzła */
        unsigned long  d_off;     /* Nie jest przesunięciem; zob. niżej */
        unsigned short d_reclen;  /* Długość tego linux_dirent */
        char           d_name[];  /* Nazwa pliku (zakończ. znakiem null) */
                          /* długość to faktycznie (d_reclen - 2 -
                             offsetof(struct linux_dirent, d_name)) */
        /*
        char           pad;       // Zerowy bajt wyrównania
        char           d_type;    // Typ pliku (tylko od  Linuksa
                                  // 2.6.4); przesunięciem jest (d_reclen - 1)
        */
    }

d_ino jest numerem i-węzła. d_off jest wartością zależną od systemu plików, która nie ma znaczenia dla przestrzeni użytkownika, choć w starszych systemach plików była odległością od początku katalogu do początku następnej struktury linux_dirent; zob. readdir(3). d_reclen jest wielkością tej całej struktury linux_dirent. d_name jest nazwą pliku zakończoną znakiem NUL.

d_type jest bajtem na końcu struktury wskazującym typ pliku. Zawiera jedną z następujących wartości (zdefiniowanym w <dirent.h>):

DT_BLK
    Jest to urządzenie blokowe

DT_CHR
    Jest to urządzenie znakowe.

DT_DIR
    Jest to katalog.

DT_FIFO
    Jest to potok nazwany (FIFO).

DT_LNK
    Jest to dowiązanie symboliczne.

DT_REG
    Jest to zwykły plik.

DT_SOCK
    Jest to gniazdo dziedziny Uniksa.

DT_UNKNOWN
    Typ pliku jest nieznany.

Pole d_type zaimplementowano od Linuksa 2.6.4. Zajmuje miejsce, które wcześniej zajmował zerowy bajt wypełnienia w strukturze linux_dirent. Z tego względu jądra do Linuksa 2.6.3, próbujące uzyskać dostęp do tego pola zawsze zwracają wartość 0 (DT_UNKNOWN).

Obecnie jedynie niektóre systemy plików (m.in Btrfs, ext2, ext3 i ext4) obsługują w pełni zwracanie typu pliku w d_type. Wszystkie programy muszą poprawnie obsługiwać zwrócenie wartości DT_UNKNOWN.

  getdents64()
Pierwotne, linuksowe wywołanie getdents() nie obsługiwało dużych systemów plików i dużych przesunięć pliku. Z tego powodu, Linux 2.4 dodał getdents64(), z szerszymi typami pól d_ino i d_off. Dodatkowo, getdents64() obsługuje wprost pole d_type.

Wywołanie systemowe getdents64() zachowuje się jak getdents(), tyle że jego drugi argument jest wskaźnikiem do bufora zawierającego strukturę następującego typu:

    struct linux_dirent64 {
        ino64_t        d_ino;    /* 64-bitowy numer i-węzła */
        off64_t        d_off;    /* Nie jest przesunięciem, zob. getdents() */
        unsigned short d_reclen; /* Rozmiar tego dirent */
        unsigned char  d_type;   /* Typ pliku */
        char           d_name[]; /* Nazwa pliku (zakończona null) */
    };

WARTOŚĆ ZWRACANA
Po pomyślnym zakończeniu zwracana jest ilość odczytanych bajtów. Na końcu katalogu zwracane jest 0. Przy błędzie zwracane jest -1 i ustawiane errno wskazując błąd.

BŁĘDY

EBADF
    Nieprawidłowy deskryptor fd.

EFAULT
    Argument wskazuje poza przestrzeń adresową wywołującego procesu.

EINVAL
    Bufor na wynik jest za mały.

ENOENT
    Nie ma takiego katalogu.

ENOTDIR
    Deskryptor pliku nie odnosi się do katalogu.

STANDARDY
Brak.

HISTORIA
SVr4.

getdents64()
    glibc 2.30.

UWAGI
glibc nie udostępnia opakowania dla getdents(); należy je wywołać za pomocą syscall(2). W takim przypadku konieczne będzie samodzielne zdefiniowanie struktury linux_dirent lub linux_dirent64.

Zamiast opisywanych wywołań systemowych, prawdopodobnie lepszym pomysłem będzie użycie readdir(3).

Te wywołania zastępują readdir(2).

PRZYKŁADY
Program poniżej demonstruje użycie getdents(). Poniższe wyjście pokazuje przykład, w którym można zaobserwować działanie tego programu w katalogu ext2:

    $ ./a.out /testfs/
    --------------- nread=120 ---------------
    i-węzeł#  typ pliku  d_reclen  d_off   d_name
           2  katalog      16         12  .
           2  katalog      16         24  ..
          11  katalog      24         44  lost+found
          12  zwykły       16         56  a
      228929  katalog      16         68  sub
       16353  katalog      16         80  sub2
      130817  katalog      16       4096  sub3

  Kod źródłowy programu
    #define _GNU_SOURCE
    #include <dirent.h>     /* Definiuje stałe DT_* */
    #include <err.h>
    #include <fcntl.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/syscall.h>
    #include <sys/types.h>
    #include <unistd.h>

    struct linux_dirent {
        unsigned long  d_ino;
        off_t          d_off;
        unsigned short d_reclen;
        char           d_name[];
    };

    #define BUF_SIZE 1024

    int
    main(int argc, char *argv[])
    {
        int                  fd;
        char                 d_type;
        char                 buf[BUF_SIZE];
        long                 nread;
        struct linux_dirent  *d;

        fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
        if (fd == -1)
            err(EXIT_FAILURE, "open");

        for (;;) {
            nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
            if (nread == -1)
                err(EXIT_FAILURE, "getdents");

            if (nread == 0)
                break;

            printf("--------------- nread=%ld ---------------\n", nread);
            printf("i-węzeł#  typ pliku  d_reclen  d_off   d_name\n");
            for (size_t bpos = 0; bpos < nread;) {
                d = (struct linux_dirent *) (buf + bpos);
                printf("%8lu  ", d->d_ino);
                d_type = *(buf + bpos + d->d_reclen - 1);
                printf("%-10s ", (d_type == DT_REG) ?  "zwykły" :
                                 (d_type == DT_DIR) ?  "katalog" :
                                 (d_type == DT_FIFO) ? "FIFO" :
                                 (d_type == DT_SOCK) ? "gniazdo" :
                                 (d_type == DT_LNK) ?  "dow. symbol." :
                                 (d_type == DT_BLK) ?  "urz. blok." :
                                 (d_type == DT_CHR) ?  "urz. znak." : "???");
                printf("%4d %10jd  %s\n", d->d_reclen,
                       (intmax_t) d->d_off, d->d_name);
                bpos += d->d_reclen;
            }
        }

        exit(EXIT_SUCCESS);
    }

ZOBACZ TAKŻE
readdir(2), readdir(3), inode(7)

TŁUMACZENIE
Tłumaczenie niniejszej strony podręcznika: Przemek Borys <pborys@dione.ids.pl>, Andrzej Krzysztofowicz <ankry@green.mf.pg.gda.pl> i Michał Kułach <michal.kulach@gmail.com>

Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 (https://www.gnu.org/licenses/gpl-3.0.html) lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.

Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.