Przeglądaj źródła

Merge pull request #42673 from thaJeztah/regex_optimisation

volume/mounts: pre-compile regular expressions
Akihiro Suda 3 lat temu
rodzic
commit
8360de9955
2 zmienionych plików z 63 dodań i 50 usunięć
  1. 19 4
      volume/mounts/lcow_parser.go
  2. 44 46
      volume/mounts/windows_parser.go

+ 19 - 4
volume/mounts/lcow_parser.go

@@ -2,7 +2,10 @@ package mounts // import "github.com/docker/docker/volume/mounts"
 
 
 import (
 import (
 	"errors"
 	"errors"
+	"fmt"
 	"path"
 	"path"
+	"regexp"
+	"strings"
 
 
 	"github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/mount"
 )
 )
@@ -24,13 +27,21 @@ func NewLCOWParser() Parser {
 //    -  Drive cannot be c: (explicitly checked in code, not RegEx)
 //    -  Drive cannot be c: (explicitly checked in code, not RegEx)
 const rxLCOWDestination = `(?P<destination>/(?:[^\\/:*?"<>\r\n]+[/]?)*)`
 const rxLCOWDestination = `(?P<destination>/(?:[^\\/:*?"<>\r\n]+[/]?)*)`
 
 
-var lcowSpecificValidators mountValidator = func(m *mount.Mount) error {
+var (
+	lcowMountDestinationRegex = regexp.MustCompile(`^` + rxLCOWDestination + `$`)
+	lcowSplitRawSpec          = regexp.MustCompile(`^` + rxSource + rxLCOWDestination + rxMode + `$`)
+)
+
+var lcowValidators mountValidator = func(m *mount.Mount) error {
 	if path.Clean(m.Target) == "/" {
 	if path.Clean(m.Target) == "/" {
 		return ErrVolumeTargetIsRoot
 		return ErrVolumeTargetIsRoot
 	}
 	}
 	if m.Type == mount.TypeNamedPipe {
 	if m.Type == mount.TypeNamedPipe {
 		return errors.New("Linux containers on Windows do not support named pipe mounts")
 		return errors.New("Linux containers on Windows do not support named pipe mounts")
 	}
 	}
+	if !lcowMountDestinationRegex.MatchString(strings.ToLower(m.Target)) {
+		return fmt.Errorf("invalid mount path: '%s'", m.Target)
+	}
 	return nil
 	return nil
 }
 }
 
 
@@ -39,13 +50,17 @@ type lcowParser struct {
 }
 }
 
 
 func (p *lcowParser) ValidateMountConfig(mnt *mount.Mount) error {
 func (p *lcowParser) ValidateMountConfig(mnt *mount.Mount) error {
-	return p.validateMountConfigReg(mnt, rxLCOWDestination, lcowSpecificValidators)
+	return p.validateMountConfigReg(mnt, lcowValidators)
 }
 }
 
 
 func (p *lcowParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
 func (p *lcowParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
-	return p.parseMountRaw(raw, volumeDriver, rxLCOWDestination, false, lcowSpecificValidators)
+	arr, err := p.splitRawSpec(raw, lcowSplitRawSpec)
+	if err != nil {
+		return nil, err
+	}
+	return p.parseMount(arr, raw, volumeDriver, false, lcowValidators)
 }
 }
 
 
 func (p *lcowParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
 func (p *lcowParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
-	return p.parseMountSpec(cfg, rxLCOWDestination, false, lcowSpecificValidators)
+	return p.parseMountSpec(cfg, false, lcowValidators)
 }
 }

+ 44 - 46
volume/mounts/windows_parser.go

