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

     1  package createconfig
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/containers/buildah/pkg/parse"
    11  	"github.com/containers/podman/v2/libpod"
    12  	"github.com/containers/podman/v2/pkg/util"
    13  	spec "github.com/opencontainers/runtime-spec/specs-go"
    14  	"github.com/pkg/errors"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  const (
    19  	// TypeBind is the type for mounting host dir
    20  	TypeBind = "bind"
    21  	// TypeVolume is the type for named volumes
    22  	TypeVolume = "volume"
    23  	// TypeTmpfs is the type for mounting tmpfs
    24  	TypeTmpfs = "tmpfs"
    25  )
    26  
    27  var (
    28  	errDuplicateDest = errors.Errorf("duplicate mount destination")
    29  	optionArgError   = errors.Errorf("must provide an argument for option")
    30  	noDestError      = errors.Errorf("must set volume destination")
    31  )
    32  
    33  // Parse all volume-related options in the create config into a set of mounts
    34  // and named volumes to add to the container.
    35  // Handles --volumes-from, --volumes, --tmpfs, --init, and --init-path flags.
    36  // TODO: Named volume options -  should we default to rprivate? It bakes into a
    37  // bind mount under the hood...
    38  // TODO: handle options parsing/processing via containers/storage/pkg/mount
    39  func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount, []*libpod.ContainerNamedVolume, error) {
    40  	// Add image volumes.
    41  	baseMounts, baseVolumes, err := config.getImageVolumes()
    42  	if err != nil {
    43  		return nil, nil, err
    44  	}
    45  
    46  	// Add --volumes-from.
    47  	// Overrides image volumes unconditionally.
    48  	vFromMounts, vFromVolumes, err := config.getVolumesFrom(runtime)
    49  	if err != nil {
    50  		return nil, nil, err
    51  	}
    52  	for dest, mount := range vFromMounts {
    53  		baseMounts[dest] = mount
    54  	}
    55  	for dest, volume := range vFromVolumes {
    56  		baseVolumes[dest] = volume
    57  	}
    58  
    59  	// Next mounts from the --mounts flag.
    60  	// Do not override yet.
    61  	unifiedMounts, unifiedVolumes, err := config.getMounts()
    62  	if err != nil {
    63  		return nil, nil, err
    64  	}
    65  
    66  	// Next --volumes flag.
    67  	// Do not override yet.
    68  	volumeMounts, volumeVolumes, err := config.getVolumeMounts()
    69  	if err != nil {
    70  		return nil, nil, err
    71  	}
    72  
    73  	// Next --tmpfs flag.
    74  	// Do not override yet.
    75  	tmpfsMounts, err := config.getTmpfsMounts()
    76  	if err != nil {
    77  		return nil, nil, err
    78  	}
    79  
    80  	// Unify mounts from --mount, --volume, --tmpfs.
    81  	// Also add mounts + volumes directly from createconfig.
    82  	// Start with --volume.
    83  	for dest, mount := range volumeMounts {
    84  		if _, ok := unifiedMounts[dest]; ok {
    85  			return nil, nil, errors.Wrapf(errDuplicateDest, dest)
    86  		}
    87  		unifiedMounts[dest] = mount
    88  	}
    89  	for dest, volume := range volumeVolumes {
    90  		if _, ok := unifiedVolumes[dest]; ok {
    91  			return nil, nil, errors.Wrapf(errDuplicateDest, dest)
    92  		}
    93  		unifiedVolumes[dest] = volume
    94  	}
    95  	// Now --tmpfs
    96  	for dest, tmpfs := range tmpfsMounts {
    97  		if _, ok := unifiedMounts[dest]; ok {
    98  			return nil, nil, errors.Wrapf(errDuplicateDest, dest)
    99  		}
   100  		unifiedMounts[dest] = tmpfs
   101  	}
   102  	// Now spec mounts and volumes
   103  	for _, mount := range config.Mounts {
   104  		dest := mount.Destination
   105  		if _, ok := unifiedMounts[dest]; ok {
   106  			return nil, nil, errors.Wrapf(errDuplicateDest, dest)
   107  		}
   108  		unifiedMounts[dest] = mount
   109  	}
   110  	for _, volume := range config.NamedVolumes {
   111  		dest := volume.Dest
   112  		if _, ok := unifiedVolumes[dest]; ok {
   113  			return nil, nil, errors.Wrapf(errDuplicateDest, dest)
   114  		}
   115  		unifiedVolumes[dest] = volume
   116  	}
   117  
   118  	// If requested, add container init binary
   119  	if config.Init {
   120  		initPath := config.InitPath
   121  		if initPath == "" {
   122  			rtc, err := runtime.GetConfig()
   123  			if err != nil {
   124  				return nil, nil, err
   125  			}
   126  			initPath = rtc.Engine.InitPath
   127  		}
   128  		initMount, err := config.addContainerInitBinary(initPath)
   129  		if err != nil {
   130  			return nil, nil, err
   131  		}
   132  		if _, ok := unifiedMounts[initMount.Destination]; ok {
   133  			return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
   134  		}
   135  		unifiedMounts[initMount.Destination] = initMount
   136  	}
   137  
   138  	// Before superseding, we need to find volume mounts which conflict with
   139  	// named volumes, and vice versa.
   140  	// We'll delete the conflicts here as we supersede.
   141  	for dest := range unifiedMounts {
   142  		if _, ok := baseVolumes[dest]; ok {
   143  			delete(baseVolumes, dest)
   144  		}
   145  	}
   146  	for dest := range unifiedVolumes {
   147  		if _, ok := baseMounts[dest]; ok {
   148  			delete(baseMounts, dest)
   149  		}
   150  	}
   151  
   152  	// Supersede volumes-from/image volumes with unified volumes from above.
   153  	// This is an unconditional replacement.
   154  	for dest, mount := range unifiedMounts {
   155  		baseMounts[dest] = mount
   156  	}
   157  	for dest, volume := range unifiedVolumes {
   158  		baseVolumes[dest] = volume
   159  	}
   160  
   161  	// If requested, add tmpfs filesystems for read-only containers.
   162  	if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs {
   163  		readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
   164  		options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
   165  		for _, dest := range readonlyTmpfs {
   166  			if _, ok := baseMounts[dest]; ok {
   167  				continue
   168  			}
   169  			if _, ok := baseVolumes[dest]; ok {
   170  				continue
   171  			}
   172  			localOpts := options
   173  			if dest == "/run" {
   174  				localOpts = append(localOpts, "noexec", "size=65536k")
   175  			} else {
   176  				localOpts = append(localOpts, "exec")
   177  			}
   178  			baseMounts[dest] = spec.Mount{
   179  				Destination: dest,
   180  				Type:        "tmpfs",
   181  				Source:      "tmpfs",
   182  				Options:     localOpts,
   183  			}
   184  		}
   185  	}
   186  
   187  	// Check for conflicts between named volumes and mounts
   188  	for dest := range baseMounts {
   189  		if _, ok := baseVolumes[dest]; ok {
   190  			return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
   191  		}
   192  	}
   193  	for dest := range baseVolumes {
   194  		if _, ok := baseMounts[dest]; ok {
   195  			return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
   196  		}
   197  	}
   198  
   199  	// Final step: maps to arrays
   200  	finalMounts := make([]spec.Mount, 0, len(baseMounts))
   201  	for _, mount := range baseMounts {
   202  		if mount.Type == TypeBind {
   203  			absSrc, err := filepath.Abs(mount.Source)
   204  			if err != nil {
   205  				return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
   206  			}
   207  			mount.Source = absSrc
   208  		}
   209  		finalMounts = append(finalMounts, mount)
   210  	}
   211  	finalVolumes := make([]*libpod.ContainerNamedVolume, 0, len(baseVolumes))
   212  	for _, volume := range baseVolumes {
   213  		finalVolumes = append(finalVolumes, volume)
   214  	}
   215  
   216  	return finalMounts, finalVolumes, nil
   217  }
   218  
   219  // Parse volumes from - a set of containers whose volumes we will mount in.
   220  // Grab the containers, retrieve any user-created spec mounts and all named
   221  // volumes, and return a list of them.
   222  // Conflicts are resolved simply - the last container specified wins.
   223  // Container names may be suffixed by mount options after a colon.
   224  // TODO: We should clean these paths if possible
   225  func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
   226  	// Both of these are maps of mount destination to mount type.
   227  	// We ensure that each destination is only mounted to once in this way.
   228  	finalMounts := make(map[string]spec.Mount)
   229  	finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
   230  
   231  	for _, vol := range config.VolumesFrom {
   232  		var (
   233  			options  = []string{}
   234  			err      error
   235  			splitVol = strings.SplitN(vol, ":", 2)
   236  		)
   237  		if len(splitVol) == 2 {
   238  			splitOpts := strings.Split(splitVol[1], ",")
   239  			for _, checkOpt := range splitOpts {
   240  				switch checkOpt {
   241  				case "z", "ro", "rw":
   242  					// Do nothing, these are valid options
   243  				default:
   244  					return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z'", splitVol[1])
   245  				}
   246  			}
   247  
   248  			if options, err = parse.ValidateVolumeOpts(splitOpts); err != nil {
   249  				return nil, nil, err
   250  			}
   251  		}
   252  		ctr, err := runtime.LookupContainer(splitVol[0])
   253  		if err != nil {
   254  			return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0])
   255  		}
   256  
   257  		logrus.Debugf("Adding volumes from container %s", ctr.ID())
   258  
   259  		// Look up the container's user volumes. This gets us the
   260  		// destinations of all mounts the user added to the container.
   261  		userVolumesArr := ctr.UserVolumes()
   262  
   263  		// We're going to need to access them a lot, so convert to a map
   264  		// to reduce looping.
   265  		// We'll also use the map to indicate if we missed any volumes along the way.
   266  		userVolumes := make(map[string]bool)
   267  		for _, dest := range userVolumesArr {
   268  			userVolumes[dest] = false
   269  		}
   270  
   271  		// Now we get the container's spec and loop through its volumes
   272  		// and append them in if we can find them.
   273  		spec := ctr.Spec()
   274  		if spec == nil {
   275  			return nil, nil, errors.Errorf("error retrieving container %s spec for volumes-from", ctr.ID())
   276  		}
   277  		for _, mnt := range spec.Mounts {
   278  			if mnt.Type != TypeBind {
   279  				continue
   280  			}
   281  			if _, exists := userVolumes[mnt.Destination]; exists {
   282  				userVolumes[mnt.Destination] = true
   283  
   284  				if len(options) != 0 {
   285  					mnt.Options = options
   286  				}
   287  
   288  				if _, ok := finalMounts[mnt.Destination]; ok {
   289  					logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID())
   290  				}
   291  				finalMounts[mnt.Destination] = mnt
   292  			}
   293  		}
   294  
   295  		// We're done with the spec mounts. Add named volumes.
   296  		// Add these unconditionally - none of them are automatically
   297  		// part of the container, as some spec mounts are.
   298  		namedVolumes := ctr.NamedVolumes()
   299  		for _, namedVol := range namedVolumes {
   300  			if _, exists := userVolumes[namedVol.Dest]; exists {
   301  				userVolumes[namedVol.Dest] = true
   302  			}
   303  
   304  			if len(options) != 0 {
   305  				namedVol.Options = options
   306  			}
   307  
   308  			if _, ok := finalMounts[namedVol.Dest]; ok {
   309  				logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID())
   310  			}
   311  			finalNamedVolumes[namedVol.Dest] = namedVol
   312  		}
   313  
   314  		// Check if we missed any volumes
   315  		for volDest, found := range userVolumes {
   316  			if !found {
   317  				logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID())
   318  			}
   319  		}
   320  	}
   321  
   322  	return finalMounts, finalNamedVolumes, nil
   323  }
   324  
   325  // getMounts takes user-provided input from the --mount flag and creates OCI
   326  // spec mounts and Libpod named volumes.
   327  // podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
   328  // podman run --mount type=tmpfs,target=/dev/shm ...
   329  // podman run --mount type=volume,source=test-volume, ...
   330  func (config *CreateConfig) getMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
   331  	finalMounts := make(map[string]spec.Mount)
   332  	finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
   333  
   334  	errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]")
   335  
   336  	// TODO(vrothberg): the manual parsing can be replaced with a regular expression
   337  	//                  to allow a more robust parsing of the mount format and to give
   338  	//                  precise errors regarding supported format versus supported options.
   339  	for _, mount := range config.MountsFlag {
   340  		arr := strings.SplitN(mount, ",", 2)
   341  		if len(arr) < 2 {
   342  			return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
   343  		}
   344  		kv := strings.Split(arr[0], "=")
   345  		// TODO: type is not explicitly required in Docker.
   346  		// If not specified, it defaults to "volume".
   347  		if len(kv) != 2 || kv[0] != "type" {
   348  			return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
   349  		}
   350  
   351  		tokens := strings.Split(arr[1], ",")
   352  		switch kv[1] {
   353  		case TypeBind:
   354  			mount, err := getBindMount(tokens)
   355  			if err != nil {
   356  				return nil, nil, err
   357  			}
   358  			if _, ok := finalMounts[mount.Destination]; ok {
   359  				return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
   360  			}
   361  			finalMounts[mount.Destination] = mount
   362  		case TypeTmpfs:
   363  			mount, err := getTmpfsMount(tokens)
   364  			if err != nil {
   365  				return nil, nil, err
   366  			}
   367  			if _, ok := finalMounts[mount.Destination]; ok {
   368  				return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
   369  			}
   370  			finalMounts[mount.Destination] = mount
   371  		case "volume":
   372  			volume, err := getNamedVolume(tokens)
   373  			if err != nil {
   374  				return nil, nil, err
   375  			}
   376  			if _, ok := finalNamedVolumes[volume.Dest]; ok {
   377  				return nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest)
   378  			}
   379  			finalNamedVolumes[volume.Dest] = volume
   380  		default:
   381  			return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1])
   382  		}
   383  	}
   384  
   385  	return finalMounts, finalNamedVolumes, nil
   386  }
   387  
   388  // Parse a single bind mount entry from the --mount flag.
   389  func getBindMount(args []string) (spec.Mount, error) {
   390  	newMount := spec.Mount{
   391  		Type: TypeBind,
   392  	}
   393  
   394  	var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
   395  
   396  	for _, val := range args {
   397  		kv := strings.SplitN(val, "=", 2)
   398  		switch kv[0] {
   399  		case "bind-nonrecursive":
   400  			newMount.Options = append(newMount.Options, "bind")
   401  		case "ro", "rw":
   402  			if setRORW {
   403  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' or 'rw' options more than once")
   404  			}
   405  			setRORW = true
   406  			// Can be formatted as one of:
   407  			// ro
   408  			// ro=[true|false]
   409  			// rw
   410  			// rw=[true|false]
   411  			switch len(kv) {
   412  			case 1:
   413  				newMount.Options = append(newMount.Options, kv[0])
   414  			case 2:
   415  				switch strings.ToLower(kv[1]) {
   416  				case "true":
   417  					newMount.Options = append(newMount.Options, kv[0])
   418  				case "false":
   419  					// Set the opposite only for rw
   420  					// ro's opposite is the default
   421  					if kv[0] == "rw" {
   422  						newMount.Options = append(newMount.Options, "ro")
   423  					}
   424  				default:
   425  					return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1])
   426  				}
   427  			default:
   428  				return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val)
   429  			}
   430  		case "nosuid", "suid":
   431  			if setSuid {
   432  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
   433  			}
   434  			setSuid = true
   435  			newMount.Options = append(newMount.Options, kv[0])
   436  		case "nodev", "dev":
   437  			if setDev {
   438  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
   439  			}
   440  			setDev = true
   441  			newMount.Options = append(newMount.Options, kv[0])
   442  		case "noexec", "exec":
   443  			if setExec {
   444  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
   445  			}
   446  			setExec = true
   447  			newMount.Options = append(newMount.Options, kv[0])
   448  		case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z":
   449  			newMount.Options = append(newMount.Options, kv[0])
   450  		case "bind-propagation":
   451  			if len(kv) == 1 {
   452  				return newMount, errors.Wrapf(optionArgError, kv[0])
   453  			}
   454  			newMount.Options = append(newMount.Options, kv[1])
   455  		case "src", "source":
   456  			if len(kv) == 1 {
   457  				return newMount, errors.Wrapf(optionArgError, kv[0])
   458  			}
   459  			if err := parse.ValidateVolumeHostDir(kv[1]); err != nil {
   460  				return newMount, err
   461  			}
   462  			newMount.Source = kv[1]
   463  			setSource = true
   464  		case "target", "dst", "destination":
   465  			if len(kv) == 1 {
   466  				return newMount, errors.Wrapf(optionArgError, kv[0])
   467  			}
   468  			if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
   469  				return newMount, err
   470  			}
   471  			newMount.Destination = filepath.Clean(kv[1])
   472  			setDest = true
   473  		case "relabel":
   474  			if setRelabel {
   475  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once")
   476  			}
   477  			setRelabel = true
   478  			if len(kv) != 2 {
   479  				return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
   480  			}
   481  			switch kv[1] {
   482  			case "private":
   483  				newMount.Options = append(newMount.Options, "z")
   484  			case "shared":
   485  				newMount.Options = append(newMount.Options, "Z")
   486  			default:
   487  				return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
   488  			}
   489  		default:
   490  			return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
   491  		}
   492  	}
   493  
   494  	if !setDest {
   495  		return newMount, noDestError
   496  	}
   497  
   498  	if !setSource {
   499  		newMount.Source = newMount.Destination
   500  	}
   501  
   502  	options, err := parse.ValidateVolumeOpts(newMount.Options)
   503  	if err != nil {
   504  		return newMount, err
   505  	}
   506  	newMount.Options = options
   507  	return newMount, nil
   508  }
   509  
   510  // Parse a single tmpfs mount entry from the --mount flag
   511  func getTmpfsMount(args []string) (spec.Mount, error) {
   512  	newMount := spec.Mount{
   513  		Type:   TypeTmpfs,
   514  		Source: TypeTmpfs,
   515  	}
   516  
   517  	var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
   518  
   519  	for _, val := range args {
   520  		kv := strings.SplitN(val, "=", 2)
   521  		switch kv[0] {
   522  		case "tmpcopyup", "notmpcopyup":
   523  			if setTmpcopyup {
   524  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once")
   525  			}
   526  			setTmpcopyup = true
   527  			newMount.Options = append(newMount.Options, kv[0])
   528  		case "ro", "rw":
   529  			if setRORW {
   530  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
   531  			}
   532  			setRORW = true
   533  			newMount.Options = append(newMount.Options, kv[0])
   534  		case "nosuid", "suid":
   535  			if setSuid {
   536  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
   537  			}
   538  			setSuid = true
   539  			newMount.Options = append(newMount.Options, kv[0])
   540  		case "nodev", "dev":
   541  			if setDev {
   542  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
   543  			}
   544  			setDev = true
   545  			newMount.Options = append(newMount.Options, kv[0])
   546  		case "noexec", "exec":
   547  			if setExec {
   548  				return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
   549  			}
   550  			setExec = true
   551  			newMount.Options = append(newMount.Options, kv[0])
   552  		case "tmpfs-mode":
   553  			if len(kv) == 1 {
   554  				return newMount, errors.Wrapf(optionArgError, kv[0])
   555  			}
   556  			newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1]))
   557  		case "tmpfs-size":
   558  			if len(kv) == 1 {
   559  				return newMount, errors.Wrapf(optionArgError, kv[0])
   560  			}
   561  			newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1]))
   562  		case "src", "source":
   563  			return newMount, errors.Errorf("source is not supported with tmpfs mounts")
   564  		case "target", "dst", "destination":
   565  			if len(kv) == 1 {
   566  				return newMount, errors.Wrapf(optionArgError, kv[0])
   567  			}
   568  			if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
   569  				return newMount, err
   570  			}
   571  			newMount.Destination = filepath.Clean(kv[1])
   572  			setDest = true
   573  		default:
   574  			return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
   575  		}
   576  	}
   577  
   578  	if !setDest {
   579  		return newMount, noDestError
   580  	}
   581  
   582  	return newMount, nil
   583  }
   584  
   585  // Parse a single volume mount entry from the --mount flag.
   586  // Note that the volume-label option for named volumes is currently NOT supported.
   587  // TODO: add support for --volume-label
   588  func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) {
   589  	newVolume := new(libpod.ContainerNamedVolume)
   590  
   591  	var setSource, setDest, setRORW, setSuid, setDev, setExec bool
   592  
   593  	for _, val := range args {
   594  		kv := strings.SplitN(val, "=", 2)
   595  		switch kv[0] {
   596  		case "ro", "rw":
   597  			if setRORW {
   598  				return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
   599  			}
   600  			setRORW = true
   601  			newVolume.Options = append(newVolume.Options, kv[0])
   602  		case "nosuid", "suid":
   603  			if setSuid {
   604  				return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
   605  			}
   606  			setSuid = true
   607  			newVolume.Options = append(newVolume.Options, kv[0])
   608  		case "nodev", "dev":
   609  			if setDev {
   610  				return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
   611  			}
   612  			setDev = true
   613  			newVolume.Options = append(newVolume.Options, kv[0])
   614  		case "noexec", "exec":
   615  			if setExec {
   616  				return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
   617  			}
   618  			setExec = true
   619  			newVolume.Options = append(newVolume.Options, kv[0])
   620  		case "volume-label":
   621  			return nil, errors.Errorf("the --volume-label option is not presently implemented")
   622  		case "src", "source":
   623  			if len(kv) == 1 {
   624  				return nil, errors.Wrapf(optionArgError, kv[0])
   625  			}
   626  			newVolume.Name = kv[1]
   627  			setSource = true
   628  		case "target", "dst", "destination":
   629  			if len(kv) == 1 {
   630  				return nil, errors.Wrapf(optionArgError, kv[0])
   631  			}
   632  			if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
   633  				return nil, err
   634  			}
   635  			newVolume.Dest = filepath.Clean(kv[1])
   636  			setDest = true
   637  		default:
   638  			return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
   639  		}
   640  	}
   641  
   642  	if !setSource {
   643  		return nil, errors.Errorf("must set source volume")
   644  	}
   645  	if !setDest {
   646  		return nil, noDestError
   647  	}
   648  
   649  	return newVolume, nil
   650  }
   651  
   652  func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
   653  	mounts := make(map[string]spec.Mount)
   654  	volumes := make(map[string]*libpod.ContainerNamedVolume)
   655  
   656  	volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
   657  
   658  	for _, vol := range config.Volumes {
   659  		var (
   660  			options []string
   661  			src     string
   662  			dest    string
   663  			err     error
   664  		)
   665  
   666  		splitVol := strings.Split(vol, ":")
   667  		if len(splitVol) > 3 {
   668  			return nil, nil, errors.Wrapf(volumeFormatErr, vol)
   669  		}
   670  
   671  		src = splitVol[0]
   672  		if len(splitVol) == 1 {
   673  			// This is an anonymous named volume. Only thing given
   674  			// is destination.
   675  			// Name/source will be blank, and populated by libpod.
   676  			src = ""
   677  			dest = splitVol[0]
   678  		} else if len(splitVol) > 1 {
   679  			dest = splitVol[1]
   680  		}
   681  		if len(splitVol) > 2 {
   682  			if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
   683  				return nil, nil, err
   684  			}
   685  		}
   686  
   687  		// Do not check source dir for anonymous volumes
   688  		if len(splitVol) > 1 {
   689  			if err := parse.ValidateVolumeHostDir(src); err != nil {
   690  				return nil, nil, err
   691  			}
   692  		}
   693  		if err := parse.ValidateVolumeCtrDir(dest); err != nil {
   694  			return nil, nil, err
   695  		}
   696  
   697  		cleanDest := filepath.Clean(dest)
   698  
   699  		if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
   700  			// This is not a named volume
   701  			newMount := spec.Mount{
   702  				Destination: cleanDest,
   703  				Type:        string(TypeBind),
   704  				Source:      src,
   705  				Options:     options,
   706  			}
   707  			if _, ok := mounts[newMount.Destination]; ok {
   708  				return nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
   709  			}
   710  			mounts[newMount.Destination] = newMount
   711  		} else {
   712  			// This is a named volume
   713  			newNamedVol := new(libpod.ContainerNamedVolume)
   714  			newNamedVol.Name = src
   715  			newNamedVol.Dest = cleanDest
   716  			newNamedVol.Options = options
   717  
   718  			if _, ok := volumes[newNamedVol.Dest]; ok {
   719  				return nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
   720  			}
   721  			volumes[newNamedVol.Dest] = newNamedVol
   722  		}
   723  
   724  		logrus.Debugf("User mount %s:%s options %v", src, dest, options)
   725  	}
   726  
   727  	return mounts, volumes, nil
   728  }
   729  
   730  // Get mounts for container's image volumes
   731  func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
   732  	mounts := make(map[string]spec.Mount)
   733  	volumes := make(map[string]*libpod.ContainerNamedVolume)
   734  
   735  	if config.ImageVolumeType == "ignore" {
   736  		return mounts, volumes, nil
   737  	}
   738  
   739  	for vol := range config.BuiltinImgVolumes {
   740  		cleanDest := filepath.Clean(vol)
   741  		logrus.Debugf("Adding image volume at %s", cleanDest)
   742  		if config.ImageVolumeType == "tmpfs" {
   743  			// Tmpfs image volumes are handled as mounts
   744  			mount := spec.Mount{
   745  				Destination: cleanDest,
   746  				Source:      TypeTmpfs,
   747  				Type:        TypeTmpfs,
   748  				Options:     []string{"rprivate", "rw", "nodev", "exec"},
   749  			}
   750  			mounts[cleanDest] = mount
   751  		} else {
   752  			// Anonymous volumes have no name.
   753  			namedVolume := new(libpod.ContainerNamedVolume)
   754  			namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
   755  			namedVolume.Dest = cleanDest
   756  			volumes[cleanDest] = namedVolume
   757  		}
   758  	}
   759  
   760  	return mounts, volumes, nil
   761  }
   762  
   763  // GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
   764  func (config *CreateConfig) getTmpfsMounts() (map[string]spec.Mount, error) {
   765  	m := make(map[string]spec.Mount)
   766  	for _, i := range config.Tmpfs {
   767  		// Default options if nothing passed
   768  		var options []string
   769  		spliti := strings.Split(i, ":")
   770  		destPath := spliti[0]
   771  		if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil {
   772  			return nil, err
   773  		}
   774  		if len(spliti) > 1 {
   775  			options = strings.Split(spliti[1], ",")
   776  		}
   777  
   778  		if _, ok := m[destPath]; ok {
   779  			return nil, errors.Wrapf(errDuplicateDest, destPath)
   780  		}
   781  
   782  		mount := spec.Mount{
   783  			Destination: filepath.Clean(destPath),
   784  			Type:        string(TypeTmpfs),
   785  			Options:     options,
   786  			Source:      string(TypeTmpfs),
   787  		}
   788  		m[destPath] = mount
   789  	}
   790  	return m, nil
   791  }
   792  
   793  // AddContainerInitBinary adds the init binary specified by path iff the
   794  // container will run in a private PID namespace that is not shared with the
   795  // host or another pre-existing container, where an init-like process is
   796  // already running.
   797  //
   798  // Note that AddContainerInitBinary prepends "/dev/init" "--" to the command
   799  // to execute the bind-mounted binary as PID 1.
   800  func (config *CreateConfig) addContainerInitBinary(path string) (spec.Mount, error) {
   801  	mount := spec.Mount{
   802  		Destination: "/dev/init",
   803  		Type:        TypeBind,
   804  		Source:      path,
   805  		Options:     []string{TypeBind, "ro"},
   806  	}
   807  
   808  	if path == "" {
   809  		return mount, fmt.Errorf("please specify a path to the container-init binary")
   810  	}
   811  	if !config.Pid.PidMode.IsPrivate() {
   812  		return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
   813  	}
   814  	if config.Systemd {
   815  		return mount, fmt.Errorf("cannot use container-init binary with systemd")
   816  	}
   817  	if _, err := os.Stat(path); os.IsNotExist(err) {
   818  		return mount, errors.Wrap(err, "container-init binary not found on the host")
   819  	}
   820  	config.Command = append([]string{"/dev/init", "--"}, config.Command...)
   821  	return mount, nil
   822  }
   823  
   824  // Supersede existing mounts in the spec with new, user-specified mounts.
   825  // TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
   826  // one mount, and we already have /tmp/a and /tmp/b, should we remove
   827  // the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
   828  func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
   829  	if len(mounts) > 0 {
   830  		// If we have overlappings mounts, remove them from the spec in favor of
   831  		// the user-added volume mounts
   832  		destinations := make(map[string]bool)
   833  		for _, mount := range mounts {
   834  			destinations[path.Clean(mount.Destination)] = true
   835  		}
   836  		// Copy all mounts from spec to defaultMounts, except for
   837  		//  - mounts overridden by a user supplied mount;
   838  		//  - all mounts under /dev if a user supplied /dev is present;
   839  		mountDev := destinations["/dev"]
   840  		for _, mount := range configMount {
   841  			if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
   842  				if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
   843  					// filter out everything under /dev if /dev is user-mounted
   844  					continue
   845  				}
   846  
   847  				logrus.Debugf("Adding mount %s", mount.Destination)
   848  				mounts = append(mounts, mount)
   849  			}
   850  		}
   851  		return mounts
   852  	}
   853  	return configMount
   854  }
   855  
   856  // Ensure mount options on all mounts are correct
   857  func InitFSMounts(mounts []spec.Mount) error {
   858  	for i, m := range mounts {
   859  		switch {
   860  		case m.Type == TypeBind:
   861  			opts, err := util.ProcessOptions(m.Options, false, m.Source)
   862  			if err != nil {
   863  				return err
   864  			}
   865  			mounts[i].Options = opts
   866  		case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
   867  			opts, err := util.ProcessOptions(m.Options, true, "")
   868  			if err != nil {
   869  				return err
   870  			}
   871  			mounts[i].Options = opts
   872  		}
   873  	}
   874  	return nil
   875  }