m/cli/takeover: refactor CPIO writing
Refactor the CPIO writing to first creating a structfs.Tree and then
writing that as CPIO in a separate function. Previously, errors during
writing were ignored; they are now properly handled.
Change-Id: Icc7cf1c94dd01bc9d69b01ee2f6696bfa4c183af
Reviewed-on: https://review.monogon.dev/c/monogon/+/4040
Tested-by: Jenkins CI
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
diff --git a/metropolis/cli/takeover/takeover.go b/metropolis/cli/takeover/takeover.go
index 95420dc..07c44de 100644
--- a/metropolis/cli/takeover/takeover.go
+++ b/metropolis/cli/takeover/takeover.go
@@ -5,7 +5,6 @@
import (
"archive/zip"
- "bytes"
_ "embed"
"fmt"
"io"
@@ -24,6 +23,7 @@
"source.monogon.dev/osbase/build/mkimage/osimage"
"source.monogon.dev/osbase/kexec"
netdump "source.monogon.dev/osbase/net/dump"
+ "source.monogon.dev/osbase/structfs"
)
//go:embed third_party/linux/bzImage
@@ -45,6 +45,43 @@
return os.NewFile(uintptr(fd), name), nil
}
+func writeCPIO(w io.Writer, root structfs.Tree) error {
+ cpioW := cpio.NewWriter(w)
+ for path, node := range root.Walk() {
+ switch {
+ case node.Mode.IsDir():
+ err := cpioW.WriteHeader(&cpio.Header{
+ Name: "/" + path,
+ Mode: cpio.TypeDir | (cpio.FileMode(node.Mode) & cpio.ModePerm),
+ })
+ if err != nil {
+ return err
+ }
+ case node.Mode.IsRegular():
+ err := cpioW.WriteHeader(&cpio.Header{
+ Name: "/" + path,
+ Size: node.Content.Size(),
+ Mode: cpio.TypeReg | (cpio.FileMode(node.Mode) & cpio.ModePerm),
+ })
+ if err != nil {
+ return err
+ }
+ content, err := node.Content.Open()
+ if err != nil {
+ return fmt.Errorf("cpio write %q: %w", path, err)
+ }
+ _, err = io.Copy(cpioW, content)
+ content.Close()
+ if err != nil {
+ return fmt.Errorf("cpio write %q: %w", path, err)
+ }
+ default:
+ return fmt.Errorf("cpio write %q: unsupported file type %s", path, node.Mode.Type().String())
+ }
+ }
+ return cpioW.Close()
+}
+
func setupTakeover(nodeParamsRaw []byte, target string) ([]string, error) {
// Validate we are running via EFI.
if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) {
@@ -57,17 +94,17 @@
return nil, err
}
- bundleRaw, err := os.Open(filepath.Join(filepath.Dir(currPath), "bundle.zip"))
+ bundleBlob, err := structfs.OSPathBlob(filepath.Join(filepath.Dir(currPath), "bundle.zip"))
if err != nil {
return nil, err
}
- bundleStat, err := bundleRaw.Stat()
+ bundleRaw, err := bundleBlob.Open()
if err != nil {
return nil, err
}
-
- bundle, err := zip.NewReader(bundleRaw, bundleStat.Size())
+ defer bundleRaw.Close()
+ bundle, err := zip.NewReader(bundleRaw.(io.ReaderAt), bundleBlob.Size())
if err != nil {
return nil, fmt.Errorf("failed to open node bundle: %w", err)
}
@@ -123,62 +160,38 @@
if err != nil {
return nil, fmt.Errorf("failed to create initramfs memfile: %w", err)
}
- if _, err := kernelFile.ReadFrom(bytes.NewReader(kernel)); err != nil {
- return nil, fmt.Errorf("failed to read kernel into memory-backed file: %w", err)
+ if _, err := kernelFile.Write(kernel); err != nil {
+ return nil, fmt.Errorf("failed to write kernel into memory-backed file: %w", err)
}
- if _, err := initramfsFile.ReadFrom(bytes.NewReader(ucode)); err != nil {
- return nil, fmt.Errorf("failed to read ucode into memory-backed file: %w", err)
+ if _, err := initramfsFile.Write(ucode); err != nil {
+ return nil, fmt.Errorf("failed to write ucode into memory-backed file: %w", err)
}
- if _, err := initramfsFile.ReadFrom(bytes.NewReader(initramfs)); err != nil {
- return nil, fmt.Errorf("failed to read initramfs into memory-backed file: %w", err)
+ if _, err := initramfsFile.Write(initramfs); err != nil {
+ return nil, fmt.Errorf("failed to write initramfs into memory-backed file: %w", err)
}
// Append this executable, the bundle and node params to initramfs
+ self, err := structfs.OSPathBlob("/proc/self/exe")
+ if err != nil {
+ return nil, err
+ }
+ root := structfs.Tree{
+ structfs.File("init", self, structfs.WithPerm(0o755)),
+ structfs.File("params.pb", structfs.Bytes(nodeParamsRaw)),
+ structfs.File("bundle.zip", bundleBlob),
+ }
compressedW, err := zstd.NewWriter(initramfsFile, zstd.WithEncoderLevel(1))
if err != nil {
return nil, fmt.Errorf("while creating zstd writer: %w", err)
}
- {
- self, err := os.Open("/proc/self/exe")
- if err != nil {
- return nil, err
- }
- selfStat, err := self.Stat()
- if err != nil {
- return nil, err
- }
-
- cpioW := cpio.NewWriter(compressedW)
- cpioW.WriteHeader(&cpio.Header{
- Name: "/init",
- Size: selfStat.Size(),
- Mode: cpio.TypeReg | 0o755,
- })
- io.Copy(cpioW, self)
- cpioW.Close()
+ err = writeCPIO(compressedW, root)
+ if err != nil {
+ return nil, err
}
- {
- cpioW := cpio.NewWriter(compressedW)
- cpioW.WriteHeader(&cpio.Header{
- Name: "/bundle.zip",
- Size: bundleStat.Size(),
- Mode: cpio.TypeReg | 0o644,
- })
- bundleRaw.Seek(0, io.SeekStart)
- io.Copy(cpioW, bundleRaw)
- cpioW.Close()
+ err = compressedW.Close()
+ if err != nil {
+ return nil, err
}
- {
- cpioW := cpio.NewWriter(compressedW)
- cpioW.WriteHeader(&cpio.Header{
- Name: "/params.pb",
- Size: int64(len(nodeParamsRaw)),
- Mode: cpio.TypeReg | 0o644,
- })
- cpioW.Write(nodeParamsRaw)
- cpioW.Close()
- }
- compressedW.Close()
initParams := bootparam.Params{
bootparam.Param{Param: "quiet"},