我有一个文件夹,我想创建 tgz 文件并计算其 sha256:
使用以下命令将文件夹转换为 tgz
"tar -c -C #{Shellwords.escape dir} #{Shellwords.escape basename} " \
"--owner=0 --group=0 --mtime='2000-01-01 00:00:00' | gzip -n > #{Shellwords.escape file}"
现在我使用 2 个不同的用户运行上述过程,它给了我 2 个文件:1 和 2
两个 tgz 文件的大小不同:
-rw-r--r--@ 1 myuser \Domain Users 9024 Jul 31 14:28 1.tgz
-rw-r--r--@ 1 myuser \Domain Users 9037 Jul 31 14:29 2.tgz
如果我尝试计算文件之间的差异,我看不到任何差异。使用以下命令获得差异。
diff <(tar -tvf 1.tgz | sort) <(tar -tvf 2.tgz | sort)
如果我使用这两个文件的 ruby 计算 sha256,那么结果是不同的。
问题是:为什么从不同的用户运行时 tgz 文件会有所不同。
编辑:阅读评论并进行一些谷歌搜索后,我发现添加文件的顺序每次都不是固定的。
看到这个https://reproducible-builds.org/docs/archives/#file-ordering。
我将尝试这一点并添加详细信息。
答案1
造成这种情况的原因有很多。
tar 存储的元数据(以及 gzip 存储的元数据,可能包括 tar 存档的修改时间)。我看到您正在使用一些 GNU tar 选项,这些选项可能会重置此元数据的某些部分,但我敢打赌这些选项并不详尽地涵盖所有变量属性。
文件的顺序。将文件提取到文件系统时,顺序对于大多数应用程序来说几乎无关紧要(尽管每个目录条目通常位于底层文件系统中同一目录中的任何其他条目之前或之后)。但是,tar 存档中的文件顺序无法保证。
gzip 压缩。文件格式保证压缩文件会解压为原始文件,但不一定保证压缩后的文件格式完全相同。此外,如果输入的内容不同(即使大小相同),那么同样,您会看到两个档案甚至可能非常不同,文件大小也不同。
总之,如果您想确定两个文件夹的内容是否相同,使用 .tgz 档案可能不是最好的方法。
答案2
总结:是的,正如您所猜测的,很可能是用户 ID 的不同导致了生成的文件大小的差异。
以下是 tar 文件格式的 C 结构定义:
https://www.gnu.org/software/tar/manual/html_node/Standard.html
您可能会注意到,即使在这个“定义”中,也存在关于 tar 文件头和元数据信息的细节、具体存储什么以及存储在哪里的争论。但是,尽管 tar 文件格式的实现不同,但至少一致认为,tar 文件内的每个文件或对象确实存储了元数据信息,这些信息存储在文件内容之前的专用头块中。对于您的用例,存储在 tar 元数据块中的两个项目是用户和组文件和目录所有者,这一点很重要。
您还可以从 FreeBSD 项目手册页上的 tar 中找到更多详细信息:
https://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5&manpath=FreeBSD+8-current
Tar 有着悠久而曲折的历史,它经历了自 20 世纪 70 年代以来计算机领域串行非随机存取存储发展的多次转折。向后兼容性要求可能会导致此类事情。:)
专家提示:要使用哈希值比较目录,md5deep 是您的答案。http://md5deep.sourceforge.net/:)
答案3
如果在运行 diff 命令时不对 tar 进行排序会怎么样?它可能只是以不同的顺序添加了文件,然后 gzip 以不同的方式对其进行压缩。
答案4
我不确定 tar 是不是最好的方法。底层变量太多,而且没有按照设计使用。压缩更是如此。
根据您的目录结构和可用时间,这可能不可行,但您是否考虑过对每个文件进行散列,然后对该列表进行散列?
一种这样的方案可能是:列出所有文件,确定性地排序,对每个单独的文件进行哈希处理,然后对该哈希/文件名组合的输出进行哈希处理。
该技术忽略所有元数据,仅处理文件的内容及其名称。
这是一个示例命令(我将在下面介绍各个部分)
find -L `pwd` 2> /dev/null | sort | awk '{ print "\""$0"\""}' | xargs md5sum 2> /dev/null > /tmp/out; md5sum /tmp/out | awk '{print $1}'; rm -rf /tmp/out &> /dev/null;
find -L \
pwd` 2> /dev/null` - 查找所有文件的列表,忽略错误sort
- 按名称对文件列表进行排序,避免文件系统返回顺序差异问题awk '{ print "\""$0"\""}'
- 在每行周围添加引号。这不是严格要求的,但如果您的路径中有任何空格或特殊字符,您就会遇到麻烦。xargs md5sum 2> /dev/null > /tmp/out
- 实际计算每一行的哈希值,并将哈希值返回到文件。md5sum /tmp/out | awk '{print $1}'
- 对最终的哈希列表进行哈希处理。awk 是可选的,但可以稍微清理一下输出。rm -rf /tmp/out &> /dev/null
- 清理临时文件
据我所知,这将导致目录树的“哈希”。
根据我的测试,它对未更改的目录树返回相同的哈希值(即使经过一段时间),但在更改任何单个文件甚至创建新的空白文件时都会更改哈希值。删除新文件并撤消更改后,哈希值会恢复为原始值。
因人而异。