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

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/containerd/log"
    11  	containertypes "github.com/docker/docker/api/types/container"
    12  	"github.com/docker/docker/api/types/mount"
    13  	mounttypes "github.com/docker/docker/api/types/mount"
    14  	volumetypes "github.com/docker/docker/api/types/volume"
    15  	"github.com/docker/docker/container"
    16  	"github.com/docker/docker/errdefs"
    17  	"github.com/docker/docker/volume"
    18  	volumemounts "github.com/docker/docker/volume/mounts"
    19  	"github.com/docker/docker/volume/service"
    20  	volumeopts "github.com/docker/docker/volume/service/opts"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  var _ volume.LiveRestorer = (*volumeWrapper)(nil)
    25  
    26  type mounts []container.Mount
    27  
    28  // Len returns the number of mounts. Used in sorting.
    29  func (m mounts) Len() int {
    30  	return len(m)
    31  }
    32  
    33  // Less returns true if the number of parts (a/b/c would be 3 parts) in the
    34  // mount indexed by parameter 1 is less than that of the mount indexed by
    35  // parameter 2. Used in sorting.
    36  func (m mounts) Less(i, j int) bool {
    37  	return m.parts(i) < m.parts(j)
    38  }
    39  
    40  // Swap swaps two items in an array of mounts. Used in sorting
    41  func (m mounts) Swap(i, j int) {
    42  	m[i], m[j] = m[j], m[i]
    43  }
    44  
    45  // parts returns the number of parts in the destination of a mount. Used in sorting.
    46  func (m mounts) parts(i int) int {
    47  	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
    48  }
    49  
    50  // registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
    51  // It follows the next sequence to decide what to mount in each final destination:
    52  //
    53  // 1. Select the previously configured mount points for the containers, if any.
    54  // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
    55  // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
    56  // 4. Cleanup old volumes that are about to be reassigned.
    57  func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig, defaultReadOnlyNonRecursive bool) (retErr error) {
    58  	binds := map[string]bool{}
    59  	mountPoints := map[string]*volumemounts.MountPoint{}
    60  	parser := volumemounts.NewParser()
    61  
    62  	ctx := context.TODO()
    63  	defer func() {
    64  		// clean up the container mountpoints once return with error
    65  		if retErr != nil {
    66  			for _, m := range mountPoints {
    67  				if m.Volume == nil {
    68  					continue
    69  				}
    70  				daemon.volumes.Release(ctx, m.Volume.Name(), container.ID)
    71  			}
    72  		}
    73  	}()
    74  
    75  	dereferenceIfExists := func(destination string) {
    76  		if v, ok := mountPoints[destination]; ok {
    77  			log.G(ctx).Debugf("Duplicate mount point '%s'", destination)
    78  			if v.Volume != nil {
    79  				daemon.volumes.Release(ctx, v.Volume.Name(), container.ID)
    80  			}
    81  		}
    82  	}
    83  
    84  	// 1. Read already configured mount points.
    85  	for destination, point := range container.MountPoints {
    86  		mountPoints[destination] = point
    87  	}
    88  
    89  	// 2. Read volumes from other containers.
    90  	for _, v := range hostConfig.VolumesFrom {
    91  		containerID, mode, err := parser.ParseVolumesFrom(v)
    92  		if err != nil {
    93  			return errdefs.InvalidParameter(err)
    94  		}
    95  
    96  		c, err := daemon.GetContainer(containerID)
    97  		if err != nil {
    98  			return errdefs.InvalidParameter(err)
    99  		}
   100  
   101  		for _, m := range c.MountPoints {
   102  			cp := &volumemounts.MountPoint{
   103  				Type:        m.Type,
   104  				Name:        m.Name,
   105  				Source:      m.Source,
   106  				RW:          m.RW && parser.ReadWrite(mode),
   107  				Driver:      m.Driver,
   108  				Destination: m.Destination,
   109  				Propagation: m.Propagation,
   110  				Spec:        m.Spec,
   111  				CopyData:    false,
   112  			}
   113  
   114  			if len(cp.Source) == 0 {
   115  				v, err := daemon.volumes.Get(ctx, cp.Name, volumeopts.WithGetDriver(cp.Driver), volumeopts.WithGetReference(container.ID))
   116  				if err != nil {
   117  					return err
   118  				}
   119  				cp.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   120  			}
   121  			dereferenceIfExists(cp.Destination)
   122  			mountPoints[cp.Destination] = cp
   123  		}
   124  	}
   125  
   126  	// 3. Read bind mounts
   127  	for _, b := range hostConfig.Binds {
   128  		bind, err := parser.ParseMountRaw(b, hostConfig.VolumeDriver)
   129  		if err != nil {
   130  			return err
   131  		}
   132  		needsSlavePropagation, err := daemon.validateBindDaemonRoot(bind.Spec)
   133  		if err != nil {
   134  			return err
   135  		}
   136  		if needsSlavePropagation {
   137  			bind.Propagation = mount.PropagationRSlave
   138  		}
   139  
   140  		// #10618
   141  		_, tmpfsExists := hostConfig.Tmpfs[bind.Destination]
   142  		if binds[bind.Destination] || tmpfsExists {
   143  			return duplicateMountPointError(bind.Destination)
   144  		}
   145  
   146  		if bind.Type == mounttypes.TypeVolume {
   147  			// create the volume
   148  			v, err := daemon.volumes.Create(ctx, bind.Name, bind.Driver, volumeopts.WithCreateReference(container.ID))
   149  			if err != nil {
   150  				return err
   151  			}
   152  			bind.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   153  			bind.Source = v.Mountpoint
   154  			// bind.Name is an already existing volume, we need to use that here
   155  			bind.Driver = v.Driver
   156  			if bind.Driver == volume.DefaultDriverName {
   157  				setBindModeIfNull(bind)
   158  			}
   159  		}
   160  
   161  		if bind.Type == mount.TypeBind && !bind.RW {
   162  			if defaultReadOnlyNonRecursive {
   163  				if bind.Spec.BindOptions == nil {
   164  					bind.Spec.BindOptions = &mounttypes.BindOptions{}
   165  				}
   166  				bind.Spec.BindOptions.ReadOnlyNonRecursive = true
   167  			}
   168  		}
   169  
   170  		binds[bind.Destination] = true
   171  		dereferenceIfExists(bind.Destination)
   172  		mountPoints[bind.Destination] = bind
   173  	}
   174  
   175  	for _, cfg := range hostConfig.Mounts {
   176  		mp, err := parser.ParseMountSpec(cfg)
   177  		if err != nil {
   178  			return errdefs.InvalidParameter(err)
   179  		}
   180  		needsSlavePropagation, err := daemon.validateBindDaemonRoot(mp.Spec)
   181  		if err != nil {
   182  			return err
   183  		}
   184  		if needsSlavePropagation {
   185  			mp.Propagation = mount.PropagationRSlave
   186  		}
   187  
   188  		if binds[mp.Destination] {
   189  			return duplicateMountPointError(cfg.Target)
   190  		}
   191  
   192  		if mp.Type == mounttypes.TypeVolume {
   193  			var v *volumetypes.Volume
   194  			if cfg.VolumeOptions != nil {
   195  				var driverOpts map[string]string
   196  				if cfg.VolumeOptions.DriverConfig != nil {
   197  					driverOpts = cfg.VolumeOptions.DriverConfig.Options
   198  				}
   199  				v, err = daemon.volumes.Create(ctx,
   200  					mp.Name,
   201  					mp.Driver,
   202  					volumeopts.WithCreateReference(container.ID),
   203  					volumeopts.WithCreateOptions(driverOpts),
   204  					volumeopts.WithCreateLabels(cfg.VolumeOptions.Labels),
   205  				)
   206  			} else {
   207  				v, err = daemon.volumes.Create(ctx, mp.Name, mp.Driver, volumeopts.WithCreateReference(container.ID))
   208  			}
   209  			if err != nil {
   210  				return err
   211  			}
   212  
   213  			mp.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   214  			mp.Name = v.Name
   215  			mp.Driver = v.Driver
   216  
   217  			// need to selinux-relabel local mounts
   218  			mp.Source = v.Mountpoint
   219  			if mp.Driver == volume.DefaultDriverName {
   220  				setBindModeIfNull(mp)
   221  			}
   222  		}
   223  
   224  		if mp.Type == mounttypes.TypeBind {
   225  			if cfg.BindOptions == nil || !cfg.BindOptions.CreateMountpoint {
   226  				mp.SkipMountpointCreation = true
   227  			}
   228  
   229  			if !mp.RW && defaultReadOnlyNonRecursive {
   230  				if mp.Spec.BindOptions == nil {
   231  					mp.Spec.BindOptions = &mounttypes.BindOptions{}
   232  				}
   233  				mp.Spec.BindOptions.ReadOnlyNonRecursive = true
   234  			}
   235  		}
   236  
   237  		binds[mp.Destination] = true
   238  		dereferenceIfExists(mp.Destination)
   239  		mountPoints[mp.Destination] = mp
   240  	}
   241  
   242  	container.Lock()
   243  
   244  	// 4. Cleanup old volumes that are about to be reassigned.
   245  	for _, m := range mountPoints {
   246  		if parser.IsBackwardCompatible(m) {
   247  			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
   248  				daemon.volumes.Release(ctx, mp.Volume.Name(), container.ID)
   249  			}
   250  		}
   251  	}
   252  	container.MountPoints = mountPoints
   253  
   254  	container.Unlock()
   255  
   256  	return nil
   257  }
   258  
   259  // lazyInitializeVolume initializes a mountpoint's volume if needed.
   260  // This happens after a daemon restart.
   261  func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volumemounts.MountPoint) error {
   262  	if len(m.Driver) > 0 && m.Volume == nil {
   263  		v, err := daemon.volumes.Get(context.TODO(), m.Name, volumeopts.WithGetDriver(m.Driver), volumeopts.WithGetReference(containerID))
   264  		if err != nil {
   265  			return err
   266  		}
   267  		m.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   268  	}
   269  	return nil
   270  }
   271  
   272  // VolumesService is used to perform volume operations
   273  func (daemon *Daemon) VolumesService() *service.VolumesService {
   274  	return daemon.volumes
   275  }
   276  
   277  type volumeMounter interface {
   278  	Mount(ctx context.Context, v *volumetypes.Volume, ref string) (string, error)
   279  	Unmount(ctx context.Context, v *volumetypes.Volume, ref string) error
   280  	LiveRestoreVolume(ctx context.Context, v *volumetypes.Volume, ref string) error
   281  }
   282  
   283  type volumeWrapper struct {
   284  	v *volumetypes.Volume
   285  	s volumeMounter
   286  }
   287  
   288  func (v *volumeWrapper) Name() string {
   289  	return v.v.Name
   290  }
   291  
   292  func (v *volumeWrapper) DriverName() string {
   293  	return v.v.Driver
   294  }
   295  
   296  func (v *volumeWrapper) Path() string {
   297  	return v.v.Mountpoint
   298  }
   299  
   300  func (v *volumeWrapper) Mount(ref string) (string, error) {
   301  	return v.s.Mount(context.TODO(), v.v, ref)
   302  }
   303  
   304  func (v *volumeWrapper) Unmount(ref string) error {
   305  	return v.s.Unmount(context.TODO(), v.v, ref)
   306  }
   307  
   308  func (v *volumeWrapper) CreatedAt() (time.Time, error) {
   309  	return time.Time{}, errors.New("not implemented")
   310  }
   311  
   312  func (v *volumeWrapper) Status() map[string]interface{} {
   313  	return v.v.Status
   314  }
   315  
   316  func (v *volumeWrapper) LiveRestoreVolume(ctx context.Context, ref string) error {
   317  	return v.s.LiveRestoreVolume(ctx, v.v, ref)
   318  }