mirror of
https://github.com/containers/podman.git
synced 2025-06-03 12:17:13 +08:00

Instead of getting mount options from /proc/self/mountinfo, which is very costly to read/parse (and can even be unreliable), let's use statfs(2) to figure out the flags we need. [v2: move getting default options to pkg/util, make it linux-specific] Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
145 lines
4.4 KiB
Go
145 lines
4.4 KiB
Go
package util
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
// ErrBadMntOption indicates that an invalid mount option was passed.
|
|
ErrBadMntOption = errors.Errorf("invalid mount option")
|
|
// ErrDupeMntOption indicates that a duplicate mount option was passed.
|
|
ErrDupeMntOption = errors.Errorf("duplicate mount option passed")
|
|
)
|
|
|
|
type defaultMountOptions struct {
|
|
noexec bool
|
|
nosuid bool
|
|
nodev bool
|
|
}
|
|
|
|
// ProcessOptions parses the options for a bind or tmpfs mount and ensures that
|
|
// they are sensible and follow convention. The isTmpfs variable controls
|
|
// whether extra, tmpfs-specific options will be allowed.
|
|
// The sourcePath variable, if not empty, contains a bind mount source.
|
|
func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) {
|
|
var (
|
|
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ bool
|
|
)
|
|
|
|
var newOptions []string
|
|
|
|
for _, opt := range options {
|
|
// Some options have parameters - size, mode
|
|
splitOpt := strings.SplitN(opt, "=", 2)
|
|
switch splitOpt[0] {
|
|
case "exec", "noexec":
|
|
if foundExec {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'noexec' and 'exec' can be used")
|
|
}
|
|
foundExec = true
|
|
case "suid", "nosuid":
|
|
if foundSuid {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nosuid' and 'suid' can be used")
|
|
}
|
|
foundSuid = true
|
|
case "nodev", "dev":
|
|
if foundDev {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nodev' and 'dev' can be used")
|
|
}
|
|
foundDev = true
|
|
case "rw", "ro":
|
|
if foundWrite {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rw' and 'ro' can be used")
|
|
}
|
|
foundWrite = true
|
|
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
|
|
if foundProp {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one root propagation mode can be used")
|
|
}
|
|
foundProp = true
|
|
case "size":
|
|
if !isTmpfs {
|
|
return nil, errors.Wrapf(ErrBadMntOption, "the 'size' option is only allowed with tmpfs mounts")
|
|
}
|
|
if foundSize {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs size can be specified")
|
|
}
|
|
foundSize = true
|
|
case "mode":
|
|
if !isTmpfs {
|
|
return nil, errors.Wrapf(ErrBadMntOption, "the 'mode' option is only allowed with tmpfs mounts")
|
|
}
|
|
if foundMode {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs mode can be specified")
|
|
}
|
|
foundMode = true
|
|
case "tmpcopyup":
|
|
if !isTmpfs {
|
|
return nil, errors.Wrapf(ErrBadMntOption, "the 'tmpcopyup' option is only allowed with tmpfs mounts")
|
|
}
|
|
if foundCopyUp {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once")
|
|
}
|
|
foundCopyUp = true
|
|
case "notmpcopyup":
|
|
if !isTmpfs {
|
|
return nil, errors.Wrapf(ErrBadMntOption, "the 'notmpcopyup' option is only allowed with tmpfs mounts")
|
|
}
|
|
if foundCopyUp {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once")
|
|
}
|
|
foundCopyUp = true
|
|
// do not propagate notmpcopyup to the OCI runtime
|
|
continue
|
|
case "bind", "rbind":
|
|
if isTmpfs {
|
|
return nil, errors.Wrapf(ErrBadMntOption, "the 'bind' and 'rbind' options are not allowed with tmpfs mounts")
|
|
}
|
|
if foundBind {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rbind' and 'bind' can be used")
|
|
}
|
|
foundBind = true
|
|
case "z", "Z":
|
|
if isTmpfs {
|
|
return nil, errors.Wrapf(ErrBadMntOption, "the 'z' and 'Z' options are not allowed with tmpfs mounts")
|
|
}
|
|
if foundZ {
|
|
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'z' and 'Z' can be used")
|
|
}
|
|
default:
|
|
return nil, errors.Wrapf(ErrBadMntOption, "unknown mount option %q", opt)
|
|
}
|
|
newOptions = append(newOptions, opt)
|
|
}
|
|
|
|
if !foundWrite {
|
|
newOptions = append(newOptions, "rw")
|
|
}
|
|
if !foundProp {
|
|
newOptions = append(newOptions, "rprivate")
|
|
}
|
|
defaults, err := getDefaultMountOptions(sourcePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !foundExec && defaults.noexec {
|
|
newOptions = append(newOptions, "noexec")
|
|
}
|
|
if !foundSuid && defaults.nosuid {
|
|
newOptions = append(newOptions, "nosuid")
|
|
}
|
|
if !foundDev && defaults.nodev {
|
|
newOptions = append(newOptions, "nodev")
|
|
}
|
|
if isTmpfs && !foundCopyUp {
|
|
newOptions = append(newOptions, "tmpcopyup")
|
|
}
|
|
if !isTmpfs && !foundBind {
|
|
newOptions = append(newOptions, "rbind")
|
|
}
|
|
|
|
return newOptions, nil
|
|
}
|