为什么两次压缩相同的内容会得到两个具有不同SHA1的文件



我遇到了一个关于git和zip文件的奇怪问题。我的构建脚本获取了一堆文档html页面,并将它们压缩到一个docs.zip中,然后我将该文件检查到git中。

我遇到的问题是,每次我重新运行构建脚本并获得一个新的zip文件时,新zip文件都会有一个与上次运行不同的SHA1。我的构建脚本正在调用ant zip任务。然而,如果我在同一个目录上压缩两次,从Mac OS X shell手动调用macOSX zip会给我一个不同的sha1。

运行1:

zip foo.zip *
openssl sha1 foo.zip 
rm foo.zip 

运行2:

zip foo.zip *
openssl sha1 foo.zip

运行1和运行2给出了不同的SHA1,即使内容在运行之间没有变化。在这两种情况下,zip都会打印出完全相同的压缩文件,但这并不表示压缩文件中包含任何特定于操作系统的文件,如.DS_Store。

zip算法是否具有确定性?如果在相同的内容上运行,它会产生完全相同的位吗?如果不是,为什么不呢?

对于以确定性的方式压缩文件,我有哪些选择?压缩文件中有数千个,我预计这些文件不会有太大变化。我知道git会对你签入的任何文件进行压缩,但压缩它们的动机是为了避免大量的文件。

根据维基百科http://en.wikipedia.org/wiki/Zip_(file_format)zip文件似乎有File last modification time和File last modification date,因此,如果从那以后从相同的内容重建zip,则任何签入git的zip文件在git看来都已更改。而且似乎没有任何标志可以告诉它不要设置这些标题。

我只是使用tar,如果多次运行,它似乎会为相同的输入产生相同的字节。

默认情况下,gzip保存文件名和时间戳

%> gzip -help 2>&1 | grep -e '-n'
 -N --name            save or restore original file name and time stamp
 -n --no-name         don't save original file name or time stamp
%> gzip -V
Apple gzip 272

使用-n选项:

%> tar cv foo/ | gzip -n > foo.tgz; shasum foo.tgz # sha256sum on Ubuntu

你会一直得到相同的散列。

尝试上面不带-n的方法,每次都会看到不同的散列。

我成功地使用zip-X--no-extra)标志创建了具有相同SHA1的文件。

我创建了一个文件夹和几个文件来压缩测试它,正如预期的那样,每次都会得到不同的SHA1哈希:

$ mkdir stuff
$ echo "Stuff 1" > stuff/stuff1.txt
$ echo "Stuff 2" > stuff/stuff2.txt
$ zip -r stuff.zip stuff/
  adding: stuff/ (stored 0%)
  adding: stuff/stuff1.txt (stored 0%)
  adding: stuff/stuff2.txt (stored 0%)
$ shasum stuff.zip
1c8be43ac859bb57603be1243da14022710d22bd  stuff.zip
$ zip -r stuff.zip stuff/
updating: stuff/ (stored 0%)
updating: stuff/stuff1.txt (stored 0%)
updating: stuff/stuff2.txt (stored 0%)
$ shasum stuff.zip
73920362d0f7de74d87286502e03e7126fdc0a6a  stuff.zip

然而,使用-X在连续压缩后会得到相同的哈希:

$ zip -r -X stuff.zip stuff/
updating: stuff/ (stored 0%)
updating: stuff/stuff1.txt (stored 0%)
updating: stuff/stuff2.txt (stored 0%)
$ shasum stuff.zip
1ed228b16d1ee803f26a8b1419f2eb3bf7fcb9f5  stuff.zip
$ zip -r -X stuff.zip stuff/
updating: stuff/ (stored 0%)
updating: stuff/stuff1.txt (stored 0%)
updating: stuff/stuff2.txt (stored 0%)
$ shasum stuff.zip
1ed228b16d1ee803f26a8b1419f2eb3bf7fcb9f5  stuff.zip

我没有时间深入研究,找出在第一种情况下是哪些额外的信息导致了差异弹出,但也许这对试图解决它的人有帮助。也只在macOS 10.12.6上测试过。

使用以下脚本创建确定的zip或jar文件

#!/bin/bash
usage() {
    echo "Usage : ./createDeterministicArtifact.sh <zip/jar file name>"
    exit 1
}
info() {
    echo "$1"
}
strip_artifact() {
    if [ -z ${file} ]; then
        usage
    fi
    if [ -f ${file} -a -s ${file} ]; then
        mkdir -p ${file}.tmp
        unzip -oq -d ${file}.tmp ${file}
        find ${file}.tmp -follow -exec touch -a -m -t 201912010000.00 {} +
        if [ "$UNAME" == "Linux" ] ; then
            find ${file}.tmp -follow -exec chattr -a {} +
        elif [[ "$UNAME" == CYGWIN* || "$UNAME" == MINGW* ]] ; then
            find ${file}.tmp -follow -exec attrib -A {} +
        fi
        cd ${file}.tmp
        zip -rq -D -X -9 -A --compression-method deflate  ../${file}.new . 
        cd -
        rm -rf ${file}.tmp
        info "Recreated deterministic artifact: ${file}.new"
    else 
        info "Input file is empty. Please validate the file and try again"
    fi
}
file=$1
strip_artifact

相关内容

  • 没有找到相关文章

最新更新