#!/bin/bash # # Copyright (c) 2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # # Utility for adding patch metadata to the iso # Debian patches are sequential and uses ostree # so any patch will produce an updated iso, this # utility injects the patch metadata into the iso. # During install the kickstart will copy the metadata # into the right location and the sw-patch query # command will output the correct patch level # source "$(dirname $0)/image-utils.sh" if [ -z "${STX_BUILD_HOME}" ]; then echo "Required environment variable STX_BUILD_HOME is not set" exit 1 fi if [ -z "${MY_REPO}" ]; then echo "Required environment variable MY_REPO is not set" exit 1 fi DEPLOY_DIR="${STX_BUILD_HOME}/localdisk/deploy" OSTREE_REPO="${DEPLOY_DIR}/ostree_repo" function usage() { echo "" echo "Usage: " echo " $(basename $0) -i -o [ -p ] ..." echo " -i : Specify input ISO file" echo " -o : Specify output ISO file" echo " -p : Patch files. You can call it multiple times." echo "" } function extract_metadata() { local patchesdir=${BUILDDIR}/patches local patchfile=$1 local patchid=$(basename $patchfile .patch) local ostree_log=$(ostree --repo=${OSTREE_REPO} log starlingx) echo "Extracting ${patchfile}" # Extract it tar xf ${patchfile} -O metadata.tar | tar x -O > ${patchesdir}/${patchid}-metadata.xml if [ $? -ne 0 ]; then echo "Failed to extract metadata from ${patchfile}" exit 1 fi # Verify if top commit from metadata exist in ostree log xml_base=$(xmllint --xpath "string(//contents/ostree/commit1/commit)" ${patchesdir}/${patchid}-metadata.xml) if [[ "$ostree_log" != *"$xml_base"* ]]; then echo "Error, xml base commit does not exist in ostree log." echo "patch base: ${xml_base}" echo "ostree log:" ostree --repo=${BUILDDIR}/ostree_repo log starlingx exit 1 fi } declare INPUT_ISO= declare OUTPUT_ISO= declare ORIG_PWD=$PWD declare DO_UPGRADES=1 while getopts "i:o:p:" opt; do case $opt in i) INPUT_ISO=$OPTARG ;; o) OUTPUT_ISO=$OPTARG ;; p) PATCH_FILES+=($OPTARG) ;; *) usage exit 1 ;; esac done if [ -z "$INPUT_ISO" -o -z "$OUTPUT_ISO" ]; then usage exit 1 fi if [ ! -f ${INPUT_ISO} ]; then echo "Input file does not exist: ${INPUT_ISO}" exit 1 fi if [ -f ${OUTPUT_ISO} ]; then echo "Output file already exists: ${OUTPUT_ISO}" exit 1 fi for PATCH in "${PATCH_FILES[@]}"; do if [ ! -f ${PATCH} ]; then echo "Patch file dos not exists: ${PATCH}" exit 1 fi if [[ ! ${PATCH} =~ \.patch$ ]]; then echo "Specified file ${PATCH} does not have .patch extension" exit 1 fi done shift $((OPTIND-1)) declare MNTDIR= declare BUILDDIR= function check_requirements { local -a required_utils=( rsync mkisofs isohybrid implantisomd5 ostree xmllint ) if [ $UID -ne 0 ]; then # If running as non-root user, additional utils are required required_utils+=( guestmount guestunmount ) fi local -i missing=0 for req in ${required_utils[@]}; do which ${req} >&/dev/null if [ $? -ne 0 ]; then echo "Unable to find required utility: ${req}" >&2 let missing++ fi done if [ ${missing} -gt 0 ]; then echo "One or more required utilities are missing. Aborting..." >&2 exit 1 fi } function mount_iso { if [ $UID -eq 0 ]; then # Mount the ISO mount -o loop ${INPUT_ISO} ${MNTDIR} if [ $? -ne 0 ]; then echo "Failed to mount ${INPUT_ISO}" >&2 exit 1 fi else # As non-root user, mount the ISO using guestmount guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR} rc=$? if [ $rc -ne 0 ]; then # Add a retry echo "Call to guestmount failed with rc=$rc. Retrying once..." guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR} rc=$? if [ $rc -ne 0 ]; then echo "Call to guestmount failed with rc=$rc. Aborting..." exit $rc fi fi fi } function unmount_iso { if [ $UID -eq 0 ]; then umount ${MNTDIR} else guestunmount ${MNTDIR} fi rmdir ${MNTDIR} } function cleanup() { if [ -n "$MNTDIR" -a -d "$MNTDIR" ]; then unmount_iso fi if [ -n "$BUILDDIR" -a -d "$BUILDDIR" ]; then chmod -R +w $BUILDDIR \rm -rf $BUILDDIR fi } check_requirements trap cleanup EXIT MNTDIR=$(mktemp -d -p $PWD patchiso_mnt_XXXXXX) if [ -z "${MNTDIR}" -o ! -d ${MNTDIR} ]; then echo "Failed to create mntdir. Aborting..." exit $rc fi BUILDDIR=$(mktemp -d -p $PWD patchiso_build_XXXXXX) if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then echo "Failed to create builddir. Aborting..." exit $rc fi # Mount the ISO mount_iso rsync -a --exclude 'ostree_repo' ${MNTDIR}/ ${BUILDDIR}/ rc=$? if [ $rc -ne 0 ]; then echo "Call to rsync ISO content. Aborting..." exit $rc fi unmount_iso # Fix for permission denied if not running as root chmod +w ${BUILDDIR} chmod -R +w ${BUILDDIR}/isolinux # Create the directory where metadata will be stored mkdir -p ${BUILDDIR}/patches chmod -R +w ${BUILDDIR}/patches echo "Copying only the latest commit from ostree_repo..." ostree --repo=${BUILDDIR}/ostree_repo init --mode=archive-z2 ostree --repo=${BUILDDIR}/ostree_repo pull-local --depth=0 ${OSTREE_REPO} starlingx ostree --repo=${BUILDDIR}/ostree_repo summary --update echo "Updated iso ostree commit:" ostree --repo=${BUILDDIR}/ostree_repo log starlingx echo "Extracting patch metadata" for PATCH in "${PATCH_FILES[@]}"; do extract_metadata $PATCH done echo "Packing iso..." # get the install label ISO_LABEL=$(grep -ri instiso "${BUILDDIR}"/isolinux/isolinux.cfg | head -1 | xargs -n1 | awk -F= /instiso/'{print $2}') if [ -z "${ISO_LABEL}" ] ; then echo "Error: Failed to get iso install label" fi echo "ISO Label: ${ISO_LABEL}" # Repack the ISO mkisofs -o "${OUTPUT_ISO}" \ -A "${ISO_LABEL}" -V "${ISO_LABEL}" \ -quiet -U -J -joliet-long -r -iso-level 2 \ -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot \ -boot-load-size 4 -boot-info-table \ -eltorito-alt-boot \ -e efi.img \ -no-emul-boot \ "${BUILDDIR}" isohybrid --uefi ${OUTPUT_ISO} implantisomd5 ${OUTPUT_ISO} # Sign the .iso with the developer private key openssl dgst -sha256 \ -sign ${MY_REPO}/build-tools/signing/dev-private-key.pem \ -binary \ -out ${OUTPUT_ISO/%.iso/.sig} \ ${OUTPUT_ISO} rc=$? if [ $rc -ne 0 ]; then echo "Call to $(basename ${SETUP_PATCH_REPO}) failed with rc=$rc. Aborting..." exit $rc fi echo "Patched ISO: ${OUTPUT_ISO}"