#!/bin/sh

set -e
set -x

DEBUG=

OPENSSL=${OPENSSL:-openssl}

export BOOTSTRAP_FINISHED="BOOTSTRAP-FINISHED"
export BOOTSTRAP_ABORTED="BOOTSTRAP-ABORTED"
export BOOTSTRAP_STARTED="BOOTSTRAP-STARTED"


# Lock directories
LOCK_BASE="/var/lock/rpi-sb-bootstrap"
STATE_BASE="/var/run/rpi-sb-state"
LOG_BASE="/var/log/rpi-sb-provisioner"

# Source common helper functions
# shellcheck disable=SC1091
. "$(dirname "$0")/rpi-sb-common.sh"
# shellcheck disable=SC1091
. /var/lib/rpi-sb-provisioner/manufacturing-data
# shellcheck disable=SC1091
. /var/lib/rpi-sb-provisioner/state-recording

HOLDING_LOCKFILE=0

log() {
    log_file="${EARLY_LOG_DIRECTORY}/bootstrap.log"
    timestamp=$(date +"%Y-%m-%d %H:%M:%S.$(date +%N | cut -c1-3)")
    message="$*"
    
    # Ensure log directory exists with proper permissions
    mkdir -p "$(dirname "$log_file")"
    touch "$log_file"
    chmod 644 "$log_file"
    
    # Write log message directly without locking
    echo "[${timestamp}] $message" >> "$log_file"
    printf "[%s] %s\n" "${timestamp}" "$message"
}

CLEANUP_DONE=0

cleanup() {
    # Capture the exit status that triggered the trap BEFORE any other
    # command runs, otherwise $? is clobbered by the guard/assignment below
    # and a genuine failure is reported as success.
    returnvalue=$?

    # Guard against multiple invocations (signal + EXIT trap)
    [ "$CLEANUP_DONE" -eq 1 ] && return
    CLEANUP_DONE=1
    if [ ${HOLDING_LOCKFILE} -eq 1 ]; then
        rm -rf "$DEVICE_LOCK"
        
        announce_start "Deleting udev lock"
        rm -f "/etc/udev/rules.d/99-rpi-sb-bootstrap-${TARGET_DEVICE_SERIAL}.rules"
        udevadm control --reload-rules
        announce_stop "Deleting udev lock"
    fi

    if [ -n "${CUSTOMER_PUBLIC_KEY_FILE}" ]; then
        announce_start "Deleting public key"
        # shellcheck disable=SC2086
        rm -f "${CUSTOMER_PUBLIC_KEY_FILE}" ${DEBUG}
        CUSTOMER_PUBLIC_KEY_FILE=
        announce_stop "Deleting public key"
    fi

    if [ -n "${FASTBOOT_STAGING_DIR}" ] && [ -d "${FASTBOOT_STAGING_DIR}" ]; then
        rm -rf "${FASTBOOT_STAGING_DIR}"
    fi

    if [ -n "${DELETE_PRIVATE_TMPDIR}" ]; then
        announce_start "Deleting customised intermediates"
        # shellcheck disable=SC2086
        rm -rf "${RPI_SB_WORKDIR}" ${DEBUG}
        DELETE_PRIVATE_TMPDIR=
        announce_stop "Deleting customised intermediates"
    fi
    
    # Clean up orphaned resources
    cleanup_orphans

    if [ "${returnvalue}" -ne 0 ] && [ "${BOOTSTRAP_SKIP_FAILURE_HOOK:-0}" -ne 1 ]; then
        case ${PROVISIONING_STYLE} in
            "secure-boot")
                run_provision_failed_hook "sb-provisioner" "bootstrap"
                ;;
            "fde-only")
                run_provision_failed_hook "fde-provisioner" "bootstrap"
                ;;
            "naked")
                run_provision_failed_hook "naked-provisioner" "bootstrap"
                ;;
        esac
    fi

    if [ $returnvalue -eq 0 ]; then
        systemd-notify --status="Provisioning completed successfully" STOPPING=1
    else
        systemd-notify --status="Provisioning failed" STOPPING=1
    fi
    
    exit $returnvalue
}

trap cleanup EXIT INT TERM

# On pre-Pi4 devices, only TARGET_DEVICE_PATH is likely to be unique.
TARGET_DEVICE_PATH="$1"
# TARGET_USB_PATH is a udev device path in the format "X-Y[.Z]" where:
# - X is the USB bus number
# - Y is the port number on that bus  
# - Z is the optional port number for devices behind a hub
# Example: "1-1.2" means bus 1, hub port 1, hub downstream port 2
# Extract USB device path from udevadm (P: line) and parse out the topology path
TARGET_USB_PATH="$(udevadm info "${TARGET_DEVICE_PATH}" | grep -oP '^P: \K.*' | grep -oE '[0-9]+-[0-9]+(\.[0-9]+)*$' || true)"
TARGET_DEVICE_FAMILY="$(udevadm info --name="$TARGET_DEVICE_PATH" --query=property --property=ID_MODEL_ID --value)"
# TARGET_DEVICE_SERIAL is best-effort, not all rpiboot devices have it set (some only show 32-bits)
TARGET_DEVICE_SERIAL="$(udevadm info --name="$TARGET_DEVICE_PATH" --query=property --property=ID_SERIAL_SHORT --value)"
# If TARGET_DEVICE_SERIAL is empty or equals "Broadcom", use TARGET_DEVICE_PATH instead
if [ -z "${TARGET_DEVICE_SERIAL}" ] || [ "${TARGET_DEVICE_SERIAL}" = "Broadcom" ]; then
    TARGET_DEVICE_SERIAL="${TARGET_DEVICE_PATH}"
    log "Using device path as serial: ${TARGET_DEVICE_SERIAL}"
else
    log "Using device serial: ${TARGET_DEVICE_SERIAL}"
fi

