Skip to content

Commit

Permalink
LookPath like impelementations for OS specific executable searches
Browse files Browse the repository at this point in the history
Signed-off-by: Arthur Sengileyev <[email protected]>
  • Loading branch information
arixmkii committed Aug 29, 2022
1 parent 72a7da3 commit fbebd13
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 8 deletions.
21 changes: 13 additions & 8 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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, ":")
Expand Down Expand Up @@ -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.
Expand All @@ -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)
}
Expand Down
56 changes: 56 additions & 0 deletions pkg/config/lpe_unix.go
Original file line number Diff line number Diff line change
@@ -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}
}
90 changes: 90 additions & 0 deletions pkg/config/lpe_windows.go
Original file line number Diff line number Diff line change
@@ -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}
}

0 comments on commit fbebd13

Please sign in to comment.