github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/volume/mounts/linux_parser.go (about)

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