# If USB path extraction from udevadm failed, try using serial-based lookup as fallback
if [ -z "${TARGET_USB_PATH}" ] && [ -n "${TARGET_DEVICE_SERIAL}" ] && [ "${TARGET_DEVICE_SERIAL}" != "${TARGET_DEVICE_PATH}" ]; then
    log "USB path extraction from udevadm failed, attempting serial-based lookup"
    TARGET_USB_PATH="$(get_usb_path_for_serial "${TARGET_DEVICE_SERIAL}" || true)"
    if [ -n "${TARGET_USB_PATH}" ]; then
        log "Found USB path via serial lookup: ${TARGET_USB_PATH}"
    else
        log "Warning: Could not determine USB path for device"
    fi
fi


EARLY_LOG_DIRECTORY="/var/log/rpi-sb-provisioner/early/${TARGET_DEVICE_PATH}"
mkdir -p "${EARLY_LOG_DIRECTORY}"

# rpiboot writes <serial>.json into this directory as it collects OTP
# metadata from the device.  We parse USER_BOARDREV out of it after the
# first rpiboot call to record the board type (e.g. Pi 5, CM5) as generic
# device metadata surfaced in the visualisers and state.db.
METADATA_DIR="${EARLY_LOG_DIRECTORY}/metadata"
mkdir -p "${METADATA_DIR}"

# Sets the global BOARD_TYPE (2-digit hex string of bits 4..11 of OTP
# USER_BOARDREV; e.g. "17" for Pi 5, "18" for CM5) and BOARD_NAME for
# logging.  Best-effort: if the metadata file is missing or unparseable
# (older firmware, non-2712/2711 device, recovery_metadata=0) the globals
# stay empty and downstream record_state calls record an empty board_type.
# Idempotent: subsequent rpiboot phases re-call this and it returns early
# if BOARD_TYPE is already set.
extract_board_type() {
    [ -n "${BOARD_TYPE}" ] && return 0
    json_file=$(find "${METADATA_DIR}" -maxdepth 1 -type f -name '*.json' 2>/dev/null | head -n1)
    [ -z "${json_file}" ] && return 0
    [ ! -s "${json_file}" ] && return 0
    user_boardrev=$(sed -nE 's/.*"USER_BOARDREV"[[:space:]]*:[[:space:]]*"([0-9a-fA-F]+)".*/\1/p' "${json_file}" | head -n1)
    [ -z "${user_boardrev}" ] && return 0
    board_type_decimal=$(( (0x${user_boardrev} >> 4) & 0xff ))
    BOARD_TYPE=$(printf '%02x' "${board_type_decimal}")
    case ${BOARD_TYPE} in
        11) BOARD_NAME="Raspberry Pi 4" ;;
        13) BOARD_NAME="Raspberry Pi 400" ;;
        14) BOARD_NAME="Compute Module 4" ;;
        15) BOARD_NAME="Compute Module 4S" ;;
        17) BOARD_NAME="Raspberry Pi 5" ;;
        18) BOARD_NAME="Compute Module 5" ;;
        19) BOARD_NAME="Raspberry Pi 500" ;;
        1a) BOARD_NAME="Compute Module 5 Lite" ;;
        *)  BOARD_NAME="Board type 0x${BOARD_TYPE}" ;;
    esac
    export BOARD_TYPE BOARD_NAME
    log "rpiboot metadata: USER_BOARDREV=0x${user_boardrev} -> board_type=0x${BOARD_TYPE} (${BOARD_NAME})"
}

# Create symlinks so the web UI can find pre-fastboot logs while bootstrap is running.
# The USB endpoint (e.g. "1-1.2") is always a clean path-safe identifier and is stored
# in the DB, so the web UI can fall back to it when the serial-keyed directory has no logs.
# When the serial IS a real serial (not a device-path fallback), also symlink by serial.
# These symlinks are replaced by real files when bootstrap completes (see mv below).
if [ -n "${TARGET_USB_PATH}" ]; then
    _endpoint_log_dir="/var/log/rpi-sb-provisioner/${TARGET_USB_PATH}"
    mkdir -p "${_endpoint_log_dir}"
    ln -sf "${EARLY_LOG_DIRECTORY}/bootstrap.log" "${_endpoint_log_dir}/bootstrap.log"
fi
if [ -n "${TARGET_DEVICE_SERIAL}" ] && [ "${TARGET_DEVICE_SERIAL}" != "${TARGET_DEVICE_PATH}" ]; then
    _serial_log_dir="/var/log/rpi-sb-provisioner/${TARGET_DEVICE_SERIAL}"
    mkdir -p "${_serial_log_dir}"
    ln -sf "${EARLY_LOG_DIRECTORY}/bootstrap.log" "${_serial_log_dir}/bootstrap.log"
fi

die() {
    # shellcheck disable=SC2086
    echo "$@" ${DEBUG}
    false
    cleanup
}

# Snapshot special flags into variables so they can be checked multiple times
# without re-reading the filesystem. One-time flags are consumed at the end
# of the bootstrap process (see consume_onetime_special_flags below).
SPECIAL_FLAG_SKIP_EEPROM=0
SPECIAL_FLAG_REPROVISION_DEVICE=0

_flag_path=$(resolve_special_flag_path "/etc/rpi-sb-provisioner/special-skip-eeprom" || true)
if [ -n "${_flag_path}" ]; then
    SPECIAL_FLAG_SKIP_EEPROM=1
    log "Special flag active: skip-eeprom for ${TARGET_DEVICE_SERIAL} (path: ${_flag_path})"
fi

_flag_path=$(resolve_special_flag_path "/etc/rpi-sb-provisioner/special-reprovision-device" || true)
if [ -n "${_flag_path}" ]; then
    SPECIAL_FLAG_REPROVISION_DEVICE=1
    log "Special flag active: reprovision-device for ${TARGET_DEVICE_SERIAL} (path: ${_flag_path})"
fi

# Consume any one-time special flags. Called at the end of the bootstrap process.
# One-time flags have "once" as their file content; persistent flags are left intact.
consume_onetime_special_flags() {
    for _flag_dir in \
        "/etc/rpi-sb-provisioner/special-skip-eeprom" \
        "/etc/rpi-sb-provisioner/special-reprovision-device"; do
        _fp=$(resolve_special_flag_path "${_flag_dir}" || true)
        if [ -n "${_fp}" ]; then
            _content=$(head -n1 "${_fp}" 2>/dev/null | tr -d '[:space:]') || true
            if [ "${_content}" = "once" ]; then
                log "Consuming one-time special flag: ${_fp}"
                rm -f "${_fp}"
            fi
        fi
    done
}

