Символическая (или мягкая) ссылка создается с помощью системного вызова symlink(). Символические ссылки чем-то напоминают жесткие, с тем отличием, что первые могут выходить за пределы файловой системы, а также ссылаться на каталоги. Символическая ссылка — всего лишь файл, содержащий имя другого файла; это имя можно извлечь, воспользовавшись системным вызовом readlink(). Символическая ссылка не учитывается счетчиком ссылок (целевого) индексного дескриптора и может оказаться зависшей, если имя файла, на который она ссылается, будет удалено. Ряд системных вызовов автоматически разыменовывают символические ссылки (то есть следуют по ним); остальные вызовы этого не делают. В некоторых случаях существуют две версии системного вызова: первая разыменовывает символические ссылки, а вторая — нет. В качестве примеров можно назвать вызовы stat() и lstat().

Каталоги создаются с помощью системного вызова mkdir(), а удаляются вызовом rmdir(). Чтобы просканировать содержимое каталога, можно применить системные вызовы opendir(), readdir() и соответствующие функции. Функция nftw() позволяет программе выполнить обход всего дерева каталога, вызывая указанную программистом функцию для работы с каждым файлом в данном дереве.

Функцию remove() можно использовать для удаления файла (то есть ссылки) или пустого каталога.

Каждый процесс имеет корневой каталог. Он определяет местоположение, относительно которого интерпретируются абсолютные имена путей. У процесса есть также текущий рабочий каталог, определяющий местоположение, относительно которого интерпретируются относительные имена путей. Для изменения этих атрибутов применяются системные вызовы chroot() и chdir(). Функция getcwd() возвращает имя текущего рабочего каталога процесса.

В Linux присутствует набор системных вызовов (например, openat()), работающие подобно своим традиционным аналогам (то есть open()), за исключением того, что относительные имена путей могут интерпретироваться по отношению к каталогу, указанному в файловом дескрипторе, переданном вызову (а не с помощью текущего рабочего каталога процесса). Это удобно использовать, чтобы избежать некоторых типов соперничества, а также для реализации виртуальных рабочих каталогов для потоков.

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

18.16. Упражнения

18.1. В разделе 4.3.2 мы отметили, что нельзя открыть файл на запись, если он в данный момент выполняется (системный вызов open() возвращает –1, а переменной errno присваивается значение ETXTBSY). Тем не менее можно выполнить из оболочки следующее:

$ cc — o longrunner longrunner.c

$./longrunner & Оставляем работать в фоновом режиме

$ vi longrunner.c Вносим изменения в исходный код

$ cc — o longrunner longrunner.c

Последняя команда перезаписывает существующий исполняемый файл с таким же именем. Почему это возможно? (Подсказка: воспользуйтесь командой ls — li, чтобы увидеть номер индексного дескриптора исполняемого файла после каждой компиляции.)

18.2. Почему приводит к ошибке системный вызов chmod() в следующем коде:

mkdir("test", S_IRUSR | S_IWUSR | S_IXUSR);

chdir("test");

fd = open("myfile", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

symlink("myfile", "../mylink");

chmod("../mylink", S_IRUSR);

18.3. Реализуйте функцию realpath().

18.4. Измените программу из листинга 18.2 (list_files.c) так, чтобы вместо функции readdir() использовалась функция readdir_r().

18.5. Реализуйте функцию, которая работает подобно функции getcwd(). Для решения данной задачи удобно воспользоваться таким приемом: можно выяснить имя текущего рабочего каталога с помощью функций opendir() и readdir(), чтобы выполнить обход каждой записи в родительском каталоге (..). Это необходимо для нахождения записи с такими же индексным дескриптором и номером устройства, что и у текущего рабочего каталога (то есть соответственно полей st_ino и st_dev в структуре stat, возвращаемой системными вызовами stat() и lstat()). Следовательно, есть возможность «собрать» путь каталога путем пошагового обхода его дерева (chdir("..")) и выполнения такого сканирования. Обход можно завершить, когда родительский каталог будет такой же, как текущий рабочий (как вы помните, /.. — это то же, что и /). Вызывающий процесс должен оставаться в том же каталоге, откуда начал работу, вне зависимости от того, как завершила работу ваша функция getcwd(), успешно или с ошибкой (для данной цели удобно применить системный вызов open() в сочетании с функцией fchdir()).

18.6. Измените программу из листинга 18.3 (nftw_dir_tree.c) так, чтобы использовать флаг FTW_DEPTH. Обратите внимание на отличие в порядке обхода каталога.

Перейти на страницу:

Похожие книги