Программирование в стандарте POSIX

       

Создание, удаление, копирование и перемещение файлов


Стандарт POSIX-2001 не требует наличия утилиты для создания   обычных файлов. Они появляются по мере необходимости как побочный продукт многочисленных служебных программ (например, утилиты копирования), поэтому сама постановка задачи - создать файл "просто так" - является отчасти надуманной. В то же время, если новый файл все-таки нужен, полезно иметь в виду возможность перенаправления вывода пустой команды, которая имеется в языке shell.

При программировании на языке C для создания   обычных файлов можно воспользоваться функцией creat() (см. пример 4.30).

#include <fcntl.h> int creat (const char *path, mode_t mode);

Листинг 4.30. Описание функции creat(). (html, txt)

Функция creat() имеет два аргумента: маршрутное имя вновь образуемого файла и устанавливаемый режим доступа (идентификаторы владельца и владеющей группы наследуются у текущего пользователя). Результатом служит файловый дескриптор (который, напомним, представляется неотрицательным целым числом), т. е. функция creat() не только создает файл, но и открывает его.

Если файл, который пытаются создать при помощи creat(), уже существует, он опустошается (размер становится равным 0), а режим доступа и владелец не изменяются.

В случае неудачи результат creat() равен -1, а внешней переменной errno присваивается код ошибки, позволяющий определить причину ее (ошибки) возникновения. Переменная errno, а также мнемоники для кодов ошибок определены в заголовочном файле   <errno.h>. Для формирования системного сообщения об ошибке можно воспользоваться функцией perror() (см. пример 4.31), которая, опираясь на значение errno, помещает в стандартный протокол описание последней ошибки.

#include <stdio.h> void perror (const char *s);

Листинг 4.31. Описание функции perror(). (html, txt)

Например, при первом выполнении программы, приведенной в пример 4.32, в стандартный протокол может быть выдан соответствующий результат (см. пример 4.33).

#include <fcntl.h> #include <errno.h> #include <stdio.h> #include <limits.h> /* Программа пытается создавать в текущем */ /* каталоге файлы с именами g1, g2, ..., */ /* пока эти попытки не закончатся неудачей */ int main (void) { int n = 0; char name [PATH_MAX]; do sprintf (name, "g%d", ++n); while (creat (name, (mode_t) 0) >= 0); perror ("CREAT failed"); fprintf (stderr, "errno = %d\n", errno); fprintf (stderr, "Неудача на файле номер %d\n", n); return 0; }


Листинг 4.32. Пример программы, использующей функции creat() и perror(). (html, txt)

CREAT failed: Too many open files errno = 24 Неудача на файле номер 1022

Листинг 4.33. Возможный результат первого выполнения программы, использующей функции creat() и perror(). (html, txt)

Результат второго запуска той же программы показан в пример 4.34.



CREAT failed: Permission denied errno = 22 Неудача на файле номер 1

Листинг 4.34. Результат повторного выполнения программы, использующей функции creat() и perror(). (html, txt)

При первом запуске причина неудачи - превышение максимально допустимого числа одновременно открытых файлов (с учетом стандартных ввода, вывода и протокола), при повторном - попытка создать существующий файл вопреки отсутствию права на запись в него (файлы создавались с нулевым режимом доступа).

Перечислим несколько других условий, способных привести к неудачному завершению вызова creat(): компонент маршрутного имени не существует или не является каталогом; у компонента маршрута отсутствует право на поиск; создание файла требует записи в каталог, права на запись в который нет; файл существует и является каталогом.

Рассмотренный в примере стиль уведомления о неудачном завершении общий для большинства функций. Неудача определяется возвращением результата, невозможного в другом случае (почти всегда это -1 или пустой указатель NULL); код ошибки заносится в переменную errno. Разумеется, в реальных программах errno не выводят, а анализируют. В данном случае уместно было бы сравнивать errno с константами EACCES, EINVAL, EMFILE и т.д. (см. <errno.h>).

файл не обязательно создавать в текущем каталоге; в качестве аргумента creat() может быть передано составное имя. Пример, когда устанавливаются все биты режима доступа, приведен в пример 4.35.

df = creat ("/tmp/sample", S_IRWXU | S_IRWXG | S_IRWXO);

