/
validate.go
140 lines (122 loc) · 3.57 KB
/
validate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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 := ValidateTmpfsMountDestination(mnt.Target); err != nil {
return &errMountConfig{mnt, err}
}
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)
}
// ValidateTmpfsMountDestination validates the destination of tmpfs mount.
// Currently, we have only two obvious rule for validation:
// - path must not be "/"
// - path must be absolute
// We should add more rules carefully (#30166)
func ValidateTmpfsMountDestination(dest string) error {
if err := validateNotRoot(dest); err != nil {
return err
}
return validateAbsolute(dest)
}