getdents(2) System Calls Manual getdents(2)

getdents, getdents64 - pobiera wpisy z katalogu

Standardowa biblioteka C (libc, -lc)

#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(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.

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.

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>):

Jest to urządzenie blokowe
Jest to urządzenie znakowe.
Jest to katalog.
Jest to potok nazwany (FIFO).
Jest to dowiązanie symboliczne.
Jest to zwykły plik.
Jest to gniazdo dziedziny Uniksa.
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.

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

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.

Nieprawidłowy deskryptor fd.
Argument wskazuje poza przestrzeń adresową wywołującego procesu.
Bufor na wynik jest za mały.
Nie ma takiego katalogu.
Deskryptor pliku nie odnosi się do katalogu.

Brak.

SVr4.

glibc 2.30.

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).

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

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

readdir(2), readdir(3), inode(7)

Autorami polskiego tłumaczenia niniejszej strony podręcznika są: 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 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.

2 maja 2024 r. Linux man-pages 6.8