podman

Форк
0
/
rootless.go 
205 строк · 5.2 Кб
1
package rootless
2

3
import (
4
	"errors"
5
	"fmt"
6
	"os"
7
	"sort"
8
	"sync"
9

10
	"github.com/containers/storage/pkg/fileutils"
11
	"github.com/containers/storage/pkg/lockfile"
12
	"github.com/moby/sys/user"
13
	spec "github.com/opencontainers/runtime-spec/specs-go"
14
)
15

16
// TryJoinPauseProcess attempts to join the namespaces of the pause PID via
17
// TryJoinFromFilePaths.  If joining fails, it attempts to delete the specified
18
// file.
19
func TryJoinPauseProcess(pausePidPath string) (bool, int, error) {
20
	if err := fileutils.Exists(pausePidPath); err != nil {
21
		if errors.Is(err, os.ErrNotExist) {
22
			return false, -1, nil
23
		}
24
		return false, -1, err
25
	}
26

27
	became, ret, err := TryJoinFromFilePaths("", false, []string{pausePidPath})
28
	if err == nil {
29
		return became, ret, nil
30
	}
31

32
	// It could not join the pause process, let's lock the file before trying to delete it.
33
	pidFileLock, err := lockfile.GetLockFile(pausePidPath)
34
	if err != nil {
35
		// The file was deleted by another process.
36
		if os.IsNotExist(err) {
37
			return false, -1, nil
38
		}
39
		return false, -1, fmt.Errorf("acquiring lock on %s: %w", pausePidPath, err)
40
	}
41

42
	pidFileLock.Lock()
43
	defer func() {
44
		pidFileLock.Unlock()
45
	}()
46

47
	// Now the pause PID file is locked.  Try to join once again in case it changed while it was not locked.
48
	became, ret, err = TryJoinFromFilePaths("", false, []string{pausePidPath})
49
	if err != nil {
50
		// It is still failing.  We can safely remove it.
51
		os.Remove(pausePidPath)
52
		return false, -1, nil //nolint: nilerr
53
	}
54
	return became, ret, err
55
}
56

57
var (
58
	uidMap      []user.IDMap
59
	uidMapError error
60
	uidMapOnce  sync.Once
61

62
	gidMap      []user.IDMap
63
	gidMapError error
64
	gidMapOnce  sync.Once
65
)
66

67
// GetAvailableUIDMap returns the UID mappings in the
68
// current user namespace.
69
func GetAvailableUIDMap() ([]user.IDMap, error) {
70
	uidMapOnce.Do(func() {
71
		var err error
72
		uidMap, err = user.ParseIDMapFile("/proc/self/uid_map")
73
		if err != nil {
74
			uidMapError = err
75
			return
76
		}
77
	})
78
	return uidMap, uidMapError
79
}
80

81
// GetAvailableGIDMap returns the GID mappings in the
82
// current user namespace.
83
func GetAvailableGIDMap() ([]user.IDMap, error) {
84
	gidMapOnce.Do(func() {
85
		var err error
86
		gidMap, err = user.ParseIDMapFile("/proc/self/gid_map")
87
		if err != nil {
88
			gidMapError = err
89
			return
90
		}
91
	})
92
	return gidMap, gidMapError
93
}
94

95
// GetAvailableIDMaps returns the UID and GID mappings in the
96
// current user namespace.
97
func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) {
98
	u, err := GetAvailableUIDMap()
99
	if err != nil {
100
		return nil, nil, err
101
	}
102
	g, err := GetAvailableGIDMap()
103
	if err != nil {
104
		return nil, nil, err
105
	}
106
	return u, g, nil
107
}
108

109
func countAvailableIDs(mappings []user.IDMap) int64 {
110
	availableUids := int64(0)
111
	for _, r := range mappings {
112
		availableUids += r.Count
113
	}
114
	return availableUids
115
}
116

117
// GetAvailableUids returns how many UIDs are available in the
118
// current user namespace.
119
func GetAvailableUids() (int64, error) {
120
	uids, err := GetAvailableUIDMap()
121
	if err != nil {
122
		return -1, err
123
	}
124

125
	return countAvailableIDs(uids), nil
126
}
127

128
// GetAvailableGids returns how many GIDs are available in the
129
// current user namespace.
130
func GetAvailableGids() (int64, error) {
131
	gids, err := GetAvailableGIDMap()
132
	if err != nil {
133
		return -1, err
134
	}
135

136
	return countAvailableIDs(gids), nil
137
}
138

139
// findIDInMappings find the mapping that contains the specified ID.
140
// It assumes availableMappings is sorted by ID.
141
func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap {
142
	i := sort.Search(len(availableMappings), func(i int) bool {
143
		return availableMappings[i].ID <= id
144
	})
145
	if i < 0 || i >= len(availableMappings) {
146
		return nil
147
	}
148
	r := &availableMappings[i]
149
	if id >= r.ID && id < r.ID+r.Count {
150
		return r
151
	}
152
	return nil
153
}
154

155
// MaybeSplitMappings checks whether the specified OCI mappings are possible
156
// in the current user namespace or the specified ranges must be split.
157
func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping {
158
	var ret []spec.LinuxIDMapping
159
	var overflow spec.LinuxIDMapping
160
	overflow.Size = 0
161
	consumed := 0
162
	sort.Slice(availableMappings, func(i, j int) bool {
163
		return availableMappings[i].ID > availableMappings[j].ID
164
	})
165
	for {
166
		cur := overflow
167
		// if there is no overflow left from the previous request, get the next one
168
		if cur.Size == 0 {
169
			if consumed == len(mappings) {
170
				// all done
171
				return ret
172
			}
173
			cur = mappings[consumed]
174
			consumed++
175
		}
176

177
		// Find the range where the first specified ID is present
178
		r := findIDInMappings(int64(cur.HostID), availableMappings)
179
		if r == nil {
180
			// The requested range is not available.  Just return the original request
181
			// and let other layers deal with it.
182
			return mappings
183
		}
184

185
		offsetInRange := cur.HostID - uint32(r.ID)
186

187
		usableIDs := uint32(r.Count) - offsetInRange
188

189
		// the current range can satisfy the whole request
190
		if usableIDs >= cur.Size {
191
			// reset the overflow
192
			overflow.Size = 0
193
		} else {
194
			// the current range can satisfy the request partially
195
			// so move the rest to overflow
196
			overflow.Size = cur.Size - usableIDs
197
			overflow.ContainerID = cur.ContainerID + usableIDs
198
			overflow.HostID = cur.HostID + usableIDs
199

200
			// and cap to the usableIDs count
201
			cur.Size = usableIDs
202
		}
203
		ret = append(ret, cur)
204
	}
205
}
206

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

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

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

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