diff --git a/cmd/containerd-shim-runhcs-v1/rootfs.go b/cmd/containerd-shim-runhcs-v1/rootfs.go index 8c8e34b2dd..dd7c648781 100644 --- a/cmd/containerd-shim-runhcs-v1/rootfs.go +++ b/cmd/containerd-shim-runhcs-v1/rootfs.go @@ -86,6 +86,29 @@ func getLCOWLayers(rootfs []*types.Mount, layerFolders []string) (*layers.LCOWLa return nil, err } return legacyLayer(scratchLayer, parentLayers), nil + case "lcow-partitioned-layer": + var ( + scratchPath string + layerData []struct { + Path string + Partition uint64 + } + ) + for _, opt := range m.Options { + if optPrefix := "scratch="; strings.HasPrefix(opt, optPrefix) { + scratchPath = strings.TrimPrefix(opt, optPrefix) + } else if optPrefix := "parent-partitioned-layers="; strings.HasPrefix(opt, optPrefix) { + layerJSON := strings.TrimPrefix(opt, optPrefix) + if err := json.Unmarshal([]byte(layerJSON), &layerData); err != nil { + return nil, err + } + } + } + roLayers := make([]*layers.LCOWLayer, 0, len(layerData)) + for _, layer := range layerData { + roLayers = append(roLayers, &layers.LCOWLayer{VHDPath: layer.Path, Partition: layer.Partition}) + } + return &layers.LCOWLayers{Layers: roLayers, ScratchVHDPath: scratchPath}, nil default: return nil, fmt.Errorf("unrecognized rootfs mount type: %s", m.Type) } diff --git a/internal/layers/layers.go b/internal/layers/layers.go index 7c1705d5ef..cf217bcff8 100644 --- a/internal/layers/layers.go +++ b/internal/layers/layers.go @@ -27,7 +27,8 @@ import ( ) type LCOWLayer struct { - VHDPath string + VHDPath string + Partition uint64 } // Defines a set of LCOW layers. @@ -104,7 +105,7 @@ func MountLCOWLayers(ctx context.Context, containerID string, layers *LCOWLayers for _, layer := range layers.Layers { log.G(ctx).WithField("layerPath", layer.VHDPath).Debug("mounting layer") - uvmPath, closer, err := addLCOWLayer(ctx, vm, layer.VHDPath) + uvmPath, closer, err := addLCOWLayer(ctx, vm, layer) if err != nil { return "", "", nil, fmt.Errorf("failed to add LCOW layer: %s", err) } @@ -384,15 +385,17 @@ func mountWCOWIsolatedLayers(ctx context.Context, containerID string, layerFolde return containerScratchPathInUVM, closer, nil } -func addLCOWLayer(ctx context.Context, vm *uvm.UtilityVM, layerPath string) (uvmPath string, _ resources.ResourceCloser, err error) { - // don't try to add as vpmem when we want additional devices on the uvm to be fully physically backed - if !vm.DevicesPhysicallyBacked() { +func addLCOWLayer(ctx context.Context, vm *uvm.UtilityVM, layer *LCOWLayer) (uvmPath string, _ resources.ResourceCloser, err error) { + // Don't add as VPMEM when we want additional devices on the UVM to be fully physically backed. + // Also don't use VPMEM when we need to mount a specific partition of the disk, as this is only + // supported for SCSI. + if !vm.DevicesPhysicallyBacked() && layer.Partition == 0 { // We first try vPMEM and if it is full or the file is too large we // fall back to SCSI. - mount, err := vm.AddVPMem(ctx, layerPath) + mount, err := vm.AddVPMem(ctx, layer.VHDPath) if err == nil { log.G(ctx).WithFields(logrus.Fields{ - "layerPath": layerPath, + "layerPath": layer.VHDPath, "layerType": "vpmem", }).Debug("Added LCOW layer") return mount.GuestPath, mount, nil @@ -401,13 +404,14 @@ func addLCOWLayer(ctx context.Context, vm *uvm.UtilityVM, layerPath string) (uvm } } - sm, err := vm.SCSIManager.AddVirtualDisk(ctx, layerPath, true, nil, &scsi.MountConfig{Options: []string{"ro"}}) + sm, err := vm.SCSIManager.AddVirtualDisk(ctx, layer.VHDPath, true, nil, &scsi.MountConfig{Partition: layer.Partition, Options: []string{"ro"}}) if err != nil { return "", nil, fmt.Errorf("failed to add SCSI layer: %s", err) } log.G(ctx).WithFields(logrus.Fields{ - "layerPath": layerPath, - "layerType": "scsi", + "layerPath": layer.VHDPath, + "layerPartition": layer.Partition, + "layerType": "scsi", }).Debug("Added LCOW layer") return sm.GuestPath(), sm, nil } diff --git a/internal/protocol/guestresource/resources.go b/internal/protocol/guestresource/resources.go index 6d426c17e0..97e4f75e0c 100644 --- a/internal/protocol/guestresource/resources.go +++ b/internal/protocol/guestresource/resources.go @@ -79,6 +79,7 @@ type LCOWMappedVirtualDisk struct { MountPath string `json:"MountPath,omitempty"` Lun uint8 `json:"Lun,omitempty"` Controller uint8 `json:"Controller,omitempty"` + Partition uint64 `json:"Partition,omitempty"` ReadOnly bool `json:"ReadOnly,omitempty"` Encrypted bool `json:"Encrypted,omitempty"` Options []string `json:"Options,omitempty"` diff --git a/internal/uvm/scsi/backend.go b/internal/uvm/scsi/backend.go index 3f387b1ee1..e52eabe09e 100644 --- a/internal/uvm/scsi/backend.go +++ b/internal/uvm/scsi/backend.go @@ -200,6 +200,7 @@ func mountRequest(controller, lun uint, path string, config *mountConfig, osType MountPath: path, Controller: uint8(controller), Lun: uint8(lun), + Partition: uint64(config.partition), ReadOnly: config.readOnly, Encrypted: config.encrypted, Options: config.options, diff --git a/internal/uvm/scsi/manager.go b/internal/uvm/scsi/manager.go index b87f69f3d7..42cbdcf385 100644 --- a/internal/uvm/scsi/manager.go +++ b/internal/uvm/scsi/manager.go @@ -65,6 +65,7 @@ func NewManager( // MountConfig specifies the options to apply for mounting a SCSI device in // the guest OS. type MountConfig struct { + Partition uint64 Encrypted bool Options []string } diff --git a/internal/uvm/scsi/mount.go b/internal/uvm/scsi/mount.go index 1e88216bbf..32f2e09640 100644 --- a/internal/uvm/scsi/mount.go +++ b/internal/uvm/scsi/mount.go @@ -35,6 +35,7 @@ type mount struct { } type mountConfig struct { + partition uint64 readOnly bool encrypted bool verity *guestresource.DeviceVerityInfo