Технологические интерфейсы
Обход файловой иерархии
Обход файловой иерархии – типовая задача, для решения которой стандартом POSIX-2001 предлагаются две сходные функции – ftw() и nftw() (см. листинг 9.46).
#include <ftw.h> int ftw (const char *path, int (*fn) (const char *, const struct stat *, int), int depth); int nftw (const char *path, int (*fn) (const char *, const struct stat *, int, struct FTW *), int depth, int flags);Листинг 9.46. Описание функций обхода файловой иерархии.
Функция ftw() рекурсивно обходит иерархию каталогов, имеющую своим корнем каталог с маршрутным именем, на которое указывает аргумент path. Для каждого объекта иерархии ftw() вызывает функцию fn(), передавая ей три аргумента: указатель на цепочку символов, ограниченную нулевым байтом и содержащую имя объекта; указатель на структуру типа stat, содержащую информацию об объекте; тип объекта, представленный целым числом.
Возможны следующие значения типа объекта.
FTW_D
Каталог.
FTW_DNR
Каталог, недоступный на чтение.
FTW_F
Обычный файл.
FTW_SL
Символьная ссылка.
FTW_NS
Объект, отличный от символьной ссылки, для которого stat() не может выполниться успешно.
Если тип объекта есть FTW_DNR, элементы этого каталога не просматриваются. Если тип есть FTW_NS, то структура типа stat будет содержать неопределенные значения. Примером объекта, который вызовет передачу функции fn() типа FTW_NS, является файл в каталоге, доступном для чтения, но не для поиска.
Функция ftw() обрабатывает каталог перед обработкой его элементов.
Функция ftw() использует не более одного файлового дескриптора на обход каждого уровня иерархии. Аргумент depth ограничивает количество используемых таким образом дескрипторов; его значение должно принадлежать диапазону [1, OPEN_MAX].
Обход завершится тогда, когда будет обойдена вся иерархия, или функция fn() вернет ненулевое значение, или возникнет ошибка, отличная от EACCES, при работе самой функции ftw() (например, ошибка ввода/вывода). Если дерево обойдено полностью, ftw() в качестве результата возвращает нуль. Если fn() вернет ненулевое значение, то ftw() прекратит обход и выдаст это значение. Если будет обнаружена ошибка при работе самой функции ftw(), то она вернет -1 и соответствующим образом установит значение переменной errno.
Обратим внимание на следующее (впрочем, довольно очевидное) обстоятельство. Функция ftw() во время своей работы временно резервирует некоторые ресурсы (оперативную память, файловые дескрипторы ). Если прервать ее нештатным образом (например, посредством нелокального перехода из функции fn() или обработчика сигнала), эти ресурсы останутся неосвобожденными. Рекомендуемый способ обработки прерываний заключается в том, чтобы зафиксировать факт получения прерывания и при очередном вызове fn() заставить ее вернуть ненулевое значение.
Функция nftw() аналогична ftw(), однако и у нее самой, и у вызываемой ею функции fn() имеется по одному дополнительному аргументу. Дополнительный аргумент flags управляет работой функции nftw(). Его значение формируется как побитное ИЛИ следующих флагов.
FTW_CHDIR
Делать текущим просматриваемый каталог. Если этот флаг не установлен, функция nftw() не изменит текущий каталог.
FTW_DEPTH
Обход вглубь: обрабатывать каталог после обработки его элементов.
FTW_MOUNT
Обрабатывать только файлы из той же файловой системы, что и path.
FTW_PHYS
Осуществлять физический обход без разрешения символьных ссылок.
Третий аргумент функции fn(), по сравнению с ftw() может принимать следующие дополнительные значения.
FTW_DP
Каталог, элементы которого уже обработаны (данное условие может быть истинным только при установленном флаге FTW_DEPTH ).
FTW_SLN
Символьная ссылка, именующая отсутствующий файл.
Дополнительный, четвертый аргумент функции fn() является указателем на структуру типа FTW. Согласно стандарту, она должна содержать по крайней мере следующие поля.
int base; /* Смещение простого имени файла */ /* от начала маршрутного имени, */ /* переданного fn() в качестве */ /* первого аргумента */ int level; /* Уровень текущего объекта */ /* относительно корня иерархии */ /* (у самого корня нулевой уровень) */
В качестве примера обхода файловой иерархии рассмотрим программу, подсчитывающую суммарный размер и высоту дерева файлов (см. листинг 9.47);
/* * * * * * * * * * * * * * * * * * * * */ /* Программа определяет суммарный размер */ /* и высоту файловой иерархии */ /* * * * * * * * * * * * * * * * * * * * */ #define _XOPEN_SOURCE 600 #include <ftw.h> #include <stdio.h> /* Суммарный размер файлов в иерархии */ static off_t fsize = 0; /* Максимальный уровень файлов в иерархии */ static int flevel = 0; /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция, вызываемая для каждого файла в иерархии */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static int nftwfunc (const char *filename, const struct stat *statptr, int filetype, struct FTW *pfwt) { /* Если установлен флаг FTW_NS, завершим обход */ if (filetype == FTW_NS) { perror ("STAT"); fprintf (stderr, "Отсутствуют данные о файле %s\n", filename); return 1; } fsize += statptr->st_size; if (pfwt->level > flevel) { flevel = pfwt->level; } return 0; } /* * * * * * * * * * * */ /* Организация обхода */ /* * * * * * * * * * * */ int main (int argc, char *argv []) { if (argc != 2) { fprintf (stderr, "Использование: %s корень_иерархии\n", argv [0]); return (1); } if (nftw (argv [1], nftwfunc, 16, FTW_MOUNT | FTW_PHYS) == -1) { perror ("NFTW"); } printf ("Суммарный размер обработанных файлов: %ld\n", fsize); printf ("Высота иерархии файлов: %d\n", flevel); return 0; }Листинг 9.47. Пример программы, осуществляющей обход файловой иерархии.
Обратим внимание на возможность досрочного завершения обхода за счет возврата ненулевого результата функцией обработки, если последней передан файл, данные о котором получить не удалось, а также на использование флагов физического обхода и игнорирования файлов из других файловых систем.
Пусть в каталоге /tmp существует непустой подкаталог gaga со следующим режимом доступа:
drw-r--r-- 2 galat sys 4096 May 7 17:26 gaga
Тогда результаты запуска приведенной программы с аргументом /tmp могут выглядеть так, как показано на листинге 9.48.
STAT: Permission denied Отсутствуют данные о файле /tmp/gaga/gugu Суммарный размер обработанных файлов: 2645778 Высота иерархии файлов: 2Листинг 9.48. Возможные результаты выполнения программы, осуществляющей обход файловой иерархии.