read_config
compute_image_summary

# Initialize signing context (validates key config, derives public key)
# This is safe to call even for naked provisioning - it will set SIGNING_MODE="none"
if ! init_signing_context; then
    log "Warning: Failed to initialize signing context"
    # Don't fail here - naked provisioning doesn't need signing
fi

systemd-notify --ready --status="Provisioning started"
# Create device-specific lock
DEVICE_LOCK="${LOCK_BASE}/${TARGET_DEVICE_SERIAL}"
if atomic_mkdir "$DEVICE_LOCK"; then
    HOLDING_LOCKFILE=1
else
    # Don't record state here, as this is an expected failure for devices
    # that produce multiple matching descriptors.
    BOOTSTRAP_SKIP_FAILURE_HOOK=1
    die "Bootstrap already in progress for ${TARGET_DEVICE_SERIAL}"
fi

get_fastboot_gadget() {
    if [ -f /etc/rpi-sb-provisioner/fastboot-gadget.img ]; then
        echo "/etc/rpi-sb-provisioner/fastboot-gadget.img"
    else
        echo "/var/lib/rpi-sb-provisioner/fastboot-gadget.img"
    fi
}

get_fastboot_gadget_2710() {
    if [ -f /etc/rpi-sb-provisioner/fastboot-gadget.2710-bootfiles-bin ]; then
        echo "/etc/rpi-sb-provisioner/fastboot-gadget.2710-bootfiles-bin"
    else
        echo "/var/lib/rpi-sb-provisioner/fastboot-gadget.2710-bootfiles-bin"
    fi
}


get_fastboot_config_file() {
    if [ -f /etc/rpi-sb-provisioner/boot_ramdisk_config.txt ]; then
        echo "/etc/rpi-sb-provisioner/boot_ramdisk_config.txt"
    else
        echo "/var/lib/rpi-sb-provisioner/boot_ramdisk_config.txt"
    fi
}

check_command_exists() {
    command_to_test=$1
    if ! command -v "${command_to_test}" 1> /dev/null; then
        log "${command_to_test} could not be found"
        exit 1
    else
        echo "$command_to_test"
    fi
}

timeout_fatal() {
    command="$*"
    timeout_seconds=60
    set +e
    log "Running command with ${timeout_seconds}-second timeout: \"${command}\""
    # shellcheck disable=SC2086
    timeout ${timeout_seconds} ${command}
    command_exit_status=$?
    
    # Handle different exit codes from the timeout command
    case ${command_exit_status} in
        0)
            # Command completed successfully within the time limit
            log "\"$command\" succeeded with exit code 0."
            ;;
        124)
            # Exit code 124 means the command timed out (TERM signal sent but command didn't exit)
            record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
            die "\"${command}\" FAILED: Timed out after ${timeout_seconds} seconds (exit code 124)."
            ;;
        125)
            # Exit code 125 means the timeout command itself failed
            record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
            die "\"${command}\" FAILED: The timeout command itself failed (exit code 125)."
            ;;
        126)
            # Exit code 126 means the command was found but could not be executed
            record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
            die "\"${command}\" FAILED: Command found but could not be executed (exit code 126)."
            ;;
        127)
            # Exit code 127 means the command was not found
            record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
            die "\"${command}\" FAILED: Command not found (exit code 127)."
            ;;
        137)
            # Exit code 137 (128+9) means the command was killed by SIGKILL (kill -9)
            record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
            die "\"${command}\" FAILED: Command was killed by SIGKILL (exit code 137)."
            ;;
        *)
            # Any other non-zero exit code is a general failure
            record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
            die "\"${command}\" FAILED: Command returned exit code ${command_exit_status}."
            ;;
    esac
    set -e
}

# NOTE: get_signing_directives() and derivePublicKey() have been moved to rpi-sb-common.sh
# Use init_signing_context() to initialize signing, then:
#   - get_openssl_sign_args() for OpenSSL signing
#   - get_eeprom_digest_sign_args() for rpi-eeprom-digest
#   - get_sign_bootcode_key_args() for rpi-sign-bootcode
#   - CUSTOMER_PUBLIC_KEY_FILE is set by init_signing_context()

enforceSecureBootloaderConfig() {
    if ! grep -Fxq "SIGNED_BOOT=1" "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"; then
        echo "SIGNED_BOOT=1" >> "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"
        sed -i -e "s/SIGNED_BOOT=0//g" "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"
    fi

    # These directives are for the bootloader to load the ramdisk
    # Don't use the traditional boot flow, and instead look for boot.img/boot.sig
    echo "boot_ramdisk=1" >> "${RPI_SB_WORKDIR}/config.txt"
    # Log to the UART, so you can inspect the process
    echo "uart_2ndstage=1" >> "${RPI_SB_WORKDIR}/config.txt"
    #echo "eeprom_write_protect=1" >> "${RPI_SB_WORKDIR}/config.txt"
}

identifyBootloaderConfig() {
    # Possible to pass in RPI_DEVICE_BOOTLOADER_CONFIG_FILE... we should make sure the right thing happens with this.
    if [ ! -f "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}" ]; then
        RPI_DEVICE_BOOTLOADER_CONFIG_FILE="$(mktemp)"
    fi
}

# Detects whether an EEPROM image carries an AB-capable layout by checking
# for the "bootsys" filename marker at offset 0x10008. Mirrors the helper of
# the same name in usbboot's tools/update-pieeprom.sh.
isABCapableImage() {
    image="$1"
    filename=$(dd if="${image}" bs=1 skip=$((0x10008)) count=8 status=none | strings)
    [ "${filename}" = "bootsys" ]
}

