Проблема, которая обсуждалась выше, основана на возможности изменить характеристики объекта в промежуток времени между двумя операциями, не трогая остального. В предыдущей ситуации изменение не касалось самого файла. Между прочим, обычному пользователю, было бы достаточно сложно изменить или даже прочитать файл /etc/shadow. Фактически, изменение касалось связи между существующей файловой записью в дереве имен и самим файлом, как физической сущностью. Вспомним, что большинство системных команд (rm, mv, ln и т.д.) воздействуют на имя файла, а не на его содержимое. Даже если вы удаляете файл (используя rm и системный вызов unlink()), по-настоящему сожержимое его удаляется, когда последняя физическая связь - последняя жесткая ссылка - будет удалена.
Ошибка, которая была сделана в предыдущей программе, происходит из-за того, что мы полагали, что ассоциация между именем файла и его содержимым неизменна, или, как минимум, постоянна в промежуток времени между операциями stat() и fopen(). Примера жесткой ссылки достаточно, чтобы удостовериться, что это соответствие совсем не постоянно. Приведем пример, используя такой тип ссылок. В директории, которая принадлежит нам, мы создаем новую ссылку на системный файл. Естественно, владелец и права доступа к файлу сохраняются. Опция -f команды ln заставляет сделать ссылку даже если имя уже существует:
Опция -i команды /bin/ls выводит номер индексного дескриптора в начале строки. Мы видим, что одно имя указывает на различные физические индексные дескрипторы. Между прочим, ясно, что обе команды "cat", обрабатывая одно и то же имя файла, выводят два совершенно различных результата, тем не менее никакие изменения не вносились в эти файлы между двумя операциями.
Фактически, нам хотелось бы, чтобы функции, которые делают проверку и осуществляют доступ к файлу, всегда обращались с одним и тем же содержимым, и с одним и тем же индексным дескриптором. И это возможно! Ядро само автоматически управляет этим соответствием, когда предоставляет нам файловый дескриптор. Когда мы открываем файл для чтения, системный вызов open() возвращает целое число, которое является дескриптором, асоциированым с физическим файлом во внутренней таблице. Все операции чтения, которые мы затем будем производить, будут относиться к содержимому этого файла, не учитывая, что происходит с именем, которое было использовано при операции открытия.
Давайте выделим этот момент: если файл был открыт, любая операция над именем файла, включая его удаление, не будет влиять на содержимое файла. Пока есть еще процесс, работающий с дескриптором файла, содержимое файла не удаляется с диска, даже если его имя исчезает из директории, где оно находилось. Ядро поддерживает ассоциацию с содержимым файла между системным вызовом open(), который предоставляет файловый дескриптор, и освобождением этого дескриптора при вызове close() или завершением процесса.
Итак мы имеем наше решение! Мы можем открыть файл, а затем проверять права доступа, изучая характеристики дескриптора, а не какого-то имени файла. Это можно сделать используя системный вызов fstat() (работает так же как и stat()), который проверяет файловый дескриптор, а не путевое имя. Чтобы получить доступ к содержимому файла, используя дескриптор, мы будем использовать функцию fdopen() (которая работает так же как и fopen()), так как она использует дескриптор, а не имя файла. Вот какая получается программа:
Теперь, после строки 20, никакое изменение имени файла (удаление, переименование, создание ссылки) не окажет влияния на поведение нашей программы; содержимое исходного физического файла будет сохранятся.