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/node/core/localstorage/crypt/crypt.go b/metropolis/node/core/localstorage/crypt/crypt.go
index 7b994d2..d6ed541 100644
--- a/metropolis/node/core/localstorage/crypt/crypt.go
+++ b/metropolis/node/core/localstorage/crypt/crypt.go
@@ -56,7 +56,7 @@
integrityDevName := fmt.Sprintf("/dev/%v-integrity", name)
integrityDMName := fmt.Sprintf("%v-integrity", name)
- integrityDev, err := devicemapper.CreateActiveDevice(integrityDMName, []devicemapper.Target{
+ integrityDev, err := devicemapper.CreateActiveDevice(integrityDMName, false, []devicemapper.Target{
devicemapper.Target{
Length: integritySectors,
Type: "integrity",
@@ -73,7 +73,7 @@
}
cryptDevName := fmt.Sprintf("/dev/%v", name)
- cryptDev, err := devicemapper.CreateActiveDevice(name, []devicemapper.Target{
+ cryptDev, err := devicemapper.CreateActiveDevice(name, false, []devicemapper.Target{
devicemapper.Target{
Length: integritySectors,
Type: "crypt",
@@ -111,7 +111,7 @@
integrityPartition.Close()
integrityDMName := fmt.Sprintf("%v-integrity", name)
- _, err = devicemapper.CreateActiveDevice(integrityDMName, []devicemapper.Target{
+ _, err = devicemapper.CreateActiveDevice(integrityDMName, false, []devicemapper.Target{
{
Length: 1,
Type: "integrity",
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)
}