39

Unix では、匿名ファイルへのハンドルを作成することができます。たとえば、creat() でファイルを作成して開き、unlink() でディレクトリ リンクを削除します。inode とストレージを含むファイルが残りますが、可能な方法はありません。それを再び開くために。そのようなファイルはしばしば一時ファイルとして使用されます (通常、これは tmpfile() が返すものです)。

私の質問: このようなファイルをディレクトリ構造に再度添付する方法はありますか? これを行うことができれば、たとえばファイル書き込みを実装して、ファイルがアトミックかつ完全に形成されているように見えることを意味します。これは私の強迫観念に訴えます。;)

関連するシステム コール関数を調べてみると、flink() という名前の link() のバージョンが見つかるはずでした (chmod()/fchmod() と比較してください) が、少なくとも Linux ではこれは存在しません。

ディスクのディレクトリ構造にファイル名を簡単に公開せずに匿名ファイルを作成する方法を教えてくれたボーナスポイント。

4

5 に答える 5

42

提案されたLinuxflink()システムコールのパッチは数年前に提出されましたが、Linusが「HELLには他の大きな侵入なしにこれを安全に行う方法はありません」と述べたとき、これを追加するかどうかの議論はほぼ終わりました。

更新:open() Linux 3.11以降、新しいフラグを使用してディレクトリエントリのないファイルを作成し、フラグを使用してon fdO_TMPFILE使用して完全に形成されたら、ファイルシステムにリンクできるようになりました。linkat()/proc/self/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 に関する投稿のおかげでlinkat(2)、詳細については彼の回答を参照してください。

匿名ファイルを実際に保存されているファイルシステムにリンクしようとしたときに実際に何が起こったのかを試してみたかった. (多くの場合/tmp、たとえば、firefox が再生しているビデオ データの場合)。


Linux 3.16 の時点では、まだ開いたままになっている削除済みファイルを元に戻す方法はまだないようです。root であっても、以前は名前が付けられていた削除済みファイルのトリックは行われませんAT_SYMLINK_FOLLOWAT_EMPTY_PATHlinkat(2)

唯一の代替手段は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
于 2015-02-21T22:55:54.037 に答える
0

明らかに、これは可能fsckです。たとえば、そうです。ただし、fsck主要なローカライズされたファイル システム mojo を使用しており、明らかに移植性がなく、権限のないユーザーとして実行することもできません。debugfs上のコメントと同じです。

その呼び出しを書くことflink(2)は興味深い練習になるでしょう。ijw が指摘しているように、一時ファイルの名前変更の現在の慣行よりもいくつかの利点があります (名前の変更、注記はアトミックであることが保証されています)。

于 2010-11-13T14:49:54.080 に答える