Browse Source

volume/mounts: pre-compile regular expressions

Compile the regular expression, instead of 'ad-hoc'. For this to work, I moved
the splitting was moved out of parseMountRaw() into ParseMountRaw(), and the
former was renamed to parseMount(). This function still receives the 'raw' string,
as it's used to include the "raw" spec for inclusion in error messages.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 4 years ago
parent
commit
efb87ad106
2 changed files with 40 additions and 30 deletions
  1. 13 3
      volume/mounts/lcow_parser.go
  2. 27 27
      volume/mounts/windows_parser.go

+ 13 - 3
volume/mounts/lcow_parser.go

@@ -3,6 +3,7 @@ package mounts // import "github.com/docker/docker/volume/mounts"
 import (
 	"errors"
 	"path"
+	"regexp"
 
 	"github.com/docker/docker/api/types/mount"
 )
@@ -24,6 +25,11 @@ func NewLCOWParser() Parser {
 //    -  Drive cannot be c: (explicitly checked in code, not RegEx)
 const rxLCOWDestination = `(?P<destination>/(?:[^\\/:*?"<>\r\n]+[/]?)*)`
 
+var (
+	lcowMountDestinationRegex = regexp.MustCompile(`^` + rxLCOWDestination + `$`)
+	lcowSplitRawSpec          = regexp.MustCompile(`^` + rxSource + rxLCOWDestination + rxMode + `$`)
+)
+
 var lcowSpecificValidators mountValidator = func(m *mount.Mount) error {
 	if path.Clean(m.Target) == "/" {
 		return ErrVolumeTargetIsRoot
@@ -39,13 +45,17 @@ type lcowParser struct {
 }
 
 func (p *lcowParser) ValidateMountConfig(mnt *mount.Mount) error {
-	return p.validateMountConfigReg(mnt, rxLCOWDestination, lcowSpecificValidators)
+	return p.validateMountConfigReg(mnt, lcowMountDestinationRegex, lcowSpecificValidators)
 }
 
 func (p *lcowParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
-	return p.parseMountRaw(raw, volumeDriver, rxLCOWDestination, false, lcowSpecificValidators)
+	arr, err := p.windowsSplitRawSpec(raw, lcowSplitRawSpec)
+	if err != nil {
+		return nil, err
+	}
+	return p.parseMount(arr, raw, volumeDriver, lcowMountDestinationRegex, false, lcowSpecificValidators)
 }
 
 func (p *lcowParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
-	return p.parseMountSpec(cfg, rxLCOWDestination, false, lcowSpecificValidators)
+	return p.parseMountSpec(cfg, lcowMountDestinationRegex, false, lcowSpecificValidators)
 }

+ 27 - 27
volume/mounts/windows_parser.go

@@ -78,12 +78,18 @@ const (
 	rxMode = `(:(?P<mode>(?i)ro|rw))?`
 )
 
-type mountValidator func(mnt *mount.Mount) error
+var (
+	volumeNameRegexp          = regexp.MustCompile(`^` + rxName + `$`)
+	reservedNameRegexp        = regexp.MustCompile(`^` + rxReservedNames + `$`)
+	hostDirRegexp             = regexp.MustCompile(`^` + rxHostDir + `$`)
+	mountDestinationRegexp    = regexp.MustCompile(`^` + rxDestination + `$`)
+	windowsSplitRawSpecRegexp = regexp.MustCompile(`^` + rxSource + rxDestination + rxMode + `$`)
+)
 
-func (p *windowsParser) windowsSplitRawSpec(raw, destRegex string) ([]string, error) {
-	specExp := regexp.MustCompile(`^` + rxSource + destRegex + rxMode + `$`)
-	match := specExp.FindStringSubmatch(strings.ToLower(raw))
+type mountValidator func(mnt *mount.Mount) error
 
+func (p *windowsParser) windowsSplitRawSpec(raw string, splitRegexp *regexp.Regexp) ([]string, error) {
+	match := splitRegexp.FindStringSubmatch(strings.ToLower(raw))
 	// Must have something back
 	if len(match) == 0 {
 		return nil, errInvalidSpec(raw)
@@ -92,7 +98,7 @@ func (p *windowsParser) windowsSplitRawSpec(raw, destRegex string) ([]string, er
 	var split []string
 	matchgroups := make(map[string]string)
 	// Pull out the sub expressions from the named capture groups
-	for i, name := range specExp.SubexpNames() {
+	for i, name := range splitRegexp.SubexpNames() {
 		matchgroups[name] = strings.ToLower(match[i])
 	}
 	if source, exists := matchgroups["source"]; exists {
@@ -115,11 +121,8 @@ func (p *windowsParser) windowsSplitRawSpec(raw, destRegex string) ([]string, er
 	// situation where the user intention was to map a file into a container through
 	// a local volume, but this is not supported by the platform.
 	if matchgroups["source"] == "" && matchgroups["destination"] != "" {
-		volExp := regexp.MustCompile(`^` + rxName + `$`)
-		reservedNameExp := regexp.MustCompile(`^` + rxReservedNames + `$`)
-
-		if volExp.MatchString(matchgroups["destination"]) {
-			if reservedNameExp.MatchString(matchgroups["destination"]) {
+		if volumeNameRegexp.MatchString(matchgroups["destination"]) {
+			if reservedNameRegexp.MatchString(matchgroups["destination"]) {
 				return nil, fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", matchgroups["destination"])
 			}
 		} else {
@@ -153,14 +156,14 @@ var windowsSpecificValidators mountValidator = func(mnt *mount.Mount) error {
 	return windowsValidateNotRoot(mnt.Target)
 }
 
-func windowsValidateRegex(p, r string) error {
-	if regexp.MustCompile(`^` + r + `$`).MatchString(strings.ToLower(p)) {
+func windowsValidateRegex(p string, r *regexp.Regexp) error {
+	if r.MatchString(strings.ToLower(p)) {
 		return nil
 	}
 	return fmt.Errorf("invalid mount path: '%s'", p)
 }
 func windowsValidateAbsolute(p string) error {
-	if err := windowsValidateRegex(p, rxDestination); err != nil {
+	if err := windowsValidateRegex(p, mountDestinationRegexp); err != nil {
 		return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
 	}
 	return nil
@@ -169,7 +172,7 @@ func windowsValidateAbsolute(p string) error {
 func windowsDetectMountType(p string) mount.Type {
 	if strings.HasPrefix(p, `\\.\pipe\`) {
 		return mount.TypeNamedPipe
-	} else if regexp.MustCompile(`^` + rxHostDir + `$`).MatchString(p) {
+	} else if hostDirRegexp.MatchString(p) {
 		return mount.TypeBind
 	} else {
 		return mount.TypeVolume
@@ -182,18 +185,16 @@ func (p *windowsParser) ReadWrite(mode string) bool {
 
 // ValidateVolumeName checks a volume name in a platform specific manner.
 func (p *windowsParser) ValidateVolumeName(name string) error {
-	nameExp := regexp.MustCompile(`^` + rxName + `$`)
-	if !nameExp.MatchString(name) {
+	if !volumeNameRegexp.MatchString(name) {
 		return errors.New("invalid volume name")
 	}
-	nameExp = regexp.MustCompile(`^` + rxReservedNames + `$`)
-	if nameExp.MatchString(name) {
+	if reservedNameRegexp.MatchString(name) {
 		return fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", name)
 	}
 	return nil
 }
 func (p *windowsParser) ValidateMountConfig(mnt *mount.Mount) error {
-	return p.validateMountConfigReg(mnt, rxDestination, windowsSpecificValidators)
+	return p.validateMountConfigReg(mnt, mountDestinationRegexp, windowsSpecificValidators)
 }
 
 type fileInfoProvider interface {
@@ -214,7 +215,7 @@ func (defaultFileInfoProvider) fileInfo(path string) (exist, isDir bool, err err
 	return true, fi.IsDir(), nil
 }
 
-func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex string, additionalValidators ...mountValidator) error {
+func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex *regexp.Regexp, additionalValidators ...mountValidator) error {
 
 	for _, v := range additionalValidators {
 		if err := v(mnt); err != nil {
@@ -299,15 +300,14 @@ func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex strin
 	return nil
 }
 func (p *windowsParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
-	return p.parseMountRaw(raw, volumeDriver, rxDestination, true, windowsSpecificValidators)
-}
-
-func (p *windowsParser) parseMountRaw(raw, volumeDriver, destRegex string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
-	arr, err := p.windowsSplitRawSpec(raw, destRegex)
+	arr, err := p.windowsSplitRawSpec(raw, windowsSplitRawSpecRegexp)
 	if err != nil {
 		return nil, err
 	}
+	return p.parseMount(arr, raw, volumeDriver, mountDestinationRegexp, true, windowsSpecificValidators)
+}
 
+func (p *windowsParser) parseMount(arr []string, raw, volumeDriver string, destRegex *regexp.Regexp, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
 	var spec mount.Mount
 	var mode string
 	switch len(arr) {
@@ -367,9 +367,9 @@ func (p *windowsParser) parseMountRaw(raw, volumeDriver, destRegex string, conve
 }
 
 func (p *windowsParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
-	return p.parseMountSpec(cfg, rxDestination, true, windowsSpecificValidators)
+	return p.parseMountSpec(cfg, mountDestinationRegexp, true, windowsSpecificValidators)
 }
-func (p *windowsParser) parseMountSpec(cfg mount.Mount, destRegex string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
+func (p *windowsParser) parseMountSpec(cfg mount.Mount, destRegex *regexp.Regexp, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
 	if err := p.validateMountConfigReg(&cfg, destRegex, additionalValidators...); err != nil {
 		return nil, err
 	}