使用原始文件中的数据块对齐来制作tar(或其他)存档,以获得更好的块级重复数据删除?


8

如何生成tar文件,使tar文件的内容像原始文件一样按块对齐,因此可以从块级重复数据删除中受益(https://unix.stackexchange.com/a/208847/9689) ?

(我是否纠正说tar格式没有任何内在因素阻止我们获得这种好处?否则,如果不是tar,也许是否有另一个内置了这种功能的存档器?)

PS我的意思是“未压缩的tar”-而不是tar + gz或其他内容-未压缩的tar,请教一些技巧,以允许对齐文件块级别。AFAIRecall tar是为与磁带机一起使用而设计的,因此也许可以在文件格式中添加一些额外的位以进行对齐吗?我希望甚至可以有工具;)。据我所知,tar文件可以串联,因此可能会有技巧来填充对齐空间。


通常,将tar与某种压缩方式结合使用,即使仅将其与tar一起使用,也肯定不会进行压缩。
psusi '16

哇!好又聪明的问题。
亚当·里奇科夫斯基

Answers:


3

从理论上讲可以做到。但这非常丑陋,本质上涉及手动构建档案。

我们面对的是什么

tar格式适用于512字节的块。此大小是固定的,旨在与传统磁盘扇区大小匹配。将文件存储在档案中时,第一个512字节块是包含文件元数据(名称,大小,类型等)的标头,随后的块包含文件内容。因此,我们的存档数据将错位512个字节。

btrfs的块大小(“ --sectorsize”)通常为4096个字节。从理论上讲,我们可以选择此选项,但实际上,它看起来必须与CPU的页面大小相匹配。因此,我们不能缩小btrfs的块。

tar程序具有较大的“记录”大小的概念,定义为块大小的倍数,几乎看起来像是有用的。事实证明,这是为了指定给定磁带驱动器的扇区大小,这样tar可以避免写入部分磁带记录。但是,数据仍以512字节为单位进行构造和打包,因此我们不能tar像您希望的那样使用它来增加的块。

数据的最后一点知道的是,tar归档结束的标志物是两个连续的全零块,除非这些块内的文件数据。因此,任何天真的填充块都不会被接受。

骇客

我们可以做的是插入填充文件。在存档的开头,在添加要删除重复数据的文件(称为dup)之前,我们添加了一个文件pad,大小为

pad's header + pad's data + dup's header = 4096 bytes.

这样,dup的数据就从块边界开始,可以进行重复数据删除。

然后,对于每个后续文件,我们还必须跟踪先前文件的大小,以便计算正确的填充。我们还必须预测是否需要某种标头扩展名:例如,基本的tar标头仅可容纳100个字节的文件路径,因此,较长的路径使用结构上专门命名的文件(其数据为完整的路径。通常,预测标头大小有很多潜在的复杂性- tar文件格式受多种历史实现的影响很大。

一个小的缺点是,所有填充文件都可以共享相同的名称,因此,当我们解压缩时,最终只会得到一个单独的小于4096字节的文件。

可靠地创建归档文件的最简洁方法可能是修改GNU tar程序。但是,如果您想快速又脏又浪费CPU和I / O时间,则可以针对每个文件执行以下操作:

#!/bin/bash

# Proof of concept and probably buggy.
# If I ever find this script in a production environment,
# I don't know whether I'll laugh or cry.

my_file="$2"
my_archive="$1"

file_size="$(wc -c <"$my_file")"
arch_size="$(tar cb 1 "$my_file" | wc -c)"  # "b 1": Remember that record size I mentioned?  Set it to equal the block size so we can measure usefully.
end_marker_size=1024  # End-of-archive marker: 2 blocks' worth of 0 bytes

hdr_size="$(( (arch_size - file_size - end_marker_size) % 4096 ))"
pad_size="$(( (4096 - 512 - hdr_size) % 4096 ))"
(( pad_size < 512 )) && pad_size="$(( pad_size + 4096 ))"

# Assume the pre-existing archive is already a multiple of 4096 bytes long
# (not including the end-of-archive marker), and add extra padding to the end
# so that it stays that way.
file_blocks_size="$(( ((file_size+511) / 512) * 512 ))"
end_pad_size="$(( 4096 - 512 - (file_blocks_size % 4096) ))"
(( end_pad_size < 512 )) && end_pad_size="$(( end_pad_size + 4096 ))"

head -c $pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_ "$my_file"
head -c $end_pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.