podman

Форк
0
/
container_stat_common.go 
154 строки · 4.9 Кб
1
//go:build !remote && (linux || freebsd)
2

3
package libpod
4

5
import (
6
	"errors"
7
	"fmt"
8
	"os"
9
	"path/filepath"
10
	"strings"
11

12
	"github.com/containers/buildah/copier"
13
	"github.com/containers/podman/v5/libpod/define"
14
	"github.com/containers/podman/v5/pkg/copy"
15
)
16

17
// statOnHost stats the specified path *on the host*.  It returns the file info
18
// along with the resolved root and the resolved path.  Both paths are absolute
19
// to the host's root.  Note that the paths may resolved outside the
20
// container's mount point (e.g., to a volume or bind mount).
21
func (c *Container) statOnHost(mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) {
22
	// Now resolve the container's path.  It may hit a volume, it may hit a
23
	// bind mount, it may be relative.
24
	resolvedRoot, resolvedPath, err := c.resolvePath(mountPoint, containerPath)
25
	if err != nil {
26
		return nil, "", "", err
27
	}
28

29
	statInfo, err := secureStat(resolvedRoot, resolvedPath)
30
	return statInfo, resolvedRoot, resolvedPath, err
31
}
32

33
func (c *Container) stat(containerMountPoint string, containerPath string) (*define.FileInfo, string, string, error) {
34
	var (
35
		resolvedRoot     string
36
		resolvedPath     string
37
		absContainerPath string
38
		statInfo         *copier.StatForItem
39
		statErr          error
40
	)
41

42
	// Make sure that "/" copies the *contents* of the mount point and not
43
	// the directory.
44
	if containerPath == "/" {
45
		containerPath = "/."
46
	}
47

48
	// Wildcards are not allowed.
49
	// TODO: it's now technically possible wildcards.
50
	// We may consider enabling support in the future.
51
	if strings.Contains(containerPath, "*") {
52
		return nil, "", "", copy.ErrENOENT
53
	}
54

55
	statInfo, resolvedRoot, resolvedPath, statErr = c.statInContainer(containerMountPoint, containerPath)
56
	if statErr != nil {
57
		if statInfo == nil {
58
			return nil, "", "", statErr
59
		}
60
		// Not all errors from secureStat map to ErrNotExist, so we
61
		// have to look into the error string.  Turning it into an
62
		// ENOENT lets the API handlers return the correct status code
63
		// which is crucial for the remote client.
64
		if os.IsNotExist(statErr) || strings.Contains(statErr.Error(), "o such file or directory") {
65
			statErr = copy.ErrENOENT
66
		}
67
	}
68

69
	switch {
70
	case statInfo.IsSymlink:
71
		// Symlinks are already evaluated and always relative to the
72
		// container's mount point.
73
		absContainerPath = statInfo.ImmediateTarget
74
	case strings.HasPrefix(resolvedPath, containerMountPoint):
75
		// If the path is on the container's mount point, strip it off.
76
		absContainerPath = strings.TrimPrefix(resolvedPath, containerMountPoint)
77
		absContainerPath = filepath.Join("/", absContainerPath)
78
	default:
79
		// No symlink and not on the container's mount point, so let's
80
		// move it back to the original input.  It must have evaluated
81
		// to a volume or bind mount but we cannot return host paths.
82
		absContainerPath = containerPath
83
	}
84

85
	// Preserve the base path as specified by the user.  The `filepath`
86
	// packages likes to remove trailing slashes and dots that are crucial
87
	// to the copy logic.
88
	absContainerPath = copy.PreserveBasePath(containerPath, absContainerPath)
89
	resolvedPath = copy.PreserveBasePath(containerPath, resolvedPath)
90

91
	info := &define.FileInfo{
92
		IsDir:      statInfo.IsDir,
93
		Name:       filepath.Base(absContainerPath),
94
		Size:       statInfo.Size,
95
		Mode:       statInfo.Mode,
96
		ModTime:    statInfo.ModTime,
97
		LinkTarget: absContainerPath,
98
	}
99

100
	return info, resolvedRoot, resolvedPath, statErr
101
}
102

103
// secureStat extracts file info for path in a chroot'ed environment in root.
104
func secureStat(root string, path string) (*copier.StatForItem, error) {
105
	var glob string
106
	var err error
107

108
	// If root and path are equal, then dir must be empty and the glob must
109
	// be ".".
110
	if filepath.Clean(root) == filepath.Clean(path) {
111
		glob = "."
112
	} else {
113
		glob, err = filepath.Rel(root, path)
114
		if err != nil {
115
			return nil, err
116
		}
117
	}
118

119
	globStats, err := copier.Stat(root, "", copier.StatOptions{}, []string{glob})
120
	if err != nil {
121
		return nil, err
122
	}
123

124
	if len(globStats) != 1 {
125
		return nil, fmt.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats))
126
	}
127
	if len(globStats) != 1 {
128
		return nil, fmt.Errorf("internal error: secureStat: expected 1 result but got %d", len(globStats[0].Results))
129
	}
130

131
	// NOTE: the key in the map differ from `glob` when hitting symlink.
132
	// Hence, we just take the first (and only) key/value pair.
133
	for _, stat := range globStats[0].Results {
134
		var statErr error
135
		if stat.Error != "" {
136
			statErr = errors.New(stat.Error)
137
		}
138
		// If necessary evaluate the symlink
139
		if stat.IsSymlink {
140
			target, err := copier.Eval(root, path, copier.EvalOptions{})
141
			if err != nil {
142
				return nil, fmt.Errorf("evaluating symlink in container: %w", err)
143
			}
144
			// Need to make sure the symlink is relative to the root!
145
			target = strings.TrimPrefix(target, root)
146
			target = filepath.Join("/", target)
147
			stat.ImmediateTarget = target
148
		}
149
		return stat, statErr
150
	}
151

152
	// Nothing found!
153
	return nil, copy.ErrENOENT
154
}
155

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

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

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

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