Files
podman/pkg/specgen/volumes.go
Sascha Grunert a46f798831 pkg: switch to golang native error wrapping
We now use the golang error wrapping format specifier `%w` instead of
the deprecated github.com/pkg/errors package.

[NO NEW TESTS NEEDED]

Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
2022-07-08 08:54:47 +02:00

209 lines
6.2 KiB
Go

package specgen
import (
"errors"
"fmt"
"path/filepath"
"strings"
"github.com/containers/common/pkg/parse"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
)
// NamedVolume holds information about a named volume that will be mounted into
// the container.
type NamedVolume struct {
// Name is the name of the named volume to be mounted. May be empty.
// If empty, a new named volume with a pseudorandomly generated name
// will be mounted at the given destination.
Name string
// Destination to mount the named volume within the container. Must be
// an absolute path. Path will be created if it does not exist.
Dest string
// Options are options that the named volume will be mounted with.
Options []string
}
// OverlayVolume holds information about a overlay volume that will be mounted into
// the container.
type OverlayVolume struct {
// Destination is the absolute path where the mount will be placed in the container.
Destination string `json:"destination"`
// Source specifies the source path of the mount.
Source string `json:"source,omitempty"`
// Options holds overlay volume options.
Options []string `json:"options,omitempty"`
}
// ImageVolume is a volume based on a container image. The container image is
// first mounted on the host and is then bind-mounted into the container. An
// ImageVolume is always mounted read-only.
type ImageVolume struct {
// Source is the source of the image volume. The image can be referred
// to by name and by ID.
Source string
// Destination is the absolute path of the mount in the container.
Destination string
// ReadWrite sets the volume writable.
ReadWrite bool
}
// GenVolumeMounts parses user input into mounts, volumes and overlay volumes
func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*NamedVolume, map[string]*OverlayVolume, error) {
errDuplicateDest := errors.New("duplicate mount destination")
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*NamedVolume)
overlayVolumes := make(map[string]*OverlayVolume)
volumeFormatErr := errors.New("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
for _, vol := range volumeFlag {
var (
options []string
src string
dest string
err error
)
splitVol := SplitVolumeString(vol)
if len(splitVol) > 3 {
return nil, nil, nil, fmt.Errorf("%v: %w", vol, volumeFormatErr)
}
src = splitVol[0]
// Support relative paths beginning with ./
if strings.HasPrefix(src, "./") {
path, err := filepath.EvalSymlinks(src)
if err != nil {
return nil, nil, nil, err
}
src, err = filepath.Abs(path)
if err != nil {
return nil, nil, nil, err
}
splitVol[0] = src
}
if len(splitVol) == 1 {
// This is an anonymous named volume. Only thing given
// is destination.
// Name/source will be blank, and populated by libpod.
src = ""
dest = splitVol[0]
} else if len(splitVol) > 1 {
dest = splitVol[1]
}
if len(splitVol) > 2 {
if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
return nil, nil, nil, err
}
}
// Do not check source dir for anonymous volumes
if len(splitVol) > 1 {
if len(src) == 0 {
return nil, nil, nil, errors.New("host directory cannot be empty")
}
}
if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") || isHostWinPath(src) {
// This is not a named volume
overlayFlag := false
chownFlag := false
upperDirFlag := false
workDirFlag := false
for _, o := range options {
if o == "O" {
overlayFlag = true
joinedOpts := strings.Join(options, "")
if strings.Contains(joinedOpts, "U") {
chownFlag = true
}
if strings.Contains(joinedOpts, "upperdir") {
upperDirFlag = true
}
if strings.Contains(joinedOpts, "workdir") {
workDirFlag = true
}
if (workDirFlag && !upperDirFlag) || (!workDirFlag && upperDirFlag) {
return nil, nil, nil, errors.New("must set both `upperdir` and `workdir`")
}
if len(options) > 2 && !(len(options) == 3 && upperDirFlag && workDirFlag) || (len(options) == 2 && !chownFlag) {
return nil, nil, nil, errors.New("can't use 'O' with other options")
}
}
}
if overlayFlag {
// This is a overlay volume
newOverlayVol := new(OverlayVolume)
newOverlayVol.Destination = dest
// convert src to absolute path so we don't end up passing
// relative values as lowerdir for overlay mounts
source, err := filepath.Abs(src)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed while resolving absolute path for source %v for overlay mount: %w", src, err)
}
newOverlayVol.Source = source
newOverlayVol.Options = options
if _, ok := overlayVolumes[newOverlayVol.Destination]; ok {
return nil, nil, nil, fmt.Errorf("%v: %w", newOverlayVol.Destination, errDuplicateDest)
}
overlayVolumes[newOverlayVol.Destination] = newOverlayVol
} else {
newMount := spec.Mount{
Destination: dest,
Type: "bind",
Source: src,
Options: options,
}
if _, ok := mounts[newMount.Destination]; ok {
return nil, nil, nil, fmt.Errorf("%v: %w", newMount.Destination, errDuplicateDest)
}
mounts[newMount.Destination] = newMount
}
} else {
// This is a named volume
newNamedVol := new(NamedVolume)
newNamedVol.Name = src
newNamedVol.Dest = dest
newNamedVol.Options = options
if _, ok := volumes[newNamedVol.Dest]; ok {
return nil, nil, nil, fmt.Errorf("%v: %w", newNamedVol.Dest, errDuplicateDest)
}
volumes[newNamedVol.Dest] = newNamedVol
}
logrus.Debugf("User mount %s:%s options %v", src, dest, options)
}
return mounts, volumes, overlayVolumes, nil
}
// Splits a volume string, accounting for Win drive paths
// when running as a WSL linux guest or Windows client
func SplitVolumeString(vol string) []string {
parts := strings.Split(vol, ":")
if !shouldResolveWinPaths() {
return parts
}
// Skip extended marker prefix if present
n := 0
if strings.HasPrefix(vol, `\\?\`) {
n = 4
}
if hasWinDriveScheme(vol, n) {
first := parts[0] + ":" + parts[1]
parts = parts[1:]
parts[0] = first
}
return parts
}