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