问题描述
如果我想将 A
重命名为 B
,但仅当 B
不存在时,天真的事情是检查 B
存在(带有 access("B",F_OK)
或类似的东西),并且如果没有继续进行 rename
.不幸的是,这打开了一个窗口,在此期间其他一些过程可能会决定创建 B
,然后它会被覆盖-甚至更糟的是,没有迹象表明发生过类似的事情.
If I want to rename A
to B
, but only if B
doesn't exist, the naive thing would be checking if B
exists (with access("B", F_OK)
or something like that), and if it doesn't proceeding with rename
. Unfortunately this opens a window during which some other process might decide to create B
, and then it gets overwritten - and even worse there's no indication that something like that ever happened.
其他文件系统访问功能不受此影响- open
具有 O_EXCL
(这样可以安全地复制文件),最近Linux拥有了完整的系列> * at
可以防止大多数其他竞争条件的系统调用-但不是特定条件(存在 renameat
,但可以防止完全不同的问题).
Other file system access functions don't suffer from this - open
has O_EXCL
(so copying files is safe), and recently Linux got an entire family of *at
syscalls that protect against most other race conditions - but not this particular one (renameat
exists, but protects against an entirely different problem).
那么有解决方案吗?
推荐答案
从Linux内核3.15(于2014年6月发布)开始,可以使用syscall(__ NR_renameat2,AT_FDCWD,"source-file",AT_FDCWD,"dest-file",RENAME_NOREPLACE)(包括< syscall.h>
,< fcntl.h>
和< linux/fs.h>
>).
As of Linux kernel 3.15 (released in June 2014), this can be done with syscall(__NR_renameat2, AT_FDCWD, "source-file", AT_FDCWD, "dest-file", RENAME_NOREPLACE) (include <syscall.h>
, <fcntl.h>
and <linux/fs.h>
).
这比link()更好,因为永远不会存在两个文件名同时存在的情况(特别是对于link()而言,定时断电可能会导致两个名字永远保留).
This is better than link(), because there is never a point where both filenames exist simultaneously (in particular, with link(), a precisely-timed power outage could cause both names to remain forever).
glibc 2.28(于2018年8月发布)添加了一个renameat2()包装器,因此您可以使用它而不是syscall.h和linux/fs.h(尽管您很可能需要< stdio.h>
和 #define __GNU_SOURCE
).
glibc 2.28 (released in August 2018) adds a renameat2() wrapper, so you can use that instead of syscall.h and linux/fs.h (though you'll most likely need <stdio.h>
and #define __GNU_SOURCE
instead).
有关更多详细信息,请参见 http://man7.org/linux/man-pages/man2/rename.2.html(虽然在撰写本文时确实如此,但不知道 glibc 现在有一个 renameat2 包装器).
For more details, see http://man7.org/linux/man-pages/man2/rename.2.html (though it does, as of writing, not know that glibc now has a renameat2 wrapper).
这篇关于如何在没有竞争条件的情况下重命名()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!