Шаг 20 - Чтение содержимого директорий diropen, readdir, closedir

На одном из почтовых серверов у меня есть директория в которую складываются пришедшие письма. За некоторое время количество файлов в этой директории начинает превышать несколько тысяч и стандартной командой удаления rm *.eml удалить ничего не получается:

root@mailserver:/mail/queue# rm *.eml
bash: /bin/rm: Argument list too long

Связано это с тем, что Bash из маски *.eml делает длинную строку из списка файлов с расширением eml, поэтому количество аргументов для команды rm превышает допустимое количество. Естественно удалить такое количество файлов можно с использованием других команд, но интерес не в этом, а в том чтобы разобраться с функциями чтения директорий.

Итак, приступим. Для работы с директориями необходимо подключить файлы:

#include <sys/types.h>
#include <dirent.h>

Директория сама по себе представляет файл состоящий из специальных записей dirent, которые содержат данные о файлах в директории:

struct dirent {
  ino_t          d_ino;       /* inode number */
  off_t          d_off;       /* offset to the next dirent */
  unsigned short d_reclen;    /* length of this record */
  unsigned char  d_type;      /* type of file; not supported
                                 by all file system types */
  char           d_name[256]; /* filename */
};

Данная структура содержит имя файла d_name, порядковый номер файла d_ino в файловой системе и несколько других. Разберемся.

Для работы с директориями необходимо определить переменную типа DIR (по смыслу она похожа на тип FILE). Для открытия/закрытия директорий существует две функции:

DIR *opendir(const char *name);
int closedir(DIR *dirp);

Функция opendir() открывает директорию для чтения с именем name и возвращает указатель на directory stream (иногда сложно перевести со смыслом, директорный поток или поток директории ?), при ошибке возвращает NULL и соответствующим образом устанавливает код ошибки errno. Функция closedir() без комментариев.

Чтение из этого файла-директории осуществляется функциями со схожими названиями:

struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);

Первая функция readdir() возвращает следующую структуру dirent считанную из файла-директории. При достижении конца списка файлов в директории или возникновении ошибки возвращает NULL. Вторая функция должна использоваться для чтения содержимого директорий при разработке программ работающих в мультипоточном режиме.

Давайте попробуем прочитать какую-нибудь директорию:

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main() {
    DIR *dir;
    struct dirent *entry;

    dir = opendir("/");
    if (!dir) {
        perror("diropen");
        exit(1);
    };

    while ( (entry = readdir(dir)) != NULL) {
        printf("%d - %s [%d] %d\n",
            entry->d_ino, entry->d_name, entry->d_type, entry->d_reclen);
    };

    closedir(dir);
};

Компилируем и запускаем:

root@home:/root/dirent# gcc dirread.c
root@home:/root/dirent# ./a.out
2 - . [0] 16
1 - .. [0] 16
114 - bin [0] 16
19 - dev [0] 16
109 - etc [0] 16
89 - lib [0] 16
117 - mnt [0] 16
127 - opt [0] 16
144 - srv [0] 16
112 - tmp [0] 16
108 - sys [0] 16
4 - var [0] 16
37 - usr [0] 16
113 - boot [0] 16
116 - home [0] 16
145 - proc [0] 16
115 - sbin [0] 16
149 - root [0] 16
691340 - readme.txt [0] 32
128 - media [0] 20
575756 - netio [0] 20

Из всего этого думаю полезностей извлечь можно не много. Думаю кроме имени файла d_name ничего особо полезного нет, но собственно больше ничего и не надо. Как и было написано в man 3 readdir поле d_type многие файловые системы не устанавливают (в примере используется reiserfs), а жаль. Судя по описанию уже по этому полю можно было бы определить тип записи: файл, директория или символьная ссылка. Ну ничего страшного.


Предыдущий Шаг | Оглавление
Автор Кузин Андрей.