@@ -78,13 +78,18 @@ const (
 	rxMode = `(:(?P<mode>(?i)ro|rw))?`
 	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
 
 
-	// Must have something back
+func (p *windowsParser) splitRawSpec(raw string, splitRegexp *regexp.Regexp) ([]string, error) {
+	match := splitRegexp.FindStringSubmatch(strings.ToLower(raw))
 	if len(match) == 0 {
 	if len(match) == 0 {
 		return nil, errInvalidSpec(raw)
 		return nil, errInvalidSpec(raw)
 	}
 	}
@@ -92,7 +97,7 @@ func (p *windowsParser) windowsSplitRawSpec(raw, destRegex string) ([]string, er
 	var split []string
 	var split []string
 	matchgroups := make(map[string]string)
 	matchgroups := make(map[string]string)
 	// Pull out the sub expressions from the named capture groups
 	// 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])
 		matchgroups[name] = strings.ToLower(match[i])
 	}
 	}
 	if source, exists := matchgroups["source"]; exists {
 	if source, exists := matchgroups["source"]; exists {
@@ -115,11 +120,8 @@ func (p *windowsParser) windowsSplitRawSpec(raw, destRegex string) ([]string, er
 	// situation where the user intention was to map a file into a container through
 	// 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.
 	// a local volume, but this is not supported by the platform.
 	if matchgroups["source"] == "" && matchgroups["destination"] != "" {
 	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"])
 				return nil, fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", matchgroups["destination"])
 			}
 			}
 		} else {
 		} else {
@@ -149,18 +151,18 @@ func windowsValidateNotRoot(p string) error {
 	return nil
 	return nil
 }
 }
 
 
-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)) {
-		return nil
+var windowsValidators mountValidator = func(m *mount.Mount) error {
+	if err := windowsValidateNotRoot(m.Target); err != nil {
+		return err
+	}
+	if !mountDestinationRegexp.MatchString(strings.ToLower(m.Target)) {
+		return fmt.Errorf("invalid mount path: '%s'", m.Target)
 	}
 	}
-	return fmt.Errorf("invalid mount path: '%s'", p)
+	return nil
 }
 }
