我很清楚如何在使用 inode 的文件系统上删除打开的文件 - unlink() 只是将链接数减少到零,并且当文件的最后一个文件句柄关闭时,inode 将被删除。
但是,当在 Linux 中使用不使用 inode 的文件系统(如 FAT32)时,它如何工作?
一些实验表明,删除打开的文件仍然是可能的(与 Windows 不同,在 Windows 上,取消链接调用不会成功),但是当文件系统未干净地卸载时会发生什么?
当文件系统本身不支持此类操作时,Linux 如何将文件标记为未链接?目录条目是否只是被删除,但保留在内存中(这无论如何都可以保证在卸载后删除,但会使文件系统处于不一致状态),或者删除是否只在内存中被标记,并在最后一个文件句柄关闭时写入,避免可能的损坏,但在未干净卸载后恢复已删除的文件?
答案1
你的假设是正确的,尽管所有目录条目在调用 unlink() 之后立即被删除,实际区块只有当没有任何东西再使用 inode 时,才会在磁盘上清除构成文件的物理文件。(我说的是“目录条目“因为在 vfat 中,一个文件实际上可以有多个这样的文件,这取决于 vfat 的长文件名支持的实现方式。)
在此背景下,索引节点,我指的是 Linux 内核用于处理文件的内存结构。即使文件系统不是“基于 inode”的,它也会使用。对于 vfat,inode 只是由磁盘上的一些块支持。
查看 Linux 内核源代码,我们发现vfat_unlink
,实现unlink()
vfat 系统调用的 大致执行以下操作(为了便于说明,进行了极其简化):
static int vfat_unlink(struct inode *dir, struct dentry *dentry)
{
fat_remove_entries(dir, &sinfo);
clear_nlink(inode);
}
因此会发生以下情况:
fat_remove_entries
只是删除目录中该文件的条目。clear_nlink
将 inode 的链接数设置为0
,这意味着没有文件(即没有目录条目)不再指向该 inode。
请注意,此时,inode 及其物理表示都不会以任何方式受到影响(除了减少的链接数),因此它仍然愉快地存在于内存和磁盘上,好像什么都没发生一样!
(顺便说一句,值得注意的是,vfat_unlink
始终将链接数设置为,0
而不是仅仅使用来减少它drop_link
。这应该提示您 FAT 文件系统不支持硬链接!并且进一步表明 FAT 本身不知道任何单独的 inode 概念。)
现在让我们看看当 inode被驱逐.evict_inode
在我们不再需要内存中的 inode 时调用。最早,这种情况当然只可能发生在任何进程不再持有该 inode 的打开文件描述符时(但理论上也可能在以后发生)。FAT 实现看起来evict_inode
(再次简化)如下:
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 中:如果 inode 的链接数为 0,则意味着实际上没有目录条目指向它。因此,我们将其大小设置为 0,并将其截断为 0 字节,这实际上是通过清除组成它的块将其从磁盘中删除。
因此,您在实验中遇到的损坏很容易解释:正如您所怀疑的那样,目录条目已被删除(由vfat_unlink
),但由于 inode 尚未被驱逐,实际块仍然未受影响,并且仍然在 FAT(文件分配表的缩写)中标记为已使用。fsck.vfat
但是检测到不再有指向这些块的目录条目,因此发出抱怨并进行修复。
顺便说一句,CHKDSK
不仅会通过将这些块标记为空闲来清除它们,还会在根目录中创建指向每个链中第一个块的新文件,名称类似FILE0001.CHK
。