Я использую дистрибутив на основе Linux 4.x, и недавно я заметил, что open()
системный вызов ядра поддерживает O_PATH
флаг открытия.
Хотя на этой man
странице есть список системных вызовов, с которыми она может теоретически использоваться, я не совсем понимаю, в чем идея. Я open(O_PATH)
только каталоги, а не файлы? И если я это сделаю, почему я хочу использовать файловый дескриптор вместо пути к каталогу? Кроме того, большинство системных вызовов, перечисленных там, не относятся к каталогам; Итак, я также открываю обычные файлы, O_PATH
чтобы как-то получить их каталог в качестве файлового дескриптора? Или получить файловый дескриптор для них, но с ограниченной функциональностью?
Может ли кто-нибудь дать убедительное объяснение того, что O_PATH
и как и для чего, мы должны использовать?
Ноты:
- Не нужно описывать историю того, как это развивалось (в соответствующих справочных страницах упоминаются изменения в Linux 2.6.x, 3.5 и 3.6), если это не нужно - мне просто важно, как обстоят дела сейчас.
- Пожалуйста, не говорите мне просто использовать libc или другие высокоуровневые средства, я это знаю.
источник
Ответы:
Описание на
open(2)
странице руководства дает некоторые подсказки для начала:Иногда мы не хотим открывать файл или каталог. Вместо этого нам просто нужна ссылка на этот объект файловой системы для выполнения определенных операций (например, на
fchdir()
каталог, на который ссылается дескриптор файла, который мы открыли с помощьюO_PATH
). Итак, тривиальный момент: если это наша цель, то открытие с помощьюO_PATH
должно быть немного дешевле, так как сам файл фактически не открывается.И менее тривиальный момент: до существования
O_PATH
способа получения такой ссылки на объект файловой системы было открывать объект с помощьюO_RDONLY
. Но использованиеO_RDONLY
требует, чтобы у нас было разрешение на чтение объекта. Однако существуют различные случаи использования, когда нам фактически не нужно читать объект: например, выполнение двоичного файла или доступ к каталогу (fchdir()
) или обращение к каталогу для прикосновения к объекту внутри каталога.Использование с системными вызовами "* at ()"
Распространенное, но не только, использование
O_PATH
, чтобы открыть каталог, чтобы иметь ссылку на этот каталог для использования с «* в» системных вызовов, таких какopenat()
,fstatat()
,fchownat()
, и так далее. Это семейство системных вызовов, которые мы можем примерно думать как современные продолжатели старых системных вызовов с похожими названиями (open()
,fstat()
,fchown()
и так далее), которые служат несколько целей, первые из которых вы касаетесь, когда вы спрашиваете " почему я хочу использовать файловый дескриптор вместо пути к каталогу? " Если мы посмотрим дальше наopen(2)
странице руководства , мы найдем этот текст (в подзаголовке с обоснованием системных вызовов "* at"):Чтобы сделать это более конкретным ... Предположим, у нас есть программа, которая хочет выполнять несколько операций в каталоге, отличном от текущего рабочего каталога, что означает, что мы должны указать некоторый префикс каталога как часть имен файлов, которые мы используем. Предположим, например, что путь есть,
/dir1/dir2/file
и мы хотим выполнить две операции:/dir1/dir2/file
(например, кто владеет файлом или в какое время он был последний раз изменен)./dir1/dir2/file.new
.Теперь сначала предположим, что мы сделали все, используя традиционные системные вызовы на основе имени пути:
Теперь, кроме того, предположим, что в префиксе каталога
/dir1/dir2
один из компонентов (скажемdir2
) был фактически символической ссылкой (которая относится к каталогу), и что между вызовомstat()
open()
злоумышленника и вызовом злоумышленника смогла изменить цель символическая ссылка,dir2
указывающая на другой каталог. Это классическое состояние гонки на время проверки. Наша программа проверила файл в одном каталоге, но затем его обманули, чтобы создать файл в другом каталоге, возможно, в защищенном каталоге. Ключевым моментом здесь является то, что путь/dir/dir2
выглядел одинаково, но то, что он ссылается, изменилось полностью.Мы можем избежать подобных проблем, используя вызовы "* at". Прежде всего, мы получаем дескриптор, ссылающийся на каталог, где мы будем выполнять нашу работу:
Критическим моментом здесь является то, что
dirfd
это стабильная ссылка на каталог, на который ссылался путь/dir1/dir2
во времяopen()
вызова. Если цель символической ссылкиdir2
впоследствии изменится, это не повлияет на то, чтоdirfd
относится к. Теперь мы можем сделать наш чек + операцию с использованием «* на» вызовы , которые эквивалентныstat()
иopen()
вызовы выше:Во время этих шагов любая манипуляция символическими ссылками в пути
/dir/dir2
не окажет никакого влияния: check (fstatat()
) и operation (openat()
) гарантированно будут происходить в одном и том же каталоге.Существует еще одна цель использования вызовов «* at ()», которая связана с идеей «текущих рабочих каталогов на поток» в многопоточных программах (и снова мы могли бы открывать каталоги, используя
O_PATH
), но я думаю, что это использование, вероятно, менее актуален для вашего вопроса, и я оставляю вас читатьopen(2)
справочную страницу, если вы хотите узнать больше.Использование с файловыми дескрипторами для обычных файлов
Одно из применений
O_PATH
с обычными файлами - открыть двоичный файл, для которого у нас есть разрешение на выполнение (но не обязательно разрешение на чтение, чтобы мы не могли открыть файл с помощьюO_RDONLY
). Этот дескриптор файла затем может быть переданfexecve(3)
для выполнения программы. Все, чтоfexecve(fd, argv, envp)
делает с егоfd
аргументом, по сути:(Хотя, начиная с glibc 2.27, реализация будет использовать
execveat(2)
системный вызов в ядрах, которые предоставляют этот системный вызов.)источник
The problem is that between the existence check and the file creation step, path or to ... could be modified
- не могу разобрать это предложение. Но я понимаю суть этого, я думаю. Так что это служит своего рода механизмом блокировки каталога. Но зачем использоватьopen()
результат, а не фактическую блокировку?