如何在文件中翻转单个位?

我想有意地损坏一个文件,以测试btrfs是否能自我修复。这篇文章讲述了将文件系统卸载,通过“翻转”单个位来损坏照片,然后重新挂载。在旧的文件系统中,这只会导致文件损坏,但据说在btrfs中可以自我修复。理论上讲,这很有道理,但我真的想测试一下。
问题是文章没有解释如何做到这一点。我该如何改变文件系统中非常特定部分的单个位?
我还应该指出,这必须在离线文件系统上完成,以便btrfs不会将我的写入视为故意的。
编辑:虽然问题(和讨论)大量涉及btrfs,但我想知道是否有文件系统独立的方法来实现这种损坏(以便可以在不同的RAID类型/控制器等之间进行比较)。

@Dan我是说如果我直接编辑文件,btrfs(或任何文件系统)都会将其视为有效的写操作。它不会导致我想要的损坏。 - Oli
这是一个测试文件系统吗(例如,您不关心内容,或者可以从备份中完全恢复)?另外,您是在单个驱动器上使用单个btrfs分区,还是在RAID阵列的顶部使用单个分区,或者其他配置? - Darth Android
@DarthAndroid 这是一个虚拟(在虚拟机中)的btrfs RAID1阵列(我将编辑文件的一份副本)。不过,我并不在意它在此过程中是否会被破坏 :) - Oli
当你说RAID1时,是指像mdadm这样的RAID1提供者,还是你正在使用btrfs跨多个设备?我有一种奇怪的感觉,后者将需要用于任何自愈。 - Darth Android
1如果btrfs支持正确的ioctls(不确定是否支持),您可以使用filefrag -v来准确查找文件的位置。 - derobert
是的,这完全是纯粹的btrfs。这是一个专门用于测试btrfs自我修复功能的测试工具。 - Oli
有一篇关于比特腐败效应的有趣论文,但他们并没有详细说明如何翻转每个比特。他们编写了一个Python脚本,循环遍历比特流,按顺序定位每个比特,反转比特,并将比特流保存为新图像。http://www.openplanetsfoundation.org/system/files/Bit%20Rot_OPF_0.pdf - Merri
3@Oli 我怀疑你会在 U&L 社区找到更感兴趣的听众,无论是投票还是回答。此外,还有这个链接:http://chat.stackexchange.com/transcript/message/13229833#13229833。 - strugee
1在恰当的位置射出一束宇宙射线。 - smcg
5个回答

我不是专家,但是btrfs-progs软件包实际上包含了一个专门用于此目的的工具,尽管您可能需要从源代码构建。无论如何,一旦您安装或构建了btrfs-progs,您应该能够使用btrfs-corrupt-block工具,这是btrfs开发人员用于测试文件系统的工具。

现在,正如我所说,我没有太多时间来玩btrfs,所以我不知道这个工具的确切用法。但是通过它,您应该能够破坏一个离线文件系统,在读取损坏的文件时会被修复(假设您已经设置了RAID或其他方式,以便有另一个副本可供使用)。


2太棒了!假设btrfs-corrupt-block确实是由btrfs开发人员编写的真正测试而不是一个“诡计”,那么这应该完全符合要求。 - allquixotic
@allquixotic 如果你想更多了解btrfs,这里有一个来自linux.conf.au 2012的精彩演讲。就像我说的那样,btrfs-corrupt-block是由开发人员使用的,所以如果它只是一个技巧的话,它就不会很有用了 :) - strugee
3@allquixotic 这就是开源的美妙之处:你可以查看 btrfs 的源代码并进行检查!当然,这不是一项容易的任务,但如果你真的想要,你是可以做到的。 - Bakuriu
@Bakuriu 我完全意识到这一点。我从未真正怀疑过btrfs-corrupt-block不是一个真诚的测试,因为只要有人对源代码进行探索,很快就会发现,并将其作为针对Oracle(至少)以及其他btrfs开发者/贡献者的负面公关。这只是一个随口说的评论。 - allquixotic
我想知道OP(@Oli)是想破坏一个块(即文件系统结构)还是一个文件(即文件的内容?)...而我相信btrfs自愈的声明是关于前者,而不是后者?[文件系统如何知道文件中哪个位被翻转了?一种CRC吗?]这个答案可能是正确的,所以+1。[但它可能改变的不仅仅是一个“单个位”?或者改变的东西比随机位发生在“任何地方”更容易修复?] - Olivier Dulac
嗯,再读一遍:这篇文章声称它可以在所有地方工作,所以即使是在文件数据中,而不仅仅是文件系统结构中...(维基百科的Btrfs页面,"校验和树和扫描"部分,讨论了文件系统和数据的校验和,并且显然在每次读取时进行验证?很好!但是当然,校验和有其限制。在许多情况下比没有好,但并非万无一失) - Olivier Dulac

