代码之家  ›  专栏  ›  技术社区  ›  ijw

重新链接匿名(未链接但打开)文件

  •  31
  • ijw  · 技术社区  · 14 年前

    在UNIX中,可以通过创建匿名文件并用creat()打开它,然后用unlink()删除目录链接来创建该文件的句柄,这会给您留下一个包含inode和存储的文件,但无法重新打开它。这些文件通常用作临时文件(通常这是tmpfile()返回给您的)。

    我的问题是:有没有办法将这样的文件重新附加到目录结构中?如果你能做到这一点,这意味着你可以实现文件写入,这样文件就可以原子化和完全成形。这就要求我必须保持整洁。;)

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

    告诉我如何创建匿名文件而不在磁盘的目录结构中短暂地公开文件名,这是一个额外的优点。

    5 回复  |  直到 8 年前
        1
  •  35
  •   mark4o    10 年前

    A patch for a proposed Linux flink() system call 是几年前提交的,但当Linus声明 "there is no way in HELL we can do this securely without major other incursions" 这几乎结束了关于是否添加这个的争论。

    更新: 从Linux3.11开始,现在可以使用 open() 与新的 O_TMPFILE 标记,并在使用 linkat() /proc/self/fd/ 峡湾 AT_SYMLINK_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);
    

    注意 链接() 删除最后一个链接后,将不允许重新附加打开的文件 unlink() .

        2
  •  1
  •   Community Romance    7 年前

    我的问题是:有没有办法将这样的文件重新附加到目录结构中?如果你能做到这一点,这意味着你可以实现文件写入,这样文件就可以原子化和完全成形。这就要求我必须保持整洁。;

    如果这是你唯一的目标,你可以用一种更简单和更广泛的方式来实现它。如果输出到 a.dat :

    1. 正常开放 a.dat.part 写。
    2. 写下你的数据。
    3. 重命名 A.DAT.部分 A.DAT .

    我可以理解想保持整洁,但是取消文件链接并将其重新链接为“整洁”有点傻。

    This question on serverfault 似乎表明这种重新链接是不安全的,不受支持。

        3
  •  1
  •   Peter Cordes Steve Bohrer    8 年前

    感谢@mark4o发布关于 linkat(2) ,详情请参阅他的答案。

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


    从Linux3.16开始,仍然没有办法恢复一个仍然保持打开状态的已删除文件。两者都不是 AT_SYMLINK_FOLLOW 也不 AT_EMPTY_PATH 对于 连杆(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
    
        4
  •  -1
  •   mpez0    14 年前

    显然,这是可能的-- fsck 比如说,是这样的。然而, FSCK 它是否与主要本地化文件系统mojo结合在一起,并且显然不可移植,也不能作为非特权用户执行。它类似于 debugfs 上面的注释。

    写下那个 flink(2) 打电话是个有趣的练习。正如IJW指出的,与当前的临时文件重命名(Rename,note,is guaranted atomic)实践相比,它将提供一些优势。

        5
  •  -2
  •   Jim Jānis Elmeris    11 年前

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