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
Tests / test (1.21.x) (push) Failing after 2s
Tests / test (1.22.x) (push) Failing after 1s
particle-os CI / Build and Release (push) Has been skipped
278 lines
8.6 KiB
Go
278 lines
8.6 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-minimal-working-image.go <rootfs-path> [output-path]")
|
|
os.Exit(1)
|
|
}
|
|
|
|
rootfsPath := os.Args[1]
|
|
outputPath := "debian-minimal-working.img"
|
|
if len(os.Args) > 2 {
|
|
outputPath = os.Args[2]
|
|
}
|
|
|
|
fmt.Printf("Creating minimal working bootable image 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 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 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-mount"
|
|
if err := os.MkdirAll(mountPoint, 0755); err != nil {
|
|
fmt.Printf("Error creating mount point: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Mount the partition
|
|
fmt.Printf("Mounting 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 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 a minimal working system
|
|
fmt.Println("Setting up minimal working 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✅ Minimal working bootable image created successfully: %s\n", outputPath)
|
|
fmt.Printf("Image size: %s\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")
|
|
}
|