//go:build linux package main import ( "encoding/json" "fmt" "os" "os/exec" "strings" ) // reinstallBootloader mounts the freshly cloned root (and ESP), bind-mounts // the API filesystems, and runs grub-install + grub-mkconfig inside a chroot. // It is best-effort: it returns a human summary and never a hard error, so a // clone still completes even when boot repair cannot. func reinstallBootloader(diskPath, optsJSON string) string { var o bootOpts if err := json.Unmarshal([]byte(optsJSON), &o); err != nil { return "skipped: bad boot opts: " + err.Error() } if o.RootPart == 0 { return "skipped: no root partition identified (pass --reinstall-bootloader only for a Linux system disk)" } att, err := attachDisk(diskPath, true) if err != nil { return "skipped: attach: " + err.Error() } defer att.cleanup() rootNode := partNode(att.blockPath, o.RootPart) root, umount, err := mountAt(rootNode, false) if err != nil { return "skipped: mount root: " + err.Error() } defer umount() if o.ESPPart != 0 { espNode := partNode(att.blockPath, o.ESPPart) espDir := root + "/boot/efi" _ = os.MkdirAll(espDir, 0755) if out, err := exec.Command("mount", espNode, espDir).CombinedOutput(); err != nil { return "skipped: mount ESP: " + strings.TrimSpace(string(out)) } defer exec.Command("umount", espDir).Run() } var log strings.Builder for _, d := range []string{"dev", "proc", "sys", "run"} { _ = exec.Command("mount", "--bind", "/"+d, root+"/"+d).Run() defer exec.Command("umount", "-l", root+"/"+d).Run() } grubTarget := att.blockPath if isDevicePath(diskPath) || looksLikeBlockDevice(diskPath) { grubTarget = diskPath } giArgs := []string{root, "grub-install", "--recheck"} if o.UEFI { giArgs = append(giArgs, "--target=x86_64-efi", "--efi-directory=/boot/efi", "--removable") } else { giArgs = append(giArgs, grubTarget) } if out, err := exec.Command("chroot", giArgs...).CombinedOutput(); err != nil { fmt.Fprintf(&log, "grub-install failed: %s; ", strings.TrimSpace(string(out))) } else { log.WriteString("grub-install ok; ") } mkcfg := "grub-mkconfig" if _, err := exec.LookPath("update-grub"); err == nil { mkcfg = "update-grub" } args := []string{root, mkcfg} if mkcfg == "grub-mkconfig" { args = append(args, "-o", "/boot/grub/grub.cfg") } if out, err := exec.Command("chroot", args...).CombinedOutput(); err != nil { fmt.Fprintf(&log, "%s failed: %s", mkcfg, strings.TrimSpace(string(out))) } else { log.WriteString(mkcfg + " ok") } return log.String() }