从文件描述符重命名?

11
这是关于 Linux 的 从文件描述符获取文件名的变体 问题。如果我有一个指向常规文件的文件描述符,我能否通过给它一个新的文件名(当然要在同一设备上)来“保存”该文件描述符?我正在寻找类似于 rename(2) 或 link(2) 的东西,但它将文件描述符作为输入而不是文件名。rename(2) 和 link(2) 的问题在于,即使您可以尝试从文件描述符转到文件名,这也可能失败。我更准确地考虑已经取消链接打开的文件描述符所引用的文件的情况,在这种情况下,该文件没有名称。似乎没有办法防止在关闭() 文件描述符时删除文件。但我错了吗?我们能否使用 Posix 或甚至 Linux API 再次为其命名?
更新:实际上我们可以在Linux的/proc/<pid>/fd/<fd>中查看已删除文件的内容,即使它看起来像是一个损坏的符号链接。但我们不能使用link(2)或ln(1)来重新生成这样的文件,因为它会认为我们正在尝试进行跨设备链接。

但是你可以将fd/<fd>文件的内容复制出来。很傻,你不能重新链接它。 - Zan Lynx
1
这里有一个基本相同的问题,并且有一个很好的答案:https://dev59.com/xG855IYBdhLWcg3wvXDd - pixelbeat
2个回答

6

如果问题涉及到Linux,并且版本大于2.6.39,您可以使用带有AT_EMPTY_PATH标志的linkat命令来给文件描述符命名。请参阅手册页面(http://man7.org/linux/man-pages/man2/link.2.html)。

linkat(fd,"",destdirfd,"filename",AT_EMPTY_PATH);

注意事项:

  • 您需要定义_GNU_SOURCE以获得AT_EMPTY_PATH的定义,
  • 对于链接计数为零的文件,不能保证其有效。我不确定手册页面对此的解释。我的猜测是,当一个文件链接计数为零时,inode已经在文件系统上被删除了,以避免文件系统崩溃后出现不一致。
  • 当然,如果旧文件不在目标目录的同一文件系统上,我不希望它可以工作。

如果这失败了,您没有其他机会,只能创建一个新文件并使用sendfile将内容复制到该文件中(省略错误检查,请参阅每个函数的手册页面获取可能的错误值):

struct stat s;
off_t offset = 0;
int targetfd = open("target/filename", O_WRONLY | O_CREAT | O_EXCL);
fstat(fd,&s);
sendfile(targetfd,fd,&offset, s.st_size);

谢谢!虽然它还不是完全通用的,但这是朝着“正确”的方向迈出的一步——并不是我在暗示我所要求的东西真的是正确的或者甚至是必需的。(我的最初问题只是出于好奇。) - Armin Rigo
我认为Linux会对“给这个文件描述符命名”的通用API有所帮助,只有当文件描述符不引用普通文件或引用另一个分区中的文件时才应该失败。此外,在进行替换现有文件时,应该有一种原子方式来完成,目前可以使用rename实现,但前提是源文件具有文件系统名称。 - pqnet

6
问题:如果有一个系统调用frename,它接受一个文件描述符,并且如果一个文件有多个名称(硬链接),那么当使用这个系统调用时,哪些名称将被移动/重命名?
这个问题没有一个好的答案,这也是这个系统调用不存在的原因之一。
rename处理目录条目,这些条目是指向文件(inode)的指针。打开的文件描述符与任何特定的目录条目都不相关,只与文件本身(inode)相关。从这个角度来看,你所询问的系统调用实际上没有意义。没有可移植的方法可以追踪inode返回指向它的目录条目(而且可能不止一个)。一些操作系统可能会提供各种非可移植的方法来寻找这个反向链接,这些方法可能或可能不保证总是能得到结果(通常不能保证),但这些方法并没有回答当存在多个目录条目时应该返回哪个目录条目的问题,据我所知,这些方法都没有被扩展成像你要找的系统调用一样。

抱歉造成困惑。这对我来说很清楚,这也是我建议使用 flink() 的原因。我不应该提到 frename() - Armin Rigo
2
flink()确实是一个很好的功能。它是唯一能够重新连接一个没有链接但仍然存在的文件的方法。但是...在我所知道的任何操作系统下都不存在这个功能。 - Celada

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接