web-dev-qa-db-ja.com

(どのように)LinuxとFATファイルシステムで開いているファイルを削除することは機能しますか?

Iノードを使用するファイルシステムで開いているファイルの削除がどのように機能するかは明らかです。unlink()はリンク数をゼロに減らすだけで、ファイルへの最後のファイルハンドルが閉じられると、iノードが削除されます。

しかし、LinuxでFAT32のようなiノードを使用しないファイルシステムを使用する場合、どのように機能しますか?

いくつかの実験では、開いているファイルを削除することはまだ可能であることが示唆されていますが(Windowsの場合とは異なり、リンク解除の呼び出しは成功しません)、ファイルシステムが不潔にアンマウントされるとどうなりますか?

ファイルシステム自体がそのような操作をサポートしていない場合、Linuxはどのようにしてファイルをリンクされていないものとしてマークしますか?ディレクトリエントリは削除されたばかりですが、メモリに保持されていますか(どのような場合でもマウント解除後の削除は保証されますが、ファイルシステムは一貫性のない状態のままになります)、または削除はメモリにのみマークされ、その時点で書き込まれますか?最後のファイルハンドルは閉じられ、破損の可能性を回避しますが、クリーンでないアンマウント後に削除されたファイルを復元しますか?

4
lxgr

Unlink()を呼び出した直後にすべてのディレクトリエントリが削除される一方で、ファイルを物理的に構成する実際のブロックは何もない場合にのみディスク上でクリアされるという仮定は正しいです。もうinodeを使用しています。 (vfatでは、vfatの長いファイル名のサポートがどのように実装されているかにより、ファイルに実際にそれらのいくつかを含めることができるため、「ディレクトリエントリ」と言います。)

このコンテキストでは、inodeとは、Linuxカーネルがファイルの処理に使用するメモリ内の構造を意味します。ファイルシステムが「iノードベース」でない場合でも使用されます。 vfatの場合、iノードはディスク上のいくつかのブロックによって単純にバックアップされます。

Linuxカーネルのソースコードを見ると、vfatのunlink()システムコールを実装するvfat_unlinkは、おおよそ次のことを行っていることがわかります(説明のために非常に簡略化されています)。

static int vfat_unlink(struct inode *dir, struct dentry *dentry)
{
        fat_remove_entries(dir, &sinfo);
        clear_nlink(inode);
}

だから何が起こるかです:

  1. fat_remove_entriesは、ディレクトリ内のファイルのエントリを削除するだけです。
  2. clear_nlinkは、iノードのリンクカウントを0に設定します。これは、このiノードを指すファイルがない(つまり、ディレクトリエントリがない)ことを意味します。

この時点では、iノードもその物理的表現も(リンク数の減少を除いて)まったく触れられていないため、何も起こらなかったかのように、メモリとディスクに問題なく存在していることに注意してください。

(ちなみに、vfat_unlinkはリンクカウントを0を使用してデクリメントするのではなく、常にdrop_linkに設定することに注意してください。これにより、FATファイルシステムがサポートしていないことがわかります。ハードリンク!そして、FAT自体が個別のiノードの概念を知らないことをさらに示しています。)

次に、iノードがevictedの場合に何が起こるかを見てみましょう。 evict_inodeは、iノードをメモリに残したくないときに呼び出されます。もちろん、これは、プロセスがそのiノードに対して開いているファイル記述子を保持していない場合にのみ発生する可能性があります(ただし、理論的には後で発生する可能性もあります)。 evict_inodeのFAT実装は、次のようになります(ここでも簡略化されています)。

static void fat_evict_inode(struct inode *inode)
{
        truncate_inode_pages(&inode->i_data, 0);
        if (!inode->i_nlink) {
                inode->i_size = 0;
                fat_truncate_blocks(inode, 0);
        }
        invalidate_inode_buffers(inode);
        clear_inode(inode);
}

魔法は正確にif-clause内で発生します。iノードのリンクカウントが0の場合、実際にそれを指しているディレクトリエントリがないことを意味します。そのため、サイズを0に設定し、実際には0バイトに切り捨てます。これにより、作成されたブロックがクリアされ、ディスクから実際に削除されます。

したがって、実験で発生している破損は簡単に説明できます。ご想像のとおり、ディレクトリエントリは(vfat_unlinkによって)すでに削除されていますが、iノードがまだ削除されていないため、実際のブロックはまだ削除されています。変更されておらず、FAT(ファイルアロケーションテーブルの頭字語)で使用済みとしてマークされています。 fsck.vfatは、しかし、それらのブロックを指すディレクトリエントリがもうないことを検出し、文句を言って、それを修復します。

ちなみに、CHKDSKは、それらのブロックを空きとしてマークしてクリアするだけでなく、ルートディレクトリにFILE0001.CHKのような名前で各チェーンの最初のブロックを指す新しいファイルを作成します。

2
Julien Oster