我正在为enum class
中列出的不同文件标记写一个安全的open(3)
,当我注意到我无法找到O_EXCL
的书面文字时。< / p>
enum class Flags {
readOnly, // O_RDONLY
truncate, // O_TRUNC
? // O_EXCL
};
我想到了两种可能的含义:
OPEN_EXCLUSIVE
OPEN_EXISTS_CLOSE
但我找不到任何有关预期意义的资源。
答案 0 :(得分:1)
在这里,Exclusive是一个正确的单词,因为该标志是O_CREAT
标志独有的,如果文件存在则使该函数失败。 (如果未设置O_CREAT
标志,则行为未定义。)
答案 1 :(得分:0)
O_EXCL
的真正含义是“error if create and file exists”,但该名称源自“EXCL
usive”,不过这有点误导,导致许多人误解该标志。
使用 O_EXCL
打开文件不会像某些人错误地认为的那样为您提供独占访问权。一个进程用O_EXCL
打开的文件可以被其他进程同时打开进行读甚至写,没问题,所以访问显然不是独占的。
这个标志存在的主要原因是“锁定文件”。早在出现文件咨询锁定(使用 fcntl
)和可以在进程之间共享的信号量/互斥锁之前,需要一种简单的方法来确保跨多个进程对某些系统资源进行原子访问。实现这一点的方法是锁定文件。想要访问资源的第一个进程将在 /var/lock
中创建一个锁定文件以声明对该资源的所有权,并在使用完后再次将其删除。其他进程会监视该目录,从而知道资源是否可用。
问题:如果两个进程查看目录,都看到某个文件不存在,所以资源可用,现在都尝试创建这个文件,如何确保两者中只有一个会成功?这就是 O_EXCL
发挥作用的地方。如果他们都尝试创建设置了 O_EXCL
的文件,则此操作只会对其中一个成功,即现在拥有资源锁的进程。
所以O_EXCL
不是为了获得对文件的独占访问权限,而是为了制作独占访问文件,其目的是规范对某种资源的独占访问。
今天 O_EXCL
的第二个最重要的用途是文件访问安全。考虑这种情况:一个 root 进程想要创建一个文件,并用只有 root 用户才能看到的内容填充它,普通用户不能看到,但它正在一个目录中创建它,普通用户有写访问权限(例如/tmp
)。如果进程会按如下方式创建文件
open("/tmp/root-only", O_CREAT | O_WRONLY, 0600);
而且文件不存在,它是创建的,归root所有,只有root有读写权限,所以普通用户看不到它的内容。任务完成。
但是如果普通用户之前已经创建了 /tmp/root-only
呢?然后这个文件归那个用户所有,这个用户可以读取它,上面的 open 调用只会打开现有的文件。即使 root 进程在打开文件后直接更改所有权和文件权限,这对之前已经打开文件的进程(例如 tail -f
)没有影响。
因此,实现该案例的正确方法实际上是:
unlink("/tmp/root-only"); // best effort, may not even exist
open("/tmp/root-only", O_CREAT | O_EXCL | O_WRONLY, 0600);
如果 open
成功,进程可以依赖该文件归进程所有者所有,其他人没有读取/写入它的访问权限。