github.com/dpiddy/docker@v1.12.2-rc1/volume/volume_unix.go (about)

     1  // +build linux freebsd darwin solaris
     2  
     3  package volume
     4  
     5  import (
     6  	"fmt"
     7  	"path/filepath"
     8  	"strings"
     9  )
    10  
    11  // read-write modes
    12  var rwModes = map[string]bool{
    13  	"rw": true,
    14  	"ro": true,
    15  }
    16  
    17  // label modes
    18  var labelModes = map[string]bool{
    19  	"Z": true,
    20  	"z": true,
    21  }
    22  
    23  // BackwardsCompatible decides whether this mount point can be
    24  // used in old versions of Docker or not.
    25  // Only bind mounts and local volumes can be used in old versions of Docker.
    26  func (m *MountPoint) BackwardsCompatible() bool {
    27  	return len(m.Source) > 0 || m.Driver == DefaultDriverName
    28  }
    29  
    30  // HasResource checks whether the given absolute path for a container is in
    31  // this mount point. If the relative path starts with `../` then the resource
    32  // is outside of this mount point, but we can't simply check for this prefix
    33  // because it misses `..` which is also outside of the mount, so check both.
    34  func (m *MountPoint) HasResource(absolutePath string) bool {
    35  	relPath, err := filepath.Rel(m.Destination, absolutePath)
    36  	return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
    37  }
    38  
    39  // ParseMountSpec validates the configuration of mount information is valid.
    40  func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
    41  	spec = filepath.ToSlash(spec)
    42  
    43  	mp := &MountPoint{
    44  		RW:          true,
    45  		Propagation: DefaultPropagationMode,
    46  	}
    47  	if strings.Count(spec, ":") > 2 {
    48  		return nil, errInvalidSpec(spec)
    49  	}
    50  
    51  	arr := strings.SplitN(spec, ":", 3)
    52  	if arr[0] == "" {
    53  		return nil, errInvalidSpec(spec)
    54  	}
    55  
    56  	switch len(arr) {
    57  	case 1:
    58  		// Just a destination path in the container
    59  		mp.Destination = filepath.Clean(arr[0])
    60  	case 2:
    61  		if isValid := ValidMountMode(arr[1]); isValid {
    62  			// Destination + Mode is not a valid volume - volumes
    63  			// cannot include a mode. eg /foo:rw
    64  			return nil, errInvalidSpec(spec)
    65  		}
    66  		// Host Source Path or Name + Destination
    67  		mp.Source = arr[0]
    68  		mp.Destination = arr[1]
    69  	case 3:
    70  		// HostSourcePath+DestinationPath+Mode
    71  		mp.Source = arr[0]
    72  		mp.Destination = arr[1]
    73  		mp.Mode = arr[2] // Mode field is used by SELinux to decide whether to apply label
    74  		if !ValidMountMode(mp.Mode) {
    75  			return nil, errInvalidMode(mp.Mode)
    76  		}
    77  		mp.RW = ReadWrite(mp.Mode)
    78  		mp.Propagation = GetPropagation(mp.Mode)
    79  	default:
    80  		return nil, errInvalidSpec(spec)
    81  	}
    82  
    83  	//validate the volumes destination path
    84  	mp.Destination = filepath.Clean(mp.Destination)
    85  	if !filepath.IsAbs(mp.Destination) {
    86  		return nil, fmt.Errorf("Invalid volume destination path: '%s' mount path must be absolute.", mp.Destination)
    87  	}
    88  
    89  	// Destination cannot be "/"
    90  	if mp.Destination == "/" {
    91  		return nil, fmt.Errorf("Invalid specification: destination can't be '/' in '%s'", spec)
    92  	}
    93  
    94  	name, source := ParseVolumeSource(mp.Source)
    95  	if len(source) == 0 {
    96  		mp.Source = "" // Clear it out as we previously assumed it was not a name
    97  		mp.Driver = volumeDriver
    98  		// Named volumes can't have propagation properties specified.
    99  		// Their defaults will be decided by docker. This is just a
   100  		// safeguard. Don't want to get into situations where named
   101  		// volumes were mounted as '[r]shared' inside container and
   102  		// container does further mounts under that volume and these
   103  		// mounts become visible on  host and later original volume
   104  		// cleanup becomes an issue if container does not unmount
   105  		// submounts explicitly.
   106  		if HasPropagation(mp.Mode) {
   107  			return nil, errInvalidSpec(spec)
   108  		}
   109  	} else {
   110  		mp.Source = filepath.Clean(source)
   111  	}
   112  
   113  	copyData, isSet := getCopyMode(mp.Mode)
   114  	// do not allow copy modes on binds
   115  	if len(name) == 0 && isSet {
   116  		return nil, errInvalidMode(mp.Mode)
   117  	}
   118  
   119  	mp.CopyData = copyData
   120  	mp.Name = name
   121  
   122  	return mp, nil
   123  }
   124  
   125  // ParseVolumeSource parses the origin sources that's mounted into the container.
   126  // It returns a name and a source. It looks to see if the spec passed in
   127  // is an absolute file. If it is, it assumes the spec is a source. If not,
   128  // it assumes the spec is a name.
   129  func ParseVolumeSource(spec string) (string, string) {
   130  	if !filepath.IsAbs(spec) {
   131  		return spec, ""
   132  	}
   133  	return "", spec
   134  }
   135  
   136  // IsVolumeNameValid checks a volume name in a platform specific manner.
   137  func IsVolumeNameValid(name string) (bool, error) {
   138  	return true, nil
   139  }
   140  
   141  // ValidMountMode will make sure the mount mode is valid.
   142  // returns if it's a valid mount mode or not.
   143  func ValidMountMode(mode string) bool {
   144  	rwModeCount := 0
   145  	labelModeCount := 0
   146  	propagationModeCount := 0
   147  	copyModeCount := 0
   148  
   149  	for _, o := range strings.Split(mode, ",") {
   150  		switch {
   151  		case rwModes[o]:
   152  			rwModeCount++
   153  		case labelModes[o]:
   154  			labelModeCount++
   155  		case propagationModes[o]:
   156  			propagationModeCount++
   157  		case copyModeExists(o):
   158  			copyModeCount++
   159  		default:
   160  			return false
   161  		}
   162  	}
   163  
   164  	// Only one string for each mode is allowed.
   165  	if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 {
   166  		return false
   167  	}
   168  	return true
   169  }
   170  
   171  // ReadWrite tells you if a mode string is a valid read-write mode or not.
   172  // If there are no specifications w.r.t read write mode, then by default
   173  // it returns true.
   174  func ReadWrite(mode string) bool {
   175  	if !ValidMountMode(mode) {
   176  		return false
   177  	}
   178  
   179  	for _, o := range strings.Split(mode, ",") {
   180  		if o == "ro" {
   181  			return false
   182  		}
   183  	}
   184  
   185  	return true
   186  }