github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/specgen/generate/storage.go (about)

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