8
"github.com/containers/podman/v5/libpod/define"
9
"github.com/containers/podman/v5/pkg/rootless"
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")
19
type defaultMountOptions struct {
25
type getDefaultMountOptionsFn func(path string) (defaultMountOptions, error)
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)
35
func processOptionsInternal(options []string, isTmpfs bool, sourcePath string, getDefaultMountOptions getDefaultMountOptionsFn) ([]string, error) {
37
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ, foundU, foundOverlay, foundIdmap, foundCopy, foundNoSwap, foundNoDereference bool
42
newOptions := make([]string, 0, len(options))
43
for _, opt := range options {
44
// Some options have parameters - size, mode
45
key, _, _ := strings.Cut(opt, "=")
47
// add advanced options such as upperdir=/path and workdir=/path, when overlay is specified
49
if strings.Contains(opt, "upperdir") {
50
newOptions = append(newOptions, opt)
53
if strings.Contains(opt, "workdir") {
54
newOptions = append(newOptions, opt)
58
if strings.HasPrefix(key, "subpath") {
59
newOptions = append(newOptions, opt)
62
if strings.HasPrefix(key, "idmap") {
64
return nil, fmt.Errorf("the 'idmap' option can only be set once: %w", ErrDupeMntOption)
67
newOptions = append(newOptions, opt)
72
case "copy", "nocopy":
74
return nil, fmt.Errorf("only one of 'nocopy' and 'copy' can be used: %w", ErrDupeMntOption)
80
// Volume-opt should be relayed and processed by driver.
81
newOptions = append(newOptions, opt)
82
case "exec", "noexec":
84
return nil, fmt.Errorf("only one of 'noexec' and 'exec' can be used: %w", ErrDupeMntOption)
87
case "suid", "nosuid":
89
return nil, fmt.Errorf("only one of 'nosuid' and 'suid' can be used: %w", ErrDupeMntOption)
94
return nil, fmt.Errorf("only one of 'nodev' and 'dev' can be used: %w", ErrDupeMntOption)
99
return nil, fmt.Errorf("only one of 'rw' and 'ro' can be used: %w", ErrDupeMntOption)
102
case "private", "rprivate", "slave", "rslave", "shared", "rshared", "unbindable", "runbindable":
104
return nil, fmt.Errorf("only one root propagation mode can be used: %w", ErrDupeMntOption)
109
return nil, fmt.Errorf("the 'size' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
112
return nil, fmt.Errorf("only one tmpfs size can be specified: %w", ErrDupeMntOption)
117
return nil, fmt.Errorf("the 'mode' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
120
return nil, fmt.Errorf("only one tmpfs mode can be specified: %w", ErrDupeMntOption)
125
return nil, fmt.Errorf("the 'tmpcopyup' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
128
return nil, fmt.Errorf("the 'tmpcopyup' or 'notmpcopyup' option can only be set once: %w", ErrDupeMntOption)
132
// Often used on MACs and mistakenly on Linux platforms.
133
// Since Docker ignores this option so shall we.
137
return nil, fmt.Errorf("the 'notmpcopyup' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
140
return nil, fmt.Errorf("the 'tmpcopyup' or 'notmpcopyup' option can only be set once: %w", ErrDupeMntOption)
143
// do not propagate notmpcopyup to the OCI runtime
148
return nil, fmt.Errorf("the 'noswap' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
150
if rootless.IsRootless() {
151
return nil, fmt.Errorf("the 'noswap' option is only allowed with rootful tmpfs mounts: %w", ErrBadMntOption)
154
return nil, fmt.Errorf("the 'tmpswap' option can only be set once: %w", ErrDupeMntOption)
157
newOptions = append(newOptions, opt)
159
case "no-dereference":
160
if foundNoDereference {
161
return nil, fmt.Errorf("the 'no-dereference' option can only be set once: %w", ErrDupeMntOption)
163
foundNoDereference = true
164
case define.TypeBind:
165
recursiveBind = false
169
return nil, fmt.Errorf("the 'bind' and 'rbind' options are not allowed with tmpfs mounts: %w", ErrBadMntOption)
172
return nil, fmt.Errorf("only one of 'rbind' and 'bind' can be used: %w", ErrDupeMntOption)
177
return nil, fmt.Errorf("the 'z' and 'Z' options are not allowed with tmpfs mounts: %w", ErrBadMntOption)
180
return nil, fmt.Errorf("only one of 'z' and 'Z' can be used: %w", ErrDupeMntOption)
185
return nil, fmt.Errorf("the 'U' option can only be set once: %w", ErrDupeMntOption)
189
return nil, fmt.Errorf("unknown mount option %q: %w", opt, ErrBadMntOption)
191
newOptions = append(newOptions, opt)
195
newOptions = append(newOptions, "rw")
199
newOptions = append(newOptions, "rprivate")
201
newOptions = append(newOptions, "private")
204
defaults, err := getDefaultMountOptions(sourcePath)
208
if !foundExec && defaults.noexec {
209
newOptions = append(newOptions, "noexec")
211
if !foundSuid && defaults.nosuid {
212
newOptions = append(newOptions, "nosuid")
214
if !foundDev && defaults.nodev {
215
newOptions = append(newOptions, "nodev")
217
if isTmpfs && !foundCopyUp {
218
newOptions = append(newOptions, "tmpcopyup")
220
if !isTmpfs && !foundBind {
221
newOptions = append(newOptions, "rbind")
224
return newOptions, nil
227
func ParseDriverOpts(option string) (string, string, error) {
228
_, val, hasVal := strings.Cut(option, "=")
230
return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
232
optKey, optVal, hasOptVal := strings.Cut(val, "=")
234
return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
236
return optKey, optVal, nil