我应该将O_PATH用于什么,以及如何使用?


8

我使用的是基于Linux 4.x的发行版,最近我注意到内核的open()系统调用支持O_PATHopen标志。

尽管man它的页面上确实列出了可以在理论上使用的系统调用列表,但我不太了解这个想法是什么。我是否open(O_PATH)仅目录,而不是文件?如果可以,为什么要使用文件描述符而不是目录路径?同样,那里列出的大多数系统调用似乎都不是目录所特有的。因此,我是否还会打开常规文件O_PATH以某种方式获取其目录作为文件描述符?还是要为它们获取文件描述符,但功能有限?

有人可以O_PATH对我们应该使用的内容,用途以及用途进行有力的解释吗?

笔记:

  • 除非必要,否则无需描述它的演变历史(相关的手册页提到Linux 2.6.x,3.5和3.6中的更改)-我只关心现在的情况。
  • 我知道,请不要告诉我只使用libc或其他更高级别的设施。


@sebasth:确实是相关的,但是:1.到目前为止,它有点旧,情况可能已经改变。2.坦白说,我的答案还不是很清楚。
einpoklum

1
您可以在该问题中发表评论,询问是否有任何更改。
Barmar

Answers:


8

open(2)手册页中的描述提供了一些以以下内容开头的线索:

   O_PATH (since Linux 2.6.39)
          Obtain a file descriptor that can be used for two purposes:
          to  indicate  a location in the filesystem tree and to per‐
          form operations that act  purely  at  the  file  descriptor
          level.  The file itself is not opened, and other file oper‐
          ations  (e.g.,  read(2),  write(2),  fchmod(2),  fchown(2),
          fgetxattr(2), ioctl(2), mmap(2)) fail with the error EBADF.

有时,我们不想打开文件或目录。相反,我们只希望引用该文件系统对象以执行某些操作(例如,fchdir()引用到使用打开的文件描述符所引用的目录O_PATH)。因此,有一点很重要:如果这是我们的目的,那么使用with打开O_PATH文件应该便宜一些,因为实际上并未打开文件本身。

还有一个不那么琐碎的要点:在存在之前O_PATH,获得对文件系统对象的此类引用的方法是使用来打开该对象O_RDONLY。但是,使用O_RDONLY要求我们具有对该对象的读取权限。但是,在许多用例中,我们不需要实际读取对象:例如,执行二进制文件或访问目录(fchdir())或通过目录访问以触摸目录中的对象。

在“ * at()”系统调用中的用法

常见的,但不是唯一的,使用的O_PATH是打开一个目录,才能有“在*”系统调用,如参考该目录与使用openat()fstatat()fchownat(),等等。这家系统调用,我们可以大致认为的现代接班人具有类似名称的旧的系统调用(open()fstat()fchown(),等),服务于几个目的,当你问:“请问哪触及第一为什么我要使用文件描述符而不是目录路径?”。如果我们在open(2)手册页中往下看,会发现以下文本(在带有“ * at”系统调用原理的子标题下):

   First,  openat()  allows  an  application to avoid race conditions
   that could occur when using open() to open  files  in  directories
   other  than  the current working directory.  These race conditions
   result from the fact that some component of the  directory  prefix
   given  to  open()  could  be  changed in parallel with the call to
   open().  Suppose, for example, that we wish  to  create  the  file
   path/to/xxx.dep  if  the  file path/to/xxx exists.  The problem is
   that between the existence check and the file creation step,  path
   or  to  (which might be symbolic links) could be modified to point
   to a different location.  Such races can be avoided by  opening  a
   file descriptor for the target directory, and then specifying that
   file descriptor as the dirfd argument of (say) fstatat(2) and ope‐
   nat().

为了使它更具体...假设我们有一个程序想要在除当前工作目录之外的目录中执行多个操作,这意味着我们必须指定一些目录前缀作为我们使用的文件名的一部分。例如,假设路径名是/dir1/dir2/file并且我们要执行两个操作:

  1. 进行一些检查/dir1/dir2/file(例如,谁拥有文件,或文件的最后修改时间)。
  2. 如果对检查的结果感到满意,那么也许我们想在同一目录中执行其他文件系统操作,例如,创建一个名为的文件/dir1/dir2/file.new

现在,首先假设我们使用传统的基于路径名的系统调用完成了所有操作:

struct stat stabuf;
stat("/dir1/dir2/file", &statbuf);
if ( /* Info returned in statbuf is to our liking */ ) {
    fd = open("/dir1/dir2/file.new", O_CREAT | O_RDWR, 0600);
    /* And then populate file referred to by fd */
}

现在,进一步假设在目录前缀/dir1/dir2中,组件之一(例如dir2)实际上是符号链接(指目录),并且在对stat()open()恶意人员的呼叫与呼叫之间可以更改目标的目标。dir2指向不同目录的符号链接。这是经典的使用时间检查竞赛条件。我们的程序检查了一个目录中的文件,但随后被诱骗在另一个目录(可能是安全敏感目录)中创建文件。这里的关键是路径名/dir/dir2看起来相同,但是它所指的却完全改变了。

我们可以使用“ * at”调用来避免这类问题。首先,我们获得一个指向将要执行工作的目录的句柄:

dirfd = open("/dir/dir2", O_PATH);

这里的关键点是对调用时路径所引用的目录dirfd稳定引用。如果随后更改符号链接的目标,则不会影响所引用的内容。现在,我们可以使用与上面的and 调用等效的“ * at”调用来执行check +操作:/dir1/dir2open()dir2dirfdstat()open()

fstatat(dirfd, ""file", &statbuf)
struct stat stabuf;
fstatat(dirfd, "file", &statbuf);
if ( /* Info returned in statbuf is to our liking */ ) {
    fd = openat(dirfd, "file.new", O_CREAT | O_RDWR, 0600);
    /* And then populate file referred to by fd */
}

在这些步骤中,对路径名中的符号链接的任何操作/dir/dir2都不会产生影响:检查(fstatat())和操作(openat())保证在同一目录中进行。

使用“ * at()”调用还有另一个目的,它涉及多线程程序中“每线程当前工作目录”的概念(同样,我们可以使用来打开目录O_PATH),但是我认为这种用法可能是与您的问题不太相关,open(2)如果您想了解更多信息,我让您阅读手册页。

与常规文件的文件描述符一起使用

O_PATH常规文件的一种用法是打开一个我们具有执行权限的二进制文件(但不一定具有读取权限,因此我们无法使用打开文件O_RDONLY)。然后可以fexecve(3)将该文件描述符传递给执行程序。所有这一切fexecve(fd, argv, envp)与它做的fd说法基本上是:

snprintf(buf, "/proc/self/fd/%d", fd);
execve(buf, argv, envp);

(尽管从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()结果而不是实际的锁?
einpoklum

@einpoklum问题是'path'和'to'没有原始手册页中显示的格式。这些是假设的路径名“ / path / to / xxx”的组成部分。而且,它不像锁:它是对文件系统对象的稳定引用;几个程序可能对同一对象有这样的引用。
mtk
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.