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 }