# This function is adapted from the functions in the usbboot repo.
# It handles both signed (secure-boot) and unsigned (naked) EEPROM updates
# Uses the global signing context (must call init_signing_context first)
#
# Arguments:
#   $1 - src_image: Source EEPROM image
#   $2 - dst_image: Destination EEPROM image
update_eeprom() {
    src_image="$1"
    dst_image="$2"
    sign_args=""

    log "update_eeprom() src_image: \"${src_image}\""

    if signing_available; then
        if ! grep -q "SIGNED_BOOT=1" "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"; then
            # If the OTP bit to require secure boot are set then then
            # SIGNED_BOOT=1 is implicitly set in the EEPROM config.
            # For debug in signed-boot mode it's normally useful to set this
            log "Warning: SIGNED_BOOT=1 not found in \"${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}\""
        fi

        #update_version=$(strings "${src_image}" | grep BUILD_TIMESTAMP | sed 's/.*=//g')

        TMP_CONFIG_SIG="$(mktemp)"
        log "Signing bootloader config"
        # shellcheck disable=SC2046
        rpi-eeprom-digest $(get_eeprom_digest_sign_args) -i "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}" -o "${TMP_CONFIG_SIG}"

        # shellcheck disable=SC2086
        cat "${TMP_CONFIG_SIG}" ${DEBUG}

        # rpi-eeprom-config needs the public key for embedding
        sign_args="-d ${TMP_CONFIG_SIG} -p ${CUSTOMER_PUBLIC_KEY_FILE}"

        case ${TARGET_DEVICE_FAMILY} in
            2712)
                customer_signed_bootcode_binary_workdir=$(mktemp -d)
                cd "${customer_signed_bootcode_binary_workdir}" || return
                rpi-eeprom-config -x "${src_image}"
                # shellcheck disable=SC2046
                rpi-sign-bootcode --debug -c 2712 -i bootcode.bin -o bootcode.bin.signed $(get_sign_bootcode_key_args) -v 0 -n 16
                if isABCapableImage "${src_image}"; then
                    # shellcheck disable=SC2046
                    rpi-sign-bootcode --debug -c 2712 -i bootsys -o bootsys.signed $(get_sign_bootcode_key_args) -v 0 -n 16
                    rpi-eeprom-config \
                        --out "${dst_image}.intermediate" \
                        --bootcode "${customer_signed_bootcode_binary_workdir}/bootcode.bin.signed" \
                        --bootsys "${customer_signed_bootcode_binary_workdir}/bootsys.signed" \
                        "${src_image}" || die "Failed to update signed bootcode/bootsys in the AB EEPROM image"
                else
                    rpi-eeprom-config \
                        --out "${dst_image}.intermediate" --bootcode "${customer_signed_bootcode_binary_workdir}/bootcode.bin.signed" \
                        "${src_image}" || die "Failed to update signed bootcode in the EEPROM image"
                fi
                cd - > /dev/null || return
                rm -rf "${customer_signed_bootcode_binary_workdir}"
                ;;
            *)
                # 2711 and earlier do _not_ require a signed bootcode binary
                cp "${src_image}" "${dst_image}.intermediate"
                ;;
        esac
    else
        # Unsigned mode: just copy the source as intermediate
        cp "${src_image}" "${dst_image}.intermediate"
    fi

    rm -f "${dst_image}"
    set -x
    # shellcheck disable=SC2086
    if ! rpi-eeprom-config \
        --config "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}" \
        --out "${dst_image}" ${sign_args} \
        "${dst_image}.intermediate"; then
        record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
        die "Failed to update EEPROM image"
    fi
    rm -f "${dst_image}.intermediate"
    rm -f "${TMP_CONFIG_SIG}"
    set +x

cat <<EOF
new-image: ${dst_image}
source-image: ${src_image}
config: ${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}
EOF
}

FIRMWARE_ROOT="/lib/firmware/raspberrypi/bootloader"
FIRMWARE_RELEASE_STATUS="latest"

# Taken from rpi-eeprom-update
BOOTLOADER_UPDATE_IMAGE=""
BOOTLOADER_UPDATE_VERSION=0
getBootloaderUpdateVersion() {
   BOOTLOADER_UPDATE_VERSION=0
   
   # Check if explicit firmware file is specified
   if [ -n "${RPI_DEVICE_FIRMWARE_FILE}" ] && [ -f "${RPI_DEVICE_FIRMWARE_FILE}" ]; then
      log "Using explicitly specified firmware file: ${RPI_DEVICE_FIRMWARE_FILE}"
      BOOTLOADER_UPDATE_IMAGE="${RPI_DEVICE_FIRMWARE_FILE}"
      BOOTLOADER_UPDATE_VERSION=$(strings "${RPI_DEVICE_FIRMWARE_FILE}" | grep BUILD_TIMESTAMP | sed 's/.*=//g')
      return
   fi
   
   # Fall back to automatic selection from release channel
   match=".*/pieeprom-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].bin"
   latest="$(find "${FIRMWARE_IMAGE_DIR}/" -maxdepth 1 -type f -follow -size "${EEPROM_SIZE}c" -regex "${match}" | sort -r | head -n1)"
   if [ -f "${latest}" ]; then
      BOOTLOADER_UPDATE_VERSION=$(strings "${latest}" | grep BUILD_TIMESTAMP | sed 's/.*=//g')
      BOOTLOADER_UPDATE_IMAGE="${latest}"
   else
      log "Warning: No firmware image found in ${FIRMWARE_IMAGE_DIR}/ matching size ${EEPROM_SIZE}c"
      log "BOOTLOADER_UPDATE_IMAGE will remain unset - may cause issues if EEPROM update is required"
   fi
}

# These tools are used to modify the supplied images, and deal with mounting and unmounting the images.
check_command_exists losetup
check_command_exists mknod
check_command_exists lsblk
check_command_exists cut
check_command_exists findmnt
check_command_exists grep

DELETE_PRIVATE_TMPDIR=
announce_start "Finding the cache directory"
if [ -z "${RPI_SB_WORKDIR}" ]; then
    RPI_SB_WORKDIR=$(make_temp_dir "rpi-sb-bootstrap.XXX")
    announce_stop "Finding the cache directory: Created ${RPI_SB_WORKDIR} (none configured)"
    DELETE_PRIVATE_TMPDIR="true"
