github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/specgen/generate/storage.go (about)

     1  package generate
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/containers/common/libimage"
    12  	"github.com/containers/common/pkg/config"
    13  	"github.com/containers/common/pkg/parse"
    14  	"github.com/hanks177/podman/v4/libpod"
    15  	"github.com/hanks177/podman/v4/libpod/define"
    16  	"github.com/hanks177/podman/v4/pkg/specgen"
    17  	"github.com/hanks177/podman/v4/pkg/util"
    18  	spec "github.com/opencontainers/runtime-spec/specs-go"
    19  	"github.com/pkg/errors"
    20  	"github.com/sirupsen/logrus"
    21  )
    22  
    23  var errDuplicateDest = errors.Errorf("duplicate mount destination")
    24  
    25  // Produce final mounts and named volumes for a container
    26  func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *libimage.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) {
    27  	// Get image volumes
    28  	baseMounts, baseVolumes, err := getImageVolumes(ctx, img, s)
    29  	if err != nil {
    30  		return nil, nil, nil, err
    31  	}
    32  
    33  	// Get volumes-from mounts
    34  	volFromMounts, volFromVolumes, err := getVolumesFrom(s.VolumesFrom, rt)
    35  	if err != nil {
    36  		return nil, nil, nil, err
    37  	}
    38  
    39  	// Supersede from --volumes-from.
    40  	for dest, mount := range volFromMounts {
    41  		baseMounts[dest] = mount
    42  	}
    43  	for dest, volume := range volFromVolumes {
    44  		baseVolumes[dest] = volume
    45  	}
    46  
    47  	// Need to make map forms of specgen mounts/volumes.
    48  	unifiedMounts := map[string]spec.Mount{}
    49  	unifiedVolumes := map[string]*specgen.NamedVolume{}
    50  	unifiedOverlays := map[string]*specgen.OverlayVolume{}
    51  
    52  	// Need to make map forms of specgen mounts/volumes.
    53  	commonMounts, commonVolumes, commonOverlayVolumes, err := specgen.GenVolumeMounts(rtc.Volumes())
    54  	if err != nil {
    55  		return nil, nil, nil, err
    56  	}
    57  
    58  	for _, m := range s.Mounts {
    59  		// Ensure that mount dest is clean, so that it can be
    60  		// compared against named volumes and avoid duplicate mounts.
    61  		if err = parse.ValidateVolumeCtrDir(m.Destination); err != nil {
    62  			return nil, nil, nil, err
    63  		}
    64  		cleanDestination := filepath.Clean(m.Destination)
    65  		if _, ok := unifiedMounts[cleanDestination]; ok {
    66  			return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", cleanDestination)
    67  		}
    68  		unifiedMounts[cleanDestination] = m
    69  	}
    70  
    71  	for _, m := range commonMounts {
    72  		if err = parse.ValidateVolumeCtrDir(m.Destination); err != nil {
    73  			return nil, nil, nil, err
    74  		}
    75  		cleanDestination := filepath.Clean(m.Destination)
    76  		if _, ok := unifiedMounts[cleanDestination]; !ok {
    77  			unifiedMounts[cleanDestination] = m
    78  		}
    79  	}
    80  
    81  	for _, v := range s.Volumes {
    82  		if err = parse.ValidateVolumeCtrDir(v.Dest); err != nil {
    83  			return nil, nil, nil, err
    84  		}
    85  		cleanDestination := filepath.Clean(v.Dest)
    86  		if _, ok := unifiedVolumes[cleanDestination]; ok {
    87  			return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination)
    88  		}
    89  		unifiedVolumes[cleanDestination] = v
    90  	}
    91  
    92  	for _, v := range commonVolumes {
    93  		if err = parse.ValidateVolumeCtrDir(v.Dest); err != nil {
    94  			return nil, nil, nil, err
    95  		}
    96  		cleanDestination := filepath.Clean(v.Dest)
    97  		if _, ok := unifiedVolumes[cleanDestination]; !ok {
    98  			unifiedVolumes[cleanDestination] = v
    99  		}
   100  	}
   101  
   102  	for _, v := range s.OverlayVolumes {
   103  		if err = parse.ValidateVolumeCtrDir(v.Destination); err != nil {
   104  			return nil, nil, nil, err
   105  		}
   106  		cleanDestination := filepath.Clean(v.Destination)
   107  		if _, ok := unifiedOverlays[cleanDestination]; ok {
   108  			return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination)
   109  		}
   110  		unifiedOverlays[cleanDestination] = v
   111  	}
   112  
   113  	for _, v := range commonOverlayVolumes {
   114  		if err = parse.ValidateVolumeCtrDir(v.Destination); err != nil {
   115  			return nil, nil, nil, err
   116  		}
   117  		cleanDestination := filepath.Clean(v.Destination)
   118  		if _, ok := unifiedOverlays[cleanDestination]; !ok {
   119  			unifiedOverlays[cleanDestination] = v
   120  		}
   121  	}
   122  
   123  	// If requested, add container init binary
   124  	if s.Init {
   125  		initPath := s.InitPath
   126  		if initPath == "" && rtc != nil {
   127  			initPath = rtc.Engine.InitPath
   128  		}
   129  		initMount, err := addContainerInitBinary(s, initPath)
   130  		if err != nil {
   131  			return nil, nil, nil, err
   132  		}
   133  		if _, ok := unifiedMounts[initMount.Destination]; ok {
   134  			return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
   135  		}
   136  		unifiedMounts[initMount.Destination] = initMount
   137  	}
   138  
   139  	// Before superseding, we need to find volume mounts which conflict with
   140  	// named volumes, and vice versa.
   141  	// We'll delete the conflicts here as we supersede.
   142  	for dest := range unifiedMounts {
   143  		delete(baseVolumes, dest)
   144  	}
   145  	for dest := range unifiedVolumes {
   146  		delete(baseMounts, dest)
   147  	}
   148  
   149  	// Supersede volumes-from/image volumes with unified volumes from above.
   150  	// This is an unconditional replacement.
   151  	for dest, mount := range unifiedMounts {
   152  		baseMounts[dest] = mount
   153  	}
   154  	for dest, volume := range unifiedVolumes {
   155  		baseVolumes[dest] = volume
   156  	}
   157  
   158  	// TODO: Investigate moving readonlyTmpfs into here. Would be more
   159  	// correct.
   160  
   161  	// Check for conflicts between named volumes and mounts
   162  	for dest := range baseMounts {
   163  		if _, ok := baseVolumes[dest]; ok {
   164  			return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
   165  		}
   166  	}
   167  	for dest := range baseVolumes {
   168  		if _, ok := baseMounts[dest]; ok {
   169  			return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
   170  		}
   171  	}
   172  	// Final step: maps to arrays
   173  	finalMounts := make([]spec.Mount, 0, len(baseMounts))
   174  	for _, mount := range baseMounts {
   175  		if mount.Type == define.TypeBind {
   176  			absSrc, err := filepath.Abs(mount.Source)
   177  			if err != nil {
   178  				return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
   179  			}
   180  			mount.Source = absSrc
   181  		}
   182  		finalMounts = append(finalMounts, mount)
   183  	}
   184  	finalVolumes := make([]*specgen.NamedVolume, 0, len(baseVolumes))
   185  	for _, volume := range baseVolumes {
   186  		finalVolumes = append(finalVolumes, volume)
   187  	}
   188  
   189  	finalOverlays := make([]*specgen.OverlayVolume, 0, len(unifiedOverlays))
   190  	for _, volume := range unifiedOverlays {
   191  		finalOverlays = append(finalOverlays, volume)
   192  	}
   193  
   194  	return finalMounts, finalVolumes, finalOverlays, nil
   195  }
   196  
   197  // Get image volumes from the given image
   198  func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGenerator) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) {
   199  	mounts := make(map[string]spec.Mount)
   200  	volumes := make(map[string]*specgen.NamedVolume)
   201  
   202  	mode := strings.ToLower(s.ImageVolumeMode)
   203  
   204  	// Image may be nil (rootfs in use), or image volume mode may be ignore.
   205  	if img == nil || mode == "ignore" {
   206  		return mounts, volumes, nil
   207  	}
   208  
   209  	inspect, err := img.Inspect(ctx, nil)
   210  	if err != nil {
   211  		return nil, nil, errors.Wrapf(err, "error inspecting image to get image volumes")
   212  	}
   213  	for volume := range inspect.Config.Volumes {
   214  		logrus.Debugf("Image has volume at %q", volume)
   215  		cleanDest := filepath.Clean(volume)
   216  		switch mode {
   217  		case "", "anonymous":
   218  			// Anonymous volumes have no name.
   219  			newVol := new(specgen.NamedVolume)
   220  			newVol.Dest = cleanDest
   221  			newVol.Options = []string{"rprivate", "rw", "nodev", "exec"}
   222  			volumes[cleanDest] = newVol
   223  			logrus.Debugf("Adding anonymous image volume at %q", cleanDest)
   224  		case "tmpfs":
   225  			mount := spec.Mount{
   226  				Destination: cleanDest,
   227  				Source:      define.TypeTmpfs,
   228  				Type:        define.TypeTmpfs,
   229  				Options:     []string{"rprivate", "rw", "nodev", "exec"},
   230  			}
   231  			mounts[cleanDest] = mount
   232  			logrus.Debugf("Adding tmpfs image volume at %q", cleanDest)
   233  		}
   234  	}
   235  
   236  	return mounts, volumes, nil
   237  }
   238  
   239  func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) {
   240  	finalMounts := make(map[string]spec.Mount)
   241  	finalNamedVolumes := make(map[string]*specgen.NamedVolume)
   242  
   243  	for _, volume := range volumesFrom {
   244  		var options []string
   245  
   246  		splitVol := strings.SplitN(volume, ":", 2)
   247  		if len(splitVol) == 2 {
   248  			splitOpts := strings.Split(splitVol[1], ",")
   249  			setRORW := false
   250  			setZ := false
   251  			for _, opt := range splitOpts {
   252  				switch opt {
   253  				case "z":
   254  					if setZ {
   255  						return nil, nil, errors.Errorf("cannot set :z more than once in mount options")
   256  					}
   257  					setZ = true
   258  				case "ro", "rw":
   259  					if setRORW {
   260  						return nil, nil, errors.Errorf("cannot set ro or rw options more than once")
   261  					}
   262  					setRORW = true
   263  				default:
   264  					return nil, nil, errors.Errorf("invalid option %q specified - volumes from another container can only use z,ro,rw options", opt)
   265  				}
   266  			}
   267  			options = splitOpts
   268  		}
   269  
   270  		ctr, err := runtime.LookupContainer(splitVol[0])
   271  		if err != nil {
   272  			return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0])
   273  		}
   274  
   275  		logrus.Debugf("Adding volumes from container %s", ctr.ID())
   276  
   277  		// Look up the container's user volumes. This gets us the
   278  		// destinations of all mounts the user added to the container.
   279  		userVolumesArr := ctr.UserVolumes()
   280  
   281  		// We're going to need to access them a lot, so convert to a map
   282  		// to reduce looping.
   283  		// We'll also use the map to indicate if we missed any volumes along the way.
   284  		userVolumes := make(map[string]bool)
   285  		for _, dest := range userVolumesArr {
   286  			userVolumes[dest] = false
   287  		}
   288  
   289  		// Now we get the container's spec and loop through its volumes
   290  		// and append them in if we can find them.
   291  		spec := ctr.Spec()
   292  		if spec == nil {
   293  			return nil, nil, errors.Errorf("retrieving container %s spec for volumes-from", ctr.ID())
   294  		}
   295  		for _, mnt := range spec.Mounts {
   296  			if mnt.Type != define.TypeBind {
   297  				continue
   298  			}
   299  			if _, exists := userVolumes[mnt.Destination]; exists {
   300  				userVolumes[mnt.Destination] = true
   301  
   302  				if len(options) != 0 {
   303  					mnt.Options = options
   304  				}
   305  
   306  				if _, ok := finalMounts[mnt.Destination]; ok {
   307  					logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID())
   308  				}
   309  				finalMounts[mnt.Destination] = mnt
   310  			}
   311  		}
   312  
   313  		// We're done with the spec mounts. Add named volumes.
   314  		// Add these unconditionally - none of them are automatically
   315  		// part of the container, as some spec mounts are.
   316  		namedVolumes := ctr.NamedVolumes()
   317  		for _, namedVol := range namedVolumes {
   318  			if _, exists := userVolumes[namedVol.Dest]; exists {
   319  				userVolumes[namedVol.Dest] = true
   320  			}
   321  
   322  			if len(options) != 0 {
   323  				namedVol.Options = options
   324  			}
   325  
   326  			if _, ok := finalMounts[namedVol.Dest]; ok {
   327  				logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID())
   328  			}
   329  			if err = parse.ValidateVolumeCtrDir(namedVol.Dest); err != nil {
   330  				return nil, nil, err
   331  			}
   332  
   333  			cleanDest := filepath.Clean(namedVol.Dest)
   334  			newVol := new(specgen.NamedVolume)
   335  			newVol.Dest = cleanDest
   336  			newVol.Options = namedVol.Options
   337  			newVol.Name = namedVol.Name
   338  
   339  			finalNamedVolumes[namedVol.Dest] = newVol
   340  		}
   341  
   342  		// Check if we missed any volumes
   343  		for volDest, found := range userVolumes {
   344  			if !found {
   345  				logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID())
   346  			}
   347  		}
   348  	}
   349  
   350  	return finalMounts, finalNamedVolumes, nil
   351  }
   352  
   353  // AddContainerInitBinary adds the init binary specified by path iff the
   354  // container will run in a private PID namespace that is not shared with the
   355  // host or another pre-existing container, where an init-like process is
   356  // already running.
   357  // This does *NOT* modify the container command - that must be done elsewhere.
   358  func addContainerInitBinary(s *specgen.SpecGenerator, path string) (spec.Mount, error) {
   359  	mount := spec.Mount{
   360  		Destination: define.ContainerInitPath,
   361  		Type:        define.TypeBind,
   362  		Source:      path,
   363  		Options:     []string{define.TypeBind, "ro"},
   364  	}
   365  
   366  	if path == "" {
   367  		return mount, fmt.Errorf("please specify a path to the container-init binary")
   368  	}
   369  	if !s.PidNS.IsPrivate() {
   370  		return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
   371  	}
   372  	if s.Systemd == "always" {
   373  		return mount, fmt.Errorf("cannot use container-init binary with systemd=always")
   374  	}
   375  	if _, err := os.Stat(path); os.IsNotExist(err) {
   376  		return mount, errors.Wrap(err, "container-init binary not found on the host")
   377  	}
   378  	return mount, nil
   379  }
   380  
   381  // Supersede existing mounts in the spec with new, user-specified mounts.
   382  // TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
   383  // one mount, and we already have /tmp/a and /tmp/b, should we remove
   384  // the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
   385  func SupersedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
   386  	if len(mounts) > 0 {
   387  		// If we have overlappings mounts, remove them from the spec in favor of
   388  		// the user-added volume mounts
   389  		destinations := make(map[string]bool)
   390  		for _, mount := range mounts {
   391  			destinations[path.Clean(mount.Destination)] = true
   392  		}
   393  		// Copy all mounts from spec to defaultMounts, except for
   394  		//  - mounts overridden by a user supplied mount;
   395  		//  - all mounts under /dev if a user supplied /dev is present;
   396  		mountDev := destinations["/dev"]
   397  		for _, mount := range configMount {
   398  			if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
   399  				if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
   400  					// filter out everything under /dev if /dev is user-mounted
   401  					continue
   402  				}
   403  
   404  				logrus.Debugf("Adding mount %s", mount.Destination)
   405  				mounts = append(mounts, mount)
   406  			}
   407  		}
   408  		return mounts
   409  	}
   410  	return configMount
   411  }
   412  
   413  func InitFSMounts(mounts []spec.Mount) error {
   414  	for i, m := range mounts {
   415  		switch {
   416  		case m.Type == define.TypeBind:
   417  			opts, err := util.ProcessOptions(m.Options, false, m.Source)
   418  			if err != nil {
   419  				return err
   420  			}
   421  			mounts[i].Options = opts
   422  		case m.Type == define.TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
   423  			opts, err := util.ProcessOptions(m.Options, true, "")
   424  			if err != nil {
   425  				return err
   426  			}
   427  			mounts[i].Options = opts
   428  		}
   429  	}
   430  	return nil
   431  }