m/{t,n}/installer: move to m/installer

Change-Id: I8fd15f1fa1e151369df251d1469e84cfeffd26fd
Reviewed-on: https://review.monogon.dev/c/monogon/+/510
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/build/genosrelease/BUILD.bazel b/metropolis/node/build/genosrelease/BUILD.bazel
index bae7f1d..b48b602 100644
--- a/metropolis/node/build/genosrelease/BUILD.bazel
+++ b/metropolis/node/build/genosrelease/BUILD.bazel
@@ -11,5 +11,8 @@
 go_binary(
     name = "genosrelease",
     embed = [":go_default_library"],
-    visibility = ["//metropolis/node:__subpackages__"],
+    visibility = [
+        "//metropolis/installer:__subpackages__",
+        "//metropolis/node:__subpackages__",
+    ],
 )
diff --git a/metropolis/node/installer/BUILD.bazel b/metropolis/node/installer/BUILD.bazel
deleted file mode 100644
index 9e1a36a..0000000
--- a/metropolis/node/installer/BUILD.bazel
+++ /dev/null
@@ -1,46 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
-load("//metropolis/node/build:def.bzl", "node_initramfs")
-load("//metropolis/node/build/genosrelease:defs.bzl", "os_release")
-load("//metropolis/node/build:efi.bzl", "efi_unified_kernel_image")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["main.go"],
-    importpath = "source.monogon.dev/metropolis/node/installer",
-    visibility = ["//visibility:private"],
-    deps = [
-        "//metropolis/node/build/mkimage/osimage:go_default_library",
-        "//metropolis/pkg/efivarfs:go_default_library",
-        "//metropolis/pkg/sysfs:go_default_library",
-        "@org_golang_x_sys//unix:go_default_library",
-    ],
-)
-
-go_binary(
-    name = "installer",
-    embed = [":go_default_library"],
-    visibility = ["//visibility:private"],
-)
-
-node_initramfs(
-    name = "initramfs",
-    files = {
-        "//metropolis/node/installer": "/init",
-    },
-    visibility = ["//metropolis/test/installer:__pkg__"],
-)
-
-os_release(
-    name = "installer-release-info",
-    os_id = "metropolis-installer",
-    os_name = "Metropolis Installer",
-    stamp_var = "STABLE_METROPOLIS_version",
-)
-
-efi_unified_kernel_image(
-    name = "kernel",
-    initramfs = ":initramfs",
-    kernel = "//third_party/linux",
-    os_release = ":installer-release-info",
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/node/installer/main.go b/metropolis/node/installer/main.go
deleted file mode 100644
index e3a4896..0000000
--- a/metropolis/node/installer/main.go
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Installer creates a Metropolis image at a suitable block device based on the
-// installer bundle present in the installation medium's ESP, after which it
-// reboots. It's meant to be used as an init process.
-package main
-
-import (
-	"archive/zip"
-	"fmt"
-	"io"
-	"os"
-	"path/filepath"
-	"strings"
-	"syscall"
-
-	"golang.org/x/sys/unix"
-
-	"source.monogon.dev/metropolis/node/build/mkimage/osimage"
-	"source.monogon.dev/metropolis/pkg/efivarfs"
-	"source.monogon.dev/metropolis/pkg/sysfs"
-)
-
-const mib = 1024 * 1024
-
-// mountPseudoFS mounts efivarfs, devtmpfs and sysfs, used by the installer in
-// the block device discovery stage.
-func mountPseudoFS() error {
-	for _, m := range []struct {
-		dir   string
-		fs    string
-		flags uintptr
-	}{
-		{"/sys", "sysfs", unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV},
-		{efivarfs.Path, "efivarfs", unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV},
-		{"/dev", "devtmpfs", unix.MS_NOEXEC | unix.MS_NOSUID},
-	} {
-		if err := unix.Mkdir(m.dir, 0700); err != nil && !os.IsExist(err) {
-			return fmt.Errorf("couldn't create the mountpoint at %q: %w", m.dir, err)
-		}
-		if err := unix.Mount(m.fs, m.dir, m.fs, m.flags, ""); err != nil {
-			return fmt.Errorf("couldn't mount %q at %q: %w", m.fs, m.dir, err)
-		}
-	}
-	return nil
-}
-
-// mountInstallerESP mounts the filesystem the installer was loaded from based
-// on espPath, which must point to the appropriate partition block device. The
-// filesystem is mounted at /installer.
-func mountInstallerESP(espPath string) error {
-	// Create the mountpoint.
-	if err := unix.Mkdir("/installer", 0700); err != nil {
-		return fmt.Errorf("couldn't create the installer mountpoint: %w", err)
-	}
-	// Mount the filesystem.
-	if err := unix.Mount(espPath, "/installer", "vfat", unix.MS_NOEXEC|unix.MS_RDONLY, ""); err != nil {
-		return fmt.Errorf("couldn't mount the installer ESP (%q -> %q): %w", espPath, "/installer", err)
-	}
-	return nil
-}
-
-// findInstallableBlockDevices returns names of all the block devices suitable
-// for hosting a Metropolis installation, limited by the size expressed in
-// bytes minSize. The install medium espDev will be excluded from the result.
-func findInstallableBlockDevices(espDev string, minSize uint64) ([]string, error) {
-	// Use the partition's name to find and return the name of its parent
-	// device. It will be excluded from the list of suitable target devices.
-	srcDev, err := sysfs.ParentBlockDevice(espDev)
-	// Build the exclusion list containing forbidden handle prefixes.
-	exclude := []string{"dm-", "zram", "ram", "loop", srcDev}
-
-	// Get the block device handles by looking up directory contents.
-	const blkDirPath = "/sys/class/block"
-	blkDevs, err := os.ReadDir(blkDirPath)
-	if err != nil {
-		return nil, fmt.Errorf("couldn't read %q: %w", blkDirPath, err)
-	}
-	// Iterate over the handles, skipping any block device that either points to
-	// a partition, matches the exclusion list, or is smaller than minSize.
-	var suitable []string
-probeLoop:
-	for _, devInfo := range blkDevs {
-		// Skip devices according to the exclusion list.
-		for _, prefix := range exclude {
-			if strings.HasPrefix(devInfo.Name(), prefix) {
-				continue probeLoop
-			}
-		}
-
-		// Skip partition symlinks.
-		if _, err := os.Stat(filepath.Join(blkDirPath, devInfo.Name(), "partition")); err == nil {
-			continue
-		} else if !os.IsNotExist(err) {
-			return nil, fmt.Errorf("while probing sysfs: %w", err)
-		}
-
-		// Skip devices of insufficient size.
-		devPath := filepath.Join("/dev", devInfo.Name())
-		dev, err := os.Open(devPath)
-		if err != nil {
-			return nil, fmt.Errorf("couldn't open a block device at %q: %w", devPath, err)
-		}
-		size, err := unix.IoctlGetInt(int(dev.Fd()), unix.BLKGETSIZE64)
-		dev.Close()
-		if err != nil {
-			return nil, fmt.Errorf("couldn't probe the size of %q: %w", devPath, err)
-		}
-		if uint64(size) < minSize {
-			continue
-		}
-
-		suitable = append(suitable, devInfo.Name())
-	}
-	return suitable, nil
-}
-
-// rereadPartitionTable causes the kernel to read the partition table present
-// in the block device at blkdevPath. It may return an error.
-func rereadPartitionTable(blkdevPath string) error {
-	dev, err := os.Open(blkdevPath)
-	if err != nil {
-		return fmt.Errorf("couldn't open the block device at %q: %w", blkdevPath, err)
-	}
-	defer dev.Close()
-	ret, err := unix.IoctlRetInt(int(dev.Fd()), unix.BLKRRPART)
-	if err != nil {
-		return fmt.Errorf("while doing an ioctl: %w", err)
-	}
-	if syscall.Errno(ret) == unix.EINVAL {
-		return fmt.Errorf("got an EINVAL from BLKRRPART ioctl")
-	}
-	return nil
-}
-
-// initializeSystemPartition writes image contents to the node's system
-// partition using the block device abstraction layer as opposed to slower
-// go-diskfs. tgtBlkdev must contain a path pointing to the block device
-// associated with the system partition. It may return an error.
-func initializeSystemPartition(image io.Reader, tgtBlkdev string) error {
-	// Check that tgtBlkdev points at an actual block device.
-	info, err := os.Stat(tgtBlkdev)
-	if err != nil {
-		return fmt.Errorf("couldn't stat the system partition at %q: %w", tgtBlkdev, err)
-	}
-	if info.Mode()&os.ModeDevice == 0 {
-		return fmt.Errorf("system partition path %q doesn't point to a block device", tgtBlkdev)
-	}
-
-	// Get the system partition's file descriptor.
-	sys, err := os.OpenFile(tgtBlkdev, os.O_WRONLY, 0600)
-	if err != nil {
-		return fmt.Errorf("couldn't open the system partition at %q: %w", tgtBlkdev, err)
-	}
-	defer sys.Close()
-	// Copy the system partition contents. Use a bigger buffer to optimize disk
-	// writes.
-	buf := make([]byte, mib)
-	if _, err := io.CopyBuffer(sys, image, buf); err != nil {
-		return fmt.Errorf("couldn't copy partition contents: %w", err)
-	}
-	return nil
-}
-
-// panicf is a replacement for log.panicf that doesn't print the error message
-// before calling panic.
-func panicf(format string, v ...interface{}) {
-	s := fmt.Sprintf(format, v...)
-	panic(s)
-}
-
-func main() {
-	// Reboot on panic after a delay. The error string will have been printed
-	// before recover is called.
-	defer func() {
-		if r := recover(); r != nil {
-			fmt.Println(r)
-			fmt.Println("The installation could not be finalized. Please reboot to continue.")
-			syscall.Pause()
-		}
-	}()
-
-	// Mount sysfs, devtmpfs and efivarfs.
-	if err := mountPseudoFS(); err != nil {
-		panicf("While mounting pseudo-filesystems: %v", err)
-	}
-	// Read the installer ESP UUID from efivarfs.
-	espUuid, err := efivarfs.ReadLoaderDevicePartUUID()
-	if err != nil {
-		panicf("While reading the installer ESP UUID: %v", err)
-	}
-	// Look up the installer partition based on espUuid.
-	espDev, err := sysfs.DeviceByPartUUID(espUuid)
-	espPath := filepath.Join("/dev", espDev)
-	if err != nil {
-		panicf("While resolving the installer device handle: %v", err)
-	}
-	// Mount the installer partition. The installer bundle will be read from it.
-	if err := mountInstallerESP(espPath); err != nil {
-		panicf("While mounting the installer ESP: %v", err)
-	}
-
-	nodeParameters, err := os.Open("/installer/metropolis-installer/nodeparams.pb")
-	if err != nil {
-		panicf("Failed to open node parameters from ESP: %v", err)
-	}
-
-	// TODO(lorenz): Replace with proper bundles
-	bundle, err := zip.OpenReader("/installer/metropolis-installer/bundle.bin")
-	if err != nil {
-		panicf("Failed to open node bundle from ESP: %v", err)
-	}
-	defer bundle.Close()
-	efiPayload, err := bundle.Open("kernel_efi.efi")
-	if err != nil {
-		panicf("Cannot open EFI payload in bundle: %v", err)
-	}
-	defer efiPayload.Close()
-	systemImage, err := bundle.Open("rootfs.img")
-	if err != nil {
-		panicf("Cannot open system image in bundle: %v", err)
-	}
-	defer systemImage.Close()
-
-	// Build the osimage parameters.
-	installParams := osimage.Params{
-		PartitionSize: osimage.PartitionSizeInfo{
-			// ESP is the size of the node ESP partition, expressed in mebibytes.
-			ESP: 128,
-			// System is the size of the node system partition, expressed in
-			// mebibytes.
-			System: 4096,
-			// Data must be nonzero in order for the data partition to be created.
-			// osimage will extend the data partition to fill all the available space
-			// whenever it's writing to block devices, such as now.
-			Data: 128,
-		},
-		// Due to a bug in go-diskfs causing slow writes, SystemImage is explicitly
-		// marked unused here, as system partition contents will be written using
-		// a workaround below instead.
-		// TODO(mateusz@monogon.tech): Address that bug either by patching go-diskfs
-		// or rewriting osimage.
-		SystemImage: nil,
-
-		EFIPayload:     efiPayload,
-		NodeParameters: nodeParameters,
-	}
-	// Calculate the minimum target size based on the installation parameters.
-	minSize := uint64((installParams.PartitionSize.ESP +
-		installParams.PartitionSize.System +
-		installParams.PartitionSize.Data + 1) * mib)
-
-	// Look for suitable block devices, given the minimum size.
-	blkDevs, err := findInstallableBlockDevices(espDev, minSize)
-	if err != nil {
-		panicf(err.Error())
-	}
-	if len(blkDevs) == 0 {
-		panicf("Couldn't find a suitable block device.")
-	}
-	// Set the first suitable block device found as the installation target.
-	tgtBlkdevName := blkDevs[0]
-	// Update the osimage parameters with a path pointing at the target device.
-	tgtBlkdevPath := filepath.Join("/dev", tgtBlkdevName)
-	installParams.OutputPath = tgtBlkdevPath
-
-	// Use osimage to partition the target block device and set up its ESP.
-	// Create will return an EFI boot entry on success.
-	fmt.Printf("Installing to %s\n", tgtBlkdevPath)
-	be, err := osimage.Create(&installParams)
-	if err != nil {
-		panicf("While installing: %v", err)
-	}
-	// The target device's partition table has just been updated. Re-read it to
-	// make the node system partition reachable through /dev.
-	if err := rereadPartitionTable(tgtBlkdevPath); err != nil {
-		panicf("While re-reading the partition table of %q: %v", tgtBlkdevPath, err)
-	}
-	// Look up the node's system partition path to be later used in the
-	// initialization step. It's always the second partition, right after
-	// the ESP.
-	sysBlkdevName, err := sysfs.PartitionBlockDevice(tgtBlkdevName, 2)
-	if err != nil {
-		panicf("While looking up the system partition: %v", err)
-	}
-	sysBlkdevPath := filepath.Join("/dev", sysBlkdevName)
-	// Copy the system partition contents.
-	if err := initializeSystemPartition(systemImage, sysBlkdevPath); err != nil {
-		panicf("While initializing the system partition at %q: %v", sysBlkdevPath, err)
-	}
-
-	// Create an EFI boot entry for Metropolis.
-	en, err := efivarfs.CreateBootEntry(be)
-	if err != nil {
-		panicf("While creating a boot entry: %v", err)
-	}
-	// Erase the preexisting boot order, leaving Metropolis as the only option.
-	if err := efivarfs.SetBootOrder(&efivarfs.BootOrder{uint16(en)}); err != nil {
-		panicf("While adjusting the boot order: %v", err)
-	}
-
-	// Reboot.
-	unix.Sync()
-	fmt.Println("Installation completed. Rebooting.")
-	unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART)
-}