- P2-1 repack_repro.sh 提交为 100755(scripts/build/ 下唯一非 755 的 .sh,而头注释就让人直接执行) - P2-2 注释与事实对齐:写明 squashfs/分片/ota_update.in 可复现,但 .upt 仍有 93 字节 Rock Ridge TF backup date 差异(genisoimage 无覆盖开关),验收口径仍是 体积+unpack 0 diff+进包 md5 - P2-3 watchdog 参数不再内联复制:build_watchdog.sh 认 ZIGCC(照 build_netmgrd.sh 口径), rebuild_binaries.sh 改成调它;meow_watchdog 加入 test_time32_abi.sh 的 BUILD_TARGETS(11 个)。 改后重编仍逐字节 25f769509a43d0f66287efd456c64281 / 100116,与内联那版一致 - 观察1:REPACK 显式指定但不存在 -> exit 1(此前会静默回退到不可复现的旧 repack.sh)
189 lines
8.0 KiB
Bash
Executable File
189 lines
8.0 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================================================
|
||
# repack_repro.sh — .upt 打包(mksquashfs + 分片 md5 链 + ISO),时间戳尽量可复现
|
||
#
|
||
# 与 hiby_os_crack/scripts/repack.sh 的产物布局逐字节等价(ota_update.in / ota_v0.ok /
|
||
# ota_config.in / rootfs.squashfs.NNNN.<prev-md5> / ota_md5_rootfs.squashfs.<md5>),
|
||
# 只把墙钟时间从「squashfs + 元数据」里拿掉。**可复现到什么程度(实测口径,别夸大)**:
|
||
# ✅ bytes-reproducible:rootfs.squashfs、ota_md5_rootfs.squashfs.*、ota_update.in、
|
||
# 分片文件名与内容、xImage 侧 —— 同一输入连续两次打包全部相同。
|
||
# ❌ not bytes-reproducible:`.upt`(ISO)本身。genisoimage 会给 Rock Ridge `TF`
|
||
# 记录写 **backup date**(TF flag=0x1a,第三个 7 字节字段),该字段取墙钟且**没有
|
||
# 覆盖开关**(`-creation-date` 只定卷的创建/修改时间)。M56 实测两次打包 `cmp -l`
|
||
# 恰好 93 字节不同,全在该字段(分钟字节),载荷 0 差异。
|
||
# 因此 `.upt` 的验收口径仍是:**体积 + unpack 0 diff + 进包 md5**。
|
||
#
|
||
# 时间戳处理:
|
||
# 1) squashfs:`-repro-time <epoch>`(= -mkfs-time + -inode-time;mksquashfs 4.7 起)。
|
||
# 只加 -all-time 不够 —— 超级块的 mkfs_time 仍是墙钟(M60 实测)。本脚本按
|
||
# `mksquashfs -help-all` 能力探测,依次尝试:
|
||
# -repro-time <t> (4.7+)
|
||
# -mkfs-time <t> -inode-time <t> (4.6)
|
||
# -mkfs-time <t> -all-time <t> (老版本)
|
||
# 都没有 → 告警并按旧口径(体积 + unpack 0 diff + 进包 md5)出货。
|
||
# 2) ISO:卷创建时间用 `-creation-date <秒数>` 固定,并把暂存目录里所有文件/目录的
|
||
# mtime touch 到同一 epoch(ISO 目录记录取文件 mtime)。backup date 无解,见上。
|
||
#
|
||
# 用法:
|
||
# repack_repro.sh -i <squashfs-root> -k <xImage> -o <out.upt>
|
||
# SOURCE_DATE_EPOCH=1735689600 覆盖固定时间(默认见 REPRO_EPOCH)。
|
||
# ============================================================================
|
||
set -euo pipefail
|
||
|
||
REPRO_EPOCH="${SOURCE_DATE_EPOCH:-1735689600}" # 2025-01-01T00:00:00Z
|
||
TEMP_DIR="${TMPDIR:-/tmp}/meow-repack-repro"
|
||
|
||
# 本机(Termux)没有 /usr/bin/mksquashfs:fw 工具集放在 ~/.cache/fwtools/shims
|
||
# (glibc 版 mksquashfs/unsquashfs/genisoimage 经 grun 运行)。
|
||
if ! command -v mksquashfs >/dev/null 2>&1 && [ -d "$HOME/.cache/fwtools/shims" ]; then
|
||
PATH="$HOME/.cache/fwtools/shims:$PATH"
|
||
fi
|
||
|
||
show_help() {
|
||
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
||
}
|
||
|
||
input_root=""
|
||
input_ximage=""
|
||
output_file=""
|
||
|
||
while getopts ":i:k:o:h" opt; do
|
||
case "$opt" in
|
||
i) input_root="$OPTARG" ;;
|
||
k) input_ximage="$OPTARG" ;;
|
||
o) output_file="$OPTARG" ;;
|
||
h) show_help; exit 0 ;;
|
||
:) echo "Error: -$OPTARG 需要参数" >&2; exit 1 ;;
|
||
\?) echo "Error: 未知参数 -$OPTARG" >&2; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
[ -n "$input_root" ] || { echo "Error: -i 必填" >&2; exit 1; }
|
||
[ -n "$input_ximage" ] || { echo "Error: -k 必填" >&2; exit 1; }
|
||
[ -n "$output_file" ] || { echo "Error: -o 必填" >&2; exit 1; }
|
||
[ -d "$input_root" ] || { echo "Error: '$input_root' 不存在" >&2; exit 1; }
|
||
[ -f "$input_ximage" ] || { echo "Error: '$input_ximage' 不存在" >&2; exit 1; }
|
||
command -v mksquashfs >/dev/null 2>&1 || { echo "Error: 缺 mksquashfs" >&2; exit 1; }
|
||
command -v genisoimage >/dev/null 2>&1 || echo "WARN: 无 genisoimage,将尝试 mkisofs" >&2
|
||
|
||
input_root=$(realpath "$input_root")
|
||
input_ximage=$(realpath "$input_ximage")
|
||
output_file=$(realpath -m "$output_file")
|
||
[ -e "$output_file" ] && rm -f "$output_file"
|
||
|
||
ISO_TOOL=genisoimage
|
||
command -v genisoimage >/dev/null 2>&1 || ISO_TOOL=mkisofs
|
||
|
||
# ---- squashfs 时间戳参数:按能力探测 ----------------------------------------
|
||
sq_help="$(mksquashfs -help-all 2>/dev/null || mksquashfs -help 2>/dev/null || true)"
|
||
sq_time=()
|
||
# 直接用 case 匹配字符串,**不要**写成 `printf ... | grep -q`:脚本是 set -o pipefail,
|
||
# grep -q 命中后立刻退出 → printf 收 SIGPIPE(141) → 整条管道判失败 → 参数被静默丢掉
|
||
# (M56 踩过一次:ISO 卷时间因此回落成墙钟,两次打包不一致)。
|
||
case "$sq_help" in
|
||
*-repro-time*) sq_time=(-repro-time "$REPRO_EPOCH") ;;
|
||
*-mkfs-time*)
|
||
case "$sq_help" in
|
||
*-inode-time*) sq_time=(-mkfs-time "$REPRO_EPOCH" -inode-time "$REPRO_EPOCH") ;;
|
||
*-all-time*) sq_time=(-mkfs-time "$REPRO_EPOCH" -all-time "$REPRO_EPOCH") ;;
|
||
esac ;;
|
||
esac
|
||
if [ "${#sq_time[@]}" -eq 0 ]; then
|
||
echo "WARN: 本机 mksquashfs 不支持 -mkfs-time/-repro-time —— squashfs 内不含可复现时间戳," >&2
|
||
echo " 只能按旧口径(体积 + unpack 0 diff + 进包 md5)验收。" >&2
|
||
fi
|
||
|
||
# genisoimage 1.1.11 的 -creation-date 只吃得下「秒数」形式:传 YYYYMMDDhhmmsscc
|
||
# (本工具 man 里推荐的 16 位串)会直接 *** buffer overflow detected *** 崩掉。
|
||
# 传秒数时 PVD 的 Creation/Modif. Time 都被写成同一时刻(xorriso -pvd_info 可核),
|
||
# 正是可复现需要的样子。
|
||
iso_date="$REPRO_EPOCH"
|
||
iso_time=()
|
||
iso_help="$("$ISO_TOOL" -help 2>&1 || true)"
|
||
case "$iso_help" in
|
||
*-creation-date*) iso_time=(-creation-date "$iso_date") ;;
|
||
esac
|
||
|
||
rm -rf "$TEMP_DIR"
|
||
mkdir -p "$TEMP_DIR/ota_v0"
|
||
pushd "$TEMP_DIR/ota_v0" >/dev/null
|
||
|
||
echo "### GENERATING NEW SQUASHFS FILES ###"
|
||
echo " mksquashfs 时间参数: ${sq_time[*]:-(无)} epoch=$REPRO_EPOCH"
|
||
mksquashfs "$input_root" rootfs.squashfs -comp lzo -all-root "${sq_time[@]}"
|
||
|
||
rootfs_size=$(stat -c%s rootfs.squashfs)
|
||
rootfs_md5=$(md5sum rootfs.squashfs | awk '{print $1}')
|
||
|
||
split rootfs.squashfs -d -a 4 -b 512k rootfs.squashfs.
|
||
rm rootfs.squashfs
|
||
|
||
md5=$rootfs_md5
|
||
ota_md5_rootfs="ota_md5_rootfs.squashfs.${rootfs_md5}"
|
||
> "$ota_md5_rootfs"
|
||
for part in $(ls rootfs.squashfs.[0-9]* | sort); do
|
||
md5next=$(md5sum "$part" | awk '{print $1}')
|
||
echo "$md5next" >> "$ota_md5_rootfs"
|
||
mv "$part" "$part.$md5"
|
||
md5="$md5next"
|
||
done
|
||
|
||
echo "### GENERATING NEW xImage FILES ###"
|
||
cp "$input_ximage" xImage
|
||
ximage_size=$(stat -c%s xImage)
|
||
ximage_md5=$(md5sum xImage | awk '{print $1}')
|
||
split xImage -d -a 4 -b 512k xImage.
|
||
rm xImage
|
||
|
||
md5=$ximage_md5
|
||
ota_md5_ximage="ota_md5_xImage.${ximage_md5}"
|
||
> "$ota_md5_ximage"
|
||
for part in $(ls xImage.[0-9]* | sort); do
|
||
md5next=$(md5sum "$part" | awk '{print $1}')
|
||
echo "$md5next" >> "$ota_md5_ximage"
|
||
mv "$part" "$part.$md5"
|
||
md5="$md5next"
|
||
done
|
||
|
||
echo "### GENERATING METADATA FILES ###"
|
||
cat > ota_update.in <<-EOM
|
||
ota_version=0
|
||
|
||
img_type=kernel
|
||
img_name=xImage
|
||
img_size=${ximage_size}
|
||
img_md5=${ximage_md5}
|
||
|
||
img_type=rootfs
|
||
img_name=rootfs.squashfs
|
||
img_size=${rootfs_size}
|
||
img_md5=${rootfs_md5}
|
||
EOM
|
||
|
||
echo > ota_v0.ok
|
||
echo "current_version=0" > ../ota_config.in
|
||
|
||
echo "### GENERATING FIRMWARE FILE ###"
|
||
# ISO 的目录记录取文件 mtime → 全树归一化到 REPRO_EPOCH(含目录自身;-h 跟随符号链接元数据)。
|
||
find "$TEMP_DIR" -exec touch -h -d "@$REPRO_EPOCH" {} +
|
||
# 先写暂存路径,成功后再 mv 到交付路径:genisoimage 中途 abort(本机 1.1.11 有已知崩溃)
|
||
# 时,交付路径上不会留下「看起来像成品」的残缺 ISO(M56 踩过:28,672 B 半成品)。
|
||
iso_tmp="${TEMP_DIR}.iso" # 暂存在打包树之外(否则 ISO 会把自己读进去)
|
||
"$ISO_TOOL" -o "$iso_tmp" -J -r "${iso_time[@]}" "$TEMP_DIR"
|
||
[ -s "$iso_tmp" ] || { echo "!!! ISO 为空: $iso_tmp" >&2; exit 1; }
|
||
mv -f "$iso_tmp" "$output_file"
|
||
iso_size=$(stat -c%s "$output_file")
|
||
# 反「静默半成品」:ISO 至少要装得下 rootfs 本体(+xImage + 元数据),小了就是没写完。
|
||
[ "$iso_size" -ge "$rootfs_size" ] || {
|
||
echo "!!! ISO 体积 $iso_size B < rootfs $rootfs_size B —— 打包不完整" >&2
|
||
rm -f "$output_file"; exit 1; }
|
||
|
||
popd >/dev/null
|
||
rm -rf "$TEMP_DIR"
|
||
|
||
echo
|
||
echo "Repacking complete!"
|
||
echo " rootfs.squashfs = ${rootfs_size} B md5=${rootfs_md5}"
|
||
echo " xImage = ${ximage_size} B md5=${ximage_md5}"
|
||
echo " firmware: $output_file"
|