Skip to content

Commit

Permalink
Enable dm-verity for multi-mapped LCOW layers
Browse files Browse the repository at this point in the history
Previously dm-verity was enabled only for dedicated VPMems. This
change adds dm-verity footer parsing logic to multi-mapped LCOW
layers

Signed-off-by: Maksim An <maksiman@microsoft.com>
  • Loading branch information
anmaxvl committed Aug 11, 2021
1 parent 5036ecc commit 133ed7f
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 56 deletions.
23 changes: 16 additions & 7 deletions internal/uvm/vpmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,27 @@ func newDefaultVPMemInfo(hostPath, uvmPath string) *vPMemInfoDefault {
}
}

// fileSystemSize retrieves ext4 fs SuperBlock and returns the file system size and block size
func fileSystemSize(vhdPath string) (int64, int, error) {
sb, err := tar2ext4.ReadExt4SuperBlock(vhdPath)
if err != nil {
return 0, 0, errors.Wrap(err, "failed to read ext4 super block")
}
blockSize := 1024 * (1 << sb.LogBlockSize)
fsSize := int64(blockSize) * int64(sb.BlocksCountLow)
return fsSize, blockSize, nil
}

// readVeritySuperBlock reads ext4 super block for a given VHD to then further read the dm-verity super block
// and root hash
func readVeritySuperBlock(ctx context.Context, layerPath string) (*guestrequest.DeviceVerityInfo, error) {
ext4sb, err := tar2ext4.ReadExt4SuperBlock(layerPath)
// dm-verity information is expected to be appended, the size of ext4 data will be the offset
// of the dm-verity super block, followed by merkle hash tree
ext4SizeInBytes, ext4BlockSize, err := fileSystemSize(layerPath)
if err != nil {
return nil, errors.Wrap(err, "failed to read ext4 super block")
return nil, err
}
// Calculate the size of ext4 file system based on the information from ext4 super block, since
// the dm-verity information is expected to be appended, the size of ext4 data will be the offset
// of the dm-verity super block, followed by merkle hash tree
ext4BlockSize := 1024 * (1 << ext4sb.LogBlockSize)
ext4SizeInBytes := int64(ext4BlockSize) * int64(ext4sb.BlocksCountLow)

dmvsb, err := dmverity.ReadDMVerityInfo(layerPath, ext4SizeInBytes)
if err != nil {
return nil, errors.Wrap(err, "failed to read dm-verity super block")
Expand Down
50 changes: 29 additions & 21 deletions internal/uvm/vpmem_mapped.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package uvm
import (
"context"
"fmt"
"os"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/Microsoft/hcsshim/ext4/tar2ext4"
"github.com/Microsoft/hcsshim/internal/guestrequest"
"github.com/Microsoft/hcsshim/internal/hcs/resourcepaths"
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
Expand Down Expand Up @@ -64,33 +64,36 @@ func pageAlign(t uint64) uint64 {
return (t/PageSize + 1) * PageSize
}

// fileSystemSize retrieves ext4 fs SuperBlock and calculates the size of the actual file system
func fileSystemSize(vhdPath string) (uint64, error) {
sb, err := tar2ext4.ReadExt4SuperBlock(vhdPath)
if err != nil {
return 0, err
}
blockSize := uint64(1024 * (1 << sb.LogBlockSize))
fsSize := blockSize * uint64(sb.BlocksCountLow)
return pageAlign(fsSize), nil
}

// newMappedVPMemModifyRequest creates an hcsschema.ModifySettingsRequest to modify VPMem devices/mappings
// for the multi-mapping setup
func newMappedVPMemModifyRequest(ctx context.Context, rType string, deviceNumber uint32, md *mappedDeviceInfo, uvm *UtilityVM) (*hcsschema.ModifySettingRequest, error) {
guestSettings := guestrequest.LCOWMappedVPMemDevice{
DeviceNumber: deviceNumber,
MountPath: md.uvmPath,
MappingInfo: &guestrequest.LCOWMappedLayer{
DeviceOffsetInBytes: md.mappedRegion.Offset(),
DeviceSizeInBytes: md.sizeInBytes,
},
}

if verity, err := readVeritySuperBlock(ctx, md.hostPath); err != nil {
log.G(ctx).WithError(err).WithField("hostPath", md.hostPath).Debug("unable to read dm-verity information from VHD")
} else {
if verity != nil {
log.G(ctx).WithFields(logrus.Fields{
"hostPath": md.hostPath,
"rootDigest": verity.RootDigest,
}).Debug("adding multi-mapped VPMem with dm-verity")
guestSettings.VerityInfo = verity
}
}

request := &hcsschema.ModifySettingRequest{
RequestType: rType,
GuestRequest: guestrequest.GuestRequest{
ResourceType: guestrequest.ResourceTypeVPMemDevice,
RequestType: rType,
Settings: guestrequest.LCOWMappedVPMemDevice{
DeviceNumber: deviceNumber,
MountPath: md.uvmPath,
MappingInfo: &guestrequest.LCOWMappedLayer{
DeviceOffsetInBytes: md.mappedRegion.Offset(),
DeviceSizeInBytes: md.sizeInBytes,
},
},
Settings: guestSettings,
},
}

Expand Down Expand Up @@ -234,10 +237,15 @@ func (uvm *UtilityVM) addVPMemMappedDevice(ctx context.Context, hostPath string)
return dev.uvmPath, nil
}

devSize, err := fileSystemSize(hostPath)
st, err := os.Stat(hostPath)
if err != nil {
return "", err
}
// NOTE: On the guest side devSize is used to create a device mapper linear target, which is then used to create
// device mapper verity target. Since the dm-verity hash device is appended after ext4 data, we need the full size
// on disk (minus VHD footer), otherwise the resulting linear target will have hash device truncated and verity
// target creation will fail as a result.
devSize := pageAlign(uint64(st.Size()))
deviceNumber, memReg, err := uvm.allocateNextVPMemMappedDeviceLocation(ctx, devSize)
if err != nil {
return "", err
Expand Down
23 changes: 16 additions & 7 deletions test/vendor/github.com/Microsoft/hcsshim/internal/uvm/vpmem.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 133ed7f

Please sign in to comment.