从Unix上的文件句柄创建硬链接?

时间:2013-06-15 20:16:30

标签: linux macos unix

如果我有一个打开文件的句柄,是否可以在从文件系统中删除对该文件的所有引用后创建该文件的硬链接?

例如,像这样:

fd = fopen("/tmp/foo", "w");
unlink("/tmp/foo");
fwrite(fd, "Hello, world!\n");
create_link_from_fd(fd, "/tmp/hello");
fclose(fd);

具体来说,我想这样做,以便我可以安全地写入大数据文件,然后原子地将它们移动到位,而不必担心我自己的程序在写入文件的过程中被杀死后自己清理

3 个答案:

答案 0 :(得分:10)

新发布的Linux 3.11通过新的O_TMPFILE open(2)标志提供了此问题的解决方案。使用此标志,您可以在某个文件系统中创建“不可见”文件(即没有硬链接的inode)(由该文件系统中的目录指定)。然后,在完全设置文件后,您可以使用linkat创建硬链接。它的工作原理如下:

fd = open("/tmp", O_TMPFILE | O_RDWR, 0600);
// write something to the file here
// fchown()/fchmod() it
linkat(fd, "", AT_FDCWD, "/tmp/test", AT_EMPTY_PATH);

请注意,除了> = 3.11内核要求之外,这还需要来自底层文件系统的支持(我在ext3上尝试了上面的代码片段并且它有效,但它似乎不适用于btrfs)。

答案 1 :(得分:4)

一般情况下,不是。 [编辑:从Linux 3.11开始,现在有linkat;见safsaf32's answer。这通常不适用于POSIX系统,因为POSIX linkat仅限于目录。]这里有一些安全注意事项:有人可以向您传递一个开放的文件描述符,通常情况下您不能open拥有,例如:

mkdir lock; chmod 700 lock
echo secret contents > lock/in
sudoish cmd < lock/in

此处cmd作为用户无法按名称open输入文件(lock/in)运行,但仍可以从中读取。如果cmd可以在同一文件系统上创建新名称,则可以将文件内容传递给后续进程。 (显然它可以复制那些内容,所以这个问题更多的是“错误地传递内容”而不是“故意传递内容”。)

也就是说,人们已经想出了通过inode / vnode在内部“重新链接”文件的方法(在大多数文件系统中都很容易),所以你可以为它自己进行私有系统调用。描述符必须引用适当安装点上的真实文件,当然 - 没有办法将管道或套接字或设备“重新链接”成为常规文件。

否则你会遇到“捕获信号并清理并希望获得最佳效果”或类似技巧,“分离子进程,运行它,如果成功/失败,请采取适当的移动/清理动作”。


编辑以添加历史记录:上面的lock示例并不是特别好,但是在V6 Unix的时代,MDQS使用了这个技巧的更高版本。 MDQS的各个部分今天以各种形式存在。

答案 2 :(得分:1)

在Linux上,您可以通过尝试调用

来尝试使用/proc/self/fd的不可移植的技巧
 char pbuf[64];
 snprintf (pbuf, sizeof(pbuf), "/proc/self/fd/%d", fd);
 link(pbuf, "/tmp/hello");

如果这句话在unlink("/tmp/foo")之后起作用,我会感到惊讶......我没有尝试过。

更便携(但不太健壮)的方式是生成“独特的临时路径”,可能就像

 int p = (int) getpid();
 int t = (int) time(0);
 int r = (int) random();
 sprintf(pbuf, sizeof(pbuf), "/tmp/out-p%d-r%d-t%d.tmp", p, r, t);
 int fd = open  (pbuf, O_CREAT|O_WRONLY);

一旦文件被写入并关闭,你rename(2)就会有一些更明智的路径。您可以在程序中使用atexit进行重命名(或删除)。

每小时都有一些cron工作来清理[旧] /tmp/out*.tmp ......