...

Source file src/github.com/Microsoft/hcsshim/internal/guest/storage/mount.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/storage

     1  //go:build linux
     2  // +build linux
     3  
     4  package storage
     5  
     6  import (
     7  	"bufio"
     8  	"context"
     9  	"fmt"
    10  	"os"
    11  	"strings"
    12  	"syscall"
    13  
    14  	"github.com/pkg/errors"
    15  	"go.opencensus.io/trace"
    16  	"golang.org/x/sys/unix"
    17  
    18  	"github.com/Microsoft/hcsshim/internal/oc"
    19  )
    20  
    21  const procMountFile = "/proc/mounts"
    22  const numProcMountFields = 6
    23  
    24  // Test dependencies
    25  var (
    26  	osStat      = os.Stat
    27  	unixUnmount = unix.Unmount
    28  	unixMount   = unix.Mount
    29  	osRemoveAll = os.RemoveAll
    30  	listMounts  = listMountPointsUnderPath
    31  
    32  	flags = map[string]struct {
    33  		clear bool
    34  		flag  uintptr
    35  	}{
    36  		"acl":           {false, unix.MS_POSIXACL},
    37  		"async":         {true, unix.MS_SYNCHRONOUS},
    38  		"atime":         {true, unix.MS_NOATIME},
    39  		"bind":          {false, unix.MS_BIND},
    40  		"defaults":      {false, 0},
    41  		"dev":           {true, unix.MS_NODEV},
    42  		"diratime":      {true, unix.MS_NODIRATIME},
    43  		"dirsync":       {false, unix.MS_DIRSYNC},
    44  		"exec":          {true, unix.MS_NOEXEC},
    45  		"iversion":      {false, unix.MS_I_VERSION},
    46  		"lazytime":      {false, unix.MS_LAZYTIME},
    47  		"loud":          {true, unix.MS_SILENT},
    48  		"mand":          {false, unix.MS_MANDLOCK},
    49  		"noacl":         {true, unix.MS_POSIXACL},
    50  		"noatime":       {false, unix.MS_NOATIME},
    51  		"nodev":         {false, unix.MS_NODEV},
    52  		"nodiratime":    {false, unix.MS_NODIRATIME},
    53  		"noexec":        {false, unix.MS_NOEXEC},
    54  		"noiversion":    {true, unix.MS_I_VERSION},
    55  		"nolazytime":    {true, unix.MS_LAZYTIME},
    56  		"nomand":        {true, unix.MS_MANDLOCK},
    57  		"norelatime":    {true, unix.MS_RELATIME},
    58  		"nostrictatime": {true, unix.MS_STRICTATIME},
    59  		"nosuid":        {false, unix.MS_NOSUID},
    60  		"rbind":         {false, unix.MS_BIND | unix.MS_REC},
    61  		"relatime":      {false, unix.MS_RELATIME},
    62  		"remount":       {false, unix.MS_REMOUNT},
    63  		"ro":            {false, unix.MS_RDONLY},
    64  		"rw":            {true, unix.MS_RDONLY},
    65  		"silent":        {false, unix.MS_SILENT},
    66  		"strictatime":   {false, unix.MS_STRICTATIME},
    67  		"suid":          {true, unix.MS_NOSUID},
    68  		"sync":          {false, unix.MS_SYNCHRONOUS},
    69  	}
    70  
    71  	propagationFlags = map[string]uintptr{
    72  		"private":     unix.MS_PRIVATE,
    73  		"shared":      unix.MS_SHARED,
    74  		"slave":       unix.MS_SLAVE,
    75  		"unbindable":  unix.MS_UNBINDABLE,
    76  		"rprivate":    unix.MS_PRIVATE | unix.MS_REC,
    77  		"rshared":     unix.MS_SHARED | unix.MS_REC,
    78  		"rslave":      unix.MS_SLAVE | unix.MS_REC,
    79  		"runbindable": unix.MS_UNBINDABLE | unix.MS_REC,
    80  	}
    81  )
    82  
    83  func ParseMountOptions(options []string) (flagOpts uintptr, pgFlags []uintptr, data []string) {
    84  	for _, o := range options {
    85  		if f, exists := flags[o]; exists && f.flag != 0 {
    86  			if f.clear {
    87  				flagOpts &= ^f.flag
    88  			} else {
    89  				flagOpts |= f.flag
    90  			}
    91  		} else if f, exists := propagationFlags[o]; exists && f != 0 {
    92  			pgFlags = append(pgFlags, f)
    93  		} else {
    94  			data = append(data, o)
    95  		}
    96  	}
    97  	return
    98  }
    99  
   100  // MountRShared creates a bind mountpoint and marks it as rshared
   101  // Expected that the filepath exists before calling this function
   102  func MountRShared(path string) error {
   103  	if path == "" {
   104  		return errors.New("path must not be empty to mount as rshared")
   105  	}
   106  	if err := unixMount(path, path, "", syscall.MS_BIND, ""); err != nil {
   107  		return fmt.Errorf("failed to create bind mount for %v: %v", path, err)
   108  	}
   109  	if err := unixMount(path, path, "", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil {
   110  		return fmt.Errorf("failed to make %v rshared: %v", path, err)
   111  	}
   112  	return nil
   113  }
   114  
   115  // UnmountPath unmounts the target path if it exists and is a mount path. If
   116  // removeTarget this will remove the previously mounted folder.
   117  func UnmountPath(ctx context.Context, target string, removeTarget bool) (err error) {
   118  	_, span := oc.StartSpan(ctx, "storage::UnmountPath")
   119  	defer span.End()
   120  	defer func() { oc.SetSpanStatus(span, err) }()
   121  
   122  	span.AddAttributes(
   123  		trace.StringAttribute("target", target),
   124  		trace.BoolAttribute("remove", removeTarget))
   125  
   126  	if _, err := osStat(target); err != nil {
   127  		if os.IsNotExist(err) {
   128  			return nil
   129  		}
   130  		return errors.Wrapf(err, "failed to determine if path '%s' exists", target)
   131  	}
   132  
   133  	if err := unixUnmount(target, 0); err != nil {
   134  		// If `Unmount` returns `EINVAL` it's not mounted. Just delete the
   135  		// folder.
   136  		if err != unix.EINVAL {
   137  			return errors.Wrapf(err, "failed to unmount path '%s'", target)
   138  		}
   139  	}
   140  	if removeTarget {
   141  		return osRemoveAll(target)
   142  	}
   143  	return nil
   144  }
   145  
   146  func UnmountAllInPath(ctx context.Context, path string, removeTarget bool) (err error) {
   147  	childMounts, err := listMounts(path)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	for i := len(childMounts) - 1; i >= 0; i-- {
   153  		childPath := childMounts[i]
   154  		if err := UnmountPath(ctx, childPath, removeTarget); err != nil {
   155  			return err
   156  		}
   157  	}
   158  	return nil
   159  }
   160  
   161  func listMountPointsUnderPath(path string) ([]string, error) {
   162  	var mountPoints []string
   163  	f, err := os.Open(procMountFile)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	defer f.Close()
   168  	scanner := bufio.NewScanner(f)
   169  	for scanner.Scan() {
   170  		line := scanner.Text()
   171  		fields := strings.Split(line, " ")
   172  		if len(fields) < numProcMountFields {
   173  			continue
   174  		}
   175  		destPath := fields[1]
   176  		if strings.HasPrefix(destPath, path) {
   177  			mountPoints = append(mountPoints, destPath)
   178  		}
   179  	}
   180  
   181  	return mountPoints, nil
   182  }
   183  

View as plain text