如何单独校验大文件的每个“块”

如何单独校验大文件的每个“块”

我在两个硬盘上存储一些多 GB 文件。经过几年的离线存储(不幸的是,在远非理想的条件下),我经常收到一些位腐烂的文件(两个副本不同),并且想要恢复该文件。问题是,文件太大,在同一个文件中,在某些存储设备上,某一位被损坏,而在另一存储设备上,不同的位被损坏,因此两个磁盘都不包含未损坏的文件。

因此,我不想计算整个文件的 MD5 校验和,而是计算每个 1KB 块的校验和。有了这么小的块,同一个 1KB 块在两个硬盘驱动器上损坏的可能性就会大大降低。

如何才能做到这一点?我确信这应该不难,但我花了一个多小时尝试不同的方法,但一直失败。

答案1

我在光学媒体(目前是 BD-R,但我在 CD-R 和 DVD-R 上使用了相同的方法)上的 bitrot 也遇到了类似的问题。

有一个程序叫par2它生成恢复数据(使用里德所罗门码),以便不仅可以检测到而且可以纠正一定数量的错误。您可以配置块大小和冗余百分比(这也是所需的额外磁盘空间量)。例如,如果您使用 1,000 个块和 10% 的冗余,那么您将额外消耗 10% 的磁盘空间来存储 100 个冗余块,总共 1100 个。但作为交换,您可以完全恢复该文件,只要您有任何1000 个未损坏的块。因此只要 100 个或更少的块包含 bitrot,就可以恢复文件。

par2 的缺点是计算恢复数据需要一段时间,并且生成的数据越多,所需的时间就越长(生成 20% 的时间比生成 10% 的时间长)。

另一个类似的工具是兹菲克,虽然我个人没有使用过。

答案2

我不会在这里提供完整的解决方案,但我希望能够指导您构建自己的解决方案。就我个人而言,我认为有更好的工具,例如rsync,但这似乎不符合您问题中的标准。

我真的不会使用,split因为这要求您能够存储分割数据以及原始数据。相反,我会选择使用 来提取块dd。像这样的方法可能对您有帮助。

file=/path/to/file
blocksize=1024    # Bytes per block
numbytes=$(stat -c '%s' "$file")
numblocks=$((numbytes / blocksize))
[[ $((numblocks * blocksize)) -lt $numbytes ]] && : $((numblocks++))

blockno=0
while [[ $blockno -lt $numblocks ]]
do
    md5sum=$(dd bs=$blocksize count=1 skip=$blockno if="$file" 2>/dev/null | md5sum)
    # Do something with the $md5sum for block $blockno
    # Here we write to stdout
    echo "$blockno $md5sum"

    : $((blockno++))
done

相关内容