m/p/efivarfs: add Delete and DeleteBootEntry

This allows us to delete EFI variables in addition to reading and
creating them.

Change-Id: I5fa4e64e5662a11bffdc0830c7af79301173f2aa
Reviewed-on: https://review.monogon.dev/c/monogon/+/2412
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/metropolis/pkg/efivarfs/efivarfs.go b/metropolis/pkg/efivarfs/efivarfs.go
index 97cd43a..ab6da26 100644
--- a/metropolis/pkg/efivarfs/efivarfs.go
+++ b/metropolis/pkg/efivarfs/efivarfs.go
@@ -156,3 +156,9 @@
 	}
 	return outVarNames, nil
 }
+
+// Delete deletes the given variable name in the given scope. Use with care,
+// some firmware fails to boot if variables it uses are deleted.
+func Delete(scope uuid.UUID, varName string) error {
+	return os.Remove(varPath(scope, varName))
+}
diff --git a/metropolis/pkg/efivarfs/variables.go b/metropolis/pkg/efivarfs/variables.go
index 3f435bf..fe59324 100644
--- a/metropolis/pkg/efivarfs/variables.go
+++ b/metropolis/pkg/efivarfs/variables.go
@@ -108,6 +108,16 @@
 	return Write(ScopeGlobal, fmt.Sprintf("Boot%04X", idx), AttrNonVolatile|AttrRuntimeAccess, bem)
 }
 
+// DeleteBootEntry deletes the boot entry at the given index.
+func DeleteBootEntry(idx int) error {
+	err := Delete(ScopeGlobal, fmt.Sprintf("Boot%04X", idx))
+	if errors.Is(err, fs.ErrNotExist) {
+		// Try non-spec-conforming lowercase entry
+		err = Delete(ScopeGlobal, fmt.Sprintf("Boot%04x", idx))
+	}
+	return err
+}
+
 // SetBootOrder replaces contents of the boot order variable with the order
 // specified in ord.
 func SetBootOrder(ord BootOrder) error {