github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/volume/linux_parser.go (about)

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