elif [ ! -d "${RPI_SB_WORKDIR}" ]; then
    RPI_SB_WORKDIR=$(make_temp_dir "rpi-sb-bootstrap.XXX")
    announce_stop "Finding the cache directory: Created ${RPI_SB_WORKDIR} (configured path isn't a directory)"
    DELETE_PRIVATE_TMPDIR="true"
else
    # Deliberately do nothing
    announce_stop "Finding the cache directory: Using specified name"
fi

# Ensure work directory has proper permissions
chmod 700 "$RPI_SB_WORKDIR"

# Create a per-run staging directory inside the persistent workdir for fastboot
# assets. This prevents concurrent device bootstraps from colliding and ensures
# rpiboot always finds a clean set of bootcode files, while the parent workdir
# retains cached data (signed bootloader images, EEPROM configs) across runs.
FASTBOOT_STAGING_DIR=$(mktemp -d "${RPI_SB_WORKDIR}/fastboot.XXXXXX")

ALLOW_SIGNED_BOOT=0
case $TARGET_DEVICE_FAMILY in
    2712 | 2711)
        ALLOW_SIGNED_BOOT=1
        ;;
    2710 | 2764)
        ALLOW_SIGNED_BOOT=0
        ;;
    *)
        record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
        die "Refusing to provision an unknown device family"
        ;;
esac

record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_STARTED}" "${TARGET_USB_PATH}"

# Run bootstrap customisation script for the chosen provisioning style
# This script runs when a device is detected, before provisioning begins
case ${PROVISIONING_STYLE} in
    "secure-boot")
        run_customisation_script "sb-provisioner" "bootstrap" "${TARGET_DEVICE_SERIAL}" "${TARGET_DEVICE_FAMILY}" "${TARGET_USB_PATH}" "${TARGET_DEVICE_PATH}"
        ;;
    "fde-only")
        run_customisation_script "fde-provisioner" "bootstrap" "${TARGET_DEVICE_SERIAL}" "${TARGET_DEVICE_FAMILY}" "${TARGET_USB_PATH}" "${TARGET_DEVICE_PATH}"
        ;;
    "naked")
        run_customisation_script "naked-provisioner" "bootstrap" "${TARGET_DEVICE_SERIAL}" "${TARGET_DEVICE_FAMILY}" "${TARGET_USB_PATH}" "${TARGET_DEVICE_PATH}"
        ;;
    *)
        log "Warning: Unknown provisioning style: ${PROVISIONING_STYLE}, skipping bootstrap customization"
        ;;
esac

