mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Remove non cryptographic randomness. Signed-off-by: Justin Cormack <justin.cormack@docker.com>
		
			
				
	
	
		
			423 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			423 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package mounts // import "github.com/docker/docker/volume/mounts"
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"path"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/docker/docker/api/types/mount"
 | 
						|
	"github.com/docker/docker/pkg/stringid"
 | 
						|
	"github.com/docker/docker/volume"
 | 
						|
)
 | 
						|
 | 
						|
type linuxParser struct {
 | 
						|
}
 | 
						|
 | 
						|
func linuxSplitRawSpec(raw string) ([]string, error) {
 | 
						|
	if strings.Count(raw, ":") > 2 {
 | 
						|
		return nil, errInvalidSpec(raw)
 | 
						|
	}
 | 
						|
 | 
						|
	arr := strings.SplitN(raw, ":", 3)
 | 
						|
	if arr[0] == "" {
 | 
						|
		return nil, errInvalidSpec(raw)
 | 
						|
	}
 | 
						|
	return arr, nil
 | 
						|
}
 | 
						|
 | 
						|
func linuxValidateNotRoot(p string) error {
 | 
						|
	p = path.Clean(strings.Replace(p, `\`, `/`, -1))
 | 
						|
	if p == "/" {
 | 
						|
		return ErrVolumeTargetIsRoot
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
func linuxValidateAbsolute(p string) error {
 | 
						|
	p = strings.Replace(p, `\`, `/`, -1)
 | 
						|
	if path.IsAbs(p) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
 | 
						|
}
 | 
						|
func (p *linuxParser) ValidateMountConfig(mnt *mount.Mount) error {
 | 
						|
	// there was something looking like a bug in existing codebase:
 | 
						|
	// - validateMountConfig on linux was called with options skipping bind source existence when calling ParseMountRaw
 | 
						|
	// - but not when calling ParseMountSpec directly... nor when the unit test called it directly
 | 
						|
	return p.validateMountConfigImpl(mnt, true)
 | 
						|
}
 | 
						|
func (p *linuxParser) validateMountConfigImpl(mnt *mount.Mount, validateBindSourceExists bool) error {
 | 
						|
	if len(mnt.Target) == 0 {
 | 
						|
		return &errMountConfig{mnt, errMissingField("Target")}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := linuxValidateNotRoot(mnt.Target); err != nil {
 | 
						|
		return &errMountConfig{mnt, err}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := linuxValidateAbsolute(mnt.Target); err != nil {
 | 
						|
		return &errMountConfig{mnt, err}
 | 
						|
	}
 | 
						|
 | 
						|
	switch mnt.Type {
 | 
						|
	case mount.TypeBind:
 | 
						|
		if len(mnt.Source) == 0 {
 | 
						|
			return &errMountConfig{mnt, errMissingField("Source")}
 | 
						|
		}
 | 
						|
		// Don't error out just because the propagation mode is not supported on the platform
 | 
						|
		if opts := mnt.BindOptions; opts != nil {
 | 
						|
			if len(opts.Propagation) > 0 && len(linuxPropagationModes) > 0 {
 | 
						|
				if _, ok := linuxPropagationModes[opts.Propagation]; !ok {
 | 
						|
					return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if mnt.VolumeOptions != nil {
 | 
						|
			return &errMountConfig{mnt, errExtraField("VolumeOptions")}
 | 
						|
		}
 | 
						|
 | 
						|
		if err := linuxValidateAbsolute(mnt.Source); err != nil {
 | 
						|
			return &errMountConfig{mnt, err}
 | 
						|
		}
 | 
						|
 | 
						|
		if validateBindSourceExists {
 | 
						|
			exists, _, err := currentFileInfoProvider.fileInfo(mnt.Source)
 | 
						|
			if err != nil {
 | 
						|
				return &errMountConfig{mnt, err}
 | 
						|
			}
 | 
						|
			if !exists {
 | 
						|
				return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	case mount.TypeVolume:
 | 
						|
		if mnt.BindOptions != nil {
 | 
						|
			return &errMountConfig{mnt, errExtraField("BindOptions")}
 | 
						|
		}
 | 
						|
 | 
						|
		if len(mnt.Source) == 0 && mnt.ReadOnly {
 | 
						|
			return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")}
 | 
						|
		}
 | 
						|
	case mount.TypeTmpfs:
 | 
						|
		if mnt.BindOptions != nil {
 | 
						|
			return &errMountConfig{mnt, errExtraField("BindOptions")}
 | 
						|
		}
 | 
						|
		if len(mnt.Source) != 0 {
 | 
						|
			return &errMountConfig{mnt, errExtraField("Source")}
 | 
						|
		}
 | 
						|
		if _, err := p.ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil {
 | 
						|
			return &errMountConfig{mnt, err}
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return &errMountConfig{mnt, errors.New("mount type unknown")}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// read-write modes
 | 
						|
var rwModes = map[string]bool{
 | 
						|
	"rw": true,
 | 
						|
	"ro": true,
 | 
						|
}
 | 
						|
 | 
						|
// label modes
 | 
						|
var linuxLabelModes = map[string]bool{
 | 
						|
	"Z": true,
 | 
						|
	"z": true,
 | 
						|
}
 | 
						|
 | 
						|
// consistency modes
 | 
						|
var linuxConsistencyModes = map[mount.Consistency]bool{
 | 
						|
	mount.ConsistencyFull:      true,
 | 
						|
	mount.ConsistencyCached:    true,
 | 
						|
	mount.ConsistencyDelegated: true,
 | 
						|
}
 | 
						|
var linuxPropagationModes = map[mount.Propagation]bool{
 | 
						|
	mount.PropagationPrivate:  true,
 | 
						|
	mount.PropagationRPrivate: true,
 | 
						|
	mount.PropagationSlave:    true,
 | 
						|
	mount.PropagationRSlave:   true,
 | 
						|
	mount.PropagationShared:   true,
 | 
						|
	mount.PropagationRShared:  true,
 | 
						|
}
 | 
						|
 | 
						|
const linuxDefaultPropagationMode = mount.PropagationRPrivate
 | 
						|
 | 
						|
func linuxGetPropagation(mode string) mount.Propagation {
 | 
						|
	for _, o := range strings.Split(mode, ",") {
 | 
						|
		prop := mount.Propagation(o)
 | 
						|
		if linuxPropagationModes[prop] {
 | 
						|
			return prop
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return linuxDefaultPropagationMode
 | 
						|
}
 | 
						|
 | 
						|
func linuxHasPropagation(mode string) bool {
 | 
						|
	for _, o := range strings.Split(mode, ",") {
 | 
						|
		if linuxPropagationModes[mount.Propagation(o)] {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func linuxValidMountMode(mode string) bool {
 | 
						|
	if mode == "" {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	rwModeCount := 0
 | 
						|
	labelModeCount := 0
 | 
						|
	propagationModeCount := 0
 | 
						|
	copyModeCount := 0
 | 
						|
	consistencyModeCount := 0
 | 
						|
 | 
						|
	for _, o := range strings.Split(mode, ",") {
 | 
						|
		switch {
 | 
						|
		case rwModes[o]:
 | 
						|
			rwModeCount++
 | 
						|
		case linuxLabelModes[o]:
 | 
						|
			labelModeCount++
 | 
						|
		case linuxPropagationModes[mount.Propagation(o)]:
 | 
						|
			propagationModeCount++
 | 
						|
		case copyModeExists(o):
 | 
						|
			copyModeCount++
 | 
						|
		case linuxConsistencyModes[mount.Consistency(o)]:
 | 
						|
			consistencyModeCount++
 | 
						|
		default:
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Only one string for each mode is allowed.
 | 
						|
	if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 || consistencyModeCount > 1 {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) ReadWrite(mode string) bool {
 | 
						|
	if !linuxValidMountMode(mode) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	for _, o := range strings.Split(mode, ",") {
 | 
						|
		if o == "ro" {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
 | 
						|
	arr, err := linuxSplitRawSpec(raw)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	var spec mount.Mount
 | 
						|
	var mode string
 | 
						|
	switch len(arr) {
 | 
						|
	case 1:
 | 
						|
		// Just a destination path in the container
 | 
						|
		spec.Target = arr[0]
 | 
						|
	case 2:
 | 
						|
		if linuxValidMountMode(arr[1]) {
 | 
						|
			// Destination + Mode is not a valid volume - volumes
 | 
						|
			// cannot include a mode. e.g. /foo:rw
 | 
						|
			return nil, errInvalidSpec(raw)
 | 
						|
		}
 | 
						|
		// Host Source Path or Name + Destination
 | 
						|
		spec.Source = arr[0]
 | 
						|
		spec.Target = arr[1]
 | 
						|
	case 3:
 | 
						|
		// HostSourcePath+DestinationPath+Mode
 | 
						|
		spec.Source = arr[0]
 | 
						|
		spec.Target = arr[1]
 | 
						|
		mode = arr[2]
 | 
						|
	default:
 | 
						|
		return nil, errInvalidSpec(raw)
 | 
						|
	}
 | 
						|
 | 
						|
	if !linuxValidMountMode(mode) {
 | 
						|
		return nil, errInvalidMode(mode)
 | 
						|
	}
 | 
						|
 | 
						|
	if path.IsAbs(spec.Source) {
 | 
						|
		spec.Type = mount.TypeBind
 | 
						|
	} else {
 | 
						|
		spec.Type = mount.TypeVolume
 | 
						|
	}
 | 
						|
 | 
						|
	spec.ReadOnly = !p.ReadWrite(mode)
 | 
						|
 | 
						|
	// cannot assume that if a volume driver is passed in that we should set it
 | 
						|
	if volumeDriver != "" && spec.Type == mount.TypeVolume {
 | 
						|
		spec.VolumeOptions = &mount.VolumeOptions{
 | 
						|
			DriverConfig: &mount.Driver{Name: volumeDriver},
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if copyData, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
 | 
						|
		if spec.VolumeOptions == nil {
 | 
						|
			spec.VolumeOptions = &mount.VolumeOptions{}
 | 
						|
		}
 | 
						|
		spec.VolumeOptions.NoCopy = !copyData
 | 
						|
	}
 | 
						|
	if linuxHasPropagation(mode) {
 | 
						|
		spec.BindOptions = &mount.BindOptions{
 | 
						|
			Propagation: linuxGetPropagation(mode),
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mp, err := p.parseMountSpec(spec, false)
 | 
						|
	if mp != nil {
 | 
						|
		mp.Mode = mode
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err)
 | 
						|
	}
 | 
						|
	return mp, err
 | 
						|
}
 | 
						|
func (p *linuxParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
 | 
						|
	return p.parseMountSpec(cfg, true)
 | 
						|
}
 | 
						|
func (p *linuxParser) parseMountSpec(cfg mount.Mount, validateBindSourceExists bool) (*MountPoint, error) {
 | 
						|
	if err := p.validateMountConfigImpl(&cfg, validateBindSourceExists); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	mp := &MountPoint{
 | 
						|
		RW:          !cfg.ReadOnly,
 | 
						|
		Destination: path.Clean(filepath.ToSlash(cfg.Target)),
 | 
						|
		Type:        cfg.Type,
 | 
						|
		Spec:        cfg,
 | 
						|
	}
 | 
						|
 | 
						|
	switch cfg.Type {
 | 
						|
	case mount.TypeVolume:
 | 
						|
		if cfg.Source == "" {
 | 
						|
			mp.Name = stringid.GenerateRandomID()
 | 
						|
		} else {
 | 
						|
			mp.Name = cfg.Source
 | 
						|
		}
 | 
						|
		mp.CopyData = p.DefaultCopyMode()
 | 
						|
 | 
						|
		if cfg.VolumeOptions != nil {
 | 
						|
			if cfg.VolumeOptions.DriverConfig != nil {
 | 
						|
				mp.Driver = cfg.VolumeOptions.DriverConfig.Name
 | 
						|
			}
 | 
						|
			if cfg.VolumeOptions.NoCopy {
 | 
						|
				mp.CopyData = false
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case mount.TypeBind:
 | 
						|
		mp.Source = path.Clean(filepath.ToSlash(cfg.Source))
 | 
						|
		if cfg.BindOptions != nil && len(cfg.BindOptions.Propagation) > 0 {
 | 
						|
			mp.Propagation = cfg.BindOptions.Propagation
 | 
						|
		} else {
 | 
						|
			// If user did not specify a propagation mode, get
 | 
						|
			// default propagation mode.
 | 
						|
			mp.Propagation = linuxDefaultPropagationMode
 | 
						|
		}
 | 
						|
	case mount.TypeTmpfs:
 | 
						|
		// NOP
 | 
						|
	}
 | 
						|
	return mp, nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) ParseVolumesFrom(spec string) (string, string, error) {
 | 
						|
	if len(spec) == 0 {
 | 
						|
		return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
 | 
						|
	}
 | 
						|
 | 
						|
	specParts := strings.SplitN(spec, ":", 2)
 | 
						|
	id := specParts[0]
 | 
						|
	mode := "rw"
 | 
						|
 | 
						|
	if len(specParts) == 2 {
 | 
						|
		mode = specParts[1]
 | 
						|
		if !linuxValidMountMode(mode) {
 | 
						|
			return "", "", errInvalidMode(mode)
 | 
						|
		}
 | 
						|
		// For now don't allow propagation properties while importing
 | 
						|
		// volumes from data container. These volumes will inherit
 | 
						|
		// the same propagation property as of the original volume
 | 
						|
		// in data container. This probably can be relaxed in future.
 | 
						|
		if linuxHasPropagation(mode) {
 | 
						|
			return "", "", errInvalidMode(mode)
 | 
						|
		}
 | 
						|
		// Do not allow copy modes on volumes-from
 | 
						|
		if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
 | 
						|
			return "", "", errInvalidMode(mode)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return id, mode, nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) DefaultPropagationMode() mount.Propagation {
 | 
						|
	return linuxDefaultPropagationMode
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) {
 | 
						|
	var rawOpts []string
 | 
						|
	if readOnly {
 | 
						|
		rawOpts = append(rawOpts, "ro")
 | 
						|
	}
 | 
						|
 | 
						|
	if opt != nil && opt.Mode != 0 {
 | 
						|
		rawOpts = append(rawOpts, fmt.Sprintf("mode=%o", opt.Mode))
 | 
						|
	}
 | 
						|
 | 
						|
	if opt != nil && opt.SizeBytes != 0 {
 | 
						|
		// calculate suffix here, making this linux specific, but that is
 | 
						|
		// okay, since API is that way anyways.
 | 
						|
 | 
						|
		// we do this by finding the suffix that divides evenly into the
 | 
						|
		// value, returning the value itself, with no suffix, if it fails.
 | 
						|
		//
 | 
						|
		// For the most part, we don't enforce any semantic to this values.
 | 
						|
		// The operating system will usually align this and enforce minimum
 | 
						|
		// and maximums.
 | 
						|
		var (
 | 
						|
			size   = opt.SizeBytes
 | 
						|
			suffix string
 | 
						|
		)
 | 
						|
		for _, r := range []struct {
 | 
						|
			suffix  string
 | 
						|
			divisor int64
 | 
						|
		}{
 | 
						|
			{"g", 1 << 30},
 | 
						|
			{"m", 1 << 20},
 | 
						|
			{"k", 1 << 10},
 | 
						|
		} {
 | 
						|
			if size%r.divisor == 0 {
 | 
						|
				size = size / r.divisor
 | 
						|
				suffix = r.suffix
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		rawOpts = append(rawOpts, fmt.Sprintf("size=%d%s", size, suffix))
 | 
						|
	}
 | 
						|
	return strings.Join(rawOpts, ","), nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) DefaultCopyMode() bool {
 | 
						|
	return true
 | 
						|
}
 | 
						|
func (p *linuxParser) ValidateVolumeName(name string) error {
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) IsBackwardCompatible(m *MountPoint) bool {
 | 
						|
	return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName
 | 
						|
}
 | 
						|
 | 
						|
func (p *linuxParser) ValidateTmpfsMountDestination(dest string) error {
 | 
						|
	if err := linuxValidateNotRoot(dest); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return linuxValidateAbsolute(dest)
 | 
						|
}
 |