当将小文件写入较大的块时,块的未使用部分(未使用的尾部)会发生什么(如何控制会发生什么)?

当将小文件写入较大的块时,块的未使用部分(未使用的尾部)会发生什么(如何控制会发生什么)?

当我将一个新的小文件(几百字节)写入我的 ext4 文件系统时,如果我理解得没错的话(我不是文件系统专家):

  • i)分配一个 4KiB 块(使用我当前的文件系统和文件系统设置)来保存文件数据,此分配记录到包含目录文件系统元数据等中
  • ii)文件数据实际上被写入了块(该过程涉及很多魔法,例如可能发生日志记录等)

我的问题是关于过程 ii)。当从一个已经写入数据的 4KiB 块开始时(因为它过去曾被使用过并且“已取消分配”,但仍包含上次使用时保存的未使用的字节),并且向其中写入一个小文件(例如 100B),块的“尾部”会发生什么(即最后的 4KiB - 100B(如果有一些元数据或其他我不知道的内容,可能应该删除多于 100B 的内容))?这个尾部是否被 0 覆盖?用随机字节覆盖?未修改/保持与以前一样?

作为文件系统的用户,有没有办法控制这种行为(即通常在以下选项之间进行选择:i)保持块尾部现有的字节不变,ii)用 0 覆盖块尾部,iii)用随机数据覆盖块尾部?)

如果这与答案相关,我正在使用 ext4(Ubuntu 20.04 上的默认设置)。

答案1

当进程执行缓冲写入时,字节会被复制到页面缓存中,然后脏页最终会被写入磁盘。这种情况可能由于多种原因而发生;程序可能请求 fsync(),写回计时器(通常为 30 秒)可能发出信号,表示应该将脏页写入后备存储器(具体而言,是与该文件页面对应的磁盘块),或者系统可能面临内存压力,页面将被清洗(或“清理”),将脏页写入后备存储器,以便不再是脏页的页面可以从页面缓存中删除,从而为该内存的其他用途腾出空间。

当进程首次写入页面缓存中的页面时,如果该页面尚未位于页面缓存中(并且要写入的内容量未填满整个页面),则文件系统将首先必须将文件内容复制到页面缓存中的页面中。例如,如果文件长度恰好为 4k,并且您将 111 个字节写入页面,则我们需要保留页面后半部分中剩余的 3985 个页面。

如果文件长度为零,或者文件中当前没有为该页面分配块,该怎么办?那么,我们先从零填充的页面开始。这有几个原因。首先,读取页面的旧内容会浪费磁盘带宽。其次,这是一个潜在的安全问题。该块的上一个用户可能是另一个用户。不再使用的块可能包含另一个用户的情书;或者它可能包含敏感数据,例如医疗信息或人们的社会安全号码。因此,您永远都不想让已删除文件的内容泄露出去;这被认为是一种不好的行为。坏的要做的事。

最后,在 ext4 的情况下,我们甚至不知道“旧”块是什么,因为 ext4 执行了所谓的“延迟分配”。选择将用于文件新分配部分的块被推迟到最后一刻 --- 即当我们需要写回文件时。这使我们能够等到知道文件有多大(大多数文件在 30 秒内完全写入),这样我们就可以更好地决定将该文件放在磁盘上的哪个位置。对于由另一个程序创建、写入、读取然后删除的临时文件,我们可能根本不需要进行块分配。(例如,这在 shell 脚本中很常见。)

因此,回答您的问题,如果文件“尾部”的块大小不是文件系统块大小(通常为 4k)的倍数,那么文件的其余部分将用零填充。这是必要的,原因如下所述,因此无法更改此行为。一般来说,虽然有一个旋钮可以控制某些东西(a)速度较慢,因为它会不必要地读取数据块,并且(b)是一个可以泄露用户/客户数据的行走、说话的安全漏洞?

答案2

