fix(iso): stop fast-path repack from clobbering merged-usr /lib

The NVIDIA GSP firmware was staged into the overlay at a bare
lib/firmware/nvidia/<ver>/. On a merged-usr Debian root /lib is a symlink
to usr/lib, so the fast-path repack's `rsync -a` (overlay stage -> the
unsquashfs'd tree) replaced that symlink with a plain directory holding
only the firmware. Everything else reachable only through /lib
(/lib/systemd/systemd, /lib/x86_64-linux-gnu/ld-linux + libc, ...) was
orphaned. The squashfs still packed and mounted, but boot panicked:

    run-init: can't execute '/sbin/init': No such file or directory
    run-init: can't execute '/bin/sh': No such file or directory
    Kernel panic - not syncing: Attempted to kill init

Full `lb build` was unaffected (live-build copies includes.chroot with
cp -a, which follows the /lib symlink), so only fast-path ISOs were bad.

Three fixes:
- build.sh: stage GSP firmware under usr/lib/firmware/nvidia/<ver>/, the
  canonical merged-usr path. Changing the overlay path also makes
  overlay_paths_were_removed() force one full build on the next run.
- fast-path.sh: add `rsync --keep-dirlinks` so a real directory in the
  overlay stage can never again replace a symlink-to-directory in the
  root. Verified: firmware lands in usr/lib/firmware, /lib stays a symlink.
- iso-validation.sh: new validate_iso_rootfs_layout, run for every variant
  on both build paths. Fails the build if the squashfs has a plain-dir
  /bin|/sbin|/lib|/lib64, or if /usr/sbin/init is present without a
  resolvable /usr/lib/systemd/systemd and /lib symlink. Verified it flags
  the broken v14.02-1-g642e686 ISO and passes a correct layout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2LLuid8PFhBqBPcxXxkQU
This commit is contained in:
Mikhail Chusavitin
2026-09-03 15:32:43 +03:00
co-authored by Claude Sonnet 5
parent 489dd2830c
commit eae5570730
3 changed files with 79 additions and 5 deletions
+6 -1
View File
@@ -135,7 +135,12 @@ fast_path_repack_squashfs() (
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/"
# --keep-dirlinks: when the extracted root has a symlink-to-directory (e.g.
# merged-usr /lib -> usr/lib) and the overlay stage carries a real directory
# of the same name, follow the symlink instead of replacing it with a plain
# directory. Without this rsync silently orphans everything reachable only
# through that symlink and the resulting image panics at boot.
rsync -a --keep-dirlinks --checksum "${OVERLAY_STAGE_DIR}/" "$_tmp/"
echo "=== fast-path: repacking as ${SQUASHFS_FILENAME} ==="
_sq_new="${_sq}.new"
rm -f "$_sq_new"