...

Source file src/github.com/Microsoft/hcsshim/internal/jobcontainers/mounts.go

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

     1  //go:build windows
     2  
     3  package jobcontainers
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/Microsoft/hcsshim/internal/layers"
    13  	"github.com/Microsoft/hcsshim/internal/log"
    14  	"github.com/Microsoft/hcsshim/internal/logfields"
    15  	specs "github.com/opencontainers/runtime-spec/specs-go"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  // namedPipePath returns true if the given path is to a named pipe.
    21  func isnamedPipePath(p string) bool {
    22  	return strings.HasPrefix(p, `\\.\pipe\`)
    23  }
    24  
    25  // Strip the drive letter (if there is one) so we don't end up with "%CONTAINER_SANDBOX_MOUNT_POINT%"\C:\path\to\mount
    26  func stripDriveLetter(name string) string {
    27  	// Remove drive letter
    28  	if len(name) == 2 && name[1] == ':' {
    29  		name = "."
    30  	} else if len(name) > 2 && name[1] == ':' {
    31  		name = name[2:]
    32  	}
    33  	return name
    34  }
    35  
    36  // fallbackMountSetup adds the mounts requested in the OCI runtime spec. This is
    37  // the fallback behavior if the Bind Filter dll is not available on the host, so
    38  // typical bind mount like functionality can't be used. Instead, symlink the
    39  // path requested to a relative path under where the container image volume is
    40  // located.
    41  func fallbackMountSetup(spec *specs.Spec, sandboxVolumePath string) error {
    42  	for _, mount := range spec.Mounts {
    43  		if mount.Destination == "" || mount.Source == "" {
    44  			return fmt.Errorf("invalid OCI spec - a mount must have both source and a destination: %+v", mount)
    45  		}
    46  
    47  		if isnamedPipePath(mount.Source) {
    48  			return errors.New("named pipe mounts not supported for job containers - interact with the pipe directly")
    49  		}
    50  
    51  		fullCtrPath := filepath.Join(sandboxVolumePath, stripDriveLetter(mount.Destination))
    52  		// Make sure all of the dirs leading up to the full path exist.
    53  		strippedCtrPath := filepath.Dir(fullCtrPath)
    54  		if err := os.MkdirAll(strippedCtrPath, 0777); err != nil {
    55  			return errors.Wrap(err, "failed to make directory for job container mount")
    56  		}
    57  
    58  		if err := os.Symlink(mount.Source, fullCtrPath); err != nil {
    59  			return errors.Wrap(err, "failed to setup mount for job container")
    60  		}
    61  	}
    62  	return nil
    63  }
    64  
    65  // setupMounts sets up all requested mounts present in the OCI runtime spec. They will be mounted from
    66  // mount.Source to mount.Destination as well as mounted from mount.Source to under the rootfs location
    67  // for backwards compat with systems that don't have the Bind Filter functionality available.
    68  func (c *JobContainer) setupMounts(ctx context.Context, spec *specs.Spec) error {
    69  	mountedDirPath, err := os.MkdirTemp("", "jobcontainer")
    70  	if err != nil {
    71  		return err
    72  	}
    73  	defer os.RemoveAll(mountedDirPath)
    74  	mountedDirPath += "\\"
    75  
    76  	// os.Mkdirall has troubles with volume paths on Windows it seems..
    77  	// For an example, it seems during the recursive portion when trying to
    78  	// figure out what parent directories to make, this is what gets spit out for
    79  	// three calls deep.
    80  	//
    81  	// First iteration: \\?\Volume{93df249b-ae90-4619-8e3c-28482c52729c}\blah
    82  	// Second: \\?\Volume{93df249b-ae90-4619-8e3c-28482c52729c}
    83  	// Third: \\?
    84  	//
    85  	// and then it will pass that to Mkdir and bail. So to avoid rolling our own
    86  	// mkdirall, just mount the volume somewhere and do the links and then dismount.
    87  	if err := layers.MountSandboxVolume(ctx, mountedDirPath, c.spec.Root.Path); err != nil {
    88  		return err
    89  	}
    90  
    91  	for _, mount := range spec.Mounts {
    92  		if mount.Destination == "" || mount.Source == "" {
    93  			return fmt.Errorf("invalid OCI spec - a mount must have both source and a destination: %+v", mount)
    94  		}
    95  
    96  		if isnamedPipePath(mount.Source) {
    97  			return errors.New("named pipe mounts not supported for job containers - interact with the pipe directly")
    98  		}
    99  
   100  		// If the destination exists, log a warning. The default behavior for bindflt is to shadow the directory,
   101  		// but on the host this may lead to more wonky situations than in a normal container. Mounts should not
   102  		// be relied on too heavily so this shouldn't be an error case.
   103  		if _, err := os.Stat(mount.Destination); err == nil {
   104  			log.G(ctx).WithFields(logrus.Fields{
   105  				logfields.ContainerID: c.id,
   106  				"mountSource":         mount.Source,
   107  				"mountDestination":    mount.Destination,
   108  			}).Warn("job container mount destination exists and will be shadowed")
   109  		}
   110  
   111  		readOnly := false
   112  		for _, o := range mount.Options {
   113  			if strings.ToLower(o) == "ro" {
   114  				readOnly = true
   115  			}
   116  		}
   117  
   118  		if err := c.job.ApplyFileBinding(mount.Destination, mount.Source, readOnly); err != nil {
   119  			return err
   120  		}
   121  
   122  		// For backwards compat with how mounts worked without the bind filter, additionally plop the directory/file
   123  		// to a relative path inside the containers rootfs.
   124  		fullCtrPath := filepath.Join(mountedDirPath, stripDriveLetter(mount.Destination))
   125  		// Make sure all of the dirs leading up to the full path exist.
   126  		strippedCtrPath := filepath.Dir(fullCtrPath)
   127  		if err := os.MkdirAll(strippedCtrPath, 0777); err != nil {
   128  			return fmt.Errorf("failed to make directory for job container mount: %w", err)
   129  		}
   130  
   131  		// Best effort; log if the backwards compatible symlink approach doesn't work.
   132  		if err := os.Symlink(mount.Source, fullCtrPath); err != nil {
   133  			log.G(ctx).WithError(err).Warnf("failed to setup symlink from %s to containers rootfs at %s", mount.Source, fullCtrPath)
   134  		}
   135  	}
   136  
   137  	return layers.RemoveSandboxMountPoint(ctx, mountedDirPath)
   138  }
   139  

View as plain text