文件系统块(或集群,随便你怎么说)由存储驱动器的逻辑块“支持”。至少在现代存储接口(ATA/SCSI/NVMe/...)中使用的命令集,没有办法执行到位子逻辑块写入,即您不能向驱动器发送几个字节并告诉它只用它们替换逻辑块中的某些字节。(即使有,也没有操作系统会使用它。)您只能做的是发送一个或多个逻辑块大小的数据有效负载,并要求驱动器用该有效负载替换某些逻辑块上的数据。

因此,如果您想在逻辑块的某些结尾字节处保留数据,则必须先将块的数据读入内存,然后在内存中对其进行修改,再将整个数据块写回。如果您使用 eg 执行块级写入,dd并且不指示它用零填充这些结尾字节,就会发生这种情况。

现在说到您所担心的情况,保留数据无论如何都没有任何意义。我的意思是,虽然您可能出于某些合理的原因希望这样做,但从文件的角度来看,数据没有任何意义。鉴于由于上述原因保留数据效率低下,只有在您向内存页面写入任何内容之前先写入内存页面中的内容才有意义,而这些内容最终将用作有效载荷。

现在的问题是,可能的(我的意思是从逻辑上来说)从填充了除零以外的数据的内存页开始?当然,但同样,这样做没有意义,因为零或多或少带有“未使用”的语义含义。虽然有些人可能有各种各样的理由想要除零以外的其他东西,但问题是,无论使用什么作为“填充数据”,数据与分配了块的文件无关,因此与文件系统的用途并且会成为一种“利基”需求。因此,我确信没有一个理智的操作系统/文件系统驱动程序会费心设置这样的“旋钮”,因为代码越多意味着(潜在)错误越多。

PS 我几乎看不出它与安全有什么关系,因为残留数据在驱动器上无论如何都会存在,直到你重新分配该块(除非在文件删除时触发 RZAT TRIM;但如果该块在释放时“实际上*归零,我们就没有什么可担心的了)。另一方面,如果内存页有残留数据,那确实是一个安全问题在内存上因为这意味着一些不相关的(但有意义/敏感的)数据可能会被“泄露”到驱动器

答案3

我无法具体评论 EXT4,但是,

对 NTFS 格式驱动器的观察:

假设簇为 4KB,簇仅部分使用,则“尾部”包含零。在这里,我们看到驱动器上存在的 JPEG(FF D9)的最后两个字节、文件的实际结尾以及尾随的零:

在此处输入图片描述

我经常使用磁盘和十六进制编辑器来挖掘驱动器,这就是我经常看到的。

如果操作系统和文件系统确实相关,我希望有人对这些参数(Linux,EXT4)更了解,也能从这个角度回答这个问题。

但我认为情况会类似:

如果我们从驱动器的角度来看待这一点,扇区是最小的可寻址单元。换句话说,如果我们需要写入甚至1 字节值为 FF 到扇区零字节偏移量零,我们需要写入整个 512 或 4 KB 扇区。因此,实际上会分配一个 4 KB 的缓冲区,将我们的字节复制到那里,然后将整个 4KB 写入驱动器。

使用非零填充的“缓冲区”是没有意义的,从驱动器读取扇区并用驱动器中该位置的任何内容填充缓冲区也是没有意义的。对于后者,这纯粹是浪费时间/资源。

这有点像打印到已经包含文本和图像的页面上。

答案4

很久以前,可以通过将一个小文件写入磁盘现有数据之上,然后读取整个扇区(因为它已完全分配给文件)来读取 HDD 上包含敏感数据的已取消分配扇区。

所有主流操作系统都正确地诊断出这是一个安全漏洞并予以修补。

今天(以及很久以前),未被进程写入的数据无法读取,并且通常被零替换。

我所知道的并不包括每个操作系统如何解决这个安全漏洞,无论是在写入文件时还是在读入文件时将文件的最后一个扇区扩展为零(前者当然是更安全的解决方案)。但是,这个安全漏洞肯定不再存在了。

相关内容