m/p/{efivars,sysfs}: use typed UUIDs

The functions ReadLoaderDevicePartUUID and DeviceByPartUUID passed
around UUIDs as strings. This is inconvenient as UUID strings are not
normalized and thus require special care.

Switch to Google's uuid library and use their type, same as most other
things in the monorepo.

Change-Id: I19deed804cff7036f756257fbf5689570cccc812
Reviewed-on: https://review.monogon.dev/c/monogon/+/1640
Tested-by: Jenkins CI
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
diff --git a/metropolis/pkg/efivarfs/efivarfs.go b/metropolis/pkg/efivarfs/efivarfs.go
index e731069..8132579 100644
--- a/metropolis/pkg/efivarfs/efivarfs.go
+++ b/metropolis/pkg/efivarfs/efivarfs.go
@@ -24,8 +24,8 @@
 	"fmt"
 	"os"
 	"path/filepath"
-	"strings"
 
+	"github.com/google/uuid"
 	"golang.org/x/text/encoding/unicode"
 )
 
@@ -58,20 +58,23 @@
 }
 
 // ReadLoaderDevicePartUUID reads the ESP UUID from an EFI variable. It
-// depends on efivarfs being already mounted. It returns a correct lowercase
-// UUID, or an error.
-func ReadLoaderDevicePartUUID() (string, error) {
+// depends on efivarfs being already mounted.
+func ReadLoaderDevicePartUUID() (uuid.UUID, error) {
 	// Read the EFI variable file containing the ESP UUID.
 	espUuidPath := filepath.Join(Path, "LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f")
 	efiVar, err := os.ReadFile(espUuidPath)
 	if err != nil {
-		return "", fmt.Errorf("couldn't read the LoaderDevicePartUUID file at %q: %w", espUuidPath, err)
+		return uuid.Nil, fmt.Errorf("couldn't read the LoaderDevicePartUUID file at %q: %w", espUuidPath, err)
 	}
 	contents, err := ExtractString(efiVar)
 	if err != nil {
-		return "", fmt.Errorf("couldn't decode an EFI variable: %w", err)
+		return uuid.Nil, fmt.Errorf("couldn't decode an EFI variable: %w", err)
 	}
-	return strings.ToLower(contents), nil
+	out, err := uuid.Parse(contents)
+	if err != nil {
+		return uuid.Nil, fmt.Errorf("value in LoaderDevicePartUUID could not be parsed as UUID: %w", err)
+	}
+	return out, nil
 }
 
 // CreateBootEntry creates an EFI boot entry variable and returns its
diff --git a/metropolis/pkg/sysfs/BUILD.bazel b/metropolis/pkg/sysfs/BUILD.bazel
index 068f9f6..06995e2 100644
--- a/metropolis/pkg/sysfs/BUILD.bazel
+++ b/metropolis/pkg/sysfs/BUILD.bazel
@@ -8,4 +8,5 @@
     ],
     importpath = "source.monogon.dev/metropolis/pkg/sysfs",
     visibility = ["//metropolis:__subpackages__"],
+    deps = ["@com_github_google_uuid//:uuid"],
 )
diff --git a/metropolis/pkg/sysfs/block.go b/metropolis/pkg/sysfs/block.go
index 9f9ebf8..c7cbbd7 100644
--- a/metropolis/pkg/sysfs/block.go
+++ b/metropolis/pkg/sysfs/block.go
@@ -26,6 +26,8 @@
 	"path/filepath"
 	"strconv"
 	"strings"
+
+	"github.com/google/uuid"
 )
 
 // PartUUIDMap returns a mapping between partition UUIDs and block device
@@ -60,12 +62,12 @@
 
 // DeviceByPartUUID returns a block device name, given its corresponding
 // partition UUID.
-func DeviceByPartUUID(uuid string) (string, error) {
+func DeviceByPartUUID(id uuid.UUID) (string, error) {
 	pm, err := PartUUIDMap()
 	if err != nil {
 		return "", err
 	}
-	if bdev, ok := pm[strings.ToLower(uuid)]; ok {
+	if bdev, ok := pm[id.String()]; ok {
 		return bdev, nil
 	}
 	return "", ErrDevNotFound