# Determine if we're enforcing secure boot, and if so, prepare the environment & eeprom accordingly.
if [ "$ALLOW_SIGNED_BOOT" -eq 1 ]; then 
    if [ "${PROVISIONING_STYLE}" = "secure-boot" ]; then
        SECURE_BOOTLOADER_DIRECTORY="${RPI_SB_WORKDIR}/secure-bootloader/"
        mkdir -p "${SECURE_BOOTLOADER_DIRECTORY}"
        if [ -f "${SECURE_BOOTLOADER_DIRECTORY}/config.txt" ]; then
            log "Secure bootloader directory already exists, skipping setup"
            if [ "${SPECIAL_FLAG_REPROVISION_DEVICE}" -eq 1 ]; then
                # This only makes sense if you're re-provisioning a device that's already been provisioned.
                # It's a special case, and should not be used in normal operation.
                # Additionally, this only works on Raspberry Pi 5-family devices.
                if [ "${TARGET_DEVICE_FAMILY}" = "2712" ]; then
                    if ! signing_available; then
                        record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
                        die "No signing key configured for re-provisioning. Aborting."
                    fi
                    log "Re-signing bootcode for special re-provisioning case"
                    # For special reprovision, use the original firmware recovery.bin as source, not the cached signed bootcode
                    BCM_CHIP=2712
                    FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}-${BCM_CHIP}/${FIRMWARE_RELEASE_STATUS}"
                    BOOTCODE_BINARY_IMAGE="${FIRMWARE_IMAGE_DIR}/recovery.bin"
                    BOOTCODE_FLASHING_NAME="${SECURE_BOOTLOADER_DIRECTORY}/bootcode5.bin"
                    # shellcheck disable=SC2046
                    rpi-sign-bootcode --debug -c 2712 -i "${BOOTCODE_BINARY_IMAGE}" -o "${BOOTCODE_FLASHING_NAME}" $(get_sign_bootcode_key_args) -v 0 -n 16
                else
                    log "Warning: Special re-provisioning only supported on Pi 5 (2712), skipping for device family ${TARGET_DEVICE_FAMILY}"
                fi
            else
                log "Normal provisioning mode (reusing cached bootloader)"
            fi
            if [ "${SPECIAL_FLAG_REPROVISION_DEVICE}" -eq 0 ]; then
                # Normal case: reuse cached bootcode
                case ${TARGET_DEVICE_FAMILY} in
                    2712)
                        BOOTCODE_BINARY_IMAGE="${SECURE_BOOTLOADER_DIRECTORY}/bootcode5.bin"
                        BOOTCODE_FLASHING_NAME="${SECURE_BOOTLOADER_DIRECTORY}/bootcode5.bin"
                        ;;
                    2711)
                        BOOTCODE_BINARY_IMAGE="${SECURE_BOOTLOADER_DIRECTORY}/bootcode4.bin"
                        BOOTCODE_FLASHING_NAME="${SECURE_BOOTLOADER_DIRECTORY}/bootcode4.bin"
                        ;;
                esac
            fi
            [ "${SPECIAL_FLAG_SKIP_EEPROM}" -eq 0 ] && timeout_fatal rpiboot -j "${METADATA_DIR}" -d "${SECURE_BOOTLOADER_DIRECTORY}" -p "${TARGET_USB_PATH}"
            extract_board_type
        else
            log "Creating secure bootloader for future reuse"
            touch "${SECURE_BOOTLOADER_DIRECTORY}/config.txt"

            announce_start "Setting up the environment for a signed-boot capable device"
            if [ -z "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}" ]; then
                RPI_DEVICE_BOOTLOADER_CONFIG_FILE=/var/lib/rpi-sb-provisioner/bootloader.secure
                log "Using default secure bootloader config file: ${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"
            else
                log "Using pre-configured bootloader config: ${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"
            fi

            SOURCE_EEPROM_IMAGE=
            DESTINATION_EEPROM_IMAGE=
            DESTINATION_EEPROM_SIGNATURE=
            BOOTCODE_BINARY_IMAGE=
            BOOTCODE_FLASHING_NAME=
            case $TARGET_DEVICE_FAMILY in
                2711)
                    BCM_CHIP=2711
                    EEPROM_SIZE=524288
                    FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}-${BCM_CHIP}/${FIRMWARE_RELEASE_STATUS}"
                    getBootloaderUpdateVersion
                    SOURCE_EEPROM_IMAGE="${BOOTLOADER_UPDATE_IMAGE}"
                    BOOTCODE_BINARY_IMAGE="${FIRMWARE_IMAGE_DIR}/recovery.bin"
                    BOOTCODE_FLASHING_NAME="${SECURE_BOOTLOADER_DIRECTORY}/bootcode4.bin"
                    ;;
                2712)
                    BCM_CHIP=2712
                    EEPROM_SIZE=2097152
                    FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}-${BCM_CHIP}/${FIRMWARE_RELEASE_STATUS}"
                    getBootloaderUpdateVersion
                    SOURCE_EEPROM_IMAGE="${BOOTLOADER_UPDATE_IMAGE}"
                    BOOTCODE_BINARY_IMAGE="${FIRMWARE_IMAGE_DIR}/recovery.bin"
                    BOOTCODE_FLASHING_NAME="${SECURE_BOOTLOADER_DIRECTORY}/bootcode5.bin"
                    ;;
                *)
                    record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
                    die "Unable to identify EEPROM parameters for non-Pi4, Pi5 device. Aborting."
            esac

            DESTINATION_EEPROM_IMAGE="${SECURE_BOOTLOADER_DIRECTORY}/pieeprom.bin"
            DESTINATION_EEPROM_SIGNATURE="${SECURE_BOOTLOADER_DIRECTORY}/pieeprom.sig"

            ### In the completely-unprovisioned state, where you have not yet written a customer OTP key, simply make the copy of the unsigned bootcode
            cp "${BOOTCODE_BINARY_IMAGE}" "${BOOTCODE_FLASHING_NAME}"
            ####

            if signing_available; then
                # Public key already derived by init_signing_context()
                identifyBootloaderConfig
                enforceSecureBootloaderConfig

                if [ ! -e "${DESTINATION_EEPROM_SIGNATURE}" ]; then
                    if [ ! -e "${SOURCE_EEPROM_IMAGE}" ]; then
                        record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
                        die "No Raspberry Pi EEPROM file to use as key vector"
                    else
                        update_eeprom "${SOURCE_EEPROM_IMAGE}" "${DESTINATION_EEPROM_IMAGE}"
                        # shellcheck disable=SC2046
                        rpi-eeprom-digest $(get_eeprom_digest_sign_args) -i "${DESTINATION_EEPROM_IMAGE}" -o "${DESTINATION_EEPROM_SIGNATURE}"
                    fi
                else
                    log "Using existing EEPROM signature: ${DESTINATION_EEPROM_SIGNATURE}"
                fi

                # This directive informs the bootloader to write the public key into OTP
                echo "program_pubkey=1" > "${SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                # Force the post-recovery reboot back into RPIBOOT so the next
                # phase (fastboot bootstrap) can attach over USB.
                echo "set_reboot_order=0x3" >> "${SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                # This directive tells the bootloader to reboot once it's written the OTP
                echo "recovery_reboot=1" >> "${SECURE_BOOTLOADER_DIRECTORY}/config.txt"

                if [ -n "${RPI_DEVICE_LOCK_JTAG}" ]; then
                    echo "program_jtag_lock=1" >> "${SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                fi

                if [ -n "${RPI_DEVICE_EEPROM_WP_SET}" ]; then
                    echo "eeprom_write_protect=1" >> "${SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                fi

                # Raspberry Pi 4 family (2711) requires program_rpiboot_gpio to program the RPIBOOT GPIO into OTP
                # alongside the secure boot public key. Without this, recovery.bin fails with:
                # "Failed to set RPIBOOT OTP gpio0 c800 -> 0"
                # Valid GPIO pins: 2, 4, 5, 6, 7, 8 (GPIO 8 is recommended)
                if [ "${TARGET_DEVICE_FAMILY}" = "2711" ]; then
                    if [ -n "${RPI_DEVICE_RPIBOOT_GPIO}" ]; then
                        echo "program_rpiboot_gpio=${RPI_DEVICE_RPIBOOT_GPIO}" >> "${SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                        log "Programming RPIBOOT GPIO ${RPI_DEVICE_RPIBOOT_GPIO} into OTP for Raspberry Pi 4 family device"
                    else
                        log "WARNING: RPI_DEVICE_RPIBOOT_GPIO is not set for Raspberry Pi 4 family device."
                        log "WARNING: Secure boot provisioning may fail with 'Failed to set RPIBOOT OTP gpio0' error."
                        log "WARNING: Set RPI_DEVICE_RPIBOOT_GPIO=8 (recommended) in the configuration."
                    fi
                fi

                log "Writing key and EEPROM configuration to the device"
                if [ "${SPECIAL_FLAG_REPROVISION_DEVICE}" -eq 1 ]; then
                    if [ "${TARGET_DEVICE_FAMILY}" = "2712" ]; then
                        # This only makes sense if you're re-provisioning a device that's already been provisioned.
                        # It's a special case, and should not be used in normal operation.
                        # Additionally, this only works on Raspberry Pi 5-family devices.
                        log "Re-signing bootcode for special re-provisioning case"
                        # shellcheck disable=SC2046
                        rpi-sign-bootcode --debug -c 2712 -i "${BOOTCODE_BINARY_IMAGE}" -o "${BOOTCODE_FLASHING_NAME}" $(get_sign_bootcode_key_args) -v 0 -n 16
                    else
                        log "Warning: Special re-provisioning only supported on Pi 5 (2712), skipping for device family ${TARGET_DEVICE_FAMILY}"
                    fi
                else
                    log "Normal provisioning mode (not re-provisioning)"
                fi
                [ "${SPECIAL_FLAG_SKIP_EEPROM}" -eq 0 ] && timeout_fatal rpiboot -j "${METADATA_DIR}" -d "${SECURE_BOOTLOADER_DIRECTORY}" -p "${TARGET_USB_PATH}"
            extract_board_type
            else
                log "No key specified, skipping eeprom update"
            fi
            log "Keywriting completed. Silently rebooting for next phase."
        fi

        # Cache signed fastboot assets in the persistent workdir so that
        # subsequent runs (which take the cached-bootloader path above) do
        # not need to re-sign them.  Only recreate when the cache is missing
        # (e.g. after a workdir clear).
        if [ ! -f "${RPI_SB_WORKDIR}/bootfiles.bin" ]; then
            case $TARGET_DEVICE_FAMILY in
                2712)
                    FASTBOOT_SIGN_DIR=$(mktemp -d)
                    cd "${FASTBOOT_SIGN_DIR}"
                    tar -vxf /usr/share/rpiboot/mass-storage-gadget64/bootfiles.bin
                    # shellcheck disable=SC2046
                    rpi-sign-bootcode --debug -c 2712 -i 2712/bootcode5.bin -o 2712/bootcode5.bin.signed $(get_sign_bootcode_key_args) -v 0 -n 16
                    mv -f "2712/bootcode5.bin.signed" "2712/bootcode5.bin"
                    tar -vcf "${RPI_SB_WORKDIR}/bootfiles.bin" -- *
                    cd -
                    rm -rf "${FASTBOOT_SIGN_DIR}"
                    ;;
                *)
                    cp /usr/share/rpiboot/mass-storage-gadget64/bootfiles.bin "${RPI_SB_WORKDIR}/bootfiles.bin"
                    ;;
            esac
        fi

        if [ ! -f "${RPI_SB_WORKDIR}/boot.img" ] || [ ! -f "${RPI_SB_WORKDIR}/boot.sig" ]; then
            announce_start "Signing fastboot image"
            cp "$(get_fastboot_gadget)" "${RPI_SB_WORKDIR}"/boot.img
            sha256sum "${RPI_SB_WORKDIR}"/boot.img | awk '{print $1}' > "${RPI_SB_WORKDIR}"/boot.sig
            printf 'rsa2048: ' >> "${RPI_SB_WORKDIR}"/boot.sig
            sign_image_hex "${RPI_SB_WORKDIR}"/boot.img >> "${RPI_SB_WORKDIR}"/boot.sig
            announce_stop "Signing fastboot image"
        fi

        # Stage cached assets into the per-run fastboot directory
        cp "${RPI_SB_WORKDIR}/bootfiles.bin" "${FASTBOOT_STAGING_DIR}/bootfiles.bin"
        cp "${RPI_SB_WORKDIR}/boot.img" "${FASTBOOT_STAGING_DIR}/boot.img"
        cp "${RPI_SB_WORKDIR}/boot.sig" "${FASTBOOT_STAGING_DIR}/boot.sig"
        cp "$(get_fastboot_config_file)" "${FASTBOOT_STAGING_DIR}/config.txt"
    else # !PROVISIONING_STYLE=secure-boot
        # For non-secure-boot provisioning on 2711/2712, still update EEPROM but without signing
        case ${TARGET_DEVICE_FAMILY} in
            2712|2711)
                NON_SECURE_BOOTLOADER_DIRECTORY="${RPI_SB_WORKDIR}/non-secure-bootloader/"
                mkdir -p "${NON_SECURE_BOOTLOADER_DIRECTORY}"
                
                if [ ! -f "${NON_SECURE_BOOTLOADER_DIRECTORY}/config.txt" ]; then
                    log "Creating non-secure bootloader for EEPROM update"
                    touch "${NON_SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                    
                    announce_start "Setting up EEPROM update for non-secure-boot device"
                    if [ -z "${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}" ]; then
                        RPI_DEVICE_BOOTLOADER_CONFIG_FILE=/var/lib/rpi-sb-provisioner/bootloader.naked
                        log "Using default naked bootloader config file: ${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"
                    else
                        log "Using pre-configured bootloader config: ${RPI_DEVICE_BOOTLOADER_CONFIG_FILE}"
                    fi
                    
                    SOURCE_EEPROM_IMAGE=
                    DESTINATION_EEPROM_IMAGE=
                    DESTINATION_EEPROM_SIGNATURE=
                    BOOTCODE_BINARY_IMAGE=
                    BOOTCODE_FLASHING_NAME=
                    
                    case $TARGET_DEVICE_FAMILY in
                        2711)
                            BCM_CHIP=2711
                            EEPROM_SIZE=524288
                            FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}-${BCM_CHIP}/${FIRMWARE_RELEASE_STATUS}"
                            getBootloaderUpdateVersion
                            SOURCE_EEPROM_IMAGE="${BOOTLOADER_UPDATE_IMAGE}"
                            BOOTCODE_BINARY_IMAGE="${FIRMWARE_IMAGE_DIR}/recovery.bin"
                            BOOTCODE_FLASHING_NAME="${NON_SECURE_BOOTLOADER_DIRECTORY}/bootcode4.bin"
                            ;;
                        2712)
                            BCM_CHIP=2712
                            EEPROM_SIZE=2097152
                            FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}-${BCM_CHIP}/${FIRMWARE_RELEASE_STATUS}"
                            getBootloaderUpdateVersion
                            SOURCE_EEPROM_IMAGE="${BOOTLOADER_UPDATE_IMAGE}"
                            BOOTCODE_BINARY_IMAGE="${FIRMWARE_IMAGE_DIR}/recovery.bin"
                            BOOTCODE_FLASHING_NAME="${NON_SECURE_BOOTLOADER_DIRECTORY}/bootcode5.bin"
                            ;;
                    esac
                    
                    DESTINATION_EEPROM_IMAGE="${NON_SECURE_BOOTLOADER_DIRECTORY}/pieeprom.bin"
                    DESTINATION_EEPROM_SIGNATURE="${NON_SECURE_BOOTLOADER_DIRECTORY}/pieeprom.sig"
                    
                    # Copy unsigned bootcode
                    cp "${BOOTCODE_BINARY_IMAGE}" "${BOOTCODE_FLASHING_NAME}"
                    
                    # Update EEPROM using the standard update_eeprom function
                    # Calling without pem_file creates an unsigned EEPROM update
                    if [ ! -e "${DESTINATION_EEPROM_SIGNATURE}" ]; then
                        if [ ! -e "${SOURCE_EEPROM_IMAGE}" ]; then
                            record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_ABORTED}" "${TARGET_USB_PATH}"
                            die "No Raspberry Pi EEPROM file found for update"
                        else
                            log "Updating EEPROM with bootloader configuration (unsigned)"
                            # Call update_eeprom - signing_available() will be false for naked mode
                            update_eeprom "${SOURCE_EEPROM_IMAGE}" "${DESTINATION_EEPROM_IMAGE}"
                            
                            # Create unsigned signature using rpi-eeprom-digest
                            # Without -k/-H parameter, creates hash + timestamp only
                            log "Creating unsigned pieeprom.sig using rpi-eeprom-digest"
                            rpi-eeprom-digest -i "${DESTINATION_EEPROM_IMAGE}" -o "${DESTINATION_EEPROM_SIGNATURE}"
                        fi
                    else
                        log "Using existing EEPROM signature: ${DESTINATION_EEPROM_SIGNATURE}"
                    fi
                    
                    # Simple recovery config to update EEPROM and reboot.
                    # Force the post-recovery reboot back into RPIBOOT so the
                    # next phase (fastboot bootstrap) can attach over USB.
                    echo "set_reboot_order=0x3" > "${NON_SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                    echo "recovery_reboot=1" >> "${NON_SECURE_BOOTLOADER_DIRECTORY}/config.txt"
                    
                    log "Updating EEPROM to latest version"
                    [ "${SPECIAL_FLAG_SKIP_EEPROM}" -eq 0 ] && timeout_fatal rpiboot -j "${METADATA_DIR}" -d "${NON_SECURE_BOOTLOADER_DIRECTORY}" -p "${TARGET_USB_PATH}"
                    extract_board_type
                    log "EEPROM update completed. Device rebooted."
                else
                    log "Reusing existing non-secure bootloader configuration"
                fi
                
                # Prepare fastboot files
                cp /usr/share/rpiboot/mass-storage-gadget64/bootfiles.bin "${FASTBOOT_STAGING_DIR}/bootfiles.bin"
                cp "$(get_fastboot_gadget)" "${FASTBOOT_STAGING_DIR}"/boot.img
                cp "$(get_fastboot_config_file)" "${FASTBOOT_STAGING_DIR}"/config.txt
                ;;
            *)
                cp "$(get_fastboot_gadget_2710)" "${FASTBOOT_STAGING_DIR}/bootfiles.bin"
                ;;
        esac
    fi
