summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--runsc/boot/fs.go45
-rw-r--r--runsc/boot/loader.go4
-rw-r--r--runsc/specutils/specutils.go25
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) {