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 7a8995292b..d67c566658 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) } @@ -392,15 +393,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 @@ -411,15 +414,16 @@ func addLCOWLayer(ctx context.Context, vm *uvm.UtilityVM, layerPath string) (uvm sm, err := vm.SCSIManager.Add( ctx, - &scsi.AttachConfig{Path: layerPath, ReadOnly: true, Type: scsi.AttachmentTypeVirtualDisk}, - &scsi.MountConfig{ReadOnly: true, Verity: scsi.ReadVerityInfo(ctx, layerPath), Options: []string{"ro"}}, + &scsi.AttachConfig{Path: layer.VHDPath, ReadOnly: true, Type: scsi.AttachmentTypeVirtualDisk}, + &scsi.MountConfig{Partition: layer.Partition, ReadOnly: true, Verity: scsi.ReadVerityInfo(ctx, layer.VHDPath), 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 579c9d4687..07c4319696 100644 --- a/internal/protocol/guestresource/resources.go +++ b/internal/protocol/guestresource/resources.go @@ -81,6 +81,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 787023c5a3..2733618d2b 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/mount.go b/internal/uvm/scsi/mount.go index f9410bce0e..b44294c474 100644 --- a/internal/uvm/scsi/mount.go +++ b/internal/uvm/scsi/mount.go @@ -39,6 +39,7 @@ type mount struct { // MountConfig specifies the options to apply for mounting a SCSI device in // the guest OS. type MountConfig struct { + Partition uint64 ReadOnly bool Encrypted bool Verity *guestresource.DeviceVerityInfo