else # !ALLOW_SIGNED_BOOT
    # No allowed signed boot? Must be pre-Pi4!
    cp "$(get_fastboot_gadget_2710)" "${FASTBOOT_STAGING_DIR}/bootfiles.bin"
fi
record_state "${TARGET_DEVICE_SERIAL}" "bootstrap-firmware-updated" "${TARGET_USB_PATH}"

announce_start "fastboot initialisation"
record_state "${TARGET_DEVICE_SERIAL}" "bootstrap-fastboot-initialisation-started" "${TARGET_USB_PATH}"

timeout_fatal rpiboot -v -j "${METADATA_DIR}" -d "${FASTBOOT_STAGING_DIR}" -p "${TARGET_USB_PATH}"
extract_board_type
set +e

if [ -n "${TARGET_DEVICE_SERIAL}" ] && [ "${TARGET_DEVICE_SERIAL}" != "${TARGET_DEVICE_PATH}" ]; then
    target_log_dir="/var/log/rpi-sb-provisioner/${TARGET_DEVICE_SERIAL}"
    early_log_dir="${EARLY_LOG_DIRECTORY}"
    
    mkdir -p "${target_log_dir}"
    if [ -d "${early_log_dir}/metadata" ]; then
        mv "${early_log_dir}/metadata" "${target_log_dir}/metadata"
    fi
    mv "${early_log_dir}/bootstrap.log" "${target_log_dir}/bootstrap.log"
    # Also link into the endpoint-keyed directory so the web UI can find it by either key
    if [ -n "${TARGET_USB_PATH}" ]; then
        _endpoint_log_dir="/var/log/rpi-sb-provisioner/${TARGET_USB_PATH}"
        mkdir -p "${_endpoint_log_dir}"
        ln -sf "${target_log_dir}/bootstrap.log" "${_endpoint_log_dir}/bootstrap.log"
    fi
