Повторное связывание анонимного (не связанного, но открытого) файла - PullRequest
31 голосов
/ 13 ноября 2010

В Unix можно создать дескриптор анонимного файла, например, создав и открыв его с помощью creat (), а затем удалив ссылку на каталог с помощью unlink () - оставив вам файл с индексом и хранилищем, нонет возможности снова открыть его.Такие файлы часто используются как временные файлы (и обычно это то, что вам возвращает tmpfile ()).

Мой вопрос: есть ли способ повторно присоединить такой файл обратно в структуру каталогов?Если вы могли бы сделать это, это означает, что вы могли бы, например, реализовать запись в файл так, чтобы файл отображался атомарно и полностью сформирован.Это обращается к моей навязчивой опрятности.;)

При просмотре соответствующих функций системного вызова я ожидал найти версию link () под названием flink () (сравните с chmod () / fchmod ()), но, по крайней мере, в Linux это не такt существует.

Бонусные баллы за то, что я сказал, как создать анонимный файл без краткого раскрытия имени файла в структуре каталогов диска.

Ответы [ 5 ]

35 голосов
/ 13 ноября 2010

Патч для предлагаемого системного вызова Linux flink() был представлен несколько лет назад, но когда Линус заявил "в АД нет способа, которым мы могли бы сделать это безопасно без серьезных других вторжений", что в значительной степени положило конец дискуссии о том, добавлять ли это.

Обновление: Начиная с Linux 3.11, теперь можно создать файл без записи каталога, используя open() с новым флагом O_TMPFILE и свяжите его с файловой системой, как только он будет полностью сформирован, используя linkat() на /proc/self/fd/ fd с AT_SYMLINK_FOLLOW flag.

На странице руководства 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().

1 голос
/ 22 февраля 2015

Благодаря @ mark4o, сообщившему о linkat(2), подробности см. В его ответе.

Я хотел бы попытаться увидеть, что на самом деле произошло, когда я пытался связать анонимный файл обратно с файловой системой, в которой он хранится. (часто /tmp, например, для видеоданных, которые воспроизводит Firefox).


Начиная с Linux 3.16, по-прежнему нет возможности восстановить удаленный файл, который все еще остается открытым. Ни AT_SYMLINK_FOLLOW, ни AT_EMPTY_PATH для linkat(2) не справляются с удаленными файлами, которые раньше имели имя, даже в качестве пользователя root.

Единственная альтернатива - tail -c +1 -f /proc/19044/fd/1 > data.recov, которая делает отдельную копию, и вы должны убить ее вручную, когда это будет сделано.


Вот оболочка Perl, которую я приготовил для тестирования. Используйте strace -eopen,linkat linkat.pl - </proc/.../fd/123 newname, чтобы убедиться, что ваша система все еще не может восстановить открытые файлы. (То же самое относится и к sudo). Очевидно, вы должны прочитать код, который вы найдете в Интернете, прежде чем запускать его, или использовать изолированную учетную запись.

#!/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
1 голос
/ 13 ноября 2010

Мой вопрос: есть ли способ повторно прикрепить такой файл обратно в структуру каталогов? Если вы могли бы сделать это, это означает, что вы могли бы, например, реализовать файл записи так, что файл выглядит атомарно и полностью сформирован. Это обращается к моей навязчивой опрятности. ;)

Если это ваша единственная цель, вы можете достичь ее гораздо более простым и широко используемым способом. Если вы выводите на a.dat:

  1. Открыть a.dat.part для записи.
  2. Напишите ваши данные.
  3. Переименовать a.dat.part в a.dat.

Я понимаю, что хотеть быть аккуратным, но отсоединение файла и повторная привязка его просто для того, чтобы он был "аккуратным", немного глупо.

Этот вопрос о сбое сервера , по-видимому, указывает на то, что такой вид пересылки небезопасен и не поддерживается.

0 голосов
/ 31 августа 2013

В последнее время в игре, но я только что нашел http://computer -forensics.sans.org / blog / 2009/01/27 / recoverying-open-but-unlinked-file-data , который может ответить на вопрос.Я не проверял это, так что, YMMV.Это выглядит звук.

0 голосов
/ 13 ноября 2010

Понятно, что это возможно - например, fsck. Тем не менее, fsck делает это с основной локализованной файловой системой mojo и явно не будет переносимым или исполняемым как непривилегированный пользователь. Это похоже на комментарий debugfs выше.

Запись вызова flink(2) будет интересным упражнением. Как указывает ijw, это даст некоторые преимущества по сравнению с существующей практикой временного переименования файлов (переименование, примечание, гарантировано атомарное).

...