osbase/fat32: adopt structfs
Change the external interface of the FAT32 writer to take a
structfs.Tree instead of a FAT32-specific data structure. Producers of
file system data are no longer specific to FAT32.
With these changes, the blkio package becomes obsolete. The
LazyFileReader did not actually work as intended when used with
osbase/fat32, because fat32 copies data with io.CopyN and thus stops
reading before reaching EOF, so the LazyFileReader is never closed. The
new Blob interface requires the consumer to explicitly Open and Close.
Change-Id: I9a71a5f0bddf36ac38c656659e6dcfe520b88fb0
Reviewed-on: https://review.monogon.dev/c/monogon/+/4037
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/cloud/agent/BUILD.bazel b/cloud/agent/BUILD.bazel
index 7e7614d..3d342ae 100644
--- a/cloud/agent/BUILD.bazel
+++ b/cloud/agent/BUILD.bazel
@@ -27,6 +27,7 @@
"//osbase/pki",
"//osbase/scsi",
"//osbase/smbios",
+ "//osbase/structfs",
"//osbase/supervisor",
"@com_github_cenkalti_backoff_v4//:backoff",
"@com_github_mdlayher_ethtool//:ethtool",
diff --git a/cloud/agent/install.go b/cloud/agent/install.go
index cdc8fa5..914b0be 100644
--- a/cloud/agent/install.go
+++ b/cloud/agent/install.go
@@ -9,7 +9,6 @@
_ "embed"
"errors"
"fmt"
- "io/fs"
"net/http"
"os"
"path/filepath"
@@ -23,24 +22,28 @@
"source.monogon.dev/osbase/build/mkimage/osimage"
"source.monogon.dev/osbase/efivarfs"
npb "source.monogon.dev/osbase/net/proto"
+ "source.monogon.dev/osbase/structfs"
)
//go:embed metropolis/node/core/abloader/abloader.efi
var abloader []byte
-// FileSizedReader is a small adapter from fs.File to fs.SizedReader
-// Panics on Stat() failure, so should only be used with sources where Stat()
-// cannot fail.
-type FileSizedReader struct {
- fs.File
+// zipBlob looks up a file in a [zip.Reader] and adapts it to [structfs.Blob].
+func zipBlob(reader *zip.Reader, name string) (zipFileBlob, error) {
+ for _, file := range reader.File {
+ if file.Name == name {
+ return zipFileBlob{file}, nil
+ }
+ }
+ return zipFileBlob{}, fmt.Errorf("file %q not found", name)
}
-func (f FileSizedReader) Size() int64 {
- stat, err := f.Stat()
- if err != nil {
- panic(err)
- }
- return stat.Size()
+type zipFileBlob struct {
+ *zip.File
+}
+
+func (f zipFileBlob) Size() int64 {
+ return int64(f.File.UncompressedSize64)
}
// install dispatches OSInstallationRequests to the appropriate installer
@@ -115,16 +118,14 @@
if err != nil {
return fmt.Errorf("failed to open node bundle: %w", err)
}
- efiPayload, err := bundle.Open("kernel_efi.efi")
+ efiPayload, err := zipBlob(bundle, "kernel_efi.efi")
if err != nil {
return fmt.Errorf("invalid bundle: %w", err)
}
- defer efiPayload.Close()
- systemImage, err := bundle.Open("verity_rootfs.img")
+ systemImage, err := zipBlob(bundle, "verity_rootfs.img")
if err != nil {
return fmt.Errorf("invalid bundle: %w", err)
}
- defer systemImage.Close()
nodeParamsRaw, err := proto.Marshal(req.NodeParameters)
if err != nil {
@@ -143,9 +144,9 @@
Data: 128,
},
SystemImage: systemImage,
- EFIPayload: FileSizedReader{efiPayload},
- ABLoader: bytes.NewReader(abloader),
- NodeParameters: bytes.NewReader(nodeParamsRaw),
+ EFIPayload: efiPayload,
+ ABLoader: structfs.Bytes(abloader),
+ NodeParameters: structfs.Bytes(nodeParamsRaw),
Output: rootDev,
}
diff --git a/cloud/agent/takeover/e2e/BUILD.bazel b/cloud/agent/takeover/e2e/BUILD.bazel
index bfa7baa..b83b131 100644
--- a/cloud/agent/takeover/e2e/BUILD.bazel
+++ b/cloud/agent/takeover/e2e/BUILD.bazel
@@ -20,6 +20,7 @@
"//cloud/agent/api",
"//osbase/fat32",
"//osbase/freeport",
+ "//osbase/structfs",
"@com_github_pkg_sftp//:sftp",
"@io_bazel_rules_go//go/runfiles",
"@org_golang_google_protobuf//proto",
diff --git a/cloud/agent/takeover/e2e/main_test.go b/cloud/agent/takeover/e2e/main_test.go
index 9ab6769..756eb40 100644
--- a/cloud/agent/takeover/e2e/main_test.go
+++ b/cloud/agent/takeover/e2e/main_test.go
@@ -27,6 +27,7 @@
"source.monogon.dev/osbase/fat32"
"source.monogon.dev/osbase/freeport"
+ "source.monogon.dev/osbase/structfs"
)
var (
@@ -79,25 +80,16 @@
t.Fatal(err)
}
- rootInode := fat32.Inode{
- Attrs: fat32.AttrDirectory,
- Children: []*fat32.Inode{
- {
- Name: "user-data",
- Content: strings.NewReader("#cloud-config\n" + string(userData)),
- },
- {
- Name: "meta-data",
- Content: strings.NewReader(""),
- },
- },
+ root := structfs.Tree{
+ structfs.File("user-data", structfs.Bytes("#cloud-config\n"+string(userData))),
+ structfs.File("meta-data", structfs.Bytes("")),
}
cloudInitDataFile, err := os.CreateTemp("", "cidata*.img")
if err != nil {
t.Fatal(err)
}
defer os.Remove(cloudInitDataFile.Name())
- if err := fat32.WriteFS(cloudInitDataFile, rootInode, fat32.Options{Label: "cidata"}); err != nil {
+ if err := fat32.WriteFS(cloudInitDataFile, root, fat32.Options{Label: "cidata"}); err != nil {
t.Fatal(err)
}