github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/volume/mounts/linux_parser.go (about)

     1  package mounts // import "github.com/docker/docker/volume/mounts"
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"path"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/docker/docker/api/types/mount"
    11  	"github.com/docker/docker/pkg/stringid"
    12  	"github.com/docker/docker/volume"
    13  )
    14  
    15  type linuxParser struct {
    16  }
    17  
    18  func linuxSplitRawSpec(raw string) ([]string, error) {
    19  	if strings.Count(raw, ":") > 2 {
    20  		return nil, errInvalidSpec(raw)
    21  	}
    22  
    23  	arr := strings.SplitN(raw, ":", 3)
    24  	if arr[0] == "" {
    25  		return nil, errInvalidSpec(raw)
    26  	}
    27  	return arr, nil
    28  }
    29  
    30  func linuxValidateNotRoot(p string) error {
    31  	p = path.Clean(strings.Replace(p, `\`, `/`, -1))
    32  	if p == "/" {
    33  		return ErrVolumeTargetIsRoot
    34  	}
    35  	return nil
    36  }
    37  func linuxValidateAbsolute(p string) error {
    38  	p = strings.Replace(p, `\`, `/`, -1)
    39  	if path.IsAbs(p) {
    40  		return nil
    41  	}
    42  	return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
    43  }
    44  func (p *linuxParser) ValidateMountConfig(mnt *mount.Mount) error {
    45  	// there was something looking like a bug in existing codebase:
    46  	// - validateMountConfig on linux was called with options skipping bind source existence when calling ParseMountRaw
    47  	// - but not when calling ParseMountSpec directly... nor when the unit test called it directly
    48  	return p.validateMountConfigImpl(mnt, true)
    49  }
    50  func (p *linuxParser) validateMountConfigImpl(mnt *mount.Mount, validateBindSourceExists bool) error {
    51  	if len(mnt.Target) == 0 {
    52  		return &errMountConfig{mnt, errMissingField("Target")}
    53  	}
    54  
    55  	if err := linuxValidateNotRoot(mnt.Target); err != nil {
    56  		return &errMountConfig{mnt, err}
    57  	}
    58  
    59  	if err := linuxValidateAbsolute(mnt.Target); err != nil {
    60  		return &errMountConfig{mnt, err}
    61  	}
    62  
    63  	switch mnt.Type {
    64  	case mount.TypeBind:
    65  		if len(mnt.Source) == 0 {
    66  			return &errMountConfig{mnt, errMissingField("Source")}
    67  		}
    68  		// Don't error out just because the propagation mode is not supported on the platform
    69  		if opts := mnt.BindOptions; opts != nil {
    70  			if len(opts.Propagation) > 0 && len(linuxPropagationModes) > 0 {
    71  				if _, ok := linuxPropagationModes[opts.Propagation]; !ok {
    72  					return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)}
    73  				}
    74  			}
    75  		}
    76  		if mnt.VolumeOptions != nil {
    77  			return &errMountConfig{mnt, errExtraField("VolumeOptions")}
    78  		}
    79  
    80  		if err := linuxValidateAbsolute(mnt.Source); err != nil {
    81  			return &errMountConfig{mnt, err}
    82  		}
    83  
    84  		if validateBindSourceExists {
    85  			exists, _, _ := currentFileInfoProvider.fileInfo(mnt.Source)
    86  			if !exists {
    87  				return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)}
    88  			}
    89  		}
    90  
    91  	case mount.TypeVolume:
    92  		if mnt.BindOptions != nil {
    93  			return &errMountConfig{mnt, errExtraField("BindOptions")}
    94  		}
    95  
    96  		if len(mnt.Source) == 0 && mnt.ReadOnly {
    97  			return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")}
    98  		}
    99  	case mount.TypeTmpfs:
   100  		if mnt.BindOptions != nil {
   101  			return &errMountConfig{mnt, errExtraField("BindOptions")}
   102  		}
   103  		if len(mnt.Source) != 0 {
   104  			return &errMountConfig{mnt, errExtraField("Source")}
   105  		}
   106  		if _, err := p.ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil {
   107  			return &errMountConfig{mnt, err}
   108  		}
   109  	default:
   110  		return &errMountConfig{mnt, errors.New("mount type unknown")}
   111  	}
   112  	return nil
   113  }
   114  
   115  // read-write modes
   116  var rwModes = map[string]bool{
   117  	"rw": true,
   118  	"ro": true,
   119  }
   120  
   121  // label modes
   122  var linuxLabelModes = map[string]bool{
   123  	"Z": true,
   124  	"z": true,
   125  }
   126  
   127  // consistency modes
   128  var linuxConsistencyModes = map[mount.Consistency]bool{
   129  	mount.ConsistencyFull:      true,
   130  	mount.ConsistencyCached:    true,
   131  	mount.ConsistencyDelegated: true,
   132  }
   133  var linuxPropagationModes = map[mount.Propagation]bool{
   134  	mount.PropagationPrivate:  true,
   135  	mount.PropagationRPrivate: true,
   136  	mount.PropagationSlave:    true,
   137  	mount.PropagationRSlave:   true,
   138  	mount.PropagationShared:   true,
   139  	mount.PropagationRShared:  true,
   140  }
   141  
   142  const linuxDefaultPropagationMode = mount.PropagationRPrivate
   143  
   144  func linuxGetPropagation(mode string) mount.Propagation {
   145  	for _, o := range strings.Split(mode, ",") {
   146  		prop := mount.Propagation(o)
   147  		if linuxPropagationModes[prop] {
   148  			return prop
   149  		}
   150  	}
   151  	return linuxDefaultPropagationMode
   152  }
   153  
   154  func linuxHasPropagation(mode string) bool {
   155  	for _, o := range strings.Split(mode, ",") {
   156  		if linuxPropagationModes[mount.Propagation(o)] {
   157  			return true
   158  		}
   159  	}
   160  	return false
   161  }
   162  
   163  func linuxValidMountMode(mode string) bool {
   164  	if mode == "" {
   165  		return true
   166  	}
   167  
   168  	rwModeCount := 0
   169  	labelModeCount := 0
   170  	propagationModeCount := 0
   171  	copyModeCount := 0
   172  	consistencyModeCount := 0
   173  
   174  	for _, o := range strings.Split(mode, ",") {
   175  		switch {
   176  		case rwModes[o]:
   177  			rwModeCount++
   178  		case linuxLabelModes[o]:
   179  			labelModeCount++
   180  		case linuxPropagationModes[mount.Propagation(o)]:
   181  			propagationModeCount++
   182  		case copyModeExists(o):
   183  			copyModeCount++
   184  		case linuxConsistencyModes[mount.Consistency(o)]:
   185  			consistencyModeCount++
   186  		default:
   187  			return false
   188  		}
   189  	}
   190  
   191  	// Only one string for each mode is allowed.
   192  	if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 || consistencyModeCount > 1 {
   193  		return false
   194  	}
   195  	return true
   196  }
   197  
   198  func (p *linuxParser) ReadWrite(mode string) bool {
   199  	if !linuxValidMountMode(mode) {
   200  		return false
   201  	}
   202  
   203  	for _, o := range strings.Split(mode, ",") {
   204  		if o == "ro" {
   205  			return false
   206  		}
   207  	}
   208  	return true
   209  }
   210  
   211  func (p *linuxParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
   212  	arr, err := linuxSplitRawSpec(raw)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	var spec mount.Mount
   218  	var mode string
   219  	switch len(arr) {
   220  	case 1:
   221  		// Just a destination path in the container
   222  		spec.Target = arr[0]
   223  	case 2:
   224  		if linuxValidMountMode(arr[1]) {
   225  			// Destination + Mode is not a valid volume - volumes
   226  			// cannot include a mode. e.g. /foo:rw
   227  			return nil, errInvalidSpec(raw)
   228  		}
   229  		// Host Source Path or Name + Destination
   230  		spec.Source = arr[0]
   231  		spec.Target = arr[1]
   232  	case 3:
   233  		// HostSourcePath+DestinationPath+Mode
   234  		spec.Source = arr[0]
   235  		spec.Target = arr[1]
   236  		mode = arr[2]
   237  	default:
   238  		return nil, errInvalidSpec(raw)
   239  	}
   240  
   241  	if !linuxValidMountMode(mode) {
   242  		return nil, errInvalidMode(mode)
   243  	}
   244  
   245  	if path.IsAbs(spec.Source) {
   246  		spec.Type = mount.TypeBind
   247  	} else {
   248  		spec.Type = mount.TypeVolume
   249  	}
   250  
   251  	spec.ReadOnly = !p.ReadWrite(mode)
   252  
   253  	// cannot assume that if a volume driver is passed in that we should set it
   254  	if volumeDriver != "" && spec.Type == mount.TypeVolume {
   255  		spec.VolumeOptions = &mount.VolumeOptions{
   256  			DriverConfig: &mount.Driver{Name: volumeDriver},
   257  		}
   258  	}
   259  
   260  	if copyData, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
   261  		if spec.VolumeOptions == nil {
   262  			spec.VolumeOptions = &mount.VolumeOptions{}
   263  		}
   264  		spec.VolumeOptions.NoCopy = !copyData
   265  	}
   266  	if linuxHasPropagation(mode) {
   267  		spec.BindOptions = &mount.BindOptions{
   268  			Propagation: linuxGetPropagation(mode),
   269  		}
   270  	}
   271  
   272  	mp, err := p.parseMountSpec(spec, false)
   273  	if mp != nil {
   274  		mp.Mode = mode
   275  	}
   276  	if err != nil {
   277  		err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err)
   278  	}
   279  	return mp, err
   280  }
   281  func (p *linuxParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
   282  	return p.parseMountSpec(cfg, true)
   283  }
   284  func (p *linuxParser) parseMountSpec(cfg mount.Mount, validateBindSourceExists bool) (*MountPoint, error) {
   285  	if err := p.validateMountConfigImpl(&cfg, validateBindSourceExists); err != nil {
   286  		return nil, err
   287  	}
   288  	mp := &MountPoint{
   289  		RW:          !cfg.ReadOnly,
   290  		Destination: path.Clean(filepath.ToSlash(cfg.Target)),
   291  		Type:        cfg.Type,
   292  		Spec:        cfg,
   293  	}
   294  
   295  	switch cfg.Type {
   296  	case mount.TypeVolume:
   297  		if cfg.Source == "" {
   298  			mp.Name = stringid.GenerateNonCryptoID()
   299  		} else {
   300  			mp.Name = cfg.Source
   301  		}
   302  		mp.CopyData = p.DefaultCopyMode()
   303  
   304  		if cfg.VolumeOptions != nil {
   305  			if cfg.VolumeOptions.DriverConfig != nil {
   306  				mp.Driver = cfg.VolumeOptions.DriverConfig.Name
   307  			}
   308  			if cfg.VolumeOptions.NoCopy {
   309  				mp.CopyData = false
   310  			}
   311  		}
   312  	case mount.TypeBind:
   313  		mp.Source = path.Clean(filepath.ToSlash(cfg.Source))
   314  		if cfg.BindOptions != nil && len(cfg.BindOptions.Propagation) > 0 {
   315  			mp.Propagation = cfg.BindOptions.Propagation
   316  		} else {
   317  			// If user did not specify a propagation mode, get
   318  			// default propagation mode.
   319  			mp.Propagation = linuxDefaultPropagationMode
   320  		}
   321  	case mount.TypeTmpfs:
   322  		// NOP
   323  	}
   324  	return mp, nil
   325  }
   326  
   327  func (p *linuxParser) ParseVolumesFrom(spec string) (string, string, error) {
   328  	if len(spec) == 0 {
   329  		return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
   330  	}
   331  
   332  	specParts := strings.SplitN(spec, ":", 2)
   333  	id := specParts[0]
   334  	mode := "rw"
   335  
   336  	if len(specParts) == 2 {
   337  		mode = specParts[1]
   338  		if !linuxValidMountMode(mode) {
   339  			return "", "", errInvalidMode(mode)
   340  		}
   341  		// For now don't allow propagation properties while importing
   342  		// volumes from data container. These volumes will inherit
   343  		// the same propagation property as of the original volume
   344  		// in data container. This probably can be relaxed in future.
   345  		if linuxHasPropagation(mode) {
   346  			return "", "", errInvalidMode(mode)
   347  		}
   348  		// Do not allow copy modes on volumes-from
   349  		if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
   350  			return "", "", errInvalidMode(mode)
   351  		}
   352  	}
   353  	return id, mode, nil
   354  }
   355  
   356  func (p *linuxParser) DefaultPropagationMode() mount.Propagation {
   357  	return linuxDefaultPropagationMode
   358  }
   359  
   360  func (p *linuxParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) {
   361  	var rawOpts []string
   362  	if readOnly {
   363  		rawOpts = append(rawOpts, "ro")
   364  	}
   365  
   366  	if opt != nil && opt.Mode != 0 {
   367  		rawOpts = append(rawOpts, fmt.Sprintf("mode=%o", opt.Mode))
   368  	}
   369  
   370  	if opt != nil && opt.SizeBytes != 0 {
   371  		// calculate suffix here, making this linux specific, but that is
   372  		// okay, since API is that way anyways.
   373  
   374  		// we do this by finding the suffix that divides evenly into the
   375  		// value, returning the value itself, with no suffix, if it fails.
   376  		//
   377  		// For the most part, we don't enforce any semantic to this values.
   378  		// The operating system will usually align this and enforce minimum
   379  		// and maximums.
   380  		var (
   381  			size   = opt.SizeBytes
   382  			suffix string
   383  		)
   384  		for _, r := range []struct {
   385  			suffix  string
   386  			divisor int64
   387  		}{
   388  			{"g", 1 << 30},
   389  			{"m", 1 << 20},
   390  			{"k", 1 << 10},
   391  		} {
   392  			if size%r.divisor == 0 {
   393  				size = size / r.divisor
   394  				suffix = r.suffix
   395  				break
   396  			}
   397  		}
   398  
   399  		rawOpts = append(rawOpts, fmt.Sprintf("size=%d%s", size, suffix))
   400  	}
   401  	return strings.Join(rawOpts, ","), nil
   402  }
   403  
   404  func (p *linuxParser) DefaultCopyMode() bool {
   405  	return true
   406  }
   407  func (p *linuxParser) ValidateVolumeName(name string) error {
   408  	return nil
   409  }
   410  
   411  func (p *linuxParser) IsBackwardCompatible(m *MountPoint) bool {
   412  	return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName
   413  }
   414  
   415  func (p *linuxParser) ValidateTmpfsMountDestination(dest string) error {
   416  	if err := linuxValidateNotRoot(dest); err != nil {
   417  		return err
   418  	}
   419  	return linuxValidateAbsolute(dest)
   420  }