github.com/MaximeAubanel/moby@v1.13.1/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 := ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil { 95 return &errMountConfig{mnt, err} 96 } 97 default: 98 return &errMountConfig{mnt, errors.New("mount type unknown")} 99 } 100 return nil 101 } 102 103 type errMountConfig struct { 104 mount *mount.Mount 105 err error 106 } 107 108 func (e *errMountConfig) Error() string { 109 return fmt.Sprintf("invalid mount config for type %q: %v", e.mount.Type, e.err.Error()) 110 } 111 112 func errExtraField(name string) error { 113 return fmt.Errorf("field %s must not be specified", name) 114 } 115 func errMissingField(name string) error { 116 return fmt.Errorf("field %s must not be empty", name) 117 } 118 119 func validateAbsolute(p string) error { 120 p = convertSlash(p) 121 if filepath.IsAbs(p) { 122 return nil 123 } 124 return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p) 125 }