39

在 Unix 中,可以通过以下方式创建匿名文件的句柄,例如,使用 creat() 创建和打开它,然后使用 unlink() 删除目录链接 - 留下一个带有 inode 和存储但没有可能的文件重新打开它。此类文件通常用作临时文件(通常这是 tmpfile() 返回给您的文件)。

我的问题:有没有办法将这样的文件重新附加到目录结构中?如果你能做到这一点,这意味着你可以例如实现文件写入,以便文件以原子方式显示并且完全形成。这吸引了我强迫性的整洁。;)

当浏览相关的系统调用函数时,我希望找到一个名为 flink() 的 link() 版本(与 chmod()/fchmod() 相比),但至少在 Linux 上这不存在。

告诉我如何创建匿名文件而不在磁盘目录结构中短暂暴露文件名的奖励积分。

4

5 回答 5

42

flink()几年前提交了一个提议的 Linux系统调用的补丁,但是当 Linus 说“在 HELL 中我们无法在没有其他重大入侵的情况下安全地做到这一点”,这几乎结束了关于是否添加这个的争论。

更新:从 Linux 3.11 开始,现在可以使用open()newO_TMPFILE标志创建没有目录条目的文件,并在完全形成后使用带有标志linkat()/proc/self/fd/fd将其链接到文件系统。AT_SYMLINK_FOLLOW

open()手册页上提供了以下示例:

    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()

于 2010-11-13T19:13:15.453 回答
2

我的问题:有没有办法将这样的文件重新附加到目录结构中?如果你能做到这一点,这意味着你可以例如实现文件写入,以便文件以原子方式显示并且完全形成。这吸引了我强迫性的整洁。;)

如果这是您唯一的目标,您可以通过更简单和更广泛使用的方式实现这一目标。如果您要输出到a.dat

  1. 打开a.dat.part写。
  2. 写下你的数据。
  3. 重命名a.dat.parta.dat.

我可以理解想要整洁,但是为了“整洁”而取消链接文件并重新链接它有点愚蠢。

这个关于 serverfault的问题似乎表明这种重新链接不安全且不受支持。

于 2010-11-13T08:54:38.040 回答
2

感谢 @mark4o 发布 about linkat(2),请参阅他的答案以获取详细信息。

我想试一试,看看在尝试将匿名文件实际链接回它所存储的文件系统时实际发生了什么。(通常/tmp,例如对于 Firefox 正在播放的视频数据)。


从 Linux 3.16 开始,似乎仍然无法取消删除仍处于打开状态的已删除文件。对于曾经有名字的已删除文件,即使是 root AT_SYMLINK_FOLLOW,也不会这样做。AT_EMPTY_PATHlinkat(2)

唯一的选择是tail -c +1 -f /proc/19044/fd/1 > data.recov,它会制作一个单独的副本,完成后您必须手动将其杀死。


这是我为测试准备的 perl 包装器。用于strace -eopen,linkat linkat.pl - </proc/.../fd/123 newname验证您的系统仍然无法取消删除打开的文件。(即使使用 也同样适用sudo)。显然,您应该在运行之前阅读您在 Internet 上找到的代码,或者使用沙盒帐户。

#!/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
于 2015-02-21T22:55:54.037 回答
0

显然,这是可能的——fsck例如,做到了。但是,fsck它是否与主要的本地化文件系统 mojo 一起使用,并且显然不可移植,也不能作为非特权用户执行。debugfs这与上面的评论类似。

写这个flink(2)电话会是一个有趣的练习。正如 ijw 指出的那样,与当前的临时文件重命名实践相比,它会提供一些优势(重命名,注意,保证是原子的)。

于 2010-11-13T14:49:54.080 回答
-2

游戏有点晚了,但我刚刚发现http://computer-forensics.sans.org/blog/2009/01/27/recovering-open-but-unlinked-file-data 可以回答这个问题。不过,我还没有测试过,所以 YMMV。看起来很好听。

于 2013-08-30T21:08:30.310 回答