diff --git a/cmd/dkl-local-server/boot-tar.go b/cmd/dkl-local-server/boot-tar.go index 34cf6cc..ffb6d37 100644 --- a/cmd/dkl-local-server/boot-tar.go +++ b/cmd/dkl-local-server/boot-tar.go @@ -2,11 +2,13 @@ package main import ( "archive/tar" - "bytes" + "bytes" "io" - "io/ioutil" + "io/ioutil" "log" "os" + + "novit.tech/direktil/local-server/pkg/utf16" ) func rmTempFile(f *os.File) { @@ -41,9 +43,9 @@ func buildBootTar(out io.Writer, ctx *renderContext) (err error) { } err = archAdd("current/vmlinuz", kernelBytes) - if err != nil { - return - } + if err != nil { + return + } // initrd initrd := new(bytes.Buffer) @@ -60,3 +62,64 @@ func buildBootTar(out io.Writer, ctx *renderContext) (err error) { // done return nil } + +func buildBootEFITar(out io.Writer, ctx *renderContext) (err error) { + arch := tar.NewWriter(out) + defer arch.Close() + + archAdd := func(path string, ba []byte) (err error) { + err = arch.WriteHeader(&tar.Header{Name: path, Mode: 0640, Size: int64(len(ba))}) + if err != nil { + return + } + _, err = arch.Write(ba) + return + } + + const ( + prefix = "EFI/dkl/" + efiPrefix = "\\EFI\\dkl\\" + ) + + // boot.csv + // -> annoyingly it's UTF-16... + bootCsvBytes := utf16.FromUTF8([]byte("" + + "current_kernel.efi,dkl current,initrd=" + efiPrefix + "current_initrd.img,Direktil current\n" + + "previous_kernel.efi,dkl previous,initrd=" + efiPrefix + "previous_initrd.img,Direktil previous\n")) + + err = archAdd(prefix+"BOOT.CSV", []byte(bootCsvBytes)) + if err != nil { + return + } + + // kernel + kernelPath, err := ctx.distFetch("kernels", ctx.Host.Kernel) + if err != nil { + return + } + + kernelBytes, err := ioutil.ReadFile(kernelPath) + if err != nil { + return + } + + err = archAdd(prefix+"current_kernel.efi", kernelBytes) + if err != nil { + return + } + + // initrd + initrd := new(bytes.Buffer) + err = buildInitrdV2(initrd, ctx) + if err != nil { + return + } + + err = archAdd(prefix+"current_initrd.img", initrd.Bytes()) + if err != nil { + return + } + + // done + return nil +} diff --git a/cmd/dkl-local-server/ws-host.go b/cmd/dkl-local-server/ws-host.go index 57409f9..431abad 100644 --- a/cmd/dkl-local-server/ws-host.go +++ b/cmd/dkl-local-server/ws-host.go @@ -56,6 +56,9 @@ func (ws *wsHost) register(rws *restful.WebService, alterRB func(*restful.RouteB b("boot.tar"). Produces(mime.TAR). Doc("Get the " + ws.hostDoc + "'s /boot archive (ie: for metal upgrades)"), + b("boot-efi.tar"). + Produces(mime.TAR). + Doc("Get the " + ws.hostDoc + "'s /boot archive (ie: for metal upgrades)"), // read-only ISO support b("boot.iso"). @@ -168,6 +171,8 @@ func renderHost(w http.ResponseWriter, r *http.Request, what string, host *local case "boot.tar": err = renderCtx(w, r, ctx, what, buildBootTar) + case "boot-efi.tar": + err = renderCtx(w, r, ctx, what, buildBootEFITar) case "boot.img": err = renderCtx(w, r, ctx, what, buildBootImg) diff --git a/pkg/utf16/utf16.go b/pkg/utf16/utf16.go new file mode 100644 index 0000000..c8061cc --- /dev/null +++ b/pkg/utf16/utf16.go @@ -0,0 +1,29 @@ +package utf16 + +import ( + "encoding/binary" + "fmt" + "unicode/utf8" +) + +func FromUTF8(data []byte) (res []byte) { + endian := binary.LittleEndian + + res = make([]byte, (len(data)+1)*2) + + res = res[:2] + endian.PutUint16(res, 0xfeff) + + for len(data) > 0 { + r, size := utf8.DecodeRune(data) + if r > 65535 { + panic(fmt.Errorf("r=0x%x > 0xffff", r)) + } + + slen := len(res) + res = res[:slen+2] + endian.PutUint16(res[slen:], uint16(r)) + data = data[size:] + } + return +}