github.com/moby/docker@v26.1.3+incompatible/daemon/create_unix.go (about)

     1  //go:build !windows
     2  
     3  package daemon // import "github.com/docker/docker/daemon"
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/containerd/log"
    12  	containertypes "github.com/docker/docker/api/types/container"
    13  	mounttypes "github.com/docker/docker/api/types/mount"
    14  	"github.com/docker/docker/container"
    15  	"github.com/docker/docker/errdefs"
    16  	"github.com/docker/docker/internal/compatcontext"
    17  	"github.com/docker/docker/oci"
    18  	volumemounts "github.com/docker/docker/volume/mounts"
    19  	volumeopts "github.com/docker/docker/volume/service/opts"
    20  	"github.com/opencontainers/selinux/go-selinux/label"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  // createContainerOSSpecificSettings performs host-OS specific container create functionality
    25  func (daemon *Daemon) createContainerOSSpecificSettings(ctx context.Context, container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
    26  	if err := daemon.Mount(container); err != nil {
    27  		return err
    28  	}
    29  	defer daemon.Unmount(container)
    30  
    31  	rootIDs := daemon.idMapping.RootPair()
    32  	if err := container.SetupWorkingDirectory(rootIDs); err != nil {
    33  		return err
    34  	}
    35  
    36  	// Set the default masked and readonly paths with regard to the host config options if they are not set.
    37  	if hostConfig.MaskedPaths == nil && !hostConfig.Privileged {
    38  		hostConfig.MaskedPaths = oci.DefaultSpec().Linux.MaskedPaths // Set it to the default if nil
    39  		container.HostConfig.MaskedPaths = hostConfig.MaskedPaths
    40  	}
    41  	if hostConfig.ReadonlyPaths == nil && !hostConfig.Privileged {
    42  		hostConfig.ReadonlyPaths = oci.DefaultSpec().Linux.ReadonlyPaths // Set it to the default if nil
    43  		container.HostConfig.ReadonlyPaths = hostConfig.ReadonlyPaths
    44  	}
    45  
    46  	for spec := range config.Volumes {
    47  		destination := filepath.Clean(spec)
    48  
    49  		// Skip volumes for which we already have something mounted on that
    50  		// destination because of a --volume-from.
    51  		if container.HasMountFor(destination) {
    52  			log.G(ctx).WithField("container", container.ID).WithField("destination", spec).Debug("mountpoint already exists, skipping anonymous volume")
    53  			// Not an error, this could easily have come from the image config.
    54  			continue
    55  		}
    56  		path, err := container.GetResourcePath(destination)
    57  		if err != nil {
    58  			return err
    59  		}
    60  
    61  		stat, err := os.Stat(path)
    62  		if err == nil && !stat.IsDir() {
    63  			return fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
    64  		}
    65  
    66  		v, err := daemon.volumes.Create(context.TODO(), "", hostConfig.VolumeDriver, volumeopts.WithCreateReference(container.ID))
    67  		if err != nil {
    68  			return err
    69  		}
    70  
    71  		if err := label.Relabel(v.Mountpoint, container.MountLabel, true); err != nil {
    72  			return err
    73  		}
    74  
    75  		container.AddMountPointWithVolume(destination, &volumeWrapper{v: v, s: daemon.volumes}, true)
    76  	}
    77  	return daemon.populateVolumes(ctx, container)
    78  }
    79  
    80  // populateVolumes copies data from the container's rootfs into the volume for non-binds.
    81  // this is only called when the container is created.
    82  func (daemon *Daemon) populateVolumes(ctx context.Context, c *container.Container) error {
    83  	for _, mnt := range c.MountPoints {
    84  		if mnt.Volume == nil {
    85  			continue
    86  		}
    87  
    88  		if mnt.Type != mounttypes.TypeVolume || !mnt.CopyData {
    89  			continue
    90  		}
    91  
    92  		if err := daemon.populateVolume(ctx, c, mnt); err != nil {
    93  			return err
    94  		}
    95  	}
    96  	return nil
    97  }
    98  
    99  func (daemon *Daemon) populateVolume(ctx context.Context, c *container.Container, mnt *volumemounts.MountPoint) error {
   100  	ctrDestPath, err := c.GetResourcePath(mnt.Destination)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	if _, err := os.Stat(ctrDestPath); err != nil {
   106  		if os.IsNotExist(err) {
   107  			return nil
   108  		}
   109  		return err
   110  	}
   111  
   112  	volumePath, cleanup, err := mnt.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), nil)
   113  	if err != nil {
   114  		if errdefs.IsNotFound(err) {
   115  			return nil
   116  		}
   117  		log.G(ctx).WithError(err).Debugf("can't copy data from %s:%s, to %s", c.ID, mnt.Destination, volumePath)
   118  		return errors.Wrapf(err, "failed to populate volume")
   119  	}
   120  	defer mnt.Cleanup(compatcontext.WithoutCancel(ctx))
   121  	defer cleanup(compatcontext.WithoutCancel(ctx))
   122  
   123  	log.G(ctx).Debugf("copying image data from %s:%s, to %s", c.ID, mnt.Destination, volumePath)
   124  	if err := c.CopyImagePathContent(volumePath, ctrDestPath); err != nil {
   125  		return err
   126  	}
   127  
   128  	return nil
   129  }