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>
This commit is contained in:
Sebastiaan van Stijn 2021-07-26 12:38:06 +02:00
parent 12f1b3ce43
commit efb87ad106
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
2 changed files with 40 additions and 30 deletions

View File

@ -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)
}

View File

@ -78,12 +78,18 @@ const (
rxMode = `(:(?P<mode>(?i)ro|rw))?`
)
var (
volumeNameRegexp = regexp.MustCompile(`^` + rxName + `$`)
reservedNameRegexp = regexp.MustCompile(`^` + rxReservedNames + `$`)
hostDirRegexp = regexp.MustCompile(`^` + rxHostDir + `$`)
mountDestinationRegexp = regexp.MustCompile(`^` + rxDestination + `$`)
windowsSplitRawSpecRegexp = regexp.MustCompile(`^` + rxSource + rxDestination + rxMode + `$`)
)
type mountValidator func(mnt *mount.Mount) error
func (p *windowsParser) windowsSplitRawSpec(raw, destRegex string) ([]string, error) {
specExp := regexp.MustCompile(`^` + rxSource + destRegex + rxMode + `$`)
match := specExp.FindStringSubmatch(strings.ToLower(raw))
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
}