diff --git a/pkg/config/config.go b/pkg/config/config.go index 858f961b6..f71046a88 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -579,6 +579,7 @@ type SecretConfig struct { // ConfigMapConfig represents the "configmap" TOML config table // // revive does not like the name because the package is already called config +// //nolint:revive type ConfigMapConfig struct { // Driver specifies the configmap driver to use. @@ -1034,10 +1035,11 @@ func (c *Config) Capabilities(user string, addCapabilities, dropCapabilities []s // Device parses device mapping string to a src, dest & permissions string // Valid values for device looklike: -// '/dev/sdc" -// '/dev/sdc:/dev/xvdc" -// '/dev/sdc:/dev/xvdc:rwm" -// '/dev/sdc:rm" +// +// '/dev/sdc" +// '/dev/sdc:/dev/xvdc" +// '/dev/sdc:/dev/xvdc:rwm" +// '/dev/sdc:rm" func Device(device string) (src, dst, permissions string, err error) { permissions = "rwm" split := strings.Split(device, ":") @@ -1300,6 +1302,7 @@ func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error) dirList = append([]string{dir}, dirList...) } + expandedDirList := []string{} for _, path := range dirList { if path == bindirPrefix || strings.HasPrefix(path, bindirPrefix+string(filepath.Separator)) { // Calculate the path to the executable first time we encounter a $BINDIR prefix. @@ -1319,11 +1322,13 @@ func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error) path = filepath.Join(bindirPath, strings.TrimPrefix(path, bindirPrefix+string(filepath.Separator))) } } - fullpath := filepath.Join(path, name) - if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() { - return fullpath, nil - } + expandedDirList = append(expandedDirList, path) } + + if fullpath, err := lookPathExplicit(name, expandedDirList); err == nil { + return fullpath, nil + } + if searchPATH { return exec.LookPath(name) } diff --git a/pkg/config/lpe_unix.go b/pkg/config/lpe_unix.go new file mode 100644 index 000000000..54c64db06 --- /dev/null +++ b/pkg/config/lpe_unix.go @@ -0,0 +1,56 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos + +package config + +import ( + "errors" + "io/fs" + "os" + "os/exec" + "path/filepath" + "strings" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +var ErrNotFound = errors.New("executable file not found in $PATH") + +func findExecutable(file string) error { + d, err := os.Stat(file) + if err != nil { + return err + } + if m := d.Mode(); !m.IsDir() && m&0o111 != 0 { + return nil + } + return fs.ErrPermission +} + +// lookPathExplicit searches for an executable named file in the +// directories listed in dirs parameter. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// The result may be an absolute path or a path relative to the current directory. +func lookPathExplicit(file string, dirs []string) (string, error) { + // NOTE(rsc): I wish we could use the Plan 9 behavior here + // (only bypass the path if file begins with / or ./ or ../) + // but that would not match all the Unix shells. + + if strings.Contains(file, "/") { + err := findExecutable(file) + if err == nil { + return file, nil + } + return "", &exec.Error{Name: file, Err: err} + } + for _, dir := range dirs { + if dir == "" { + // Unix shell semantics: path element "" means "." + dir = "." + } + path := filepath.Join(dir, file) + if err := findExecutable(path); err == nil { + return path, nil + } + } + return "", &exec.Error{Name: file, Err: ErrNotFound} +} diff --git a/pkg/config/lpe_windows.go b/pkg/config/lpe_windows.go new file mode 100644 index 000000000..997ecbf26 --- /dev/null +++ b/pkg/config/lpe_windows.go @@ -0,0 +1,90 @@ +package config + +import ( + "errors" + "io/fs" + "os" + "os/exec" + "path/filepath" + "strings" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +var ErrNotFound = errors.New("executable file not found in %PATH%") + +func chkStat(file string) error { + d, err := os.Stat(file) + if err != nil { + return err + } + if d.IsDir() { + return fs.ErrPermission + } + return nil +} + +func hasExt(file string) bool { + i := strings.LastIndex(file, ".") + if i < 0 { + return false + } + return strings.LastIndexAny(file, `:\/`) < i +} + +func findExecutable(file string, exts []string) (string, error) { + if len(exts) == 0 { + return file, chkStat(file) + } + if hasExt(file) { + if chkStat(file) == nil { + return file, nil + } + } + for _, e := range exts { + if f := file + e; chkStat(f) == nil { + return f, nil + } + } + return "", fs.ErrNotExist +} + +// lookPathExplicit searches for an executable named file in the +// directories listed in dirs parameter. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// LookPath also uses PATHEXT environment variable to match +// a suitable candidate. +// The result may be an absolute path or a path relative to the current directory. +func lookPathExplicit(file string, dirs []string) (string, error) { + var exts []string + x := os.Getenv(`PATHEXT`) + if x != "" { + for _, e := range strings.Split(strings.ToLower(x), `;`) { + if e == "" { + continue + } + if e[0] != '.' { + e = "." + e + } + exts = append(exts, e) + } + } else { + exts = []string{".com", ".exe", ".bat", ".cmd"} + } + + if strings.ContainsAny(file, `:\/`) { + if f, err := findExecutable(file, exts); err == nil { + return f, nil + } else { + return "", &exec.Error{Name: file, Err: err} + } + } + if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { + return f, nil + } + for _, dir := range dirs { + if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { + return f, nil + } + } + return "", &exec.Error{Name: file, Err: ErrNotFound} +}