diff options
-rw-r--r-- | runsc/boot/fs.go | 45 | ||||
-rw-r--r-- | runsc/boot/loader.go | 4 | ||||
-rw-r--r-- | runsc/specutils/specutils.go | 25 |
3 files changed, 64 insertions, 10 deletions
diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 8996b1398..6f5379a6d 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -16,6 +16,7 @@ package boot import ( "fmt" + "path" "path/filepath" "strconv" "strings" @@ -701,3 +702,47 @@ func setFileSystemForProcess(procArgs *kernel.CreateProcessArgs, spec *specs.Spe procArgs.Root = containerRootDirent return nil } + +// GetExecutablePathInternal traverses the *container's* filesystem to resolve +// exec's absolute path. For example, if the container is being served files by +// the fsgofer serving /foo/bar as the container root, it will search within +// /foo/bar, not the host root. +// TODO: Unit test this. +func GetExecutablePathInternal(ctx context.Context, procArgs *kernel.CreateProcessArgs) (string, error) { + exec := filepath.Clean(procArgs.Filename) + + // Don't search PATH if exec is a path to a file (absolute or relative). + if strings.IndexByte(exec, '/') >= 0 { + return exec, nil + } + + // Search the PATH for a file whose name matches the one we are looking + // for. + pathDirs := specutils.GetPath(procArgs.Envv) + for _, p := range pathDirs { + // Walk to the end of the path. + curDir := procArgs.Root + for _, pc := range strings.Split(p, "/") { + var err error + if curDir, err = curDir.Walk(ctx, curDir, pc); err != nil { + break + } + } + if curDir == nil { + continue + } + // Check for the executable in the path directory. + dirent, err := curDir.Walk(ctx, curDir, exec) + if err != nil { + continue + } + // Check whether we can read and execute the file in question. + if err := dirent.Inode.CheckPermission(ctx, fs.PermMask{Read: true, Execute: true}); err != nil { + log.Infof("Found executable at %q, but user cannot execute it: %v", path.Join(p, exec), err) + continue + } + return path.Join("/", p, exec), nil + } + + return "", fmt.Errorf("could not find executable %s in path %v", exec, pathDirs) +} diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 7debf0ac2..2f212c704 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -428,6 +428,10 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config return 0, fmt.Errorf("failed to create new process: %v", err) } + if procArgs.Filename, err = GetExecutablePathInternal(procArgs.NewContext(k), &procArgs); err != nil { + return 0, err + } + tg, err := l.k.CreateProcess(procArgs) if err != nil { return 0, fmt.Errorf("failed to create process in sentry: %v", err) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 27441cbde..5fb53edb2 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -126,6 +126,8 @@ func ReadSpec(bundleDir string) (*specs.Spec, error) { // GetExecutablePath returns the absolute path to the executable, relative to // the root. It searches the environment PATH for the first file that exists // with the given name. +// TODO: Remove this in favor of finding executables via +// boot.GetExecutablePathInternal. func GetExecutablePath(exec, root string, env []string) (string, error) { exec = filepath.Clean(exec) @@ -134,18 +136,9 @@ func GetExecutablePath(exec, root string, env []string) (string, error) { return exec, nil } - // Get the PATH from the environment. - const prefix = "PATH=" - var path []string - for _, e := range env { - if strings.HasPrefix(e, prefix) { - path = strings.Split(strings.TrimPrefix(e, prefix), ":") - break - } - } - // Search the PATH for a file whose name matches the one we are looking // for. + path := GetPath(env) for _, p := range path { abs := filepath.Join(root, p, exec) // Do not follow symlink link because the target is in the container @@ -161,6 +154,18 @@ func GetExecutablePath(exec, root string, env []string) (string, error) { return exec, nil } +// GetPath returns the PATH as a slice of strings given the environemnt +// variables. +func GetPath(env []string) []string { + const prefix = "PATH=" + for _, e := range env { + if strings.HasPrefix(e, prefix) { + return strings.Split(strings.TrimPrefix(e, prefix), ":") + } + } + return nil +} + // Capabilities takes in spec and returns a TaskCapabilities corresponding to // the spec. func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) { |