From e2a3a2a862aefa335c6961313d9a034145270118 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:31:12 +0000 Subject: [PATCH 1/9] Initial plan From dfa5b2f4149f4d4b57ceb80774ad0f7803a504cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:39:56 +0000 Subject: [PATCH 2/9] Fix Docker image build by avoiding loop devices and installing udev Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- scripts/docker-image-create-uefi-old.sh | 156 ++++++++++++++++++++++++ scripts/docker-image-create-uefi.sh | 141 +++++++++++---------- scripts/docker-image-install-tools.sh | 3 +- 3 files changed, 232 insertions(+), 68 deletions(-) create mode 100755 scripts/docker-image-create-uefi-old.sh diff --git a/scripts/docker-image-create-uefi-old.sh b/scripts/docker-image-create-uefi-old.sh new file mode 100755 index 0000000..0058a4b --- /dev/null +++ b/scripts/docker-image-create-uefi-old.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# Create UEFI-bootable SparkOS image with GPT partition table + +set -e + +mkdir -p /output /staging/esp /staging/root + +echo "=== Creating UEFI-bootable SparkOS image with GRUB ===" + +# Create 1GB disk image (larger for kernel + bootloader) +dd if=/dev/zero of=/output/sparkos.img bs=1M count=1024 + +# Create GPT partition table +echo "Creating GPT partition table..." +parted -s /output/sparkos.img mklabel gpt + +# Create EFI System Partition (ESP) - 200MB, FAT32 +echo "Creating EFI System Partition..." +parted -s /output/sparkos.img mkpart ESP fat32 1MiB 201MiB +parted -s /output/sparkos.img set 1 esp on + +# Create root partition - remaining space, ext4 +echo "Creating root partition..." +parted -s /output/sparkos.img mkpart primary ext4 201MiB 100% + +# Use libguestfs to format and populate partitions without loop devices +echo "Formatting partitions using guestfish..." +guestfish -a /output/sparkos.img <<'EOF' +run +mkfs vfat /dev/sda1 label:SPARKOSEFI +mkfs ext4 /dev/sda2 label:SparkOS +mount /dev/sda2 / +mkdir-p /boot +EOF + +# Prepare ESP contents +echo "Preparing ESP contents..." + +# Prepare ESP contents +echo "Preparing ESP contents..." +mkdir -p /staging/esp/EFI/BOOT +mkdir -p /staging/esp/boot/grub + +# Create GRUB EFI binary using grub-mkstandalone +grub-mkstandalone \ + --format=x86_64-efi \ + --output=/staging/esp/EFI/BOOT/BOOTX64.EFI \ + --locales="" \ + --fonts="" \ + "boot/grub/grub.cfg=/dev/null" + +# Find the kernel +KERNEL_PATH=$(find /kernel/boot -name "vmlinuz-*" | head -1) +KERNEL_VERSION=$(basename $KERNEL_PATH | sed 's/vmlinuz-//') +INITRD_PATH=$(find /kernel/boot -name "initrd.img-*" | head -1) + +# Copy kernel and initrd to staging +echo "Copying kernel to staging..." +cp $KERNEL_PATH /staging/esp/boot/vmlinuz +if [ -f "$INITRD_PATH" ]; then cp $INITRD_PATH /staging/esp/boot/initrd.img; fi + +# Create GRUB configuration +printf '%s\n' \ + 'set timeout=3' \ + 'set default=0' \ + '' \ + 'menuentry "SparkOS" {' \ + ' linux /boot/vmlinuz root=LABEL=SparkOS rw init=/sbin/init console=tty1 quiet' \ + '}' \ + > /staging/esp/boot/grub/grub.cfg + +# Prepare root filesystem contents +echo "Preparing root filesystem..." +mkdir -p /staging/root/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var/{log,run},root,home/spark,boot} + +# Install SparkOS init +cp /build/init /staging/root/sbin/init +chmod 755 /staging/root/sbin/init + +# Install busybox +echo "Installing busybox..." +cp /bin/busybox /staging/root/bin/busybox +chmod 755 /staging/root/bin/busybox + +# Create busybox symlinks for essential commands +for cmd in sh ls cat echo mount umount mkdir rm cp mv chmod chown ln ps kill; do + ln -sf busybox /staging/root/bin/$cmd +done + +# Create system configuration files +echo "sparkos" > /staging/root/etc/hostname +echo "127.0.0.1 localhost" > /staging/root/etc/hosts +echo "127.0.1.1 sparkos" >> /staging/root/etc/hosts +echo "root:x:0:0:root:/root:/bin/sh" > /staging/root/etc/passwd +echo "spark:x:1000:1000:SparkOS User:/home/spark:/bin/sh" >> /staging/root/etc/passwd +echo "root:x:0:" > /staging/root/etc/group +echo "spark:x:1000:" >> /staging/root/etc/group + +# Copy README to root partition +cp /build/config/image-readme.txt /staging/root/README.txt + +# Copy ESP contents to the image +echo "Populating EFI System Partition..." +guestfish -a /output/sparkos.img <<'EOF' +run +mount /dev/sda1 / +mkdir-p /EFI +mkdir-p /EFI/BOOT +mkdir-p /boot +mkdir-p /boot/grub +EOF + +# Copy files using guestfish +echo "Copying bootloader files..." +guestfish -a /output/sparkos.img -m /dev/sda1 < /mnt/esp/boot/grub/grub.cfg + > /staging/esp/boot/grub/grub.cfg -# Set up root filesystem -echo "Setting up root filesystem..." -mkdir -p /mnt/root/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var/{log,run},root,home/spark,boot} +# Prepare root filesystem contents +echo "Preparing root filesystem..." +mkdir -p /staging/root/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var/{log,run},root,home/spark,boot} # Install SparkOS init -cp /build/init /mnt/root/sbin/init -chmod 755 /mnt/root/sbin/init +cp /build/init /staging/root/sbin/init +chmod 755 /staging/root/sbin/init # Install busybox echo "Installing busybox..." -cp /bin/busybox /mnt/root/bin/busybox -chmod 755 /mnt/root/bin/busybox +cp /bin/busybox /staging/root/bin/busybox +chmod 755 /staging/root/bin/busybox # Create busybox symlinks for essential commands for cmd in sh ls cat echo mount umount mkdir rm cp mv chmod chown ln ps kill; do - ln -sf busybox /mnt/root/bin/$cmd + ln -sf busybox /staging/root/bin/$cmd done # Create system configuration files -echo "sparkos" > /mnt/root/etc/hostname -echo "127.0.0.1 localhost" > /mnt/root/etc/hosts -echo "127.0.1.1 sparkos" >> /mnt/root/etc/hosts -echo "root:x:0:0:root:/root:/bin/sh" > /mnt/root/etc/passwd -echo "spark:x:1000:1000:SparkOS User:/home/spark:/bin/sh" >> /mnt/root/etc/passwd -echo "root:x:0:" > /mnt/root/etc/group -echo "spark:x:1000:" >> /mnt/root/etc/group +echo "sparkos" > /staging/root/etc/hostname +echo "127.0.0.1 localhost" > /staging/root/etc/hosts +echo "127.0.1.1 sparkos" >> /staging/root/etc/hosts +echo "root:x:0:0:root:/root:/bin/sh" > /staging/root/etc/passwd +echo "spark:x:1000:1000:SparkOS User:/home/spark:/bin/sh" >> /staging/root/etc/passwd +echo "root:x:0:" > /staging/root/etc/group +echo "spark:x:1000:" >> /staging/root/etc/group # Copy README to root partition -cp /build/config/image-readme.txt /mnt/root/README.txt +cp /build/config/image-readme.txt /staging/root/README.txt -# Sync and unmount +# Create 1GB disk image +echo "Creating disk image..." +dd if=/dev/zero of=/output/sparkos.img bs=1M count=1024 + +# Create GPT partition table using sgdisk +echo "Creating GPT partition table..." +sgdisk -Z /output/sparkos.img 2>/dev/null || true +sgdisk -n 1:2048:411647 -t 1:ef00 -c 1:"EFI System" /output/sparkos.img +sgdisk -n 2:411648:0 -t 2:8300 -c 2:"Linux filesystem" /output/sparkos.img + +# Extract partition regions using dd +echo "Extracting partition regions..." +dd if=/output/sparkos.img of=/tmp/esp.img bs=512 skip=2048 count=409600 2>/dev/null +dd if=/output/sparkos.img of=/tmp/root.img bs=512 skip=411648 2>/dev/null + +# Format ESP partition (FAT32) +echo "Formatting EFI System Partition (FAT32)..." +mkfs.vfat -F 32 -n "SPARKOSEFI" /tmp/esp.img >/dev/null + +# Populate ESP using mtools (no mount needed!) +echo "Populating ESP with bootloader and kernel..." +export MTOOLS_SKIP_CHECK=1 +mmd -i /tmp/esp.img ::/EFI +mmd -i /tmp/esp.img ::/EFI/BOOT +mmd -i /tmp/esp.img ::/boot +mmd -i /tmp/esp.img ::/boot/grub +mcopy -i /tmp/esp.img /staging/esp/EFI/BOOT/BOOTX64.EFI ::/EFI/BOOT/ +mcopy -i /tmp/esp.img /staging/esp/boot/vmlinuz ::/boot/ +if [ -f "/staging/esp/boot/initrd.img" ]; then + mcopy -i /tmp/esp.img /staging/esp/boot/initrd.img ::/boot/ +fi +mcopy -i /tmp/esp.img /staging/esp/boot/grub/grub.cfg ::/boot/grub/ + +# Format root partition (ext4) with directory contents (no mount needed!) +echo "Formatting root partition (ext4) and populating..." +mke2fs -t ext4 -L "SparkOS" -d /staging/root /tmp/root.img >/dev/null 2>&1 + +# Write partitions back to image +echo "Writing partitions to image..." +dd if=/tmp/esp.img of=/output/sparkos.img bs=512 seek=2048 count=409600 conv=notrunc 2>/dev/null +dd if=/tmp/root.img of=/output/sparkos.img bs=512 seek=411648 conv=notrunc 2>/dev/null + +# Clean up temporary files +rm -f /tmp/esp.img /tmp/root.img + +# Finalize echo "Finalizing image..." sync -umount /mnt/esp -umount /mnt/root -losetup -d $LOOP_DEV # Compress the image echo "Compressing image..." diff --git a/scripts/docker-image-install-tools.sh b/scripts/docker-image-install-tools.sh index f58c18a..d73b93a 100755 --- a/scripts/docker-image-install-tools.sh +++ b/scripts/docker-image-install-tools.sh @@ -16,5 +16,6 @@ apt-get install -y \ grub-common \ wget \ busybox-static \ - kmod + kmod \ + udev rm -rf /var/lib/apt/lists/* From 37ebd5fdfacfcd6410600451500a46a14c248005 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:41:29 +0000 Subject: [PATCH 3/9] Fix kernel download script to get actual kernel package Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- scripts/docker-image-download-kernel.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/docker-image-download-kernel.sh b/scripts/docker-image-download-kernel.sh index c1a0310..29ea3f2 100755 --- a/scripts/docker-image-download-kernel.sh +++ b/scripts/docker-image-download-kernel.sh @@ -6,5 +6,11 @@ set -e mkdir -p /kernel apt-get update apt-get download linux-image-generic -dpkg -x linux-image-*.deb /kernel + +# The linux-image-generic is a metapackage, we need to get the actual kernel +KERNEL_VERSION=$(apt-cache depends linux-image-generic | grep Depends | grep linux-image | head -1 | awk '{print $2}') +apt-get download $KERNEL_VERSION + +# Extract the actual kernel package (not the metapackage) +dpkg -x ${KERNEL_VERSION}*.deb /kernel rm -rf /var/lib/apt/lists/* linux-image-*.deb From b784fef82bce43e1322b5be7a5a478d03e8e9396 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:42:42 +0000 Subject: [PATCH 4/9] Switch to building kernel from kernel.org instead of Ubuntu package Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- scripts/docker-image-download-kernel.sh | 71 +++++++++++++++++++++---- scripts/docker-image-install-tools.sh | 9 +++- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/scripts/docker-image-download-kernel.sh b/scripts/docker-image-download-kernel.sh index 29ea3f2..272842c 100755 --- a/scripts/docker-image-download-kernel.sh +++ b/scripts/docker-image-download-kernel.sh @@ -1,16 +1,67 @@ #!/bin/bash -# Download a minimal Linux kernel for UEFI image +# Download and build a minimal Linux kernel from kernel.org set -e -mkdir -p /kernel -apt-get update -apt-get download linux-image-generic +echo "=== Building minimal Linux kernel from kernel.org ===" -# The linux-image-generic is a metapackage, we need to get the actual kernel -KERNEL_VERSION=$(apt-cache depends linux-image-generic | grep Depends | grep linux-image | head -1 | awk '{print $2}') -apt-get download $KERNEL_VERSION +# Use a stable LTS kernel version +KERNEL_VERSION="6.6.68" +KERNEL_MAJOR=$(echo $KERNEL_VERSION | cut -d. -f1) -# Extract the actual kernel package (not the metapackage) -dpkg -x ${KERNEL_VERSION}*.deb /kernel -rm -rf /var/lib/apt/lists/* linux-image-*.deb +mkdir -p /kernel/build +cd /kernel/build + +# Download kernel source +echo "Downloading kernel ${KERNEL_VERSION}..." +wget -q --show-progress https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz + +# Extract +echo "Extracting kernel source..." +tar -xf linux-${KERNEL_VERSION}.tar.xz +cd linux-${KERNEL_VERSION} + +# Create minimal config for x86_64 UEFI boot +echo "Configuring kernel for minimal UEFI boot..." +make defconfig +make kvm_guest.config + +# Enable essential features for SparkOS +scripts/config --enable CONFIG_EFI +scripts/config --enable CONFIG_EFI_STUB +scripts/config --enable CONFIG_DEVTMPFS +scripts/config --enable CONFIG_DEVTMPFS_MOUNT +scripts/config --enable CONFIG_TMPFS +scripts/config --enable CONFIG_PROC_FS +scripts/config --enable CONFIG_SYSFS +scripts/config --enable CONFIG_EXT4_FS +scripts/config --enable CONFIG_VFAT_FS +scripts/config --enable CONFIG_NLS_CODEPAGE_437 +scripts/config --enable CONFIG_NLS_ISO8859_1 +scripts/config --enable CONFIG_TTY +scripts/config --enable CONFIG_SERIAL_8250 +scripts/config --enable CONFIG_SERIAL_8250_CONSOLE + +# Disable unnecessary features to speed up build +scripts/config --disable CONFIG_DEBUG_INFO +scripts/config --disable CONFIG_DEBUG_INFO_BTF +scripts/config --disable CONFIG_DEBUG_INFO_DWARF4 +scripts/config --disable CONFIG_DEBUG_INFO_DWARF5 +scripts/config --disable CONFIG_GDB_SCRIPTS +scripts/config --set-str CONFIG_LOCALVERSION "-sparkos" + +# Build kernel (use all available cores) +echo "Building kernel (this may take several minutes)..." +make -j$(nproc) bzImage + +# Copy kernel to expected location +echo "Installing kernel..." +mkdir -p /kernel/boot +cp arch/x86/boot/bzImage /kernel/boot/vmlinuz-${KERNEL_VERSION} + +# Clean up build directory to save space +cd / +rm -rf /kernel/build + +echo "Kernel build complete: /kernel/boot/vmlinuz-${KERNEL_VERSION}" +ls -lh /kernel/boot/ diff --git a/scripts/docker-image-install-tools.sh b/scripts/docker-image-install-tools.sh index d73b93a..2b4cf9f 100755 --- a/scripts/docker-image-install-tools.sh +++ b/scripts/docker-image-install-tools.sh @@ -17,5 +17,12 @@ apt-get install -y \ wget \ busybox-static \ kmod \ - udev + udev \ + bc \ + bison \ + flex \ + libelf-dev \ + libssl-dev \ + xz-utils \ + cpio rm -rf /var/lib/apt/lists/* From f9219615b3fc93fc6f8aa665ea468dbd4cfe40e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:44:37 +0000 Subject: [PATCH 5/9] Fix wget command to work in non-interactive Docker build Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- scripts/docker-image-download-kernel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker-image-download-kernel.sh b/scripts/docker-image-download-kernel.sh index 272842c..3088364 100755 --- a/scripts/docker-image-download-kernel.sh +++ b/scripts/docker-image-download-kernel.sh @@ -14,7 +14,7 @@ cd /kernel/build # Download kernel source echo "Downloading kernel ${KERNEL_VERSION}..." -wget -q --show-progress https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz +wget https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz # Extract echo "Extracting kernel source..." From 726d59d7659a8e5707cecea3f08715c27f3dbb43 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:45:41 +0000 Subject: [PATCH 6/9] Revert to Ubuntu kernel due to Docker network constraints, fix package detection Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- scripts/docker-image-download-kernel.sh | 84 +++++++++---------------- scripts/docker-image-install-tools.sh | 9 +-- 2 files changed, 31 insertions(+), 62 deletions(-) diff --git a/scripts/docker-image-download-kernel.sh b/scripts/docker-image-download-kernel.sh index 3088364..6dcd6ca 100755 --- a/scripts/docker-image-download-kernel.sh +++ b/scripts/docker-image-download-kernel.sh @@ -1,67 +1,43 @@ #!/bin/bash -# Download and build a minimal Linux kernel from kernel.org +# Download a minimal Linux kernel for UEFI image set -e -echo "=== Building minimal Linux kernel from kernel.org ===" +echo "=== Downloading Linux kernel from Ubuntu repositories ===" -# Use a stable LTS kernel version -KERNEL_VERSION="6.6.68" -KERNEL_MAJOR=$(echo $KERNEL_VERSION | cut -d. -f1) +mkdir -p /kernel +apt-get update -mkdir -p /kernel/build -cd /kernel/build +# Get the actual kernel package name (not the metapackage) +echo "Finding latest kernel package..." +KERNEL_PKG=$(apt-cache depends linux-image-generic | grep -E 'Depends.*linux-image-[0-9]' | head -1 | awk '{print $2}') -# Download kernel source -echo "Downloading kernel ${KERNEL_VERSION}..." -wget https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz +if [ -z "$KERNEL_PKG" ]; then + echo "ERROR: Could not determine kernel package name" + exit 1 +fi -# Extract -echo "Extracting kernel source..." -tar -xf linux-${KERNEL_VERSION}.tar.xz -cd linux-${KERNEL_VERSION} +echo "Downloading kernel package: $KERNEL_PKG" +apt-get download "$KERNEL_PKG" -# Create minimal config for x86_64 UEFI boot -echo "Configuring kernel for minimal UEFI boot..." -make defconfig -make kvm_guest.config +# Extract the kernel package +echo "Extracting kernel..." +dpkg -x ${KERNEL_PKG}*.deb /kernel -# Enable essential features for SparkOS -scripts/config --enable CONFIG_EFI -scripts/config --enable CONFIG_EFI_STUB -scripts/config --enable CONFIG_DEVTMPFS -scripts/config --enable CONFIG_DEVTMPFS_MOUNT -scripts/config --enable CONFIG_TMPFS -scripts/config --enable CONFIG_PROC_FS -scripts/config --enable CONFIG_SYSFS -scripts/config --enable CONFIG_EXT4_FS -scripts/config --enable CONFIG_VFAT_FS -scripts/config --enable CONFIG_NLS_CODEPAGE_437 -scripts/config --enable CONFIG_NLS_ISO8859_1 -scripts/config --enable CONFIG_TTY -scripts/config --enable CONFIG_SERIAL_8250 -scripts/config --enable CONFIG_SERIAL_8250_CONSOLE +# Verify kernel was extracted +if [ ! -d /kernel/boot ]; then + echo "ERROR: Kernel boot directory not found after extraction" + exit 1 +fi -# Disable unnecessary features to speed up build -scripts/config --disable CONFIG_DEBUG_INFO -scripts/config --disable CONFIG_DEBUG_INFO_BTF -scripts/config --disable CONFIG_DEBUG_INFO_DWARF4 -scripts/config --disable CONFIG_DEBUG_INFO_DWARF5 -scripts/config --disable CONFIG_GDB_SCRIPTS -scripts/config --set-str CONFIG_LOCALVERSION "-sparkos" +KERNEL_FILE=$(find /kernel/boot -name "vmlinuz-*" | head -1) +if [ -z "$KERNEL_FILE" ]; then + echo "ERROR: No kernel image found" + exit 1 +fi -# Build kernel (use all available cores) -echo "Building kernel (this may take several minutes)..." -make -j$(nproc) bzImage - -# Copy kernel to expected location -echo "Installing kernel..." -mkdir -p /kernel/boot -cp arch/x86/boot/bzImage /kernel/boot/vmlinuz-${KERNEL_VERSION} - -# Clean up build directory to save space -cd / -rm -rf /kernel/build - -echo "Kernel build complete: /kernel/boot/vmlinuz-${KERNEL_VERSION}" +echo "Kernel extracted successfully: $KERNEL_FILE" ls -lh /kernel/boot/ + +# Clean up +rm -rf /var/lib/apt/lists/* ${KERNEL_PKG}*.deb diff --git a/scripts/docker-image-install-tools.sh b/scripts/docker-image-install-tools.sh index 2b4cf9f..d73b93a 100755 --- a/scripts/docker-image-install-tools.sh +++ b/scripts/docker-image-install-tools.sh @@ -17,12 +17,5 @@ apt-get install -y \ wget \ busybox-static \ kmod \ - udev \ - bc \ - bison \ - flex \ - libelf-dev \ - libssl-dev \ - xz-utils \ - cpio + udev rm -rf /var/lib/apt/lists/* From f74390d632da82795ceb8ffdafe674f361be13aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:49:52 +0000 Subject: [PATCH 7/9] Implement immutable base OS with overlay filesystem - Fix ext4 partition size calculation - Mount root filesystem as read-only - Add overlayfs on /var for writable runtime data - Add tmpfs on /run for runtime files - Update GRUB config to mount root as ro - Update init to inform users about immutability Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- scripts/docker-image-create-uefi.sh | 17 ++++++++++++----- src/init.c | 26 +++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/scripts/docker-image-create-uefi.sh b/scripts/docker-image-create-uefi.sh index d3f2322..c64185b 100755 --- a/scripts/docker-image-create-uefi.sh +++ b/scripts/docker-image-create-uefi.sh @@ -31,13 +31,13 @@ echo "Copying kernel to staging..." cp $KERNEL_PATH /staging/esp/boot/vmlinuz if [ -f "$INITRD_PATH" ]; then cp $INITRD_PATH /staging/esp/boot/initrd.img; fi -# Create GRUB configuration +# Create GRUB configuration for immutable root with overlay printf '%s\n' \ 'set timeout=3' \ 'set default=0' \ '' \ - 'menuentry "SparkOS" {' \ - ' linux /boot/vmlinuz root=LABEL=SparkOS rw init=/sbin/init console=tty1 quiet' \ + 'menuentry "SparkOS (Immutable Base + Overlay)" {' \ + ' linux /boot/vmlinuz root=LABEL=SparkOS ro init=/sbin/init console=tty1 quiet' \ '}' \ > /staging/esp/boot/grub/grub.cfg @@ -84,7 +84,14 @@ sgdisk -n 2:411648:0 -t 2:8300 -c 2:"Linux filesystem" /output/sparkos.img # Extract partition regions using dd echo "Extracting partition regions..." dd if=/output/sparkos.img of=/tmp/esp.img bs=512 skip=2048 count=409600 2>/dev/null -dd if=/output/sparkos.img of=/tmp/root.img bs=512 skip=411648 2>/dev/null + +# Calculate exact size for root partition +ROOT_START=411648 +ROOT_END=$(sgdisk -p /output/sparkos.img | grep "^ 2" | awk '{print $3}') +ROOT_SIZE=$((ROOT_END - ROOT_START + 1)) +echo "Root partition: start=$ROOT_START, end=$ROOT_END, size=$ROOT_SIZE sectors" + +dd if=/output/sparkos.img of=/tmp/root.img bs=512 skip=$ROOT_START count=$ROOT_SIZE 2>/dev/null # Format ESP partition (FAT32) echo "Formatting EFI System Partition (FAT32)..." @@ -111,7 +118,7 @@ mke2fs -t ext4 -L "SparkOS" -d /staging/root /tmp/root.img >/dev/null 2>&1 # Write partitions back to image echo "Writing partitions to image..." dd if=/tmp/esp.img of=/output/sparkos.img bs=512 seek=2048 count=409600 conv=notrunc 2>/dev/null -dd if=/tmp/root.img of=/output/sparkos.img bs=512 seek=411648 conv=notrunc 2>/dev/null +dd if=/tmp/root.img of=/output/sparkos.img bs=512 seek=$ROOT_START count=$ROOT_SIZE conv=notrunc 2>/dev/null # Clean up temporary files rm -f /tmp/esp.img /tmp/root.img diff --git a/src/init.c b/src/init.c index fb957fb..b002f28 100644 --- a/src/init.c +++ b/src/init.c @@ -100,6 +100,28 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Warning: Failed to mount /tmp\n"); } + // Set up overlay filesystem for immutable base OS + printf("Setting up overlay filesystem for writable layer...\n"); + + // Create overlay directories in tmpfs + if (system("mkdir -p /tmp/overlay/upper /tmp/overlay/work 2>/dev/null") != 0) { + fprintf(stderr, "Warning: Failed to create overlay directories\n"); + } + + // Mount overlay on /var for logs and runtime data + if (system("mount -t overlay overlay -o lowerdir=/var,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work /var 2>/dev/null") != 0) { + fprintf(stderr, "Warning: Failed to mount overlay on /var - system may be read-only\n"); + } else { + printf("Overlay filesystem mounted on /var (base OS is immutable)\n"); + } + + // Mount tmpfs on /run for runtime data + if (system("mkdir -p /run 2>/dev/null") == 0) { + if (system("mount -t tmpfs tmpfs /run 2>/dev/null") != 0) { + fprintf(stderr, "Warning: Failed to mount /run\n"); + } + } + // Initialize network (wired only for bootstrap) printf("Initializing wired network...\n"); if (system("/sbin/init-network 2>/dev/null") != 0) { @@ -108,7 +130,9 @@ int main(int argc, char *argv[]) { printf("Starting shell...\n"); printf("Welcome to SparkOS!\n"); - printf("===================\n\n"); + printf("===================\n"); + printf("Base OS: Read-only (immutable)\n"); + printf("Writable: /tmp, /var (overlay), /run\n\n"); // Main loop - keep respawning shell while (1) { From f5dceb8aeeaef0b189d7730e3c9770863561a175 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:50:55 +0000 Subject: [PATCH 8/9] Fix sgdisk partition table parsing Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- scripts/docker-image-create-uefi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker-image-create-uefi.sh b/scripts/docker-image-create-uefi.sh index c64185b..b5b8c83 100755 --- a/scripts/docker-image-create-uefi.sh +++ b/scripts/docker-image-create-uefi.sh @@ -87,7 +87,7 @@ dd if=/output/sparkos.img of=/tmp/esp.img bs=512 skip=2048 count=409600 2>/dev/n # Calculate exact size for root partition ROOT_START=411648 -ROOT_END=$(sgdisk -p /output/sparkos.img | grep "^ 2" | awk '{print $3}') +ROOT_END=$(sgdisk -p /output/sparkos.img 2>/dev/null | grep "^ 2" | awk '{print $3}') ROOT_SIZE=$((ROOT_END - ROOT_START + 1)) echo "Root partition: start=$ROOT_START, end=$ROOT_END, size=$ROOT_SIZE sectors" From 927163d8eb178c821a39448f8f0cc525e7e15936 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:58:25 +0000 Subject: [PATCH 9/9] Add overlay filesystem support for spark user home directory - Create separate overlay directories for /var and /home/spark - Mount overlay on /home/spark for writable user data - Update welcome message to show /home/spark is writable - Ensures user files persist across sessions in tmpfs overlay Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- src/init.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/init.c b/src/init.c index b002f28..cbf5d68 100644 --- a/src/init.c +++ b/src/init.c @@ -104,17 +104,27 @@ int main(int argc, char *argv[]) { printf("Setting up overlay filesystem for writable layer...\n"); // Create overlay directories in tmpfs - if (system("mkdir -p /tmp/overlay/upper /tmp/overlay/work 2>/dev/null") != 0) { - fprintf(stderr, "Warning: Failed to create overlay directories\n"); + if (system("mkdir -p /tmp/overlay/var-upper /tmp/overlay/var-work 2>/dev/null") != 0) { + fprintf(stderr, "Warning: Failed to create overlay directories for /var\n"); + } + if (system("mkdir -p /tmp/overlay/home-upper /tmp/overlay/home-work 2>/dev/null") != 0) { + fprintf(stderr, "Warning: Failed to create overlay directories for /home/spark\n"); } // Mount overlay on /var for logs and runtime data - if (system("mount -t overlay overlay -o lowerdir=/var,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work /var 2>/dev/null") != 0) { + if (system("mount -t overlay overlay -o lowerdir=/var,upperdir=/tmp/overlay/var-upper,workdir=/tmp/overlay/var-work /var 2>/dev/null") != 0) { fprintf(stderr, "Warning: Failed to mount overlay on /var - system may be read-only\n"); } else { printf("Overlay filesystem mounted on /var (base OS is immutable)\n"); } + // Mount overlay on /home/spark for user data + if (system("mount -t overlay overlay -o lowerdir=/home/spark,upperdir=/tmp/overlay/home-upper,workdir=/tmp/overlay/home-work /home/spark 2>/dev/null") != 0) { + fprintf(stderr, "Warning: Failed to mount overlay on /home/spark - home directory may be read-only\n"); + } else { + printf("Overlay filesystem mounted on /home/spark (writable user home)\n"); + } + // Mount tmpfs on /run for runtime data if (system("mkdir -p /run 2>/dev/null") == 0) { if (system("mount -t tmpfs tmpfs /run 2>/dev/null") != 0) { @@ -132,7 +142,7 @@ int main(int argc, char *argv[]) { printf("Welcome to SparkOS!\n"); printf("===================\n"); printf("Base OS: Read-only (immutable)\n"); - printf("Writable: /tmp, /var (overlay), /run\n\n"); + printf("Writable: /tmp, /var (overlay), /home/spark (overlay), /run\n\n"); // Main loop - keep respawning shell while (1) {