尽管文件相同,但 tar 文件的 shasum 在 os x / ubuntu 上有所不同

尽管文件相同,但 tar 文件的 shasum 在 os x / ubuntu 上有所不同

我有一个令我绝望的问题,并且在过去几天里困扰着我,我希望有人可以给我提示一下我忽略了什么,因为 bash/sh 不是我每天工作的领域:

设想:我有一个在 OS X 10.11.6 中开发的项目,它被打包成一个 tar 文件,并且计算了该 tar 文件的 sha 256 校验和。

在 git pre-commit 钩子上,我将计算出的校验和添加到存储库中的 .sha 文件中,以便想要安装该项目的另一个系统也可以通过将文件打包到 tar 文件中并计算校验和并将其与目录中的 .sha 校验和进行比较来比较文件是否相同。如果这些校验和相同,则此包的版本已“验证”并且对最终用户有效,否则会显示警告。

因此,预提交钩子和 checksum.sh 文件基本上做相同的事情,只是第一个将计算出的校验和添加到存储库。

我在两个系统上使用相同的 tar 实用程序,Ubuntu 上使用 (GNU) tar 1.28(也尝试使用 1.30,没有区别),OS X 上使用 gtar (gnu-tar) 1.30。

问题:尽管 pkgdiff / diffMerge / filemerge(OS X)显示任何文件均无差异,并且我在构建 tar 时排除并规范化了一堆东西,排除任何 git 部分、临时文件、安装后目录、奇怪的不一致的 npm 文件(请参阅我的另一个问题:npm 安装不同的包锁) 和 .sha/sha.tar 文件本身,以及规范化修改时间并将所有者:组设置为 root:root。

当我将 Ubuntu 构建的 tar 档案与使用 pkgdiff 构建的 OS X 进行比较时,我没有发现任何差异,而 OSX 上的 FileMerge 则有一堆混淆的(?) 和重新排列的代码,我怀疑这可能是问题所在,因为我稍后会比较这些 tar 档案的校验和,但我不知道这种差异的根源是什么。

系统 1 - OS X: 10.11.6、gtar 1.30、git v 2.10.1 系统 2 - Ubuntu 16.04 LTS、tar 1.28(和 tar 1.30)、git 2.74

如果有人对这个问题有专业知识并愿意帮助其他开发人员解决这个问题,我会非常高兴,但我也很感激任何意见 - 提前感谢!

我的 checksum.sh 基本上是这样的:

unameOut="$(uname -s)"

case "${unameOut}" in
    Linux*)     tar --mtime='2017-01-01' --exclude='.sha' --exclude='*.git' --exclude='.DS_Store' --exclude='node_modules' --exclude='package-lock.json' --exclude='workstation.json' --exclude="npm-debug.log" --exclude-vcs --exclude=".gitignore" --exclude="sha.tar" --owner=0 --group=0 -cf ./sha.tar ./ 2>/dev/null;
    sha256sum ./sha.tar | cut -d " " -f 1 > .sha_temp_check;;
    Darwin*)    command -v gtar >/dev/null 2>&1 || { echo >&2 "On MacOS gnu compatible TAR is needed, please install gtar via homebrew \n -> brew install gnu-tar ('xcode-select --install' maybe also needed)!\n…Aborting."; exit 1; };
    gtar --mtime='2017-01-01' --exclude='.sha' --exclude='*.git' --exclude='.DS_Store' --exclude='node_modules' --exclude='package-lock.json' --exclude='workstation.json' --exclude="npm-debug.log" --exclude-vcs --exclude=".gitignore" --exclude="sha.tar" --owner=0 --group=0 -cf ./sha.tar ./ 2>/dev/null;
    shasum -a 256 ./sha.tar | cut -d " " -f 1 > .sha_temp_check;;
#    CYGWIN*)    machine=Cygwin;;
#    MINGW*)     machine=MinGw;;
    *)          echo >&2 "Incompatible OS: ${unameOut} \n…Aborting."; exit 1;;
esac

rm sha.tar

stored_sha=$(cat .sha)
checked_sha=$(cat .sha_temp_check)