Листинг 4.35. Пример вызова функции creat(). (html, txt)

Для создания (пустых) каталогов служит утилита

mkdir [-p] [-m режим_доступа] каталог ...



Листинг 4.32. Пример программы, использующей функции creat() и perror().

CREAT failed: Too many open files errno = 24 Неудача на файле номер 1022

Листинг 4.33. Возможный результат первого выполнения программы, использующей функции creat() и perror().

Результат второго запуска той же программы показан в пример 4.34.

CREAT failed: Permission denied errno = 22 Неудача на файле номер 1

Листинг 4.34. Результат повторного выполнения программы, использующей функции creat() и perror().

При первом запуске причина неудачи - превышение максимально допустимого числа одновременно открытых файлов (с учетом стандартных ввода, вывода и протокола), при повторном - попытка создать существующий файл вопреки отсутствию права на запись в него (файлы создавались с нулевым режимом доступа).

Перечислим несколько других условий, способных привести к неудачному завершению вызова creat(): компонент маршрутного имени не существует или не является каталогом; у компонента маршрута отсутствует право на поиск; создание файла требует записи в каталог, права на запись в который нет; файл существует и является каталогом.

Рассмотренный в примере стиль уведомления о неудачном завершении общий для большинства функций. Неудача определяется возвращением результата, невозможного в другом случае (почти всегда это -1 или пустой указатель NULL); код ошибки заносится в переменную errno. Разумеется, в реальных программах errno не выводят, а анализируют. В данном случае уместно было бы сравнивать errno с константами EACCES, EINVAL, EMFILE и т.д. (см. <errno.h>).

файл не обязательно создавать в текущем каталоге; в качестве аргумента creat() может быть передано составное имя. Пример, когда устанавливаются все биты режима доступа, приведен в пример 4.35.

df = creat ("/tmp/sample", S_IRWXU | S_IRWXG | S_IRWXO);

Листинг 4.35. Пример вызова функции creat().

Для создания (пустых) каталогов служит утилита

mkdir [-p] [-m режим_доступа] каталог ...

и функция mkdir()

#include <sys/stat.h> int mkdir (const char *path, mode_t mode);



Мобильное приложение должно считать, что исходные и целевой файлы обязаны принадлежать одной файловой системеи создать новую жесткую ссылку на каталог или символьную ссылку нельзя.

Функция link() аналогична служебной программе ln в первой форме, без опций. Ее нормальным результатом служит 0; в случае ошибки возвращается -1. Важно отметить, что с точки зрения файловой системы образование нового элемента каталога и увеличение счетчика жестких ссылок на исходный файл осуществляется функцией link() как неделимое действие.

Утилита ln с опцией -s и функция symlink() создают новые символьные ссылки. Исходные файлы не обязаны существовать; соответствующие аргументы трактуются как цепочки символов и задают содержимое ссылок.

Опция -f позволяет замещать существующие элементы каталогов новыми (по умолчанию задание существующего файла в качестве целевого считается ошибкой).

Примером смены содержимого символьной ссылки посредством служебной программы ln с опциями -s и -f может служить фрагмент действий при загрузке ОС Linux, приведенный в пример 4.39.

if [ -n "$mver" ]; then ln -sf /lib/modules/$mver\ /lib/modules/default fi

Листинг 4.39. Использование утилиты ln для формирования содержимого символьной ссылки

Для удаления файлов служат утилиты

rm [-fiRr] файл ...

и

rmdir [-p] файл ...

а также функции unlink(), rmdir() и remove() (см. пример 4.40).

#include <unistd.h> int unlink (const char *path); #include <unistd.h> int rmdir (const char *path); #include <stdio.h> int remove (const char *path);

Листинг 4.40. Описание функций unlink(), rmdir() и remove().

Строго говоря, перечисленные утилиты и функции удаляют не файлы, а указанные ссылки на них, содержащиеся в каталогах - маршрутных префиксах, и, соответственно, уменьшают на единицу число жестких ссылок на эти файлы. файл удаляется и занятое им пространство освобождается, только если других ссылок на него не остается. Мы, однако, для краткости будем употреблять не совсем точное словосочетание "удаление файлов".



В пример 4. 42 приведен пример программы, которая с помощью функций link() и unlink() осуществляет ответственную обработку файлов с сохранением копии текущей версии.

