summaryrefslogtreecommitdiff
path: root/vendor/github.com/projectatomic/buildah/imagebuildah/chroot_symlink.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/projectatomic/buildah/imagebuildah/chroot_symlink.go')
-rw-r--r--vendor/github.com/projectatomic/buildah/imagebuildah/chroot_symlink.go145
1 files changed, 145 insertions, 0 deletions
diff --git a/vendor/github.com/projectatomic/buildah/imagebuildah/chroot_symlink.go b/vendor/github.com/projectatomic/buildah/imagebuildah/chroot_symlink.go
new file mode 100644
index 000000000..b2452b61c
--- /dev/null
+++ b/vendor/github.com/projectatomic/buildah/imagebuildah/chroot_symlink.go
@@ -0,0 +1,145 @@
+package imagebuildah
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/storage/pkg/reexec"
+ "github.com/pkg/errors"
+ "golang.org/x/sys/unix"
+)
+
+const (
+ symlinkChrootedCommand = "chrootsymlinks-resolve"
+ maxSymlinksResolved = 40
+)
+
+func init() {
+ reexec.Register(symlinkChrootedCommand, resolveChrootedSymlinks)
+}
+
+func resolveChrootedSymlinks() {
+ status := 0
+ flag.Parse()
+ if len(flag.Args()) < 1 {
+ os.Exit(1)
+ }
+ // Our first parameter is the directory to chroot into.
+ if err := unix.Chdir(flag.Arg(0)); err != nil {
+ fmt.Fprintf(os.Stderr, "chdir(): %v\n", err)
+ os.Exit(1)
+ }
+ if err := unix.Chroot(flag.Arg(0)); err != nil {
+ fmt.Fprintf(os.Stderr, "chroot(): %v\n", err)
+ os.Exit(1)
+ }
+
+ // Our second paramter is the path name to evaluate for symbolic links
+ symLink, err := getSymbolicLink(flag.Arg(0), flag.Arg(1))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error getting symbolic links: %v\n", err)
+ os.Exit(1)
+ }
+ if _, err := os.Stdout.WriteString(symLink); err != nil {
+ fmt.Fprintf(os.Stderr, "error writing string to stdout: %v\n", err)
+ os.Exit(1)
+ }
+ os.Exit(status)
+}
+
+func resolveSymLink(rootdir, filename string) (string, error) {
+ // The child process expects a chroot and one path that
+ // will be consulted relative to the chroot directory and evaluated
+ // for any symbolic links present.
+ cmd := reexec.Command(symlinkChrootedCommand, rootdir, filename)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return "", errors.Wrapf(err, string(output))
+ }
+
+ // Hand back the resolved symlink, will be "" if a symlink is not found
+ return string(output), nil
+}
+
+// getSymbolic link goes through each part of the path and continues resolving symlinks as they appear.
+// Returns what the whole target path for what "path" resolves to.
+func getSymbolicLink(rootdir, path string) (string, error) {
+ var (
+ symPath string
+ symLinksResolved int
+ )
+
+ // Splitting path as we need to resolve each parth of the path at a time
+ splitPath := strings.Split(path, "/")
+ if splitPath[0] == "" {
+ splitPath = splitPath[1:]
+ symPath = "/"
+ }
+
+ for _, p := range splitPath {
+ // If we have resolved 40 symlinks, that means something is terribly wrong
+ // will return an error and exit
+ if symLinksResolved >= maxSymlinksResolved {
+ return "", errors.Errorf("have resolved %q symlinks, something is terribly wrong!", maxSymlinksResolved)
+ }
+
+ symPath = filepath.Join(symPath, p)
+ isSymlink, resolvedPath, err := hasSymlink(symPath)
+ if err != nil {
+ return "", errors.Wrapf(err, "error checking symlink for %q", symPath)
+ }
+ // if isSymlink is true, check if resolvedPath is potentially another symlink
+ // keep doing this till resolvedPath is not a symlink and isSymlink is false
+ for isSymlink == true {
+ // Need to keep track of number of symlinks resolved
+ // Will also return an error if the symlink points to itself as that will exceed maxSymlinksResolved
+ if symLinksResolved >= maxSymlinksResolved {
+ return "", errors.Errorf("have resolved %q symlinks, something is terribly wrong!", maxSymlinksResolved)
+ }
+ isSymlink, resolvedPath, err = hasSymlink(resolvedPath)
+ if err != nil {
+ return "", errors.Wrapf(err, "error checking symlink for %q", resolvedPath)
+ }
+ symLinksResolved++
+ }
+ // Assign resolvedPath to symPath. The next part of the loop will append the next part of the original path
+ // and continue resolving
+ symPath = resolvedPath
+ symLinksResolved++
+ }
+ return symPath, nil
+}
+
+// hasSymlink returns true and the target if path is symlink
+// otherwise it returns false and path
+func hasSymlink(path string) (bool, string, error) {
+ info, err := os.Lstat(path)
+ if os.IsNotExist(err) {
+ if err = os.MkdirAll(path, 0755); err != nil {
+ return false, "", errors.Wrapf(err, "error ensuring volume path %q exists", path)
+ }
+ info, err = os.Lstat(path)
+ if err != nil {
+ return false, "", errors.Wrapf(err, "error running lstat on %q", path)
+ }
+ }
+ // Return false and path as path is not a symlink
+ if info.Mode()&os.ModeSymlink != os.ModeSymlink {
+ return false, path, nil
+ }
+
+ // Read the symlink to get what it points to
+ targetDir, err := os.Readlink(path)
+ if err != nil {
+ return false, "", errors.Wrapf(err, "error reading link %q", path)
+ }
+ // if the symlink points to a relative path, prepend the path till now to the resolved path
+ if !filepath.IsAbs(targetDir) {
+ targetDir = filepath.Join(path, targetDir)
+ }
+ // run filepath.Clean to remove the ".." from relative paths
+ return true, filepath.Clean(targetDir), nil
+}