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