Allow (but ignore) Cached and Delegated volume options

These are only used on OS X Docker, and ignored elsewhere - but
since they are ignored, they're guaranteed to be safe everywhere,
and people are using them.

Fixes: #3340

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
Matthew Heon
2019-06-18 16:21:40 -04:00
parent 292a48cab4
commit 8e5b294ac3
2 changed files with 40 additions and 21 deletions

View File

@ -251,9 +251,11 @@ func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]
return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z", splitVol[1]) return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z", splitVol[1])
} }
options = strings.Split(splitVol[1], ",") options = strings.Split(splitVol[1], ",")
if err := ValidateVolumeOpts(options); err != nil { opts, err := ValidateVolumeOpts(options)
if err != nil {
return nil, nil, err return nil, nil, err
} }
options = opts
} }
ctr, err := runtime.LookupContainer(splitVol[0]) ctr, err := runtime.LookupContainer(splitVol[0])
if err != nil { if err != nil {
@ -447,9 +449,11 @@ func getBindMount(args []string) (spec.Mount, error) {
newMount.Source = newMount.Destination newMount.Source = newMount.Destination
} }
if err := ValidateVolumeOpts(newMount.Options); err != nil { opts, err := ValidateVolumeOpts(newMount.Options)
if err != nil {
return newMount, err return newMount, err
} }
newMount.Options = opts
return newMount, nil return newMount, nil
} }
@ -575,35 +579,52 @@ func ValidateVolumeCtrDir(ctrDir string) error {
} }
// ValidateVolumeOpts validates a volume's options // ValidateVolumeOpts validates a volume's options
func ValidateVolumeOpts(options []string) error { func ValidateVolumeOpts(options []string) ([]string, error) {
var foundRootPropagation, foundRWRO, foundLabelChange, bindType int var foundRootPropagation, foundRWRO, foundLabelChange, bindType int
finalOpts := make([]string, 0, len(options))
discardOpts := []string{"cached", "delegated"}
for _, opt := range options { for _, opt := range options {
// The discarded ops are OS X specific volume options introduced
// in a recent Docker version.
// They have no meaning on Linux, so here we silently drop them.
// This matches Docker's behavior (the options are intended to
// be always safe to use, even not on OS X).
bad := false
for _, discard := range discardOpts {
if opt == discard {
bad = true
}
}
if bad {
continue
}
switch opt { switch opt {
case "rw", "ro": case "rw", "ro":
foundRWRO++ foundRWRO++
if foundRWRO > 1 { if foundRWRO > 1 {
return errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", ")) return nil, errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", "))
} }
case "z", "Z": case "z", "Z":
foundLabelChange++ foundLabelChange++
if foundLabelChange > 1 { if foundLabelChange > 1 {
return errors.Errorf("invalid options %q, can only specify 1 'z' or 'Z' option", strings.Join(options, ", ")) return nil, errors.Errorf("invalid options %q, can only specify 1 'z' or 'Z' option", strings.Join(options, ", "))
} }
case "private", "rprivate", "shared", "rshared", "slave", "rslave": case "private", "rprivate", "shared", "rshared", "slave", "rslave":
foundRootPropagation++ foundRootPropagation++
if foundRootPropagation > 1 { if foundRootPropagation > 1 {
return errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private' or '[r]slave' option", strings.Join(options, ", ")) return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private' or '[r]slave' option", strings.Join(options, ", "))
} }
case "bind", "rbind": case "bind", "rbind":
bindType++ bindType++
if bindType > 1 { if bindType > 1 {
return errors.Errorf("invalid options %q, can only specify 1 '[r]bind' option", strings.Join(options, ", ")) return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]bind' option", strings.Join(options, ", "))
} }
default: default:
return errors.Errorf("invalid option type %q", opt) return nil, errors.Errorf("invalid mount option %q", opt)
} }
finalOpts = append(finalOpts, opt)
} }
return nil return finalOpts, nil
} }
// GetVolumeMounts takes user provided input for bind mounts and creates Mount structs // GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
@ -633,9 +654,11 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
} }
if len(splitVol) > 2 { if len(splitVol) > 2 {
options = strings.Split(splitVol[2], ",") options = strings.Split(splitVol[2], ",")
if err := ValidateVolumeOpts(options); err != nil { opts, err := ValidateVolumeOpts(options)
if err != nil {
return nil, nil, err return nil, nil, err
} }
options = opts
} }
if err := ValidateVolumeHostDir(src); err != nil { if err := ValidateVolumeHostDir(src); err != nil {

View File

@ -20,26 +20,22 @@ func ProcessOptions(options []string) []string {
foundbind, foundrw, foundro bool foundbind, foundrw, foundro bool
rootProp string rootProp string
) )
for _, opt := range options { for _, opt := range options {
switch opt { switch opt {
case "bind", "rbind": case "bind", "rbind":
foundbind = true foundbind = true
break case "ro":
foundro = true
case "rw":
foundrw = true
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
rootProp = opt
} }
} }
if !foundbind { if !foundbind {
options = append(options, "rbind") options = append(options, "rbind")
} }
for _, opt := range options {
switch opt {
case "rw":
foundrw = true
case "ro":
foundro = true
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
rootProp = opt
}
}
if !foundrw && !foundro { if !foundrw && !foundro {
options = append(options, "rw") options = append(options, "rw")
} }