207 lines
8.1 KiB
Bash
Executable File
207 lines
8.1 KiB
Bash
Executable File
copy_memtest_from_deb() {
|
|
deb="$1"
|
|
dst_boot="$2"
|
|
tmpdir="$(mktemp -d)"
|
|
|
|
dpkg-deb -x "$deb" "$tmpdir"
|
|
for f in memtest86+x64.bin memtest86+x64.efi; do
|
|
if [ -f "$tmpdir/boot/$f" ]; then
|
|
cp "$tmpdir/boot/$f" "$dst_boot/$f"
|
|
fi
|
|
done
|
|
rm -rf "$tmpdir"
|
|
}
|
|
|
|
reset_live_build_stage() {
|
|
lb_dir="$1"
|
|
stage="$2"
|
|
|
|
for root in \
|
|
"$lb_dir/.build" \
|
|
"$lb_dir/.stage" \
|
|
"$lb_dir/auto"; do
|
|
[ -d "$root" ] || continue
|
|
find "$root" -maxdepth 1 \( -name "${stage}" -o -name "${stage}.*" -o -name "*${stage}*" \) -exec rm -rf {} + 2>/dev/null || true
|
|
done
|
|
}
|
|
|
|
# State written after every successful full lb build for this variant. Keep it
|
|
# outside the rsync-managed live-build workdir so source synchronization cannot
|
|
# delete the state that decides whether the fast path is safe.
|
|
FULL_BUILD_STATE_DIR="${CACHE_ROOT}/full-build-state-${BUILD_VARIANT}"
|
|
mkdir -p "${FULL_BUILD_STATE_DIR}"
|
|
FULL_BUILD_MARKER="${FULL_BUILD_STATE_DIR}/complete"
|
|
FULL_BUILD_HASH_FILE="${FULL_BUILD_STATE_DIR}/heavy-config.sha256"
|
|
FULL_BUILD_ABI_FILE="${FULL_BUILD_STATE_DIR}/kernel-abi"
|
|
FULL_BUILD_OVERLAY_MANIFEST="${FULL_BUILD_STATE_DIR}/overlay.manifest"
|
|
|
|
# Hashes the content of every "heavy" config input (VERSIONS, package lists,
|
|
# hooks, archives, auto/config, Dockerfile). Bootloader templates are excluded:
|
|
# the fast path regenerates the complete outer ISO layer from them. Deliberately content-
|
|
# based rather than mtime-based: mtimes get reset by git checkouts, rsync, and
|
|
# retried builds in ways that don't track "did this content actually change
|
|
# since the last full build", which previously let needs_full_build() silently
|
|
# take the fast path (reusing an old squashfs built against different package
|
|
# pins) with no error.
|
|
hash_heavy_config() {
|
|
(
|
|
cd "${BUILDER_DIR}"
|
|
find \
|
|
VERSIONS auto/config Dockerfile \
|
|
config/package-lists config/hooks config/archives \
|
|
-type f -print0 2>/dev/null |
|
|
sort -z |
|
|
xargs -0 -r sha256sum
|
|
) | sha256sum | awk '{print $1}'
|
|
}
|
|
|
|
write_overlay_manifest() {
|
|
out_path="$1"
|
|
(
|
|
cd "${OVERLAY_STAGE_DIR}"
|
|
find . -mindepth 1 -printf '%y %P\n' | sort
|
|
) > "$out_path"
|
|
}
|
|
|
|
overlay_paths_were_removed() {
|
|
[ -f "${FULL_BUILD_OVERLAY_MANIFEST}" ] || return 0
|
|
current_manifest="$(mktemp)"
|
|
write_overlay_manifest "$current_manifest"
|
|
if comm -23 "${FULL_BUILD_OVERLAY_MANIFEST}" "$current_manifest" | grep -q .; then
|
|
rm -f "$current_manifest"
|
|
return 0
|
|
fi
|
|
rm -f "$current_manifest"
|
|
return 1
|
|
}
|
|
|
|
# Returns 0 if full lb build is needed, 1 if fast-path is safe.
|
|
# Fast-path is safe when only light files changed since the last full build
|
|
# (Go source, overlay scripts/configs). Heavy changes (VERSIONS, package lists,
|
|
# hooks, archives, Dockerfile, auto/config) require a full lb build.
|
|
needs_full_build() {
|
|
[ -f "${FULL_BUILD_MARKER}" ] || return 0
|
|
[ -f "${FULL_BUILD_HASH_FILE}" ] || return 0
|
|
[ -f "${FULL_BUILD_ABI_FILE}" ] || return 0
|
|
[ -f "${FULL_BUILD_OVERLAY_MANIFEST}" ] || return 0
|
|
[ -f "${BUILD_WORK_DIR}/live-image-amd64.hybrid.iso" ] || return 0
|
|
# Accept any versioned squashfs (filesystem-v*.squashfs or legacy filesystem.squashfs)
|
|
_any_sq=$(find "${BUILD_WORK_DIR}/binary/live" -maxdepth 1 \
|
|
-name 'filesystem*.squashfs' 2>/dev/null | head -1)
|
|
[ -n "$_any_sq" ] || return 0
|
|
|
|
_old_abi="$(cat "${FULL_BUILD_ABI_FILE}" 2>/dev/null)"
|
|
if [ "${DEBIAN_KERNEL_ABI}" != "$_old_abi" ]; then
|
|
echo "=== full build required: kernel ABI changed (${_old_abi:-unknown} -> ${DEBIAN_KERNEL_ABI}) ==="
|
|
return 0
|
|
fi
|
|
|
|
if overlay_paths_were_removed; then
|
|
echo "=== full build required: overlay paths were removed or changed type ==="
|
|
return 0
|
|
fi
|
|
|
|
_new_hash="$(hash_heavy_config)"
|
|
_old_hash="$(cat "${FULL_BUILD_HASH_FILE}" 2>/dev/null)"
|
|
|
|
if [ "$_new_hash" != "$_old_hash" ]; then
|
|
echo "=== full build required: heavy config content changed since last full build ==="
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Fast path: unsquash existing filesystem, rsync overlay on top, repack.
|
|
# CACHE_ROOT must have enough free space for the extracted root filesystem.
|
|
fast_path_repack_squashfs() (
|
|
_old_sq=$(find "${BUILD_WORK_DIR}/binary/live" -maxdepth 1 \
|
|
-name 'filesystem*.squashfs' | sort | head -1)
|
|
_sq="${BUILD_WORK_DIR}/binary/live/${SQUASHFS_FILENAME}"
|
|
_tmp_parent="$(mktemp -d "${CACHE_ROOT}/fast-unsquash-${BUILD_VARIANT}.XXXXXX")"
|
|
_tmp="${_tmp_parent}/root"
|
|
trap 'rm -rf "$_tmp_parent"' EXIT
|
|
echo "=== fast-path: unsquash $(basename "$_old_sq") ($(du -sh "$_old_sq" | cut -f1) compressed) ==="
|
|
unsquashfs -d "$_tmp" "$_old_sq"
|
|
echo "=== fast-path: syncing overlay stage ==="
|
|
rsync -a --checksum "${OVERLAY_STAGE_DIR}/" "$_tmp/"
|
|
echo "=== fast-path: repacking as ${SQUASHFS_FILENAME} ==="
|
|
_sq_new="${_sq}.new"
|
|
rm -f "$_sq_new"
|
|
mksquashfs "$_tmp" "$_sq_new" -comp zstd -b 1048576 -noappend -no-progress -no-xattrs
|
|
mv "$_sq_new" "$_sq"
|
|
rm -rf "$_tmp_parent"
|
|
for _candidate in "${BUILD_WORK_DIR}/binary/live/"filesystem*.squashfs; do
|
|
[ -e "$_candidate" ] || continue
|
|
[ "$_candidate" = "$_sq" ] || rm -f "$_candidate"
|
|
done
|
|
echo "=== fast-path: squashfs repacked ($(du -sh "$_sq" | cut -f1)) ==="
|
|
)
|
|
|
|
# Fast-path: rebuild ISO replacing the squashfs via xorriso.
|
|
# Boot structure (El Torito, EFI, MBR hybrid) is replayed from the prior ISO.
|
|
recover_iso_memtest() {
|
|
lb_dir="$1"
|
|
iso_path="$2"
|
|
binary_boot="$lb_dir/binary/boot"
|
|
|
|
echo "=== attempting memtest recovery in binary tree ==="
|
|
|
|
mkdir -p "$binary_boot"
|
|
|
|
for root in \
|
|
"$lb_dir/chroot/boot" \
|
|
"/boot"; do
|
|
for f in memtest86+x64.bin memtest86+x64.efi; do
|
|
if [ ! -f "$binary_boot/$f" ] && [ -f "$root/$f" ]; then
|
|
cp "$root/$f" "$binary_boot/$f"
|
|
echo "memtest recovery: copied $f from $root"
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [ ! -f "$binary_boot/memtest86+x64.bin" ] || [ ! -f "$binary_boot/memtest86+x64.efi" ]; then
|
|
for dir in \
|
|
"$lb_dir/cache/packages.binary" \
|
|
"$lb_dir/cache/packages.chroot" \
|
|
"$lb_dir/chroot/var/cache/apt/archives" \
|
|
"${BEE_CACHE_DIR:-${DIST_DIR}/cache}/lb-packages" \
|
|
"/var/cache/apt/archives"; do
|
|
[ -d "$dir" ] || continue
|
|
deb="$(find "$dir" -maxdepth 1 -type f -name 'memtest86+*.deb' 2>/dev/null | head -1)"
|
|
[ -n "$deb" ] || continue
|
|
echo "memtest recovery: extracting payload from $deb"
|
|
copy_memtest_from_deb "$deb" "$binary_boot"
|
|
break
|
|
done
|
|
fi
|
|
|
|
if [ ! -f "$binary_boot/memtest86+x64.bin" ] || [ ! -f "$binary_boot/memtest86+x64.efi" ]; then
|
|
tmpdl="$(mktemp -d)"
|
|
if (
|
|
cd "$tmpdl" && apt-get download memtest86+ >/dev/null 2>&1
|
|
); then
|
|
deb="$(find "$tmpdl" -maxdepth 1 -type f -name 'memtest86+*.deb' 2>/dev/null | head -1)"
|
|
if [ -n "$deb" ]; then
|
|
echo "memtest recovery: downloaded $deb"
|
|
copy_memtest_from_deb "$deb" "$binary_boot"
|
|
fi
|
|
fi
|
|
rm -rf "$tmpdl"
|
|
fi
|
|
|
|
enforce_live_build_bootloader_assets "$lb_dir"
|
|
|
|
reset_live_build_stage "$lb_dir" "binary_checksums"
|
|
reset_live_build_stage "$lb_dir" "binary_iso"
|
|
reset_live_build_stage "$lb_dir" "binary_zsync"
|
|
|
|
run_optional_step_sh "rebuild live-build checksums after memtest recovery" "91-lb-checksums" "lb binary_checksums 2>&1"
|
|
run_optional_step_sh "rebuild ISO after memtest recovery" "92-lb-binary-iso" "rm -f '$iso_path' && lb binary_iso 2>&1"
|
|
run_optional_step_sh "rebuild zsync after memtest recovery" "93-lb-zsync" "lb binary_zsync 2>&1"
|
|
|
|
if [ ! -f "$iso_path" ]; then
|
|
memtest_fail "ISO rebuild was skipped or failed after memtest recovery: $iso_path" "$iso_path"
|
|
fi
|
|
}
|