Linux 重新链接匿名(未链接但已打开)文件
在Unix中,可以创建匿名文件的句柄,例如,使用creat()创建并打开匿名文件,然后使用unlink()删除目录链接-留下一个带有inode和存储的文件,但无法重新打开它。这些文件通常用作临时文件(通常这是tmpfile()返回给您的) 我的问题:有没有办法将这样的文件重新附加到目录结构中?如果您可以这样做,这意味着您可以(例如)实现文件写入,以便文件以原子方式显示并完全形成。这吸引了我强迫性的整洁 在浏览相关的系统调用函数时,我希望找到一个名为flink()的link()版本(与chmod()/fchmod()相比),但至少在Linux上不存在这种版本 告诉我如何创建匿名文件而不在磁盘目录结构中短暂公开文件名的好处 我的问题:有没有办法将这样的文件重新附加到目录结构中?如果您可以这样做,这意味着您可以(例如)实现文件写入,以便文件以原子方式显示并完全形成。这吸引了我强迫性的整洁 如果这是您的唯一目标,那么您可以以更简单、更广泛的方式实现这一目标。如果您正在输出到Linux 重新链接匿名(未链接但已打开)文件,linux,file,unix,unlink,Linux,File,Unix,Unlink,在Unix中,可以创建匿名文件的句柄,例如,使用creat()创建并打开匿名文件,然后使用unlink()删除目录链接-留下一个带有inode和存储的文件,但无法重新打开它。这些文件通常用作临时文件(通常这是tmpfile()返回给您的) 我的问题:有没有办法将这样的文件重新附加到目录结构中?如果您可以这样做,这意味着您可以(例如)实现文件写入,以便文件以原子方式显示并完全形成。这吸引了我强迫性的整洁 在浏览相关的系统调用函数时,我希望找到一个名为flink()的link()版本(与chmod(
a.dat
:
a.dat.part
进行写入a.dat.part
重命名为a.dat
似乎表明这种重新链接不安全且不受支持。显然,这是可能的--
fsck
就是这样做的。然而,fsck
使用主要的本地化文件系统mojo来实现这一点,并且显然不能作为非特权用户进行移植或执行。它类似于上面的debugfs
注释
编写flink(2)调用将是一个有趣的练习。正如ijw所指出的,与当前的临时文件重命名实践相比,它将提供一些优势(请注意,重命名是原子性的)。是几年前提交的,但当Linus声明时,这几乎结束了关于是否添加此项的辩论
更新:从Linux 3.11开始,现在可以使用新的O\u TMPFILE
标志创建一个没有目录项的文件,并在文件完全成形后使用on/proc/self/fd/
fd和AT\u SYMLINK\u FOLLOW
标志将其链接到文件系统中
手册页面上提供了以下示例:
char path[PATH_MAX];
fd = open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
/* File I/O on 'fd'... */
snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd);
linkat(AT_FDCWD, path, AT_FDCWD, "/path/for/file", AT_SYMLINK_FOLLOW);
请注意,
linkat()
将不允许在使用unlink()
删除最后一个链接后重新附加打开的文件,这有点晚了,但我刚刚找到了答案。不过我还没有测试过,所以。这听起来不错。多亏@mark4o发布了关于linkat(2)
的帖子,详情请参见他的答案
我想给它一个机会,看看当试图将一个匿名文件链接回它所存储的文件系统时到底发生了什么。(通常是/tmp
,例如用于firefox正在播放的视频数据)
从Linux3.16开始,似乎仍然无法取消删除仍保持打开状态的已删除文件。无论是
AT\u SYMLINK\u FOLLOW
还是AT\u EMPTY\u PATH
forlinkat(2)
都不会对过去有名称的已删除文件执行此技巧,即使是根文件
唯一的替代方案是tail-c+1-f/proc/19044/fd/1>data.recov,它制作一个单独的副本,完成后必须手动将其杀死
这是我为测试准备的perl包装器。使用strace-eopen,linkat linkat.pl-cdhowie是正确的,只需写入临时文件就更好了。请注意,您链接到的问题基本上是这样的:您不能从/proc
硬链接到另一个文件系统。@poolie不知怎的,我错过了这一点。切换链接到关于serverfault的更合适的问题。区别在于,在serverfault问题中,程序是不透明的(作为一个系统管理员论坛和所有人——在这里,我谈论的是从流程中以编程方式处理文件句柄。如果你也可以明确排除这一点,我们有一个答案;)此外,我不认为这是愚蠢的。该文件具有临时文件的所有优点-基本上不存在,保证只有一个编写器/读取器,等等-直到你决定是时候把它交给用户。如果你的程序也崩溃了,它就会消失,而不是一个半写的文件。想要这样做并不愚蠢,但它有点愚蠢考虑到我们今天使用的Unix模型和API,lly希望这样做。Ta。他提出了一个解决方案,提醒您,这个解决方案也应该有效。尽管为了完全强制整洁,您可能还需要一种调用create()的方法在目录上创建文件和inode,但不创建目录项,因此它从一开始就不会链接。更新充满了win。我不能+2你,但如果可以,我会。令人困惑的是,linkat()
在尝试重新连接正常打开但未链接的文件时给出enoint
。(使用AT_SYMLINK\u FOLLOW
或AT_EMPTY\u PATH
)我发布了一个perl包装器(实际上并不有用,因为您仍然无法重新链接没有现有链接的文件)作为单独的答案。linkat
是否也可以在没有/proc
的系统上使用(例如macOS)?如果是这样,第一个path参数是什么?正如我所预料的那样,这只是cat/proc//fd/N>newfile
。如果您不知道/proc/fd,但不知道这个问题的答案,那就太好了。使用cp
或cat
(获取快照后,不会反映对已删除文件的进一步更改
#!/usr/bin/perl -w
# 2015 Peter Cordes <peter@cordes.ca>
# public domain. If it breaks, you get to keep both pieces. Share and enjoy
# Linux-only linkat(2) wrapper (opens "." to get a directory FD for relative paths)
if ($#ARGV != 1) {
print "wrong number of args. Usage:\n";
print "linkat old new \t# will use AT_SYMLINK_FOLLOW\n";
print "linkat - <old new\t# to use the AT_EMPTY_PATH flag (requires root, and still doesn't re-link arbitrary files)\n";
exit(1);
}
# use POSIX qw(linkat AT_EMPTY_PATH AT_SYMLINK_FOLLOW); #nope, not even POSIX linkat is there
require 'syscall.ph';
use Errno;
# /usr/include/linux/fcntl.h
# #define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
# #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
# #define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
unless (defined &AT_SYMLINK_NOFOLLOW) { sub AT_SYMLINK_NOFOLLOW() { 0x0100 } }
unless (defined &AT_SYMLINK_FOLLOW ) { sub AT_SYMLINK_FOLLOW () { 0x0400 } }
unless (defined &AT_EMPTY_PATH ) { sub AT_EMPTY_PATH () { 0x1000 } }
sub my_linkat ($$$$$) {
# tmp copies: perl doesn't know that the string args won't be modified.
my ($oldp, $newp, $flags) = ($_[1], $_[3], $_[4]);
return !syscall(&SYS_linkat, fileno($_[0]), $oldp, fileno($_[2]), $newp, $flags);
}
sub linkat_dotpaths ($$$) {
open(DOTFD, ".") or die "open . $!";
my $ret = my_linkat(DOTFD, $_[0], DOTFD, $_[1], $_[2]);
close DOTFD;
return $ret;
}
sub link_stdin ($) {
my ($newp, ) = @_;
open(DOTFD, ".") or die "open . $!";
my $ret = my_linkat(0, "", DOTFD, $newp, &AT_EMPTY_PATH);
close DOTFD;
return $ret;
}
sub linkat_follow_dotpaths ($$) {
return linkat_dotpaths($_[0], $_[1], &AT_SYMLINK_FOLLOW);
}
## main
my $oldp = $ARGV[0];
my $newp = $ARGV[1];
# link($oldp, $newp) or die "$!";
# my_linkat(fileno(DIRFD), $oldp, fileno(DIRFD), $newp, AT_SYMLINK_FOLLOW) or die "$!";
if ($oldp eq '-') {
print "linking stdin to '$newp'. You will get ENOENT without root (or CAP_DAC_READ_SEARCH). Even then doesn't work when links=0\n";
$ret = link_stdin( $newp );
} else {
$ret = linkat_follow_dotpaths($oldp, $newp);
}
# either way, you still can't re-link deleted files (tested Linux 3.16 and 4.2).
# print STDERR
die "error: linkat: $!.\n" . ($!{ENOENT} ? "ENOENT is the error you get when trying to re-link a deleted file\n" : '') unless $ret;
# if you want to see exactly what happened, run
# strace -eopen,linkat linkat.pl