Files
podman/vendor/github.com/containers/common/pkg/parse/parse.go
Valentin Rothberg 34235b2726 system df: fix image-size calculations
Fix two bugs in `system df`:

1.  The total size was calculated incorrectly as it was creating the sum
    of all image sizes but did not consider that a) the same image may
    be listed more than once (i.e., for each repo-tag pair), and that
    b) images share layers.

    The total size is now calculated directly in `libimage` by taking
    multi-layer use into account.

2.  The reclaimable size was calculated incorrectly.  This number
    indicates which data we can actually remove which means the total
    size minus what containers use (i.e., the "unique" size of the image
    in use by containers).

NOTE: The c/storage version is pinned back to the previous commit as it
      is buggy.  c/common already requires the buggy version, so use a
      `replace` to force/pin.

Fixes: #16135
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-10-19 11:20:41 +02:00

192 lines
5.6 KiB
Go

package parse
// this package contains functions that parse and validate
// user input and is shared either amongst container engine subcommands
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
)
// ValidateVolumeOpts validates a volume's options
func ValidateVolumeOpts(options []string) ([]string, error) {
var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid, foundChown, foundUpperDir, foundWorkDir, foundCopy int
finalOpts := make([]string, 0, len(options))
for _, opt := range options {
// support advanced options like upperdir=/path, workdir=/path
if strings.Contains(opt, "upperdir") {
foundUpperDir++
if foundUpperDir > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 upperdir per overlay", strings.Join(options, ", "))
}
finalOpts = append(finalOpts, opt)
continue
}
if strings.Contains(opt, "workdir") {
foundWorkDir++
if foundWorkDir > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 workdir per overlay", strings.Join(options, ", "))
}
finalOpts = append(finalOpts, opt)
continue
}
if strings.HasPrefix(opt, "idmap") {
finalOpts = append(finalOpts, opt)
continue
}
switch opt {
case "noexec", "exec":
foundExec++
if foundExec > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 'noexec' or 'exec' option", strings.Join(options, ", "))
}
case "nodev", "dev":
foundDev++
if foundDev > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 'nodev' or 'dev' option", strings.Join(options, ", "))
}
case "nosuid", "suid":
foundSuid++
if foundSuid > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 'nosuid' or 'suid' option", strings.Join(options, ", "))
}
case "rw", "ro":
foundRWRO++
if foundRWRO > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", "))
}
case "z", "Z", "O":
foundLabelChange++
if foundLabelChange > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", strings.Join(options, ", "))
}
case "U":
foundChown++
if foundChown > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 'U' option", strings.Join(options, ", "))
}
case "private", "rprivate", "shared", "rshared", "slave", "rslave", "unbindable", "runbindable":
foundRootPropagation++
if foundRootPropagation > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private', '[r]slave' or '[r]unbindable' option", strings.Join(options, ", "))
}
case "bind", "rbind":
bindType++
if bindType > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 '[r]bind' option", strings.Join(options, ", "))
}
case "cached", "delegated":
// The discarded ops are OS X specific volume options
// introduced in a recent Docker version.
// They have no meaning on Linux, so here we silently
// drop them. This matches Docker's behavior (the options
// are intended to be always safe to use, even not on OS
// X).
continue
case "copy", "nocopy":
foundCopy++
if foundCopy > 1 {
return nil, fmt.Errorf("invalid options %q, can only specify 1 'copy' or 'nocopy' option", strings.Join(options, ", "))
}
default:
return nil, fmt.Errorf("invalid option type %q", opt)
}
finalOpts = append(finalOpts, opt)
}
return finalOpts, nil
}
// Device parses device mapping string to a src, dest & permissions string
// Valid values for device looklike:
//
// '/dev/sdc"
// '/dev/sdc:/dev/xvdc"
// '/dev/sdc:/dev/xvdc:rwm"
// '/dev/sdc:rm"
func Device(device string) (src, dest, permissions string, err error) {
permissions = "rwm"
arr := strings.Split(device, ":")
switch len(arr) {
case 3:
if !isValidDeviceMode(arr[2]) {
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2])
}
permissions = arr[2]
fallthrough
case 2:
if isValidDeviceMode(arr[1]) {
permissions = arr[1]
} else {
if arr[1] == "" || arr[1][0] != '/' {
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[1])
}
dest = arr[1]
}
fallthrough
case 1:
if len(arr[0]) > 0 {
src = arr[0]
break
}
fallthrough
default:
return "", "", "", fmt.Errorf("invalid device specification: %s", device)
}
if dest == "" {
dest = src
}
return src, dest, permissions, nil
}
// isValidDeviceMode checks if the mode for device is valid or not.
// isValid mode is a composition of r (read), w (write), and m (mknod).
func isValidDeviceMode(mode string) bool {
legalDeviceMode := map[rune]bool{
'r': true,
'w': true,
'm': true,
}
if mode == "" {
return false
}
for _, c := range mode {
if !legalDeviceMode[c] {
return false
}
legalDeviceMode[c] = false
}
return true
}
// ValidateVolumeHostDir validates a volume mount's source directory
func ValidateVolumeHostDir(hostDir string) error {
if hostDir == "" {
return errors.New("host directory cannot be empty")
}
if filepath.IsAbs(hostDir) {
if _, err := os.Stat(hostDir); err != nil {
return err
}
}
// If hostDir is not an absolute path, that means the user wants to create a
// named volume. This will be done later on in the code.
return nil
}
// ValidateVolumeCtrDir validates a volume mount's destination directory.
func ValidateVolumeCtrDir(ctrDir string) error {
if ctrDir == "" {
return errors.New("container directory cannot be empty")
}
if !path.IsAbs(ctrDir) {
return fmt.Errorf("invalid container path %q, must be an absolute path", ctrDir)
}
return nil
}