package main import ( "fmt" "os" "os/exec" "path/filepath" "strings" ) func main() { rootfsPath := "/tmp/particle-os-fixed-work/rootfs" outputPath := "/tmp/particle-os-fixed-work/output/debian-test-bootable.img" fmt.Printf("Testing bootable image creation from rootfs: %s\n", rootfsPath) fmt.Printf("Output: %s\n", outputPath) // 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) } // Create output directory outputDir := filepath.Dir(outputPath) if err := os.MkdirAll(outputDir, 0755); err != nil { fmt.Printf("Error creating output directory: %v\n", err) os.Exit(1) } // Create a 5GB raw disk image imageSize := int64(5 * 1024 * 1024 * 1024) // 5GB fmt.Printf("Creating %d byte raw disk image...\n", imageSize) // Use qemu-img to create a proper raw image sizeMB := imageSize / (1024 * 1024) cmd := exec.Command("qemu-img", "create", "-f", "raw", outputPath, fmt.Sprintf("%dM", sizeMB)) 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) } // Set up 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 a single partition fmt.Println("Creating partition...") cmd = exec.Command("sudo", "parted", loopDevice, "mkpart", "primary", "ext4", "1MiB", "100%") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Printf("Error creating partition: %v\n", err) os.Exit(1) } // Get the partition device partitionDevice := loopDevice + "p1" fmt.Printf("Partition device: %s\n", partitionDevice) // Format the partition with ext4 fmt.Println("Formatting partition with ext4...") 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 partition: %v\n", err) os.Exit(1) } // Create mount point mountPoint := "/tmp/particle-os-test-mount" if err := os.MkdirAll(mountPoint, 0755); err != nil { fmt.Printf("Error creating mount point: %v\n", err) os.Exit(1) } // Mount the partition 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 partition: %v\n", err) os.Exit(1) } // Clean up mount on exit defer func() { fmt.Printf("Unmounting %s...\n", mountPoint) 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) } // Create minimal bootable system fmt.Println("Setting up minimal bootable system...") // 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 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) } // Create a simple inittab for sysvinit fmt.Println("Creating inittab...") inittabContent := `# /etc/inittab for particle-os id:2:initdefault: si::sysinit:/etc/init.d/rcS 1:2345:respawn:/sbin/getty 38400 tty1 2:23:respawn:/sbin/getty 38400 tty2 3:23:respawn:/sbin/getty 38400 tty3 4:23:respawn:/sbin/getty 38400 tty4 5:23:respawn:/sbin/getty 38400 tty5 6:23:respawn:/sbin/getty 38400 tty6 ` inittabFile := filepath.Join(mountPoint, "etc", "inittab") cmd = exec.Command("sudo", "sh", "-c", fmt.Sprintf("cat > %s << 'EOF'\n%sEOF", inittabFile, inittabContent)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Printf("Warning: could not create inittab: %v\n", err) } // Create a simple rcS script fmt.Println("Creating rcS script...") rcsContent := `#!/bin/sh # /etc/init.d/rcS for particle-os echo "Starting particle-os..." mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t devpts devpts /dev/pts echo "particle-os started successfully" ` rcsFile := filepath.Join(mountPoint, "etc", "init.d", "rcS") if err := os.MkdirAll(filepath.Dir(rcsFile), 0755); err != nil { fmt.Printf("Warning: could not create init.d directory: %v\n", err) } cmd = exec.Command("sudo", "sh", "-c", fmt.Sprintf("cat > %s << 'EOF'\n%sEOF", rcsFile, rcsContent)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Printf("Warning: could not create rcS: %v\n", err) } exec.Command("sudo", "chmod", "+x", rcsFile).Run() // Create a simple kernel command line cmdline := "root=/dev/sda1 rw console=ttyS0 init=/bin/sh" cmdlineFile := filepath.Join(bootDir, "cmdline.txt") cmd = exec.Command("sudo", "sh", "-c", fmt.Sprintf("echo '%s' > %s", cmdline, cmdlineFile)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Printf("Warning: could not create cmdline.txt: %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") } // Create a simple syslinux config syslinuxConfig := `DEFAULT linux TIMEOUT 50 PROMPT 0 LABEL linux KERNEL /boot/vmlinuz APPEND root=/dev/sda1 rw console=ttyS0 init=/bin/sh ` syslinuxFile := filepath.Join(bootDir, "syslinux.cfg") cmd = exec.Command("sudo", "sh", "-c", fmt.Sprintf("cat > %s << 'EOF'\n%sEOF", syslinuxFile, syslinuxConfig)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Printf("Warning: could not create syslinux.cfg: %v\n", err) } // Install syslinux if available if _, err := exec.LookPath("syslinux"); err == nil { fmt.Println("Installing syslinux...") cmd = exec.Command("sudo", "syslinux", "--install", partitionDevice) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Printf("Warning: syslinux installation failed: %v\n", err) } else { fmt.Println("syslinux installed successfully") } } else { fmt.Println("syslinux not available") } fmt.Printf("\n✅ Bootable image created successfully: %s\n", outputPath) fmt.Printf("Image size: %d bytes\n", imageSize) fmt.Printf("Filesystem: ext4\n") fmt.Printf("Bootloader: extlinux/syslinux\n") fmt.Printf("Init system: Simple sysvinit with /bin/sh\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) fmt.Printf("\nNote: This image will boot to a shell prompt. You can run commands like:\n") fmt.Printf(" ls / # List files\n") fmt.Printf(" cat /etc/os-release # Show OS info\n") fmt.Printf(" exit # Exit shell\n") }