From 95bf2a7d7d49534cc2e5a7c7db2eae12c7488d61 Mon Sep 17 00:00:00 2001 From: Yann CARDAILLAC Date: Tue, 16 Jun 2026 16:56:23 +0200 Subject: [PATCH 1/2] link hostapp's kernel in boot directory To avoid kernel duplication, we fetch directly in hostapp image layer and hardlink it directly in inactive partition's current/boot Change-type: minor Signed-off-by: Yann CARDAILLAC --- .../hostapp-update/files/hostapp-update | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update b/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update index 9aa505c071..2aec13a4bf 100644 --- a/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update +++ b/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update @@ -23,6 +23,50 @@ ERROR() { fi } +resolve_boot_dir_from_image() { + hostapp_image="$1" + overlay_root="${SYSROOT}/balena/overlay2" + layerdb_root="${SYSROOT}/balena/image/overlay2/layerdb/sha256" + + # We expect Hostapp images to be single-layer + # get the unique layer_id from the image + layer_id=$(balena inspect -f '{{index .RootFS.Layers 0}}' "$hostapp_image") + if [ -z "$layer_id" ]; then + ERROR "overlay2: image ${hostapp_image} has no RootFS layers" + exit 1 + fi + + # use cache-id to resolve boot dir + cache_id_path="${layerdb_root}/${layer_id#sha256:}/cache-id" + boot_dir="${overlay_root}/$(cat "$cache_id_path")/diff/boot" + if [ ! -d "$boot_dir" ]; then + ERROR "overlay2: failed to resolve boot dir for image ${hostapp_image}" + exit 1 + fi + + echo "$boot_dir" +} + +link_boot_from_overlay_layers() { + dest_boot="${SYSROOT}/hostapps/.new/boot" + + rm -rf "${SYSROOT}/hostapps/.new" + mkdir -p "$dest_boot" + + boot_dir=$(resolve_boot_dir_from_image "$HOSTAPP_IMAGE") + for src in $(find "$boot_dir" -type f); do + # rel takes ${SYSROOT}/balena/overlay2/.../diff/boot/bzImage and makes it boot/bzImage + rel="${src#${boot_dir}/}" + dst="${dest_boot}/${rel}" + if [ -e "$dst" ]; then + continue + fi + mkdir -p "$(dirname "$dst")" + ln "$src" "$dst" + INFO "Hardlinked boot file: $src -> $dst" + done +} + run_current_hooks_and_recover () { if [ "$hooks_rollback" = 1 ]; then # Run the current ones to cleanup the system. @@ -124,13 +168,9 @@ elif [ "$remote_image" != "" ]; then _out="$(balena pull "$HOSTAPP_IMAGE" 2>&1)" INFO "${_out}" fi -CONTAINER_ID=$(balena create --runtime="bare" --volume=/boot "$HOSTAPP_IMAGE" /bin/sh) -BOOTSTRAP=$(balena inspect -f "{{range .Mounts}}{{.Destination}} {{.Source}}{{end}}" "$CONTAINER_ID" | awk '$1 == "/boot" { print $2 }' | head -n1) +CONTAINER_ID=$(balena create --runtime="bare" "$HOSTAPP_IMAGE" /bin/sh) -# Create boot entry -rm -rf "$SYSROOT/hostapps/.new" -mkdir -p "$SYSROOT/hostapps/.new" -ln -sr "$BOOTSTRAP" "$SYSROOT/hostapps/.new/boot" +link_boot_from_overlay_layers sync -f "$SYSROOT" mv -T "$SYSROOT/hostapps/.new" "$SYSROOT/hostapps/$CONTAINER_ID" sync -f "$SYSROOT" From c7534c49ff400d73a8397339041161b5ed9239e3 Mon Sep 17 00:00:00 2001 From: Yann CARDAILLAC Date: Fri, 19 Jun 2026 15:21:38 +0200 Subject: [PATCH 2/2] symlink boot from overlay2 instead of using a hardlink Signed-off-by: Yann CARDAILLAC --- .../hostapp-update/files/hostapp-update | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update b/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update index 2aec13a4bf..8448aae309 100644 --- a/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update +++ b/meta-balena-common/recipes-containers/hostapp-update/files/hostapp-update @@ -47,24 +47,17 @@ resolve_boot_dir_from_image() { echo "$boot_dir" } -link_boot_from_overlay_layers() { - dest_boot="${SYSROOT}/hostapps/.new/boot" +# Symlink hostapps boot to overlay2 layer diff/boot (no copy). +symlink_boot_from_overlay_layers() { + hostapp_new="${SYSROOT}/hostapps/.new" - rm -rf "${SYSROOT}/hostapps/.new" - mkdir -p "$dest_boot" + rm -rf "$hostapp_new" + mkdir -p "$hostapp_new" boot_dir=$(resolve_boot_dir_from_image "$HOSTAPP_IMAGE") - for src in $(find "$boot_dir" -type f); do - # rel takes ${SYSROOT}/balena/overlay2/.../diff/boot/bzImage and makes it boot/bzImage - rel="${src#${boot_dir}/}" - dst="${dest_boot}/${rel}" - if [ -e "$dst" ]; then - continue - fi - mkdir -p "$(dirname "$dst")" - ln "$src" "$dst" - INFO "Hardlinked boot file: $src -> $dst" - done + cache_id=$(basename "$(dirname "$(dirname "$boot_dir")")") + ln -sfn "../../balena/overlay2/${cache_id}/diff/boot" "${hostapp_new}/boot" + INFO "Symlinked ${hostapp_new}/boot -> ../../balena/overlay2/${cache_id}/diff/boot" } run_current_hooks_and_recover () { @@ -170,7 +163,7 @@ elif [ "$remote_image" != "" ]; then fi CONTAINER_ID=$(balena create --runtime="bare" "$HOSTAPP_IMAGE" /bin/sh) -link_boot_from_overlay_layers +symlink_boot_from_overlay_layers sync -f "$SYSROOT" mv -T "$SYSROOT/hostapps/.new" "$SYSROOT/hostapps/$CONTAINER_ID" sync -f "$SYSROOT"