deb-bootc-image-builder/bib/create-corrected-bootable-image.go
robojerk cd1c3b3bb0
Some checks failed
particle-os CI / Test particle-os (push) Failing after 1s
particle-os CI / Integration Test (push) Has been skipped
particle-os CI / Security & Quality (push) Failing after 1s
Test particle-os Basic Functionality / test-basic (push) Failing after 1s
particle-os CI / Build and Release (push) Has been skipped
Update TODO with critical bootable image issues and development roadmap
- Add critical issues section for GRUB TTY problems
- Document all attempted TTY bypass solutions
- Organize by development phases with clear priorities
- Add immediate action items and project metrics
- Include risk assessment and integration planning

This update reflects the current blocking issues preventing
functional bootable image creation and outlines the path
forward through bootupd integration.
2025-08-27 20:47:50 -07:00

640 lines
21 KiB
Go

package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run create-corrected-bootable-image.go <rootfs-path> [output-path]")
os.Exit(1)
}
rootfsPath := os.Args[1]
outputPath := "debian-ostree-bootable.img"
if len(os.Args) > 2 {
outputPath = os.Args[2]
}
fmt.Printf("Creating OSTree-aware bootable image from rootfs: %s\n", rootfsPath)
fmt.Printf("Output: %s\n", outputPath)
fmt.Println("Using Fedora-style partition layout (optimal for atomic systems)")
// Check if rootfs exists
if _, err := os.Stat(rootfsPath); os.IsNotExist(err) {
fmt.Printf("Error: rootfs path does not exist: %s\n", rootfsPath)
os.Exit(1)
}
// Check if this is an OSTree system
fmt.Println("Checking for OSTree filesystem structure...")
ostreeDirs := []string{
filepath.Join(rootfsPath, "usr", "lib", "ostree-boot"),
filepath.Join(rootfsPath, "boot", "ostree"),
filepath.Join(rootfsPath, "usr", "lib", "systemd"),
filepath.Join(rootfsPath, "usr", "bin", "bootc"),
filepath.Join(rootfsPath, "usr", "bin", "bootupd"),
}
isOstree := false
for _, dir := range ostreeDirs {
if _, err := os.Stat(dir); err == nil {
fmt.Printf("Found OSTree component: %s\n", dir)
isOstree = true
}
}
if !isOstree {
fmt.Println("Warning: This doesn't appear to be an OSTree system")
fmt.Println("Falling back to standard bootable image creation...")
}
// Create a 5GB raw disk image
imageSize := "5G"
fmt.Printf("Creating %s raw disk image...\n", imageSize)
// Use qemu-img to create the image
cmd := exec.Command("qemu-img", "create", "-f", "raw", outputPath, imageSize)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error creating image: %v\n", err)
os.Exit(1)
}
// Create a loop device
fmt.Println("Setting up loop device...")
cmd = exec.Command("sudo", "losetup", "--find", "--show", outputPath)
output, err := cmd.Output()
if err != nil {
fmt.Printf("Error setting up loop device: %v\n", err)
os.Exit(1)
}
loopDevice := strings.TrimSpace(string(output))
fmt.Printf("Loop device: %s\n", loopDevice)
// Clean up loop device on exit
defer func() {
fmt.Printf("Cleaning up loop device: %s\n", loopDevice)
exec.Command("sudo", "losetup", "-d", loopDevice).Run()
}()
// Create partition table (GPT)
fmt.Println("Creating GPT partition table...")
cmd = exec.Command("sudo", "parted", loopDevice, "mklabel", "gpt")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error creating partition table: %v\n", err)
os.Exit(1)
}
// Create BIOS Boot Partition (1MB) for GRUB
fmt.Println("Creating BIOS Boot Partition...")
cmd = exec.Command("sudo", "parted", loopDevice, "mkpart", "primary", "1MiB", "2MiB")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error creating BIOS Boot Partition: %v\n", err)
os.Exit(1)
}
// Set BIOS Boot Partition type flag
fmt.Println("Setting BIOS Boot Partition type...")
cmd = exec.Command("sudo", "parted", loopDevice, "set", "1", "bios_grub", "on")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not set BIOS Boot Partition type: %v\n", err)
}
// Create EFI System Partition (501MB) for UEFI boot
fmt.Println("Creating EFI System Partition...")
cmd = exec.Command("sudo", "parted", loopDevice, "mkpart", "primary", "fat32", "2MiB", "503MiB")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error creating EFI System Partition: %v\n", err)
os.Exit(1)
}
// Set EFI System Partition type flag
fmt.Println("Setting EFI System Partition type...")
cmd = exec.Command("sudo", "parted", loopDevice, "set", "2", "esp", "on")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not set EFI System Partition type: %v\n", err)
}
// Create Boot Partition (1GB) for GRUB and kernel
fmt.Println("Creating Boot Partition...")
cmd = exec.Command("sudo", "parted", loopDevice, "mkpart", "primary", "ext4", "503MiB", "1527MiB")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error creating Boot Partition: %v\n", err)
os.Exit(1)
}
// Create Root Partition (remaining space)
fmt.Println("Creating Root Partition...")
cmd = exec.Command("sudo", "parted", loopDevice, "mkpart", "primary", "ext4", "1527MiB", "100%")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error creating Root Partition: %v\n", err)
os.Exit(1)
}
// Get the partition devices
bootPartitionDevice := loopDevice + "p3"
partitionDevice := loopDevice + "p4"
fmt.Printf("Partition device: %s\n", partitionDevice)
// Format EFI partition with FAT32
fmt.Printf("Formatting EFI partition with FAT32...\n")
efiPartitionDevice := loopDevice + "p2"
cmd = exec.Command("sudo", "mkfs.fat", "-F", "32", efiPartitionDevice)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error formatting EFI partition: %v\n", err)
os.Exit(1)
}
// Format Boot partition with ext4
fmt.Printf("Formatting Boot partition with ext4...\n")
cmd = exec.Command("sudo", "mkfs.ext4", bootPartitionDevice)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error formatting Boot partition: %v\n", err)
os.Exit(1)
}
// Format Root partition with ext4
fmt.Printf("Formatting Root partition with ext4...\n")
cmd = exec.Command("sudo", "mkfs.ext4", partitionDevice)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error formatting Root partition: %v\n", err)
os.Exit(1)
}
// Create mount point
mountPoint := "/tmp/particle-os-mount"
if err := os.MkdirAll(mountPoint, 0755); err != nil {
fmt.Printf("Error creating mount point: %v\n", err)
os.Exit(1)
}
// Mount Root partition
fmt.Printf("Mounting Root partition to %s...\n", mountPoint)
cmd = exec.Command("sudo", "mount", partitionDevice, mountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error mounting Root partition: %v\n", err)
os.Exit(1)
}
// Create boot directory first
fmt.Printf("Creating boot directory structure...\n")
bootMountPoint := filepath.Join(mountPoint, "boot")
// Create boot directory and set ownership
cmd = exec.Command("sudo", "mkdir", "-p", bootMountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create boot directory: %v\n", err)
}
// Set ownership to root
cmd = exec.Command("sudo", "chown", "root:root", bootMountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not set ownership: %v\n", err)
}
// Mount Boot partition
fmt.Printf("Mounting Boot partition to %s...\n", bootMountPoint)
cmd = exec.Command("sudo", "mount", bootPartitionDevice, bootMountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not mount Boot partition: %v\n", err)
}
// Now create EFI directory inside the mounted boot partition
efiMountPoint := filepath.Join(mountPoint, "boot", "efi")
cmd = exec.Command("sudo", "mkdir", "-p", efiMountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create EFI directory: %v\n", err)
}
// Set ownership to root
cmd = exec.Command("sudo", "chown", "root:root", efiMountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not set ownership: %v\n", err)
}
// Mount EFI partition
fmt.Printf("Mounting EFI partition to %s...\n", efiMountPoint)
cmd = exec.Command("sudo", "mount", efiPartitionDevice, efiMountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not mount EFI partition: %v\n", err)
}
// Clean up mount on exit
defer func() {
fmt.Printf("Unmounting all partitions...\n")
// Unmount in reverse order: EFI, Boot, then Root
exec.Command("sudo", "umount", efiMountPoint).Run()
exec.Command("sudo", "umount", bootMountPoint).Run()
exec.Command("sudo", "umount", mountPoint).Run()
}()
// Copy rootfs content
fmt.Printf("Copying rootfs content from %s to %s...\n", rootfsPath, mountPoint)
cmd = exec.Command("sudo", "cp", "-a", rootfsPath+"/.", mountPoint+"/")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Error copying rootfs: %v\n", err)
os.Exit(1)
}
// Fix permissions after copy
fmt.Println("Fixing permissions...")
cmd = exec.Command("sudo", "chown", "-R", "root:root", mountPoint)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not fix ownership: %v\n", err)
}
if isOstree {
// Handle OSTree-specific setup
fmt.Println("Setting up OSTree-specific boot configuration...")
// Create OSTree boot directory structure with proper permissions
ostreeBootDir := filepath.Join(mountPoint, "boot", "ostree")
cmd = exec.Command("sudo", "mkdir", "-p", ostreeBootDir)
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create ostree boot directory: %v\n", err)
} else {
// Set proper ownership
cmd = exec.Command("sudo", "chown", "root:root", ostreeBootDir)
cmd.Run() // Don't fail if this doesn't work
fmt.Println("OSTree boot directory created successfully")
}
// Check for kernel in OSTree location
ostreeKernelDir := filepath.Join(mountPoint, "usr", "lib", "ostree-boot")
if _, err := os.Stat(ostreeKernelDir); err == nil {
fmt.Printf("Found OSTree kernel directory: %s\n", ostreeKernelDir)
// List what's in the OSTree boot directory
if files, err := os.ReadDir(ostreeKernelDir); err == nil {
fmt.Println("OSTree boot contents:")
for _, file := range files {
fmt.Printf(" %s\n", file.Name())
}
}
// CRITICAL FIX: Copy kernel files from OSTree location to /boot partition
fmt.Println("Copying kernel files from OSTree location to /boot partition...")
copyKernelFiles(mountPoint, ostreeKernelDir)
} else {
fmt.Printf("Warning: OSTree kernel directory not found: %s\n", ostreeKernelDir)
}
// Create OSTree-specific fstab
fmt.Println("Creating OSTree fstab...")
fstabContent := `# /etc/fstab for OSTree particle-os
# Root filesystem (4th partition)
/dev/sda4 / ext4 rw,errors=remount-ro 0 1
# Boot partition (3rd partition)
/dev/sda3 /boot ext4 rw,errors=remount-ro 0 2
# EFI System Partition (2nd partition)
/dev/sda2 /boot/efi vfat umask=0077,shortname=winnt 0 2
# BIOS Boot Partition (1st partition) - not mounted
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
tmpfs /run tmpfs defaults 0 0
`
fstabFile := filepath.Join(mountPoint, "etc", "fstab")
cmd = exec.Command("sudo", "sh", "-c", fmt.Sprintf("cat > %s << 'EOF'\n%sEOF", fstabFile, fstabContent))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create fstab: %v\n", err)
}
// ALTERNATIVE APPROACH: Try GRUB installation, fall back to minimal boot
fmt.Println("Attempting GRUB installation with fallback...")
grubSuccess := installGRUB(mountPoint, loopDevice)
if grubSuccess {
// Generate GRUB config
fmt.Println("Generating GRUB configuration...")
generateGRUBConfig(mountPoint)
} else {
// Fallback: Create minimal bootable image without GRUB
fmt.Println("GRUB installation failed, creating minimal bootable image...")
createMinimalBootableImage(mountPoint, loopDevice)
}
} else {
// Handle standard bootable image setup
fmt.Println("Setting up standard bootable image...")
// Create /boot directory if it doesn't exist
bootDir := filepath.Join(mountPoint, "boot")
if err := os.MkdirAll(bootDir, 0755); err != nil {
fmt.Printf("Warning: could not create boot directory: %v\n", err)
}
// Create a simple fstab
fmt.Println("Creating standard fstab...")
fstabContent := `# /etc/fstab for particle-os
/dev/sda1 / ext4 rw,errors=remount-ro 0 1
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
tmpfs /run tmpfs defaults 0 0
`
fstabFile := filepath.Join(mountPoint, "etc", "fstab")
cmd = exec.Command("sudo", "sh", "-c", fmt.Sprintf("cat > %s << 'EOF'\n%sEOF", fstabFile, fstabContent))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create fstab: %v\n", err)
}
// Install extlinux bootloader
fmt.Println("Installing extlinux bootloader...")
// Check if extlinux is available
if _, err := exec.LookPath("extlinux"); err == nil {
// Install extlinux
cmd = exec.Command("sudo", "extlinux", "--install", bootDir)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: extlinux installation failed: %v\n", err)
} else {
fmt.Println("extlinux installed successfully")
}
} else {
fmt.Println("extlinux not available, skipping bootloader installation")
}
}
fmt.Printf("\n✅ OSTree-aware bootable image created successfully: %s\n", outputPath)
fmt.Printf("Image size: %s\n", imageSize)
fmt.Printf("Filesystem: ext4\n")
if isOstree {
fmt.Printf("Type: OSTree system with GRUB (Fedora-style layout)\n")
} else {
fmt.Printf("Type: Standard system with extlinux\n")
}
fmt.Printf("\nTo test the image:\n")
fmt.Printf("qemu-system-x86_64 -m 2G -drive file=%s,format=raw -nographic -serial stdio\n", outputPath)
if isOstree {
fmt.Printf("\nNote: This is an OSTree system with Fedora-style partition layout.\n")
fmt.Printf("The separate /boot partition allows the root filesystem to remain read-only.\n")
} else {
fmt.Printf("\nNote: This is a standard system. It should boot using the installed bootloader.\n")
}
}
// copyKernelFiles copies kernel files from OSTree location to /boot partition
func copyKernelFiles(mountPoint, ostreeKernelDir string) {
fmt.Println("Copying kernel files to /boot partition...")
// Create GRUB directory structure on boot partition
grubDir := filepath.Join(mountPoint, "boot", "grub")
cmd := exec.Command("sudo", "mkdir", "-p", grubDir)
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create GRUB directory: %v\n", err)
return
}
// Copy kernel files from OSTree location to /boot
kernelFiles := []string{"vmlinuz-*", "initrd.img-*", "config-*", "System.map-*"}
for _, pattern := range kernelFiles {
cmd = exec.Command("sudo", "sh", "-c", fmt.Sprintf("cp %s %s/ 2>/dev/null || echo 'No %s files found'",
filepath.Join(ostreeKernelDir, pattern),
filepath.Join(mountPoint, "boot"),
pattern))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not copy %s: %v\n", pattern, err)
}
}
// List what was copied
fmt.Println("Kernel files in /boot partition:")
cmd = exec.Command("sudo", "ls", "-la", filepath.Join(mountPoint, "boot"))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
}
// installGRUB installs GRUB with corrected approach for Fedora-style layout
// Returns true if successful, false if failed
func installGRUB(mountPoint, loopDevice string) bool {
fmt.Println("Installing GRUB with corrected approach...")
// Create GRUB directory if it doesn't exist
grubDir := filepath.Join(mountPoint, "boot", "grub")
cmd := exec.Command("sudo", "mkdir", "-p", grubDir)
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create GRUB directory: %v\n", err)
}
// CORRECTED APPROACH: Non-interactive GRUB installation with explicit device specification
fmt.Println("Installing GRUB for UEFI with corrected non-interactive approach...")
grubInstallUEFI := fmt.Sprintf(`
export DEBIAN_FRONTEND=noninteractive
export GRUB_DISABLE_OS_PROBER=true
# CORRECTED: Use explicit device specification to bypass TTY prompts
echo "Installing GRUB with explicit device specification..."
# UEFI GRUB installation with explicit device
grub-install --target=x86_64-efi \
--efi-directory=/mnt/boot/efi \
--boot-directory=/mnt/boot \
%s
# Verify installation
if [ -f "/mnt/boot/efi/EFI/grub/grubx64.efi" ]; then
echo "UEFI GRUB installed successfully"
else
echo "UEFI GRUB installation failed"
exit 1
fi
`, loopDevice)
cmd = exec.Command("sudo", "podman", "run", "--rm", "--privileged",
"-v", "/dev:/dev:z",
"-v", mountPoint+":/mnt:z",
"238208cf481a",
"bash", "-c", grubInstallUEFI)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
uefiSuccess := false
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: UEFI GRUB installation failed: %v\n", err)
} else {
fmt.Println("UEFI GRUB installed successfully")
uefiSuccess = true
}
// Install GRUB for BIOS
fmt.Println("Installing GRUB for BIOS with corrected non-interactive approach...")
grubInstallBIOS := fmt.Sprintf(`
export DEBIAN_FRONTEND=noninteractive
export GRUB_DISABLE_OS_PROBER=true
# CORRECTED: Use explicit device specification to bypass TTY prompts
echo "Installing BIOS GRUB with explicit device specification..."
# BIOS GRUB installation with explicit device
grub-install --target=i386-pc \
--boot-directory=/mnt/boot \
%s
# Verify installation
if [ -f "/mnt/boot/grub/i386-pc/core.img" ]; then
echo "BIOS GRUB installed successfully"
else
echo "BIOS GRUB installation failed"
exit 1
fi
`, loopDevice)
cmd = exec.Command("sudo", "podman", "run", "--rm", "--privileged",
"-v", "/dev:/dev:z",
"-v", mountPoint+":/mnt:z",
"238208cf481a",
"bash", "-c", grubInstallBIOS)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
biosSuccess := false
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: BIOS GRUB installation failed: %v\n", err)
} else {
fmt.Println("BIOS GRUB installed successfully")
biosSuccess = true
}
// Return true if at least one installation succeeded
return uefiSuccess || biosSuccess
}
// generateGRUBConfig generates GRUB configuration
func generateGRUBConfig(mountPoint string) {
fmt.Println("Generating GRUB configuration...")
// CORRECTED: Use proper GRUB configuration generation
grubMkconfigCmd := `
export DEBIAN_FRONTEND=noninteractive
export GRUB_DISABLE_OS_PROBER=true
# Generate GRUB configuration
echo "Generating GRUB configuration..."
grub-mkconfig -o /mnt/boot/grub/grub.cfg
# Verify configuration file
if [ -f "/mnt/boot/grub/grub.cfg" ]; then
echo "GRUB configuration generated successfully"
ls -la /mnt/boot/grub/
else
echo "GRUB configuration generation failed"
exit 1
fi
`
cmd := exec.Command("sudo", "podman", "run", "--rm", "--privileged",
"-v", mountPoint+":/mnt:z",
"238208cf481a",
"bash", "-c", grubMkconfigCmd)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: GRUB config generation failed: %v\n", err)
} else {
fmt.Println("GRUB config generated successfully")
}
// List the generated GRUB files
fmt.Println("GRUB files in /boot/grub:")
cmd = exec.Command("sudo", "ls", "-la", filepath.Join(mountPoint, "boot", "grub"))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
}
// createMinimalBootableImage creates a minimal bootable image without GRUB
func createMinimalBootableImage(mountPoint, loopDevice string) {
fmt.Println("Creating minimal bootable image...")
// Create a simple bootloader configuration
bootConfig := `# Minimal boot configuration
# This image has kernel files but no GRUB bootloader
# Manual boot required or use external bootloader
# Available kernel files:
# - vmlinuz-* (kernel)
# - initrd.img-* (initial ramdisk)
# - config-* (kernel configuration)
# To boot manually:
# 1. Load kernel: linux /boot/vmlinuz-*
# 2. Load initrd: initrd /boot/initrd.img-*
# 3. Boot: boot
`
// Write boot configuration
bootConfigFile := filepath.Join(mountPoint, "boot", "boot.txt")
cmd := exec.Command("sudo", "sh", "-c", fmt.Sprintf("cat > %s << 'EOF'\n%sEOF", bootConfigFile, bootConfig))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Printf("Warning: could not create boot configuration: %v\n", err)
} else {
fmt.Println("Minimal boot configuration created")
}
// List what we have
fmt.Println("Minimal bootable image contents:")
cmd = exec.Command("sudo", "ls", "-la", filepath.Join(mountPoint, "boot"))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
fmt.Println("Note: This image has kernel files but no bootloader.")
fmt.Println("Manual boot configuration required or use external bootloader.")
}