github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/volume/validate.go (about)

     1  package volume
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/docker/docker/api/types/mount"
    10  )
    11  
    12  var errBindNotExist = errors.New("bind source path does not exist")
    13  
    14  type validateOpts struct {
    15  	skipBindSourceCheck   bool
    16  	skipAbsolutePathCheck bool
    17  }
    18  
    19  func validateMountConfig(mnt *mount.Mount, options ...func(*validateOpts)) error {
    20  	opts := validateOpts{}
    21  	for _, o := range options {
    22  		o(&opts)
    23  	}
    24  
    25  	if len(mnt.Target) == 0 {
    26  		return &errMountConfig{mnt, errMissingField("Target")}
    27  	}
    28  
    29  	if err := validateNotRoot(mnt.Target); err != nil {
    30  		return &errMountConfig{mnt, err}
    31  	}
    32  
    33  	if !opts.skipAbsolutePathCheck {
    34  		if err := validateAbsolute(mnt.Target); err != nil {
    35  			return &errMountConfig{mnt, err}
    36  		}
    37  	}
    38  
    39  	switch mnt.Type {
    40  	case mount.TypeBind:
    41  		if len(mnt.Source) == 0 {
    42  			return &errMountConfig{mnt, errMissingField("Source")}
    43  		}
    44  		// Don't error out just because the propagation mode is not supported on the platform
    45  		if opts := mnt.BindOptions; opts != nil {
    46  			if len(opts.Propagation) > 0 && len(propagationModes) > 0 {
    47  				if _, ok := propagationModes[opts.Propagation]; !ok {
    48  					return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)}
    49  				}
    50  			}
    51  		}
    52  		if mnt.VolumeOptions != nil {
    53  			return &errMountConfig{mnt, errExtraField("VolumeOptions")}
    54  		}
    55  
    56  		if err := validateAbsolute(mnt.Source); err != nil {
    57  			return &errMountConfig{mnt, err}
    58  		}
    59  
    60  		// Do not allow binding to non-existent path
    61  		if !opts.skipBindSourceCheck {
    62  			fi, err := os.Stat(mnt.Source)
    63  			if err != nil {
    64  				if !os.IsNotExist(err) {
    65  					return &errMountConfig{mnt, err}
    66  				}
    67  				return &errMountConfig{mnt, errBindNotExist}
    68  			}
    69  			if err := validateStat(fi); err != nil {
    70  				return &errMountConfig{mnt, err}
    71  			}
    72  		}
    73  	case mount.TypeVolume:
    74  		if mnt.BindOptions != nil {
    75  			return &errMountConfig{mnt, errExtraField("BindOptions")}
    76  		}
    77  
    78  		if len(mnt.Source) == 0 && mnt.ReadOnly {
    79  			return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")}
    80  		}
    81  
    82  		if len(mnt.Source) != 0 {
    83  			if valid, err := IsVolumeNameValid(mnt.Source); !valid {
    84  				if err == nil {
    85  					err = errors.New("invalid volume name")
    86  				}
    87  				return &errMountConfig{mnt, err}
    88  			}
    89  		}
    90  	case mount.TypeTmpfs:
    91  		if len(mnt.Source) != 0 {
    92  			return &errMountConfig{mnt, errExtraField("Source")}
    93  		}
    94  		if err := ValidateTmpfsMountDestination(mnt.Target); err != nil {
    95  			return &errMountConfig{mnt, err}
    96  		}
    97  		if _, err := ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil {
    98  			return &errMountConfig{mnt, err}
    99  		}
   100  	default:
   101  		return &errMountConfig{mnt, errors.New("mount type unknown")}
   102  	}
   103  	return nil
   104  }
   105  
   106  type errMountConfig struct {
   107  	mount *mount.Mount
   108  	err   error
   109  }
   110  
   111  func (e *errMountConfig) Error() string {
   112  	return fmt.Sprintf("invalid mount config for type %q: %v", e.mount.Type, e.err.Error())
   113  }
   114  
   115  func errExtraField(name string) error {
   116  	return fmt.Errorf("field %s must not be specified", name)
   117  }
   118  func errMissingField(name string) error {
   119  	return fmt.Errorf("field %s must not be empty", name)
   120  }
   121  
   122  func validateAbsolute(p string) error {
   123  	p = convertSlash(p)
   124  	if filepath.IsAbs(p) {
   125  		return nil
   126  	}
   127  	return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
   128  }
   129  
   130  // ValidateTmpfsMountDestination validates the destination of tmpfs mount.
   131  // Currently, we have only two obvious rule for validation:
   132  //  - path must not be "/"
   133  //  - path must be absolute
   134  // We should add more rules carefully (#30166)
   135  func ValidateTmpfsMountDestination(dest string) error {
   136  	if err := validateNotRoot(dest); err != nil {
   137  		return err
   138  	}
   139  	return validateAbsolute(dest)
   140  }