podman

Форк
0
/
mount_opts.go 
237 строк · 7.3 Кб
1
package util
2

3
import (
4
	"errors"
5
	"fmt"
6
	"strings"
7

8
	"github.com/containers/podman/v5/libpod/define"
9
	"github.com/containers/podman/v5/pkg/rootless"
10
)
11

12
var (
13
	// ErrBadMntOption indicates that an invalid mount option was passed.
14
	ErrBadMntOption = errors.New("invalid mount option")
15
	// ErrDupeMntOption indicates that a duplicate mount option was passed.
16
	ErrDupeMntOption = errors.New("duplicate mount option passed")
17
)
18

19
type defaultMountOptions struct {
20
	noexec bool
21
	nosuid bool
22
	nodev  bool
23
}
24

25
type getDefaultMountOptionsFn func(path string) (defaultMountOptions, error)
26

27
// ProcessOptions parses the options for a bind or tmpfs mount and ensures that
28
// they are sensible and follow convention. The isTmpfs variable controls
29
// whether extra, tmpfs-specific options will be allowed.
30
// The sourcePath variable, if not empty, contains a bind mount source.
31
func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) {
32
	return processOptionsInternal(options, isTmpfs, sourcePath, getDefaultMountOptions)
33
}
34

35
func processOptionsInternal(options []string, isTmpfs bool, sourcePath string, getDefaultMountOptions getDefaultMountOptionsFn) ([]string, error) {
36
	var (
37
		foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ, foundU, foundOverlay, foundIdmap, foundCopy, foundNoSwap, foundNoDereference bool
38
	)
39

40
	recursiveBind := true
41

42
	newOptions := make([]string, 0, len(options))
43
	for _, opt := range options {
44
		// Some options have parameters - size, mode
45
		key, _, _ := strings.Cut(opt, "=")
46

47
		// add advanced options such as upperdir=/path and workdir=/path, when overlay is specified
48
		if foundOverlay {
49
			if strings.Contains(opt, "upperdir") {
50
				newOptions = append(newOptions, opt)
51
				continue
52
			}
53
			if strings.Contains(opt, "workdir") {
54
				newOptions = append(newOptions, opt)
55
				continue
56
			}
57
		}
58
		if strings.HasPrefix(key, "subpath") {
59
			newOptions = append(newOptions, opt)
60
			continue
61
		}
62
		if strings.HasPrefix(key, "idmap") {
63
			if foundIdmap {
64
				return nil, fmt.Errorf("the 'idmap' option can only be set once: %w", ErrDupeMntOption)
65
			}
66
			foundIdmap = true
67
			newOptions = append(newOptions, opt)
68
			continue
69
		}
70

71
		switch key {
72
		case "copy", "nocopy":
73
			if foundCopy {
74
				return nil, fmt.Errorf("only one of 'nocopy' and 'copy' can be used: %w", ErrDupeMntOption)
75
			}
76
			foundCopy = true
77
		case "O":
78
			foundOverlay = true
79
		case "volume-opt":
80
			// Volume-opt should be relayed and processed by driver.
81
			newOptions = append(newOptions, opt)
82
		case "exec", "noexec":
83
			if foundExec {
84
				return nil, fmt.Errorf("only one of 'noexec' and 'exec' can be used: %w", ErrDupeMntOption)
85
			}
86
			foundExec = true
87
		case "suid", "nosuid":
88
			if foundSuid {
89
				return nil, fmt.Errorf("only one of 'nosuid' and 'suid' can be used: %w", ErrDupeMntOption)
90
			}
91
			foundSuid = true
92
		case "nodev", "dev":
93
			if foundDev {
94
				return nil, fmt.Errorf("only one of 'nodev' and 'dev' can be used: %w", ErrDupeMntOption)
95
			}
96
			foundDev = true
97
		case "rw", "ro":
98
			if foundWrite {
99
				return nil, fmt.Errorf("only one of 'rw' and 'ro' can be used: %w", ErrDupeMntOption)
100
			}
101
			foundWrite = true
102
		case "private", "rprivate", "slave", "rslave", "shared", "rshared", "unbindable", "runbindable":
103
			if foundProp {
104
				return nil, fmt.Errorf("only one root propagation mode can be used: %w", ErrDupeMntOption)
105
			}
106
			foundProp = true
107
		case "size":
108
			if !isTmpfs {
109
				return nil, fmt.Errorf("the 'size' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
110
			}
111
			if foundSize {
112
				return nil, fmt.Errorf("only one tmpfs size can be specified: %w", ErrDupeMntOption)
113
			}
114
			foundSize = true
115
		case "mode":
116
			if !isTmpfs {
117
				return nil, fmt.Errorf("the 'mode' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
118
			}
119
			if foundMode {
120
				return nil, fmt.Errorf("only one tmpfs mode can be specified: %w", ErrDupeMntOption)
121
			}
122
			foundMode = true
123
		case "tmpcopyup":
124
			if !isTmpfs {
125
				return nil, fmt.Errorf("the 'tmpcopyup' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
126
			}
127
			if foundCopyUp {
128
				return nil, fmt.Errorf("the 'tmpcopyup' or 'notmpcopyup' option can only be set once: %w", ErrDupeMntOption)
129
			}
130
			foundCopyUp = true
131
		case "consistency":
132
			// Often used on MACs and mistakenly on Linux platforms.
133
			// Since Docker ignores this option so shall we.
134
			continue
135
		case "notmpcopyup":
136
			if !isTmpfs {
137
				return nil, fmt.Errorf("the 'notmpcopyup' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
138
			}
139
			if foundCopyUp {
140
				return nil, fmt.Errorf("the 'tmpcopyup' or 'notmpcopyup' option can only be set once: %w", ErrDupeMntOption)
141
			}
142
			foundCopyUp = true
143
			// do not propagate notmpcopyup to the OCI runtime
144
			continue
145
		case "noswap":
146

147
			if !isTmpfs {
148
				return nil, fmt.Errorf("the 'noswap' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
149
			}
150
			if rootless.IsRootless() {
151
				return nil, fmt.Errorf("the 'noswap' option is only allowed with rootful tmpfs mounts: %w", ErrBadMntOption)
152
			}
153
			if foundNoSwap {
154
				return nil, fmt.Errorf("the 'tmpswap' option can only be set once: %w", ErrDupeMntOption)
155
			}
156
			foundNoSwap = true
157
			newOptions = append(newOptions, opt)
158
			continue
159
		case "no-dereference":
160
			if foundNoDereference {
161
				return nil, fmt.Errorf("the 'no-dereference' option can only be set once: %w", ErrDupeMntOption)
162
			}
163
			foundNoDereference = true
164
		case define.TypeBind:
165
			recursiveBind = false
166
			fallthrough
167
		case "rbind":
168
			if isTmpfs {
169
				return nil, fmt.Errorf("the 'bind' and 'rbind' options are not allowed with tmpfs mounts: %w", ErrBadMntOption)
170
			}
171
			if foundBind {
172
				return nil, fmt.Errorf("only one of 'rbind' and 'bind' can be used: %w", ErrDupeMntOption)
173
			}
174
			foundBind = true
175
		case "z", "Z":
176
			if isTmpfs {
177
				return nil, fmt.Errorf("the 'z' and 'Z' options are not allowed with tmpfs mounts: %w", ErrBadMntOption)
178
			}
179
			if foundZ {
180
				return nil, fmt.Errorf("only one of 'z' and 'Z' can be used: %w", ErrDupeMntOption)
181
			}
182
			foundZ = true
183
		case "U":
184
			if foundU {
185
				return nil, fmt.Errorf("the 'U' option can only be set once: %w", ErrDupeMntOption)
186
			}
187
			foundU = true
188
		default:
189
			return nil, fmt.Errorf("unknown mount option %q: %w", opt, ErrBadMntOption)
190
		}
191
		newOptions = append(newOptions, opt)
192
	}
193

194
	if !foundWrite {
195
		newOptions = append(newOptions, "rw")
196
	}
197
	if !foundProp {
198
		if recursiveBind {
199
			newOptions = append(newOptions, "rprivate")
200
		} else {
201
			newOptions = append(newOptions, "private")
202
		}
203
	}
204
	defaults, err := getDefaultMountOptions(sourcePath)
205
	if err != nil {
206
		return nil, err
207
	}
208
	if !foundExec && defaults.noexec {
209
		newOptions = append(newOptions, "noexec")
210
	}
211
	if !foundSuid && defaults.nosuid {
212
		newOptions = append(newOptions, "nosuid")
213
	}
214
	if !foundDev && defaults.nodev {
215
		newOptions = append(newOptions, "nodev")
216
	}
217
	if isTmpfs && !foundCopyUp {
218
		newOptions = append(newOptions, "tmpcopyup")
219
	}
220
	if !isTmpfs && !foundBind {
221
		newOptions = append(newOptions, "rbind")
222
	}
223

224
	return newOptions, nil
225
}
226

227
func ParseDriverOpts(option string) (string, string, error) {
228
	_, val, hasVal := strings.Cut(option, "=")
229
	if !hasVal {
230
		return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
231
	}
232
	optKey, optVal, hasOptVal := strings.Cut(val, "=")
233
	if !hasOptVal {
234
		return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
235
	}
236
	return optKey, optVal, nil
237
}
238

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

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

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

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