local-server/cmd/dkl-local-server/boot-img.go

175 lines
2.9 KiB
Go

package main
import (
"archive/tar"
"compress/gzip"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"github.com/pierrec/lz4"
)
func buildBootImg(out io.Writer, ctx *renderContext) (err error) {
bootImg, err := ioutil.TempFile(os.TempDir(), "boot.img-")
if err != nil {
return
}
defer rmTempFile(bootImg)
err = setupBootImage(bootImg, ctx)
if err != nil {
return
}
// send the result
bootImg.Seek(0, os.SEEK_SET)
io.Copy(out, bootImg)
return
}
func buildBootImgLZ4(out io.Writer, ctx *renderContext) (err error) {
lz4Out := lz4.NewWriter(out)
if err = buildBootImg(lz4Out, ctx); err != nil {
return
}
lz4Out.Close()
return
}
func buildBootImgGZ(out io.Writer, ctx *renderContext) (err error) {
gzOut := gzip.NewWriter(out)
if err = buildBootImg(gzOut, ctx); err != nil {
return
}
gzOut.Close()
return
}
var grubSupportVersion = flag.String("grub-support", "1.0.1", "GRUB support version")
func setupBootImage(bootImg *os.File, ctx *renderContext) (err error) {
path, err := ctx.distFetch("grub-support", *grubSupportVersion)
if err != nil {
return
}
baseImage, err := os.Open(path)
if err != nil {
return
}
defer baseImage.Close()
baseImageGz, err := gzip.NewReader(baseImage)
if err != nil {
return
}
defer baseImageGz.Close()
_, err = io.Copy(bootImg, baseImageGz)
if err != nil {
return
}
log.Print("running losetup...")
cmd := exec.Command("losetup", "--find", "--show", "--partscan", bootImg.Name())
cmd.Stderr = os.Stderr
devb, err := cmd.Output()
if err != nil {
return
}
dev := strings.TrimSpace(string(devb))
defer func() {
log.Print("detaching ", dev)
run("losetup", "-d", dev)
}()
log.Print("device: ", dev)
tempDir := bootImg.Name() + ".p1.mount"
err = os.Mkdir(tempDir, 0755)
if err != nil {
return
}
defer func() {
log.Print("removing ", tempDir)
os.RemoveAll(tempDir)
}()
err = syscall.Mount(dev+"p1", tempDir, "vfat", 0, "")
if err != nil {
return fmt.Errorf("failed to mount %s to %s: %v", dev+"p1", tempDir, err)
}
defer func() {
log.Print("unmounting ", tempDir)
syscall.Unmount(tempDir, 0)
}()
// add system elements
tarOut, tarIn := io.Pipe()
go func() {
err2 := buildBootTar(tarIn, ctx)
tarIn.Close()
if err2 != nil {
err = err2
}
}()
defer tarOut.Close()
tarRd := tar.NewReader(tarOut)
for {
hdr, err := tarRd.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
log.Print("tar: extracting ", hdr.Name)
outPath := filepath.Join(tempDir, hdr.Name)
os.MkdirAll(filepath.Dir(outPath), 0755)
f, err := os.Create(outPath)
if err != nil {
return err
}
_, err = io.Copy(f, tarRd)
f.Close()
if err != nil {
return err
}
}
return
}
func run(program string, args ...string) (err error) {
cmd := exec.Command(program, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}