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 }