@Oli - 嗨,我是吉姆·索尔特,实际上是那篇文章的作者。我使用了一个虚拟机,这让事情变得更简单。我所做的是从一个JPEG文件开始,在十六进制编辑器中打开它。我使用的特定编辑器是Bless,在Ubuntu上可以通过简单的apt-get install bless命令进行安装。
在Bless中打开JPEG文件后,我按下了几次Page Down键,以便深入到JPEG的“核心”,然后只是选择了大约五十个字节的数据,并将其复制粘贴到文本编辑器中(在我的情况下是gEdit)。这给了我一些要搜索的内容。
现在我已经将JPEG保存到虚拟机上的每个数组中。这些数组背后的存储是一系列的.qcow2文件。一旦我将JPEG保存到数组中,我就可以加载与每个数组关联的.qcow2文件到Bless中,并对它们进行搜索 - 它们并不是很大,只包含JPEG和一些元数据 - 以查找我从JPEG中突出显示并复制出来的那个50字节的模式。哇,我得到了要损坏的块!此时,我可以使用Bless手动编辑存储在虚拟机虚拟磁盘上的JPEG的各个字节 - 更重要的是,在每个数组上以完全相同的方式进行编辑。
唯一的问题是,在测试文章中的RAID5数组的情况下,我必须确保编辑的是条带中实际的数据副本,而不是条带本身的奇偶校验位 - 这是一个在其他空数组上的小图像,因此在条带中的下一个块中没有任何数据,使得奇偶校验块包含未经修改的数据块。如果我意外地编辑了奇偶校验块而不是数据块,图像将显示为未更改。
一个最后的说明-你不需要虚拟机来做这个-你可以用裸金属以相同的方式做相同的事情;但是,这会更加痛苦,因为你需要使用整个原始驱动器而不是好看的小.qcow2文件,并且你要么必须把驱动器拔出来放到另一台机器上,要么引导到一个实时(或仅备用)环境来搞他们。(我在7年前第一次对下一代文件系统产生兴趣时就在真正的裸金属机器上以这种方式测试了ZFS的数据恢复。)
希望这能帮到你!

获取块设备上单个扇区的值(例如`/dev/sda1`),偏移量为100万个扇区(仅作为示例):
``` sudo dd if=/dev/sda1 of=/root/mysector bs=512 count=1 skip=1M ```
这个任意选择的1M * 512字节的偏移量只是为了确保您不在文件系统的元数据部分,而是在包含数据的扇区上。
使用十六进制编辑器编辑原始扇区数据。例如,请参考Linux上需要一个好的十六进制编辑器
将扇区放回驱动器,将`if`和`of`参数颠倒:
``` sudo dd if=/root/mysector of=/dev/sda1 bs=512 count=1 seek=1M ```

2这样做对他的测试没有帮助,除非第一百万个区块实际上是文件的一部分。他如何查找特定文件从哪个区块开始? - Darth Android
3那差不多快成功了。如果你能将dd命令锁定在文件的确切位置,这可能是解决这个问题的最佳方式。 - Oli
@Oli 是的,我知道如何在Ext文件系统家族中做到这一点,但是对于btrfs我没有那么多经验。让我看看能否找到一种方法。 - gertvdijk
2@Oli:你可以简单地使用一个循环,逐块输出(即像上面那样,但是“skip=N”,N在1到最大之间),直到你能从要编辑的文件中grep到一行。[尝试生成一行在其他地方不会出现的内容...例如从密码生成器中获取,并且足够长?] 然后你编辑那个特定的块。重新挂载,测试更改是否被还原(我怀疑,参见我在顶部答案中的评论...似乎存在文件数据(=内容)和文件系统结构本身(=文件及其内容的组织方式)之间的混淆?) - Olivier Dulac

你可以尝试一个小程序,它会对打开的文件执行FIBMAP ioctl(2)操作。
通过快速的网络搜索,我找到了这篇博客文章,详细介绍了如何做到这一点 - 它甚至会给你一个链接,可以编译和运行一个示例程序。
$ git clone git://kernel.ubuntu.com/cking/debug-code
$ cd debug-code/block-mapper-fibmap
$ make
$ sudo ./fibmap /path/to/your/image-file.jpg

这正是@falconer提到的`hdparm --fibmap`的实现方式。
找到块号后,您可以使用`dd`功夫修改文件,就像@gertvdijk所描述的那样。或者,您可以直接修改上面的`fibmap.c`程序,通过绕过文件系统层直接写入设备文件来进行位翻转(程序有三个参数:1. 文件路径,2. 包含文件系统的设备文件,3. 您要修改的偏移量和位)。
(免责声明:我没有测试过并且不能保证`FIBMAP`的`ioctl(2)`对于回环设备或btrfs文件系统中的文件是否有效,但我强烈认为它应该有效。我猜测`hdparm`在执行文件的`ioctl(2)`之前会检查设备类型,因此失败了。)

sudo hdparm --fibmap /PATH/TO/FILE

会给你文件所在的逻辑块地址(LBAs)。之后,你可以使用 @gertvdijk 的答案。

很遗憾,这似乎不起作用。它输出了一个0,39: 在/dev中找不到设备的错误。可能是因为它是btrfs文件系统,或者更有可能是因为我正在使用它在回环文件上。我会尝试使用一个“正式”的虚拟机来完成这个任务。 - Oli
@Oli 嗯,我以为 hdparm 可以在任何文件系统上工作,但也许并非如此。 - falconer