treewide: port everything to blockdev

This gets rid of most ad-hoc block device code, using blockdev for
everything. It also gets rid of diskfs for everything but tests. This
enables Metropolis to be installed on non-512-byte block sizes.

Change-Id: I644b5b68bb7bed8106585df3179674789031687a
Reviewed-on: https://review.monogon.dev/c/monogon/+/1873
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/metropolis/pkg/blkio/BUILD.bazel b/metropolis/pkg/blkio/BUILD.bazel
new file mode 100644
index 0000000..8b071ec
--- /dev/null
+++ b/metropolis/pkg/blkio/BUILD.bazel
@@ -0,0 +1,8 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "blkio",
+    srcs = ["blkio.go"],
+    importpath = "source.monogon.dev/metropolis/pkg/blkio",
+    visibility = ["//visibility:public"],
+)
diff --git a/metropolis/pkg/blkio/blkio.go b/metropolis/pkg/blkio/blkio.go
new file mode 100644
index 0000000..d0b7174
--- /dev/null
+++ b/metropolis/pkg/blkio/blkio.go
@@ -0,0 +1,83 @@
+package blkio
+
+import (
+	"fmt"
+	"io"
+	"os"
+)
+
+type ReaderWithSize struct {
+	io.Reader
+	size int64
+}
+
+// SizedReader is an io.Reader with a known size
+type SizedReader interface {
+	io.Reader
+	Size() int64
+}
+
+// NewSizedReader returns a SizedReader given a reader and a size.
+// The returned SizedReader is a ReaderWithSize.
+func NewSizedReader(r io.Reader, size int64) SizedReader {
+	return &ReaderWithSize{r, size}
+}
+
+func (r *ReaderWithSize) Size() int64 {
+	return r.size
+}
+
+// LazyFileReader implements a SizedReader which opens a file on first read
+// and closes it again after the reader has reached EOF.
+type LazyFileReader struct {
+	name string
+	size int64
+	f    *os.File
+	done bool
+}
+
+func (r *LazyFileReader) init() error {
+	f, err := os.Open(r.name)
+	if err != nil {
+		return fmt.Errorf("failed to open file for reading: %w", err)
+	}
+	r.f = f
+	return nil
+}
+
+func (r *LazyFileReader) Size() int64 {
+	return r.size
+}
+
+func (r *LazyFileReader) Read(b []byte) (n int, err error) {
+	if r.done {
+		return 0, io.EOF
+	}
+	if r.f == nil {
+		if err = r.init(); err != nil {
+			return
+		}
+	}
+	n, err = r.f.Read(b)
+	if err == io.EOF {
+		r.done = true
+		r.f.Close()
+	}
+	return
+}
+
+func (r *LazyFileReader) Close() {
+	r.done = true
+	r.f.Close()
+}
+
+func NewFileReader(name string) (*LazyFileReader, error) {
+	info, err := os.Stat(name)
+	if err != nil {
+		return nil, fmt.Errorf("failed to stat: %w", err)
+	}
+	return &LazyFileReader{
+		size: info.Size(),
+		name: name,
+	}, nil
+}