github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/volumes.go (about)

     1  package specgen
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  
     7  	"github.com/containers/buildah/pkg/parse"
     8  	spec "github.com/opencontainers/runtime-spec/specs-go"
     9  	"github.com/pkg/errors"
    10  	"github.com/sirupsen/logrus"
    11  )
    12  
    13  // NamedVolume holds information about a named volume that will be mounted into
    14  // the container.
    15  type NamedVolume struct {
    16  	// Name is the name of the named volume to be mounted. May be empty.
    17  	// If empty, a new named volume with a pseudorandomly generated name
    18  	// will be mounted at the given destination.
    19  	Name string
    20  	// Destination to mount the named volume within the container. Must be
    21  	// an absolute path. Path will be created if it does not exist.
    22  	Dest string
    23  	// Options are options that the named volume will be mounted with.
    24  	Options []string
    25  }
    26  
    27  // OverlayVolume holds information about a overlay volume that will be mounted into
    28  // the container.
    29  type OverlayVolume struct {
    30  	// Destination is the absolute path where the mount will be placed in the container.
    31  	Destination string `json:"destination"`
    32  	// Source specifies the source path of the mount.
    33  	Source string `json:"source,omitempty"`
    34  }
    35  
    36  // ImageVolume is a volume based on a container image.  The container image is
    37  // first mounted on the host and is then bind-mounted into the container.  An
    38  // ImageVolume is always mounted read only.
    39  type ImageVolume struct {
    40  	// Source is the source of the image volume.  The image can be referred
    41  	// to by name and by ID.
    42  	Source string
    43  	// Destination is the absolute path of the mount in the container.
    44  	Destination string
    45  	// ReadWrite sets the volume writable.
    46  	ReadWrite bool
    47  }
    48  
    49  // GenVolumeMounts parses user input into mounts, volumes and overlay volumes
    50  func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*NamedVolume, map[string]*OverlayVolume, error) {
    51  	errDuplicateDest := errors.Errorf("duplicate mount destination")
    52  
    53  	mounts := make(map[string]spec.Mount)
    54  	volumes := make(map[string]*NamedVolume)
    55  	overlayVolumes := make(map[string]*OverlayVolume)
    56  
    57  	volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
    58  
    59  	for _, vol := range volumeFlag {
    60  		var (
    61  			options []string
    62  			src     string
    63  			dest    string
    64  			err     error
    65  		)
    66  
    67  		splitVol := strings.Split(vol, ":")
    68  		if len(splitVol) > 3 {
    69  			return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol)
    70  		}
    71  
    72  		src = splitVol[0]
    73  		if len(splitVol) == 1 {
    74  			// This is an anonymous named volume. Only thing given
    75  			// is destination.
    76  			// Name/source will be blank, and populated by libpod.
    77  			src = ""
    78  			dest = splitVol[0]
    79  		} else if len(splitVol) > 1 {
    80  			dest = splitVol[1]
    81  		}
    82  		if len(splitVol) > 2 {
    83  			if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
    84  				return nil, nil, nil, err
    85  			}
    86  		}
    87  
    88  		// Do not check source dir for anonymous volumes
    89  		if len(splitVol) > 1 {
    90  			if len(src) == 0 {
    91  				return nil, nil, nil, errors.New("host directory cannot be empty")
    92  			}
    93  		}
    94  		if err := parse.ValidateVolumeCtrDir(dest); err != nil {
    95  			return nil, nil, nil, err
    96  		}
    97  
    98  		cleanDest := filepath.Clean(dest)
    99  
   100  		if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
   101  			// This is not a named volume
   102  			overlayFlag := false
   103  			for _, o := range options {
   104  				if o == "O" {
   105  					overlayFlag = true
   106  					if len(options) > 1 {
   107  						return nil, nil, nil, errors.New("can't use 'O' with other options")
   108  					}
   109  				}
   110  			}
   111  			if overlayFlag {
   112  				// This is a overlay volume
   113  				newOverlayVol := new(OverlayVolume)
   114  				newOverlayVol.Destination = cleanDest
   115  				newOverlayVol.Source = src
   116  				if _, ok := overlayVolumes[newOverlayVol.Destination]; ok {
   117  					return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination)
   118  				}
   119  				overlayVolumes[newOverlayVol.Destination] = newOverlayVol
   120  			} else {
   121  				newMount := spec.Mount{
   122  					Destination: cleanDest,
   123  					Type:        "bind",
   124  					Source:      src,
   125  					Options:     options,
   126  				}
   127  				if _, ok := mounts[newMount.Destination]; ok {
   128  					return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
   129  				}
   130  				mounts[newMount.Destination] = newMount
   131  			}
   132  		} else {
   133  			// This is a named volume
   134  			newNamedVol := new(NamedVolume)
   135  			newNamedVol.Name = src
   136  			newNamedVol.Dest = cleanDest
   137  			newNamedVol.Options = options
   138  
   139  			if _, ok := volumes[newNamedVol.Dest]; ok {
   140  				return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
   141  			}
   142  			volumes[newNamedVol.Dest] = newNamedVol
   143  		}
   144  
   145  		logrus.Debugf("User mount %s:%s options %v", src, dest, options)
   146  	}
   147  
   148  	return mounts, volumes, overlayVolumes, nil
   149  }