_syscalls_ sem10 - Интерфейс библиотечных функций и системных вызовов. Работа с файлами. Работа с файловой системой (1114919), страница 5
Текст из файла (страница 5)
Последние 6 символов шаблона должны быть XXXXXX, и эти символы заменяются на символы, делающие всё имя временного файла уникальным. Файл создаётся сфлагом O_EXCL в режиме чтение/запись (O_RDWR) и с правами доступа 0600. Поскольку строка template модифицируется, это не должна быть строковая константа (строковыйлитерал).Функция возвращает файловый дескриптор временного файла при успешном завершении и -1 при ошибке. Переменная errno в этом случае может принимать следующие значения: EINVAL — последние 6 символов шаблона не равны XXXXXX. В этом случае строка template не меняется.
EEXIST — исчерпано пространство случайных имён временныхфайлов (то есть, все возможные файлы уже существуют). В этом случае значение строкиtemplate неопределено. Кроме того, могут возвращаться ошибки системного вызова open.Следующая программа иллюстрирует работу со временными файлами.#include#include#include#include<stdio.h><stdlib.h><limits.h><unistd.h>/* если P_tmpdir неопределён, установим его в /tmp */#ifndef P_tmpdir#define P_tmpdir "/tmp"#endifint main(void){char tmpn[PATH_MAX];12charintFILEint*s;fd;*f;a, b, c;/* если переменная окружения TMPDIR установлена,* используем её, иначе - P_tmpdir */if (!(s = getenv("TMPDIR"))) s = P_tmpdir;/* формируем шаблон */snprintf(tmpn, PATH_MAX, "%s/progXXXXXX", s);/* создаём временный файл */if ((fd = mkstemp(tmpn)) < 0) {perror("mkstemp");return 1;}/* удаляем его */unlink(tmpn);/* формируем дескриптор потока */if (!(f = fdopen(fd, "r+"))) {perror("fdopen");return 1;}/* запишем в него что-нибудь */fprintf(f, "%d %d %d\n", 1, 1, 2001);/* ...
*//* f нельзя закрывать!* устанавливаемся на начало файла для чтения */fseek(f, 0, SEEK_SET);/* читаем данные */if (fscanf(f, "%d%d%d", &a, &b, &c) != 3) {fprintf(stderr, "temporary file error\n");return 1;}printf("Read values: %d %d %d\n", a, b, c);/* временный файл больше не нужен, закрываем его */fclose(f);return 0;}3Работа с файловой системойТипичные задачи, которые возникают при работе с файловой системой, — это просмотрсодержимого каталогов, рекурсивный обход всех каталогов файловой системы, получениеинформации о состоянии файла.Поскольку операционная система, как правило, поддерживает большое количество типов файловых систем, которые сильно отличаются друг от друга по используемому способухранения данных, ядро операционной системы должно предоставлять некоторый обобщённый интерфейс для доступа к содержимому каталогов.
Этот интерфейс базируется на пред13положении, что каталог хранит только имена содержащихся в нём файлов. Вся остальнаяинформация хранится в индексном дескрипторе. Если конкретная файловая система не имеет индексных дескрипторов (например, FAT), ядро всё равно будет эмулировать их наличие.Для работы с каталогом используются библиотечные функции opendir, closedir,readdir, seekdir, telldir.#include <sys/types.h>#include <dirent.h>DIR*opendir(const char *name);struct dirent *readdir(DIR *dir);off_ttelldir(DIR *dir);voidseekdir(DIR *dir, off_t offset);intclosedir(DIR *dir);Функция opendir ассоциирует с именем каталога, переданным ей в качестве аргумента,дескриптор каталога, указатель на который возвращается из функции для дальнейшего использования во всех функциях работы с каталогом.
Дескриптор каталога имеет тип DIR, аработа с ним аналогична работе с дескриптором потока: в обоих случаях всегда используется указатель на структуру. В случае ошибки функция opendir возвращает NULL. Каждыйоткрытый дескриптор каталога использует один файловый дескриптор, и, поскольку максимальное число одновременно открытых файловых дескрипторов в процессе ограничено, этонужно учитывать при рекурсивном обходе дерева файловой системы.Функция closedir закрывает дескриптор каталога, передаваемый ей в качестве аргумента. При успешном завершении функция возвращает 0, а при ошибке — -1.Функция telldir возвращает текущую позицию в каталоге. Следующий вызовreaddir возвращает запись, начинающуюся с этой позиции.
Функция seekdir позволяетустановить позицию чтения. Аргумент offset должен быть либо значением, полученнымот telldir, либо 0, что означает начало каталога.Функция readdir считывает очередную запись в каталоге. Информация о записи возвращается в виде указателя на структуру struct dirent. Память под эту структуру выделена в дескрипторе каталога DIR, поэтому каждый вызов readdir переписывает староесодержимое структуры. Структура содержит поле d_name типа массива символов некоторого зависящего от операционной системы размера.
Поле d_name содержит имя записи вкаталоге, то есть последнюю компоненту пути к файлу. Чтобы сформировать полный путь кфайлу или каталогу, нужно эту последнюю компоненту добавить к имени каталога, разделивих символом ’/’.Если функция readdir не может прочитать очередную запись (при ошибке или когдакаталог закончился), функция возвращает NULL. Для простоты можно полагать, что еслиreaddir вернул NULL, каталог не содержит больше записей.Каждый каталог всегда содержит две записи с именами . и .., указывающие на самэтот каталог и на его родительский каталог, однако в некоторых файловых системах функцияreaddir может не выдавать вообще эти записи (несмотря на то, что функция stat к этимфайлам всё применима), в других системах две записи могут не идти первыми.
Поэтому длямаксимальной переносимости программа не должна делать предположений о том, что записи. и .. присутствуют и идут первыми.Структура struct dirent может ещё содержать поле d_ino, которое содержит номериндексного дескриптора этой записи, но это поле не должно использоваться. Если необходимо получить номер индексного дескриптора, нужно использовать системный вызов stat(или lstat). В противном случае программа будет работать неправильно в каталогах, яв14ляющихся точками монтирования файловых систем.
Дело в том, что функция readdir считывает каталог в том виде, в котором он хранится на диске, а система при монтированиифайловых систем модифицирует отдельные записи в каталогах и индексные дескрипторы впамяти ядра, но не на диске.Информацию о файле можно получить с помощью одного из системных вызовов:stat, lstat, fstat. Информация возвращается в структуре struct stat, адрескоторой передаётся в эти системные вызовы.
Поле st_mode структуры содержит правадоступа к файлу и тип файла. Для проверки типа файла следует использовать специальные макросы, например, S_ISDIR для проверки того, является ли запись каталогом.Если макросы недоступны, следует сначала наложить маску S_IFMT на значение поляst_mode, а затем сравнить получившееся значение с проверяемым типом записи.
То есть,проверка на то, что запись является каталогом должна выглядеть следующим образом:s.st_mode & S_IFMT == S_IFDIR, где s — переменная типа struct stat. Чтобыполучить права доступа к файлу, нужно на значение поля st_mode наложить маску 07777.Поле st_ino содержит номер индексного дескриптора файла, а поле st_dev содержитномер устройства (файловой системы). Как было сказано ранее, каждый файл однозначноидентифицируется именно по этой паре: h st_ino, st_dev i.Полное описание структуры можно найти в приложении.Ниже приведена программа, которая рекурсивно обходит всю файловую систему и печатает пути ко всем найденным файлам.#include#include#include#include#include<stdio.h><sys/types.h><sys/stat.h><dirent.h><limits.h>void traverse(char const *dir){char name[PATH_MAX];DIR *d;struct dirent *dd;off_t o;struct stat s;char *delim = "/";/* если в качестве каталога передан корневой каталог,* разделитель не нужен */if (!strcmp(dir, "/")) delim = "";/* открываем каталог */if (!(d = opendir(dir))) {/* не смогли открыть */perror(dir);return;}/* считываем, пока dd не равен NULL, то есть* пока есть записи в каталоге */while ((dd = readdir(d))) {/* пропускаем .
и .. */if (!strcmp(dd->d_name, ".") || !strcmp(dd->d_name, ".."))15continue;/* формируем полный путь */snprintf(name, PATH_MAX, "%s%s%s", dir, delim, dd->d_name);/* получаем информацию о файле* используем lstat, чтобы не зациклиться на символических* связях */if (lstat(name, &s) < 0) continue;/* проверяем, что это каталог */if (S_ISDIR(s.st_mode)) {/* запоминаем текущее положение в каталоге */o = telldir(d);/* экономим файловые дескрипторы */closedir(d);/* вызываем себя рекурсивно */traverse(name);/* восстанавливаем старое положение */if (!(d = opendir(dir))) {perror(dir);return;}seekdir(d, o);} else {/* печатаем путь */printf("%s\n", name);}}closedir(d);}int main(void){traverse("/");return 0;}16.