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
- 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.
640 lines
21 KiB
Go
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.")
|
|
}
|