elif [ -n "${TARGET_USB_PATH}" ]; then
    # No real serial available - move logs to the endpoint-keyed directory instead
    target_log_dir="/var/log/rpi-sb-provisioner/${TARGET_USB_PATH}"
    early_log_dir="${EARLY_LOG_DIRECTORY}"
    
    mkdir -p "${target_log_dir}"
    if [ -d "${early_log_dir}/metadata" ]; then
        mv "${early_log_dir}/metadata" "${target_log_dir}/metadata"
    fi
    mv "${early_log_dir}/bootstrap.log" "${target_log_dir}/bootstrap.log"
else
    log "Warning: Neither serial nor USB path available, logs will remain in ${EARLY_LOG_DIRECTORY}"
    echo "[$(date +"%Y-%m-%d %H:%M:%S.$(date +%N | cut -c1-3)")] Warning: Neither serial nor USB path available, logs remain in ${EARLY_LOG_DIRECTORY}" >> "${EARLY_LOG_DIRECTORY}/bootstrap.log"
fi

announce_stop "fastboot initialisation"
record_state "${TARGET_DEVICE_SERIAL}" "${BOOTSTRAP_FINISHED}" "${TARGET_USB_PATH}"
set -e

# Consume any one-time special flags now that bootstrap has completed successfully
consume_onetime_special_flags

# Exit with success code for systemd
true
