| package volume |
| |
| import ( |
| "errors" |
| "fmt" |
| "os" |
| "path/filepath" |
| |
| "github.com/docker/docker/api/types/mount" |
| ) |
| |
| var errBindNotExist = errors.New("bind source path does not exist") |
| |
| type validateOpts struct { |
| skipBindSourceCheck bool |
| skipAbsolutePathCheck bool |
| } |
| |
| func validateMountConfig(mnt *mount.Mount, options ...func(*validateOpts)) error { |
| opts := validateOpts{} |
| for _, o := range options { |
| o(&opts) |
| } |
| |
| if len(mnt.Target) == 0 { |
| return &errMountConfig{mnt, errMissingField("Target")} |
| } |
| |
| if err := validateNotRoot(mnt.Target); err != nil { |
| return &errMountConfig{mnt, err} |
| } |
| |
| if !opts.skipAbsolutePathCheck { |
| if err := validateAbsolute(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(propagationModes) > 0 { |
| if _, ok := propagationModes[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 := validateAbsolute(mnt.Source); err != nil { |
| return &errMountConfig{mnt, err} |
| } |
| |
| // Do not allow binding to non-existent path |
| if !opts.skipBindSourceCheck { |
| fi, err := os.Stat(mnt.Source) |
| if err != nil { |
| if !os.IsNotExist(err) { |
| return &errMountConfig{mnt, err} |
| } |
| return &errMountConfig{mnt, errBindNotExist} |
| } |
| if err := validateStat(fi); err != nil { |
| return &errMountConfig{mnt, err} |
| } |
| } |
| 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")} |
| } |
| |
| if len(mnt.Source) != 0 { |
| if valid, err := IsVolumeNameValid(mnt.Source); !valid { |
| if err == nil { |
| err = errors.New("invalid volume name") |
| } |
| return &errMountConfig{mnt, err} |
| } |
| } |
| case mount.TypeTmpfs: |
| if len(mnt.Source) != 0 { |
| return &errMountConfig{mnt, errExtraField("Source")} |
| } |
| if _, err := ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil { |
| return &errMountConfig{mnt, err} |
| } |
| default: |
| return &errMountConfig{mnt, errors.New("mount type unknown")} |
| } |
| return nil |
| } |
| |
| type errMountConfig struct { |
| mount *mount.Mount |
| err error |
| } |
| |
| func (e *errMountConfig) Error() string { |
| return fmt.Sprintf("invalid mount config for type %q: %v", e.mount.Type, e.err.Error()) |
| } |
| |
| func errExtraField(name string) error { |
| return fmt.Errorf("field %s must not be specified", name) |
| } |
| func errMissingField(name string) error { |
| return fmt.Errorf("field %s must not be empty", name) |
| } |
| |
| func validateAbsolute(p string) error { |
| p = convertSlash(p) |
| if filepath.IsAbs(p) { |
| return nil |
| } |
| return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p) |
| } |