m/p/devicemapper: Support creating read-only devices

I originally thought this is not going to be needed as R/W control can be done through devicemapper itself, but verity requires a read-only table.

While we're here let's also add some doc comments to the Target struct.

Existing functionality is covered by existing tests, read-only functionality will be exercised by verity tests once they land.

Change-Id: Ib76bcffb14b5fe40d8d77bd9731b591d0d8cf22f
Reviewed-on: https://review.monogon.dev/c/monogon/+/262
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/pkg/devicemapper/devicemapper.go b/metropolis/pkg/devicemapper/devicemapper.go
index d56e8f9..5807676 100644
--- a/metropolis/pkg/devicemapper/devicemapper.go
+++ b/metropolis/pkg/devicemapper/devicemapper.go
@@ -203,14 +203,24 @@
 	return nil
 }
 
+// Target represents a byte region inside a devicemapper table for a given
+// device provided by a given target implementation.
 type Target struct {
+	// StartSector is the first sector (defined as being 512 bytes long) this
+	// target covers.
 	StartSector uint64
-	Length      uint64
-	Type        string
-	Parameters  string
+	// Length is the number of sectors (defined as being 512 bytes long) this
+	// target covers, starting from StartSector.
+	Length uint64
+	// Type is the type of target handling this byte region.
+	// Types implemented by the Linux kernel can be found at
+	// @linux//drivers/md/... by looking for dm_register_target() calls.
+	Type string
+	// Parameters are additional parameters specific to the target type.
+	Parameters string
 }
 
-func LoadTable(name string, targets []Target) error {
+func LoadTable(name string, readOnly bool, targets []Target) error {
 	req := newReq()
 	if err := stringToDelimitedBuf(req.Name[:], name); err != nil {
 		return err
@@ -244,6 +254,9 @@
 	}
 	req.DataSize = baseDataSize + uint32(data.Len())
 	copy(req.Data[:], data.Bytes())
+	if readOnly {
+		req.Flags = DM_READONLY_FLAG
+	}
 	fd, err := getFd()
 	if err != nil {
 		return err
@@ -281,12 +294,12 @@
 	return suspendResume(name, false)
 }
 
-func CreateActiveDevice(name string, targets []Target) (uint64, error) {
+func CreateActiveDevice(name string, readOnly bool, targets []Target) (uint64, error) {
 	dev, err := CreateDevice(name)
 	if err != nil {
 		return 0, fmt.Errorf("DM_DEV_CREATE failed: %w", err)
 	}
-	if err := LoadTable(name, targets); err != nil {
+	if err := LoadTable(name, readOnly, targets); err != nil {
 		_ = RemoveDevice(name)
 		return 0, fmt.Errorf("DM_TABLE_LOAD failed: %w", err)
 	}