1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/volume/validate.go
John Starks 54354db850 Windows: Add named pipe mount support
Current insider builds of Windows have support for mounting individual
named pipe servers from the host to the guest. This allows, for example,
exposing the docker engine's named pipe to a container.

This change allows the user to request such a mount via the normal bind
mount syntax in the CLI:

  docker run -v \\.\pipe\docker_engine:\\.\pipe\docker_engine <args>

Signed-off-by: John Starks <jostarks@microsoft.com>
2017-08-07 11:34:36 -07:00

162 lines
4.2 KiB
Go

package volume
import (
"errors"
"fmt"
"os"
"runtime"
"github.com/docker/docker/api/types/mount"
)
var errBindNotExist = errors.New("bind source path does not exist")
type validateOpts struct {
skipBindSourceCheck 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 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}
}
case mount.TypeNamedPipe:
if runtime.GOOS != "windows" {
return &errMountConfig{mnt, errors.New("named pipe bind mounts are not supported on this OS")}
}
if len(mnt.Source) == 0 {
return &errMountConfig{mnt, errMissingField("Source")}
}
if mnt.BindOptions != nil {
return &errMountConfig{mnt, errExtraField("BindOptions")}
}
if mnt.ReadOnly {
return &errMountConfig{mnt, errExtraField("ReadOnly")}
}
if detectMountType(mnt.Source) != mount.TypeNamedPipe {
return &errMountConfig{mnt, fmt.Errorf("'%s' is not a valid pipe path", mnt.Source)}
}
if detectMountType(mnt.Target) != mount.TypeNamedPipe {
return &errMountConfig{mnt, fmt.Errorf("'%s' is not a valid pipe path", mnt.Target)}
}
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 isAbsPath(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)
}