#include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <sys/stat.h> /* Программа выполняет обработку */ /* с осторожным замещением */ /* основного файла рабочим */ #define MAIN_FILE "/home/galat/garb/temp/mfile" #define OLD_FILE "/home/galat/garb/temp/ofile" #define WORK_FILE "/home/galat/garb/temp/wfile" int main (void) { /* Необходимые описания */ /* . . . */ int work_success = 1;

/* Выполнение операций над рабочим файлом */ /* . . . */

/* В случае неудачи выдадим диагностическое сообщение и удалим рабочий файл */ if (! work_success) { fprintf (stderr, "\nНеудачное завершение операций над рабочим файлом %s\n", WORK_FILE); unlink (WORK_FILE); return (-1); }

/* Установим режим доступа к рабочему файлу, */ /* подходящие для последующего использования */ /* Пусть, например, это будет доступ для всех */ /* только на чтение */ chmod (WORK_FILE, S_IRUSR | S_IRGRP | S_IROTH);

/* Удалим ранее сохраненную старую версию основного файла */ unlink (OLD_FILE);

/* Сохраним текущую версию основного файла */ if (link (MAIN_FILE, OLD_FILE)) { perror ("Не удалось сохранить текущую версию основного файла"); return (-1); }

/* Удалим текущую версию основного файла */ unlink (MAIN_FILE);

/* Сделаем рабочий файл основным */ if (link (WORK_FILE, MAIN_FILE)) { perror ("Не удалось сделать рабочий файл основным"); /* Восстановим основной файл */ link (OLD_FILE, MAIN_FILE); return (-1); }

/* Удалим рабочий файл */ unlink (WORK_FILE);

return 0; }

Листинг 4.42. Пример программы, использующей функции link() и unlink().

Выше мы отмечали, что, как правило, файлы создаются по мере необходимости при выполнении определенных операций. Одной из таких операций является копирование файлов, выполняемое служебной программой cp:

cp [-fip] исходный_файл целевой_файл cp [-fip] исходный_файл ...


На самом деле утилита mv, как правило, применяется для переименования файлов, и тогда ее работа сводится к созданию новых элементов каталогов и уничтожению старых, а реальное копирование может потребоваться только при перемещении файлов между файловыми системами (например, со съемного носителя на постоянный).

По опции -i   запрашивается подтверждение перед замещением существующего файла, опция -f влечет отсутствие подобных запросов (по поводу логики запроса подтверждений см. выше описание команды rm).

Приведем пример употребления утилиты mv. Пусть текущий каталог содержит только файлы   x и y и пустой каталог   d1 (см. пример 4.43). Тогда после выполнения команд, показанных в пример 4.44, будет создан каталог   d2, где окажутся файлы   x и y, а каталог   d1 исчезнет (см. пример 4.45).

.: d1/ x y ./d1:

Листинг 4.43. Состояние текущего каталога перед перемещением файлов.

ls -RF mv x y d1 mv d1 d2 ls -RF

Листинг 4.44. Использование утилиты mv для перемещения файлов и файловых иерархий.

.: d2/ ./d2: x y

Листинг 4.45. Состояние текущего каталога после перемещения файлов.

Продолжим этот пример двумя одинаковыми командами копирования (см. пример 4.46).

cp -R d2 d1 cp -R d2 d1 ls -RF

Листинг 4.46. Применение утилиты cp для копирования файловых иерархий.

Первая команда скопирует иерархию с корнем d2 во вновь созданный каталог   d1, вторая - под d1 (с сохранением имени d2 для корня копии). Результат показан в пример 4.47.

.: d1/ d2/ ./d1: d2/ x y ./d1/d2: x y ./d2: x y

Листинг 4.47. Результат использования утилиты cp для копирования файловых иерархий.

Для перемещения (переименования) одного файла служит функция rename() из репертуара C99 (см. пример 4.48).

#include <stdio.h> int rename (const char *old_path, const char *new_path);

Листинг 4.48. Описание функции rename().

Функция rename() обладает частью функциональности служебной программы mv. Она не перемещает файловых иерархий и не раскрывает символьных ссылок, если их имена заданы в качестве аргументов.

Содержание раздела