diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ad743e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Anthony De Lorenzo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index d518d15..a225f35 100644 --- a/README.md +++ b/README.md @@ -237,4 +237,4 @@ Metrics include: `kubesolo_os_info`, `boot_success`, `boot_counter`, `uptime_sec ## License -TBD +MIT License — see [LICENSE](LICENSE) for details. diff --git a/build/Dockerfile.builder b/build/Dockerfile.builder index 300ee60..f843ae5 100644 --- a/build/Dockerfile.builder +++ b/build/Dockerfile.builder @@ -36,6 +36,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gcc-aarch64-linux-gnu \ binutils-aarch64-linux-gnu \ git \ + kpartx \ + unzip \ wget \ xorriso \ xz-utils \ diff --git a/build/config/versions.env b/build/config/versions.env index 7c64b62..ad4010a 100644 --- a/build/config/versions.env +++ b/build/config/versions.env @@ -25,9 +25,9 @@ NET_BRIDGING_TCZ_SHA256="" IPTABLES_TCZ_SHA256="" # piCore64 (ARM64 — Raspberry Pi) -PICORE_VERSION=15.0 +PICORE_VERSION=15.0.0 PICORE_ARCH=aarch64 -PICORE_IMAGE=piCore-${PICORE_VERSION}.img.gz +PICORE_IMAGE=piCore64-${PICORE_VERSION}.zip PICORE_IMAGE_URL=http://www.tinycorelinux.net/${PICORE_VERSION%%.*}.x/${PICORE_ARCH}/releases/RPi/${PICORE_IMAGE} # Raspberry Pi firmware (boot blobs, DTBs) diff --git a/build/scripts/create-disk-image.sh b/build/scripts/create-disk-image.sh index e831790..e90ea16 100755 --- a/build/scripts/create-disk-image.sh +++ b/build/scripts/create-disk-image.sh @@ -51,10 +51,39 @@ size=1048576, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, name="SystemB" type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, name="Data" EOF -# Set up loop device -LOOP=$(losetup --show -fP "$IMG_OUTPUT") +# Set up loop device with partition mappings +LOOP=$(losetup --show -f "$IMG_OUTPUT") echo "==> Loop device: $LOOP" +# Use kpartx for reliable partition device nodes (works in Docker/containers) +USE_KPARTX=false +if [ ! -b "${LOOP}p1" ]; then + if command -v kpartx >/dev/null 2>&1; then + kpartx -a "$LOOP" + USE_KPARTX=true + sleep 1 + LOOP_NAME=$(basename "$LOOP") + P1="/dev/mapper/${LOOP_NAME}p1" + P2="/dev/mapper/${LOOP_NAME}p2" + P3="/dev/mapper/${LOOP_NAME}p3" + P4="/dev/mapper/${LOOP_NAME}p4" + else + # Retry with -P flag + losetup -d "$LOOP" + LOOP=$(losetup --show -fP "$IMG_OUTPUT") + sleep 1 + P1="${LOOP}p1" + P2="${LOOP}p2" + P3="${LOOP}p3" + P4="${LOOP}p4" + fi +else + P1="${LOOP}p1" + P2="${LOOP}p2" + P3="${LOOP}p3" + P4="${LOOP}p4" +fi + MNT_EFI=$(mktemp -d) MNT_SYSA=$(mktemp -d) MNT_SYSB=$(mktemp -d) @@ -65,22 +94,25 @@ cleanup() { umount "$MNT_SYSA" 2>/dev/null || true umount "$MNT_SYSB" 2>/dev/null || true umount "$MNT_DATA" 2>/dev/null || true + if [ "$USE_KPARTX" = true ]; then + kpartx -d "$LOOP" 2>/dev/null || true + fi losetup -d "$LOOP" 2>/dev/null || true rm -rf "$MNT_EFI" "$MNT_SYSA" "$MNT_SYSB" "$MNT_DATA" 2>/dev/null || true } trap cleanup EXIT # Format partitions -mkfs.vfat -F 32 -n KSOLOEFI "${LOOP}p1" -mkfs.ext4 -q -L KSOLOA "${LOOP}p2" -mkfs.ext4 -q -L KSOLOB "${LOOP}p3" -mkfs.ext4 -q -L KSOLODATA "${LOOP}p4" +mkfs.vfat -F 32 -n KSOLOEFI "$P1" +mkfs.ext4 -q -L KSOLOA "$P2" +mkfs.ext4 -q -L KSOLOB "$P3" +mkfs.ext4 -q -L KSOLODATA "$P4" # Mount all partitions -mount "${LOOP}p1" "$MNT_EFI" -mount "${LOOP}p2" "$MNT_SYSA" -mount "${LOOP}p3" "$MNT_SYSB" -mount "${LOOP}p4" "$MNT_DATA" +mount "$P1" "$MNT_EFI" +mount "$P2" "$MNT_SYSA" +mount "$P3" "$MNT_SYSB" +mount "$P4" "$MNT_DATA" # --- EFI/Boot Partition --- echo " Installing GRUB..." diff --git a/build/scripts/extract-core.sh b/build/scripts/extract-core.sh index de228e6..e5bdbc9 100755 --- a/build/scripts/extract-core.sh +++ b/build/scripts/extract-core.sh @@ -29,23 +29,40 @@ if [ "$EXTRACT_ARCH" = "arm64" ]; then echo "==> Extracting piCore64 image: $PICORE_IMG" - # Decompress .img.gz to raw image + # Decompress to raw image (.img.gz or .zip) PICORE_RAW="$CACHE_DIR/piCore-${PICORE_VERSION}.img" if [ ! -f "$PICORE_RAW" ]; then echo " Decompressing..." - gunzip -k "$PICORE_IMG" 2>/dev/null || \ - zcat "$PICORE_IMG" > "$PICORE_RAW" + case "$PICORE_IMG" in + *.zip) + unzip -o -j "$PICORE_IMG" '*.img' -d "$CACHE_DIR" 2>/dev/null || \ + unzip -o "$PICORE_IMG" -d "$CACHE_DIR" + # Find the extracted .img file + EXTRACTED_IMG=$(find "$CACHE_DIR" -maxdepth 1 -name '*.img' -newer "$PICORE_IMG" | head -1) + if [ -n "$EXTRACTED_IMG" ] && [ "$EXTRACTED_IMG" != "$PICORE_RAW" ]; then + mv "$EXTRACTED_IMG" "$PICORE_RAW" + fi + ;; + *.img.gz) + gunzip -k "$PICORE_IMG" 2>/dev/null || \ + zcat "$PICORE_IMG" > "$PICORE_RAW" + ;; + *) + echo "ERROR: Unknown piCore image format: $PICORE_IMG" + exit 1 + ;; + esac fi - # Mount the piCore rootfs partition (partition 2 in the SD image) - # Use losetup to find the partition offset + # Mount the piCore boot partition (partition 1) to find kernel/initramfs + # piCore layout: p1=boot (FAT32, has kernel+initramfs), p2=rootfs (ext4, has tce/) IMG_MNT=$(mktemp -d) - echo " Mounting piCore rootfs partition..." + echo " Mounting piCore boot partition..." - # Get partition 2 offset (piCore layout: boot=p1, rootfs=p2) - OFFSET=$(fdisk -l "$PICORE_RAW" 2>/dev/null | awk '/^.*img2/{print $2}') + # Get partition 1 offset (boot/FAT partition with kernel+initramfs) + OFFSET=$(fdisk -l "$PICORE_RAW" 2>/dev/null | awk '/^.*img1/{print $2}') if [ -z "$OFFSET" ]; then - # Fallback: try sfdisk + # Fallback: try sfdisk (first partition) OFFSET=$(sfdisk -d "$PICORE_RAW" 2>/dev/null | awk -F'[=,]' '/start=/{print $2; exit}' | tr -d ' ') fi if [ -z "$OFFSET" ]; then @@ -56,13 +73,13 @@ if [ "$EXTRACT_ARCH" = "arm64" ]; then BYTE_OFFSET=$((OFFSET * 512)) mount -o loop,ro,offset="$BYTE_OFFSET" "$PICORE_RAW" "$IMG_MNT" || { - echo "ERROR: Failed to mount piCore rootfs (need root for losetup)" + echo "ERROR: Failed to mount piCore boot partition (need root for losetup)" exit 1 } - # Find initramfs in the piCore rootfs + # Find initramfs in the piCore boot partition COREGZ="" - for f in "$IMG_MNT"/boot/corepure64.gz "$IMG_MNT"/boot/core.gz "$IMG_MNT"/*.gz; do + for f in "$IMG_MNT"/rootfs-piCore64*.gz "$IMG_MNT"/boot/corepure64.gz "$IMG_MNT"/boot/core.gz "$IMG_MNT"/corepure64.gz "$IMG_MNT"/core.gz; do [ -f "$f" ] && COREGZ="$f" && break done diff --git a/build/scripts/fetch-rpi-firmware.sh b/build/scripts/fetch-rpi-firmware.sh index c3161b8..40e8d38 100755 --- a/build/scripts/fetch-rpi-firmware.sh +++ b/build/scripts/fetch-rpi-firmware.sh @@ -44,7 +44,7 @@ trap 'rm -rf "$TEMP_DIR"' EXIT # Extract only the boot/ directory from the archive # Archive structure: firmware-/boot/... -tar -xzf "$RPI_FW_ARCHIVE" -C "$TEMP_DIR" --strip-components=1 '*/boot/' +tar -xzf "$RPI_FW_ARCHIVE" -C "$TEMP_DIR" --strip-components=1 --wildcards '*/boot/' BOOT_SRC="$TEMP_DIR/boot" if [ ! -d "$BOOT_SRC" ]; then