path_resolution(7) как путь преобразуется в файл

ОПИСАНИЕ

У некоторых системных вызовов UNIX/Linux есть параметр для указания одного или нескольких имён файлов. Имя файла (или путь) преобразуется (resolved) следующим образом.

Шаг 1: запуск процесса преобразования

Если путь начинается с символа «/», то начальным каталогом поиска назначается корневой каталог вызывающего процесса (процесс наследует свой корневой каталог от родителя. Обычно, это корневой каталог файловой иерархии. Процесс может получить другой корневой каталог, используя системный вызов chroot(2). Процесс может получить полностью закрытое пространство имён монтирования когда он сам или один из его предков был запущен системным вызовом clone(2) с флагом CLONE_NEWNS). Так обрабатывается часть пути «/».

Если путь не начинается с символа «/», то начальным каталогом поиска процесса преобразование выбирается текущий рабочий каталог процесса (также наследуемый от родителя; может быть системным вызовом chdir(2)).

Пути, начинающиеся с символа «/», называются абсолютными путями. Пути, не начинающиеся с символа «/», называются относительными путями.

Шаг 2: обход пути

Назначаем текущим каталогом поиска начальный каталог поиска. Теперь для каждого не конечного компонента пути (подстрока, разделённая символами «/») будет производиться поиск в текущем каталоге поиска.

Если у процесса нет права поиска в текущем каталоге просмотра, будет возвращена ошибка EACCES («Доступ запрещён»).

Если компонент не найдет, будет возвращена ошибка ENOENT («Нет такого файла или каталога»).

Если компонент найден, но не является ни каталогом, ни символической ссылкой, будет возвращена ошибка ENOTDIR («Не является каталогом»).

Если компонент найден и является каталогом, значением текущего каталога поиска устанавливается этот каталог, после чего осуществляется переход к следующему компоненту.

Если компонент найден и это символическая ссылка (symlink), то сначала мы разрешаем эту символическую ссылку (от текущего каталога, как начального пути поиска). При ошибках, возвращаем ошибку. Если результат не каталог, то возвращается ошибка ENOTDIR. Если разрешение символической ссылки удалось и получен каталог, то мы назначаем этот каталог текущим каталогом поиска и переходим к следующему компоненту. Заметим, что процесс разрешения здесь может вызвать рекурсию, если компонент префикса («dirname») пути содержит имя символической ссылки, которая разрешается в каталог (где префикс компонента этого каталога может содержать символическую ссылку и т .д.). Для защиты ядра от переполнения стека, а также от отказа в обслуживании, есть ограничение на максимальную глубину рекурсии и на максимальное количество переходов по символическим ссылкам. При превышении возвращается ошибка ELOOP («Слишком много уровней символических ссылок»).

В текущей реализации Linux максимальное количество допустимых переходов по символическим ссылкам при преобразовании пути равно 40. В ядрах до версии 2.6.18 ограничение глубины рекурсии было равно 5. Начиная с Linux 2.6.18, это ограничение увеличилось до 8. В Linux 4.2 код ядра разрешения пути был переработан и возникновение рекурсии больше не происходит, поэтому осталось только ограничение на 40 разрешений для всего пути.

Шаг 3: поиск последнего элемента

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

. и ..

По соглашению, в каждом каталоге есть записи «.» и «..», которые ссылаются на сам каталог и на его родительских каталог, соответственно.

Процесс определения пути предполагает, что эти записи имеют свои общепринятые значения, независимо от того, присутствуют ли они на самом деле в физической файловой системе.

Невозможно перейти за пределы корня: путь «/..» равен «/».

Точки монтирования

После команды «mount устройство путь» путь указывает на корень иерархии файловой системы устройства, и больше не указывает на то, что было раньше.

Можно выйти за пределы смонтированной файловой системы: «путь/..» указывает на родительский каталог «пути», лежащий вне иерархии файловой системы устройства.

Конечные символы косой черты

Если путь заканчивается на «/», то это приводит к обработке предыдущего компонента согласно шагу 2: он существует и преобразуется в каталог. В противном случае конечный «/» будет проигнорирован (или, что тоже самое, путь с конечной косой чертой «/» будет равен пути, полученному добавлением к его концу «.»).

Символьная ссылка в конце

Если последний компонент пути является символической ссылкой, то это зависит от системного вызова, будет ли файл указывать на саму символическую ссылку или на её содержимое. Например, системный вызов lstat(2) будет работать с символической ссылкой, а stat(2) работает с файлом, на который указывает символическая ссылка.

Ограничение длины

Существует максимально допустимая длина пути. Если путь (или какой-нибудь промежуточный путь, полученный при разрешении символических ссылок) очень длинный, то возвращается ENAMETOOLONG («Имя файла очень длинное»).

Пустой путь

В оригинальном UNIX, пустой путь считался текущим каталогом. В наши дни в POSIX указано, что пустой путь не должен быть преобразован без ошибки. В Linux в этом случае возвращается ENOENT.

Права доступа

Биты доступа к файлу состоят из трёх групп по три бита (смотрите chmod(1) и stat(2)). Первая группа используется тогда, когда эффективный идентификатор пользователя процесса вызова равен идентификатору владельца файла. Вторая группа используется если групповой идентификатор либо равен эффективному идентификатору группы вызывающего процесса, либо является одним из дополнительных групп вызывающего процесса (установлен setgroups(2)). В остальных случаях используется третья группа.

Три бита используются следующим образом: первым битом задаётся доступ на чтение, вторым на запись, а последним, в случае обычных файлов — разрешение на выполнение, в в случае каталогов разрешение на поиск.

Для проверки доступа в Linux используется fsuid вместо эффективного идентификатора пользователя. Обычно, fsuid равен эффективному идентификатору пользователя, fsuid может быть изменён системным вызовом setfsuid(2).

(Здесь «fsuid» сокращение от «пользовательский идентификатор в файловой системе, filesystem user ID». Это требовалось для реализации сервера NFS в пользовательском пространстве во времена, когда процессы могли посылать сигнал процессу с тем же эффективным идентификатором пользователя. Сейчас это устарело. Никто не должен использовать setfsuid(2).)

Схожим образом в Linux используется fsgid ("filesystem group ID") вместо идентификатора эффективной группы. Смотрите setfsgid(2).

Пропуск проверки прав доступа: суперпользователь и мандаты

В традиционных системах UNIX суперпользователю (root, идентификатор пользователя 0) доступно всё и при доступе к файлам никаких проверок ограничений не производится.

В Linux права суперпользователя делятся мандатами (смотрите capabilities(7)). Два мандата относятся к проверка доступа к файлам: CAP_DAC_OVERRIDE и CAP_DAC_READ_SEARCH (процесс имеет эти мандаты, если его fsuid равен 0).

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

Мандат CAP_DAC_READ_SEARCH разрешает чтение и поиск в по каталогу, а также чтение обычных файлов.