...

Source file src/github.com/Microsoft/hcsshim/internal/hcsoci/resources_lcow.go

Documentation: github.com/Microsoft/hcsshim/internal/hcsoci

     1  //go:build windows
     2  // +build windows
     3  
     4  package hcsoci
     5  
     6  // Contains functions relating to a LCOW container, as opposed to a utility VM
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"strings"
    15  
    16  	specs "github.com/opencontainers/runtime-spec/specs-go"
    17  	"github.com/pkg/errors"
    18  
    19  	"github.com/Microsoft/hcsshim/internal/guestpath"
    20  	"github.com/Microsoft/hcsshim/internal/layers"
    21  	"github.com/Microsoft/hcsshim/internal/log"
    22  	"github.com/Microsoft/hcsshim/internal/resources"
    23  	"github.com/Microsoft/hcsshim/internal/uvm"
    24  )
    25  
    26  func allocateLinuxResources(ctx context.Context, coi *createOptionsInternal, r *resources.Resources, isSandbox bool) error {
    27  	if coi.Spec.Root == nil {
    28  		coi.Spec.Root = &specs.Root{}
    29  	}
    30  	containerRootInUVM := r.ContainerRootInUVM()
    31  	if coi.Spec.Windows != nil && len(coi.Spec.Windows.LayerFolders) > 0 {
    32  		log.G(ctx).Debug("hcsshim::allocateLinuxResources mounting storage")
    33  		rootPath, scratchPath, closer, err := layers.MountLCOWLayers(ctx, coi.actualID, coi.Spec.Windows.LayerFolders, containerRootInUVM, coi.HostingSystem)
    34  		if err != nil {
    35  			return errors.Wrap(err, "failed to mount container storage")
    36  		}
    37  		coi.Spec.Root.Path = rootPath
    38  		// If this is the pause container in a hypervisor-isolated pod, we can skip cleanup of
    39  		// layers, as that happens automatically when the UVM is terminated.
    40  		if !isSandbox || coi.HostingSystem == nil {
    41  			r.SetLayers(closer)
    42  		}
    43  		r.SetLcowScratchPath(scratchPath)
    44  	} else if coi.Spec.Root.Path != "" {
    45  		// This is the "Plan 9" root filesystem.
    46  		// TODO: We need a test for this. Ask @jstarks how you can even lay this out on Windows.
    47  		hostPath := coi.Spec.Root.Path
    48  		uvmPathForContainersFileSystem := path.Join(r.ContainerRootInUVM(), guestpath.RootfsPath)
    49  		share, err := coi.HostingSystem.AddPlan9(ctx, hostPath, uvmPathForContainersFileSystem, coi.Spec.Root.Readonly, false, nil)
    50  		if err != nil {
    51  			return errors.Wrap(err, "adding plan9 root")
    52  		}
    53  		coi.Spec.Root.Path = uvmPathForContainersFileSystem
    54  		r.Add(share)
    55  	} else {
    56  		return errors.New("must provide either Windows.LayerFolders or Root.Path")
    57  	}
    58  
    59  	for i, mount := range coi.Spec.Mounts {
    60  		switch mount.Type {
    61  		case MountTypeBind:
    62  		case MountTypePhysicalDisk:
    63  		case MountTypeVirtualDisk:
    64  		default:
    65  			// Unknown mount type
    66  			continue
    67  		}
    68  		if mount.Destination == "" || mount.Source == "" {
    69  			return fmt.Errorf("invalid OCI spec - a mount must have both source and a destination: %+v", mount)
    70  		}
    71  
    72  		if coi.HostingSystem != nil {
    73  			hostPath := mount.Source
    74  			uvmPathForShare := path.Join(containerRootInUVM, fmt.Sprintf(guestpath.LCOWMountPathPrefixFmt, i))
    75  			uvmPathForFile := uvmPathForShare
    76  
    77  			readOnly := false
    78  			for _, o := range mount.Options {
    79  				if strings.ToLower(o) == "ro" {
    80  					readOnly = true
    81  					break
    82  				}
    83  			}
    84  
    85  			l := log.G(ctx).WithField("mount", fmt.Sprintf("%+v", mount))
    86  			if mount.Type == MountTypePhysicalDisk {
    87  				l.Debug("hcsshim::allocateLinuxResources Hot-adding SCSI physical disk for OCI mount")
    88  				uvmPathForShare = fmt.Sprintf(guestpath.LCOWGlobalMountPrefixFmt, coi.HostingSystem.UVMMountCounter())
    89  				scsiMount, err := coi.HostingSystem.AddSCSIPhysicalDisk(ctx, hostPath, uvmPathForShare, readOnly, mount.Options)
    90  				if err != nil {
    91  					return errors.Wrapf(err, "adding SCSI physical disk mount %+v", mount)
    92  				}
    93  
    94  				uvmPathForFile = scsiMount.UVMPath
    95  				r.Add(scsiMount)
    96  				coi.Spec.Mounts[i].Type = "none"
    97  			} else if mount.Type == MountTypeVirtualDisk {
    98  				l.Debug("hcsshim::allocateLinuxResources Hot-adding SCSI virtual disk for OCI mount")
    99  				uvmPathForShare = fmt.Sprintf(guestpath.LCOWGlobalMountPrefixFmt, coi.HostingSystem.UVMMountCounter())
   100  
   101  				// if the scsi device is already attached then we take the uvm path that the function below returns
   102  				// that is where it was previously mounted in UVM
   103  				scsiMount, err := coi.HostingSystem.AddSCSI(
   104  					ctx,
   105  					hostPath,
   106  					uvmPathForShare,
   107  					readOnly,
   108  					false,
   109  					mount.Options,
   110  					uvm.VMAccessTypeIndividual,
   111  				)
   112  				if err != nil {
   113  					return errors.Wrapf(err, "adding SCSI virtual disk mount %+v", mount)
   114  				}
   115  
   116  				uvmPathForFile = scsiMount.UVMPath
   117  				r.Add(scsiMount)
   118  				coi.Spec.Mounts[i].Type = "none"
   119  			} else if strings.HasPrefix(mount.Source, guestpath.SandboxMountPrefix) {
   120  				// Mounts that map to a path in UVM are specified with 'sandbox://' prefix.
   121  				// example: sandbox:///a/dirInUvm destination:/b/dirInContainer
   122  				uvmPathForFile = mount.Source
   123  			} else if strings.HasPrefix(mount.Source, guestpath.HugePagesMountPrefix) {
   124  				// currently we only support 2M hugepage size
   125  				hugePageSubDirs := strings.Split(strings.TrimPrefix(mount.Source, guestpath.HugePagesMountPrefix), "/")
   126  				if len(hugePageSubDirs) < 2 {
   127  					return errors.Errorf(
   128  						`%s mount path is invalid, expected format: %s<hugepage-size>/<hugepage-src-location>`,
   129  						mount.Source,
   130  						guestpath.HugePagesMountPrefix,
   131  					)
   132  				}
   133  
   134  				// hugepages:// should be followed by pagesize
   135  				if hugePageSubDirs[0] != "2M" {
   136  					return errors.Errorf(`only 2M (megabytes) pagesize is supported, got %s`, hugePageSubDirs[0])
   137  				}
   138  				// Hugepages inside a container are backed by a mount created inside a UVM.
   139  				uvmPathForFile = mount.Source
   140  			} else {
   141  				st, err := os.Stat(hostPath)
   142  				if err != nil {
   143  					return errors.Wrap(err, "could not open bind mount target")
   144  				}
   145  				restrictAccess := false
   146  				var allowedNames []string
   147  				if !st.IsDir() {
   148  					// Map the containing directory in, but restrict the share to a single
   149  					// file.
   150  					var fileName string
   151  					hostPath, fileName = filepath.Split(hostPath)
   152  					allowedNames = append(allowedNames, fileName)
   153  					restrictAccess = true
   154  					uvmPathForFile = path.Join(uvmPathForShare, fileName)
   155  				}
   156  				l.Debug("hcsshim::allocateLinuxResources Hot-adding Plan9 for OCI mount")
   157  
   158  				share, err := coi.HostingSystem.AddPlan9(ctx, hostPath, uvmPathForShare, readOnly, restrictAccess, allowedNames)
   159  				if err != nil {
   160  					return errors.Wrapf(err, "adding plan9 mount %+v", mount)
   161  				}
   162  				r.Add(share)
   163  			}
   164  			coi.Spec.Mounts[i].Source = uvmPathForFile
   165  		}
   166  	}
   167  
   168  	if coi.HostingSystem == nil {
   169  		return nil
   170  	}
   171  
   172  	if coi.hasWindowsAssignedDevices() {
   173  		windowsDevices, closers, err := handleAssignedDevicesLCOW(ctx, coi.HostingSystem, coi.Spec.Annotations, coi.Spec.Windows.Devices)
   174  		if err != nil {
   175  			return err
   176  		}
   177  		r.Add(closers...)
   178  		coi.Spec.Windows.Devices = windowsDevices
   179  	}
   180  	return nil
   181  }
   182  

View as plain text