github.com/gravitational/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  }