n/b/mkimage/osimage: init
Common parts of mkimage were moved to osimage.
mkimage remains in use as a frontend to osimage, with backward
compatible use semantics.
Change-Id: I08c1c44afdf7bf0c07333ab3c6bbebf53ff4d0db
Reviewed-on: https://review.monogon.dev/c/monogon/+/324
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/build/mkimage/main.go b/metropolis/node/build/mkimage/main.go
index f41d643..e5c25df 100644
--- a/metropolis/node/build/mkimage/main.go
+++ b/metropolis/node/build/mkimage/main.go
@@ -14,166 +14,78 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// mkimage is a tool to generate Metropolis node disk images.
+// It can be used both to initialize block devices and to create image
+// files.
+//
+// The tool takes a path to an EFI payload (--efi), and a path to a
+// Metropolis system image (--system) as its only required inputs. In
+// addition, an output path must be supplied (--out).
+// Node parameters file path (--node_parameters) may also be supplied, in
+// which case the file will be copied to the EFI system partition.
+// Partition sizes are fixed and may be overridden by command line flags.
package main
-// mkimage is a tool to generate a Metropolis node disk image containing the
-// given EFI payload, and optionally, a given external initramfs image and
-// node parameters
-
import (
"flag"
- "fmt"
- "io"
- "io/ioutil"
"log"
"os"
- diskfs "github.com/diskfs/go-diskfs"
- "github.com/diskfs/go-diskfs/disk"
- "github.com/diskfs/go-diskfs/filesystem"
- "github.com/diskfs/go-diskfs/partition/gpt"
+ "source.monogon.dev/metropolis/node/build/mkimage/osimage"
)
-var NodeDataPartition gpt.Type = gpt.Type("9eeec464-6885-414a-b278-4305c51f7966")
-var NodeSystemPartition gpt.Type = gpt.Type("ee96055b-f6d0-4267-8bbb-724b2afea74c")
-
-var (
- flagEFI string
- flagOut string
- flagSystemPath string
- flagNodeParameters string
- flagDataPartitionSize uint64
- flagESPPartitionSize uint64
- flagSystemPartitionSize uint64
-)
-
-func mibToSectors(size uint64) uint64 {
- return (size * 1024 * 1024) / 512
-}
-
-type devZeroReader struct{}
-
-func (_ devZeroReader) Read(b []byte) (n int, err error) {
- for i := range b {
- b[i] = 0
- }
- return len(b), nil
-}
-
-// devZero is a /dev/zero-like reader which reads an infinite number of zeroes
-var devZero = devZeroReader{}
-
func main() {
- flag.StringVar(&flagEFI, "efi", "", "UEFI payload")
- flag.StringVar(&flagOut, "out", "", "Output disk image")
- flag.StringVar(&flagSystemPath, "system", "", "System partition [optional]")
- flag.StringVar(&flagNodeParameters, "node_parameters", "", "Node parameters [optional]")
- flag.Uint64Var(&flagDataPartitionSize, "data_partition_size", 2048, "Override the data partition size (default 2048 MiB)")
- flag.Uint64Var(&flagESPPartitionSize, "esp_partition_size", 128, "Override the ESP partition size (default: 128MiB)")
- flag.Uint64Var(&flagSystemPartitionSize, "system_partition_size", 1024, "Override the System partition size (default: 1024MiB)")
+ // Fill in the image parameters based on flags.
+ var (
+ efiPayload string
+ systemImage string
+ nodeParams string
+ cfg osimage.Params
+ )
+ flag.StringVar(&efiPayload, "efi", "", "Path to the UEFI payload used")
+ flag.StringVar(&systemImage, "system", "", "Path to the system partition image used")
+ flag.StringVar(&nodeParams, "node_parameters", "", "Path to Node Parameters to be written to the ESP (default: don't write Node Parameters)")
+ flag.StringVar(&cfg.OutputPath, "out", "", "Path to the resulting disk image or block device")
+ flag.Uint64Var(&cfg.PartitionSize.Data, "data_partition_size", 2048, "Override the data partition size (default 2048 MiB). Used only when generating image files.")
+ flag.Uint64Var(&cfg.PartitionSize.ESP, "esp_partition_size", 128, "Override the ESP partition size (default: 128MiB)")
+ flag.Uint64Var(&cfg.PartitionSize.System, "system_partition_size", 1024, "Override the System partition size (default: 1024MiB)")
+ flag.StringVar(&cfg.DiskGUID, "GUID", "", "Disk GUID marked in the resulting image's partition table (default: randomly generated)")
flag.Parse()
- if flagEFI == "" || flagOut == "" {
- log.Fatalf("efi and initramfs must be set")
- }
-
- _ = os.Remove(flagOut)
- diskImg, err := diskfs.Create(flagOut, 4*1024*1024*1024, diskfs.Raw)
+ // Open the input files for osimage.Create, fill in reader objects and
+ // metadata in osimage.Params.
+ // Start with the EFI Payload the OS will boot from.
+ p, err := os.Open(efiPayload)
if err != nil {
- log.Fatalf("diskfs.Create(%q): %v", flagOut, err)
+ log.Fatalf("while opening the EFI payload at %q: %s", efiPayload, err.Error())
}
+ defer p.Close()
+ cfg.EFIPayload = p
- table := &gpt.Table{
- // This is appropriate at least for virtio disks. Might need to be
- // adjusted for real ones.
- LogicalSectorSize: 512,
- PhysicalSectorSize: 512,
- ProtectiveMBR: true,
- Partitions: []*gpt.Partition{
- {
- Type: gpt.EFISystemPartition,
- Name: "ESP",
- Start: mibToSectors(1),
- End: mibToSectors(flagESPPartitionSize) - 1,
- },
- {
- Type: NodeSystemPartition,
- Name: "METROPOLIS-SYSTEM",
- Start: mibToSectors(flagESPPartitionSize),
- End: mibToSectors(flagESPPartitionSize+flagSystemPartitionSize) - 1,
- },
- {
- Type: NodeDataPartition,
- Name: "METROPOLIS-NODE-DATA",
- Start: mibToSectors(flagESPPartitionSize + flagSystemPartitionSize),
- End: mibToSectors(flagESPPartitionSize+flagSystemPartitionSize+flagDataPartitionSize) - 1,
- },
- },
- }
- if err := diskImg.Partition(table); err != nil {
- log.Fatalf("Failed to apply partition table: %v", err)
- }
-
- if flagSystemPath != "" {
- systemPart, err := os.Open(flagSystemPath)
+ // Attempt to open the system image if its path is set. In case the path
+ // isn't set, the system partition will still be created, but no
+ // contents will be written into it.
+ if systemImage != "" {
+ img, err := os.Open(systemImage)
if err != nil {
- log.Fatalf("Failed to open system partition: %v", err)
+ log.Fatalf("while opening the system image at %q: %s", systemImage, err.Error())
}
- defer systemPart.Close()
- systemPartMeta, err := systemPart.Stat()
+ defer img.Close()
+ cfg.SystemImage = img
+ }
+
+ // Attempt to open the node parameters file if its path is set.
+ if nodeParams != "" {
+ np, err := os.Open(nodeParams)
if err != nil {
- log.Fatalf("Failed to stat system partition: %v", err)
+ log.Fatalf("while opening node parameters at %q: %s", nodeParams, err.Error())
}
- padding := int64(flagSystemPartitionSize*1024*1024) - systemPartMeta.Size()
- systemPartMulti := io.MultiReader(systemPart, io.LimitReader(devZero, padding))
- if _, err := diskImg.WritePartitionContents(2, systemPartMulti); err != nil {
- log.Fatalf("Failed to write system partition: %v", err)
- }
+ defer np.Close()
+ cfg.NodeParameters = np
}
- fs, err := diskImg.CreateFilesystem(disk.FilesystemSpec{Partition: 1, FSType: filesystem.TypeFat32, VolumeLabel: "ESP"})
- if err != nil {
- log.Fatalf("Failed to create filesystem: %v", err)
- }
-
- // Create EFI partition structure.
- for _, dir := range []string{"/EFI", "/EFI/BOOT", "/EFI/metropolis"} {
- if err := fs.Mkdir(dir); err != nil {
- log.Fatalf("Mkdir(%q): %v", dir, err)
- }
- }
-
- put(fs, flagEFI, "/EFI/BOOT/BOOTX64.EFI")
-
- if flagNodeParameters != "" {
- put(fs, flagNodeParameters, "/EFI/metropolis/parameters.pb")
- }
-
- if err := diskImg.File.Close(); err != nil {
- log.Fatalf("Failed to finalize image: %v", err)
- }
- log.Printf("Success! You can now boot %v", flagOut)
-}
-
-// put copies a file from the host filesystem into the target image.
-func put(fs filesystem.FileSystem, src, dst string) {
- target, err := fs.OpenFile(dst, os.O_CREATE|os.O_RDWR)
- if err != nil {
- log.Fatalf("fs.OpenFile(%q): %v", dst, err)
- }
- source, err := os.Open(src)
- if err != nil {
- log.Fatalf("os.Open(%q): %v", src, err)
- }
- defer source.Close()
- // If this is streamed (e.g. using io.Copy) it exposes a bug in diskfs, so
- // do it in one go.
- data, err := ioutil.ReadAll(source)
- if err != nil {
- log.Fatalf("Reading %q: %v", src, err)
- }
- if _, err := target.Write(data); err != nil {
- fmt.Printf("writing file %q: %v", dst, err)
- os.Exit(1)
+ // Write the parametrized OS image.
+ if _, err := osimage.Create(&cfg); err != nil {
+ log.Fatalf("while creating a Metropolis OS image: %s", err.Error())
}
}