podman

Форк
0
/
container_path_resolution.go 
191 строка · 6.8 Кб
1
//go:build !remote
2

3
package libpod
4

5
import (
6
	"fmt"
7
	"path/filepath"
8
	"strings"
9

10
	"github.com/containers/podman/v5/libpod/define"
11
	securejoin "github.com/cyphar/filepath-securejoin"
12
	"github.com/opencontainers/runtime-spec/specs-go"
13
	"github.com/sirupsen/logrus"
14
)
15

16
// pathAbs returns an absolute path.  If the specified path is
17
// relative, it will be resolved relative to the container's working dir.
18
func (c *Container) pathAbs(path string) string {
19
	if !filepath.IsAbs(path) {
20
		// If the containerPath is not absolute, it's relative to the
21
		// container's working dir.  To be extra careful, let's first
22
		// join the working dir with "/", and the add the containerPath
23
		// to it.
24
		path = filepath.Join(filepath.Join("/", c.WorkingDir()), path)
25
	}
26
	return path
27
}
28

29
// resolvePath resolves the container's mount point and the container
30
// path as specified by the user.  Both may resolve to paths outside of the
31
// container's mount point when the container path hits a volume or bind mount.
32
//
33
// It returns a bool, indicating whether containerPath resolves outside of
34
// mountPoint (e.g., via a mount or volume), the resolved root (e.g., container
35
// mount, bind mount or volume) and the resolved path on the root (absolute to
36
// the host).
37
func (c *Container) resolvePath(mountPoint string, containerPath string) (string, string, error) {
38
	// Let's first make sure we have a path relative to the mount point.
39
	pathRelativeToContainerMountPoint := c.pathAbs(containerPath)
40
	resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint)
41
	pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint)
42
	pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint)
43

44
	// Now we have an "absolute container Path" but not yet resolved on the
45
	// host (e.g., "/foo/bar/file.txt").  As mentioned above, we need to
46
	// check if "/foo/bar/file.txt" is on a volume or bind mount.  To do
47
	// that, we need to walk *down* the paths to the root.  Assuming
48
	// volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar",
49
	// we must select "/foo/bar".  Once selected, we need to rebase the
50
	// remainder (i.e, "/file.txt") on the volume's mount point on the
51
	// host.  Same applies to bind mounts.
52

53
	searchPath := pathRelativeToContainerMountPoint
54
	for {
55
		volume, err := findVolume(c, searchPath)
56
		if err != nil {
57
			return "", "", err
58
		}
59
		if volume != nil {
60
			logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath)
61

62
			// TODO: We really need to force the volume to mount
63
			// before doing this, but that API is not exposed
64
			// externally right now and doing so is beyond the scope
65
			// of this commit.
66
			mountPoint, err := volume.MountPoint()
67
			if err != nil {
68
				return "", "", err
69
			}
70
			if mountPoint == "" {
71
				return "", "", fmt.Errorf("volume %s is not mounted, cannot copy into it", volume.Name())
72
			}
73

74
			// We found a matching volume for searchPath.  We now
75
			// need to first find the relative path of our input
76
			// path to the searchPath, and then join it with the
77
			// volume's mount point.
78
			pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
79
			absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(mountPoint, pathRelativeToVolume)
80
			if err != nil {
81
				return "", "", err
82
			}
83
			return mountPoint, absolutePathOnTheVolumeMount, nil
84
		}
85

86
		if mount := findBindMount(c, searchPath); mount != nil {
87
			logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath)
88
			// We found a matching bind mount for searchPath.  We
89
			// now need to first find the relative path of our
90
			// input path to the searchPath, and then join it with
91
			// the source of the bind mount.
92
			pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
93
			absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount)
94
			if err != nil {
95
				return "", "", err
96
			}
97
			return mount.Source, absolutePathOnTheBindMount, nil
98
		}
99

100
		if searchPath == "/" {
101
			// Cannot go beyond "/", so we're done.
102
			break
103
		}
104
		// Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar").
105
		searchPath = filepath.Dir(searchPath)
106
	}
107

108
	// No volume, no bind mount but just a normal path on the container.
109
	return mountPoint, resolvedPathOnTheContainerMountPoint, nil
110
}
111

112
// findVolume checks if the specified containerPath matches the destination
113
// path of a Volume.  Returns a matching Volume or nil.
114
func findVolume(c *Container, containerPath string) (*Volume, error) {
115
	runtime := c.Runtime()
116
	cleanedContainerPath := filepath.Clean(containerPath)
117
	for _, vol := range c.config.NamedVolumes {
118
		if cleanedContainerPath == filepath.Clean(vol.Dest) {
119
			return runtime.GetVolume(vol.Name)
120
		}
121
	}
122
	return nil, nil
123
}
124

125
// isSubDir checks whether path is a subdirectory of root.
126
func isSubDir(path, root string) bool {
127
	// check if the specified container path is below a bind mount.
128
	rel, err := filepath.Rel(root, path)
129
	if err != nil {
130
		return false
131
	}
132
	return rel != ".." && !strings.HasPrefix(rel, "../")
133
}
134

135
// isPathOnVolume returns true if the specified containerPath is a subdir of any
136
// Volume's destination.
137
func isPathOnVolume(c *Container, containerPath string) bool {
138
	cleanedContainerPath := filepath.Clean(containerPath)
139
	for _, vol := range c.config.NamedVolumes {
140
		cleanedDestination := filepath.Clean(vol.Dest)
141
		if cleanedContainerPath == cleanedDestination {
142
			return true
143
		}
144
		if isSubDir(cleanedContainerPath, cleanedDestination) {
145
			return true
146
		}
147
		for dest := cleanedDestination; dest != "/" && dest != "."; dest = filepath.Dir(dest) {
148
			if cleanedContainerPath == dest {
149
				return true
150
			}
151
		}
152
	}
153
	return false
154
}
155

156
// findBindMount checks if the specified containerPath matches the destination
157
// path of a Mount.  Returns a matching Mount or nil.
158
func findBindMount(c *Container, containerPath string) *specs.Mount {
159
	cleanedPath := filepath.Clean(containerPath)
160
	for _, m := range c.config.Spec.Mounts {
161
		if m.Type != define.TypeBind {
162
			continue
163
		}
164
		if cleanedPath == filepath.Clean(m.Destination) {
165
			mount := m
166
			return &mount
167
		}
168
	}
169
	return nil
170
}
171

172
// / isPathOnMount returns true if the specified containerPath is a subdir of any
173
// Mount's destination.
174
func isPathOnMount(c *Container, containerPath string) bool {
175
	cleanedContainerPath := filepath.Clean(containerPath)
176
	for _, m := range c.config.Spec.Mounts {
177
		cleanedDestination := filepath.Clean(m.Destination)
178
		if cleanedContainerPath == cleanedDestination {
179
			return true
180
		}
181
		if isSubDir(cleanedContainerPath, cleanedDestination) {
182
			return true
183
		}
184
		for dest := cleanedDestination; dest != "/" && dest != "."; dest = filepath.Dir(dest) {
185
			if cleanedContainerPath == dest {
186
				return true
187
			}
188
		}
189
	}
190
	return false
191
}
192

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.