echo "STORED checksum: $stored_sha"
echo "CALC'D checksum: $checked_sha"

if [ "$checked_sha" = "$stored_sha" ]
then
    echo >&1 "Version verified. Continuing. "
    rm .sha_temp_check
    exit 0
else
    echo >&2 "Keys didn't match. UNVERIFIED VERSION! \n Stored SHA: $stored_sha \n Checked SHA: $checked_sha"
    rm .sha_temp_check
    exit 1
fi

答案1

我刚刚在我的 Linux Debian 和 Mac OS 上进行了测试,结果完全相同。

也许 shasum 命令不是原因,而只是因为您的 ./sha.tar 文件不一样。您是否尝试使用 diff 命令比较这两个 sha.tar 文件?

答案2

最后,在比较 Ubuntu 和 OS X 创建的 tar 文件并消除差异后,找到了解决方案:

部分原因是 shell/linux 不是我的正常工作领域,我忽略了一些可用于跨平台 tar 存档创建的参数/选项,如下所示:

  • 所有权: 我用了

    --所有者=root --组=root
    代替
    --所有者=0 --组=0
    规范输入文件的所有权。但是,由于 Ubuntu 中有一个“root”组,而我的 OS X 上没有,因此值“0”假定为默认用户/组,而“root”或任何其他显式声明必须首先映射到系统上才能分配正确的用户/组。显然,这对组不起作用,因为在 OS XI 上,tar 标头中始终有默认的“staff”组 ID。

  • 权限:我不知道文件权限也必须规范化,因此

    --模式=“600”
    选项只是将要打包到档案中的所有文件设置为相同的值(无论哪一个都无所谓,因为我仅使用 tar 档案来计算校验和,而不是分发文件)。

  • 其他旗帜:为了以防万一,我加入了

    - 可移植性
    --取消引用
    标志 - 第一个参见https://www.math.utah.edu/docs/info/tar_8.html

    当您指定它时,tar 会忽略有关目录、管道、fifos、连续文件和设备文件的信息,并通过组和用户 ID 而不是组和用户名来指定文件所有权。

    并在同一页面上取消引用:

    导致 tar 归档符号链接指向的文件,而不是链接本身

  • 查找和排序:通过比较 tar 档案,我注意到文件的顺序有很大差异。事实证明,在列出原始文件夹的内容时,Ubuntu 使用了不同的“文件排序顺序”,可以通过导出 .bashrc 文件中的“LC_COLLATE=C”变量来使用另一种排序样式(它与数字/日期/名称无关,而是与大写字母和隐藏文件/目录的不同顺序有关。这意味着,Ubuntu 上的 tar 工具在创建存档时可能也有不同的文件顺序,因为我最初将所有文件夹内容定义为输入。但是,解决方案是规范化存档的输入文件的顺序,因此“-T”选项很有用,它接受要存档的文件列表。结合所有这些,首先找到当前目录中的文件,排除一些路径(不断更改 git 哈希),通过明确设置 LC_COLLATE=C 变量将结果传输到排序工具,然后将结果传输到 tar 归档器,这样使用“-T-”选项只会存档预先排序/预过滤的文件。

完成所有这些操作后,在 OS X 和 Ubuntu 上创建具有相同 sha256 校验和的跨平台 tar 存档的最终工作命令是(在 Ubuntu 上将“gtar”替换为“tar”,因为 gtar 是 Homebrew 在 OS X 上安装的 tar 的 gnu-tar 版本):

find . -type f -not -path "./.git/*" -not -path "./node_modules/*" | LC_COLLATE=C sort | gtar --mtime='2017-01-01' --exclude='.sha' --exclude='*.git' --exclude='.DS_Store' --exclude='node_modules' --exclude='package-lock.json' --exclude='workstation.json' --exclude="npm-debug.log" --exclude-vcs --exclude=".gitignore" --exclude="sha.tar" --portability --mode="600" --owner=0 --group=0 --dereference -T - -cf ./sha.tar

(分析 tar 头的有用链接:tar 头格式

相关内容