+
 func windowsValidateAbsolute(p string) error {
 func windowsValidateAbsolute(p string) error {
-	if err := windowsValidateRegex(p, rxDestination); err != nil {
+	if !mountDestinationRegexp.MatchString(strings.ToLower(p)) {
 		return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
 		return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
 	}
 	}
 	return nil
 	return nil
@@ -169,7 +171,7 @@ func windowsValidateAbsolute(p string) error {
 func windowsDetectMountType(p string) mount.Type {
 func windowsDetectMountType(p string) mount.Type {
 	if strings.HasPrefix(p, `\\.\pipe\`) {
 	if strings.HasPrefix(p, `\\.\pipe\`) {
 		return mount.TypeNamedPipe
 		return mount.TypeNamedPipe
-	} else if regexp.MustCompile(`^` + rxHostDir + `$`).MatchString(p) {
+	} else if hostDirRegexp.MatchString(p) {
 		return mount.TypeBind
 		return mount.TypeBind
 	} else {
 	} else {
 		return mount.TypeVolume
 		return mount.TypeVolume
@@ -182,18 +184,16 @@ func (p *windowsParser) ReadWrite(mode string) bool {
 
 
 // ValidateVolumeName checks a volume name in a platform specific manner.
 // ValidateVolumeName checks a volume name in a platform specific manner.
 func (p *windowsParser) ValidateVolumeName(name string) error {
 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")
 		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 fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", name)
 	}
 	}
 	return nil
 	return nil
 }
 }
 func (p *windowsParser) ValidateMountConfig(mnt *mount.Mount) error {
 func (p *windowsParser) ValidateMountConfig(mnt *mount.Mount) error {
-	return p.validateMountConfigReg(mnt, rxDestination, windowsSpecificValidators)
+	return p.validateMountConfigReg(mnt, windowsValidators)
 }
 }
 
 
 type fileInfoProvider interface {
 type fileInfoProvider interface {
@@ -214,20 +214,15 @@ func (defaultFileInfoProvider) fileInfo(path string) (exist, isDir bool, err err
 	return true, fi.IsDir(), nil
 	return true, fi.IsDir(), nil
 }
 }
 
 
-func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex string, additionalValidators ...mountValidator) error {
-
+func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, additionalValidators ...mountValidator) error {
+	if len(mnt.Target) == 0 {
+		return &errMountConfig{mnt, errMissingField("Target")}
+	}
 	for _, v := range additionalValidators {
 	for _, v := range additionalValidators {
 		if err := v(mnt); err != nil {
 		if err := v(mnt); err != nil {
 			return &errMountConfig{mnt, err}
 			return &errMountConfig{mnt, err}
 		}
 		}
 	}
 	}
-	if len(mnt.Target) == 0 {
-		return &errMountConfig{mnt, errMissingField("Target")}
-	}
-
-	if err := windowsValidateRegex(mnt.Target, destRegex); err != nil {
-		return &errMountConfig{mnt, err}
-	}
 
 
 	switch mnt.Type {
 	switch mnt.Type {
 	case mount.TypeBind:
 	case mount.TypeBind:
@@ -298,16 +293,16 @@ func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex strin
 	}
 	}
 	return nil
 	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)
+func (p *windowsParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
+	arr, err := p.splitRawSpec(raw, windowsSplitRawSpecRegexp)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
+	return p.parseMount(arr, raw, volumeDriver, true, windowsValidators)
+}
 
 
+func (p *windowsParser) parseMount(arr []string, raw, volumeDriver string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
 	var spec mount.Mount
 	var spec mount.Mount
 	var mode string
 	var mode string
 	switch len(arr) {
 	switch len(arr) {
@@ -356,7 +351,7 @@ func (p *windowsParser) parseMountRaw(raw, volumeDriver, destRegex string, conve
 		spec.VolumeOptions.NoCopy = !copyData
 		spec.VolumeOptions.NoCopy = !copyData
 	}
 	}
 
 
-	mp, err := p.parseMountSpec(spec, destRegex, convertTargetToBackslash, additionalValidators...)
+	mp, err := p.parseMountSpec(spec, convertTargetToBackslash, additionalValidators...)
 	if mp != nil {
 	if mp != nil {
 		mp.Mode = mode
 		mp.Mode = mode
 	}
 	}
@@ -367,10 +362,11 @@ func (p *windowsParser) parseMountRaw(raw, volumeDriver, destRegex string, conve
 }
 }
 
 
 func (p *windowsParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
 func (p *windowsParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
-	return p.parseMountSpec(cfg, rxDestination, true, windowsSpecificValidators)
+	return p.parseMountSpec(cfg, true, windowsValidators)
 }
 }
-func (p *windowsParser) parseMountSpec(cfg mount.Mount, destRegex string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
-	if err := p.validateMountConfigReg(&cfg, destRegex, additionalValidators...); err != nil {
+
+func (p *windowsParser) parseMountSpec(cfg mount.Mount, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
+	if err := p.validateMountConfigReg(&cfg, additionalValidators...); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	mp := &MountPoint{
 	mp := &MountPoint{
@@ -439,21 +435,23 @@ func (p *windowsParser) ParseVolumesFrom(spec string) (string, string, error) {
 }
 }
 
 
 func (p *windowsParser) DefaultPropagationMode() mount.Propagation {
 func (p *windowsParser) DefaultPropagationMode() mount.Propagation {
-	return mount.Propagation("")
+	return ""
 }
 }
 
 
 func (p *windowsParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) {
 func (p *windowsParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) {
 	return "", fmt.Errorf("%s does not support tmpfs", runtime.GOOS)
 	return "", fmt.Errorf("%s does not support tmpfs", runtime.GOOS)
 }
 }
+
 func (p *windowsParser) DefaultCopyMode() bool {
 func (p *windowsParser) DefaultCopyMode() bool {
 	return false
 	return false
 }
 }
+
 func (p *windowsParser) IsBackwardCompatible(m *MountPoint) bool {
 func (p *windowsParser) IsBackwardCompatible(m *MountPoint) bool {
 	return false
 	return false
 }
 }
 
 
 func (p *windowsParser) ValidateTmpfsMountDestination(dest string) error {
 func (p *windowsParser) ValidateTmpfsMountDestination(dest string) error {
-	return errors.New("Platform does not support tmpfs")
+	return errors.New("platform does not support tmpfs")
 }
 }
 
 
 func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool {
 func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool {