1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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
}
|