github.com/rawahars/moby@v24.0.4+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 containertypes "github.com/docker/docker/api/types/container" 11 "github.com/docker/docker/api/types/mount" 12 mounttypes "github.com/docker/docker/api/types/mount" 13 volumetypes "github.com/docker/docker/api/types/volume" 14 "github.com/docker/docker/container" 15 "github.com/docker/docker/errdefs" 16 "github.com/docker/docker/volume" 17 volumemounts "github.com/docker/docker/volume/mounts" 18 "github.com/docker/docker/volume/service" 19 volumeopts "github.com/docker/docker/volume/service/opts" 20 "github.com/pkg/errors" 21 "github.com/sirupsen/logrus" 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) (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 logrus.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 binds[bind.Destination] = true 162 dereferenceIfExists(bind.Destination) 163 mountPoints[bind.Destination] = bind 164 } 165 166 for _, cfg := range hostConfig.Mounts { 167 mp, err := parser.ParseMountSpec(cfg) 168 if err != nil { 169 return errdefs.InvalidParameter(err) 170 } 171 needsSlavePropagation, err := daemon.validateBindDaemonRoot(mp.Spec) 172 if err != nil { 173 return err 174 } 175 if needsSlavePropagation { 176 mp.Propagation = mount.PropagationRSlave 177 } 178 179 if binds[mp.Destination] { 180 return duplicateMountPointError(cfg.Target) 181 } 182 183 if mp.Type == mounttypes.TypeVolume { 184 var v *volumetypes.Volume 185 if cfg.VolumeOptions != nil { 186 var driverOpts map[string]string 187 if cfg.VolumeOptions.DriverConfig != nil { 188 driverOpts = cfg.VolumeOptions.DriverConfig.Options 189 } 190 v, err = daemon.volumes.Create(ctx, 191 mp.Name, 192 mp.Driver, 193 volumeopts.WithCreateReference(container.ID), 194 volumeopts.WithCreateOptions(driverOpts), 195 volumeopts.WithCreateLabels(cfg.VolumeOptions.Labels), 196 ) 197 } else { 198 v, err = daemon.volumes.Create(ctx, mp.Name, mp.Driver, volumeopts.WithCreateReference(container.ID)) 199 } 200 if err != nil { 201 return err 202 } 203 204 mp.Volume = &volumeWrapper{v: v, s: daemon.volumes} 205 mp.Name = v.Name 206 mp.Driver = v.Driver 207 208 // need to selinux-relabel local mounts 209 mp.Source = v.Mountpoint 210 if mp.Driver == volume.DefaultDriverName { 211 setBindModeIfNull(mp) 212 } 213 } 214 215 if mp.Type == mounttypes.TypeBind && (cfg.BindOptions == nil || !cfg.BindOptions.CreateMountpoint) { 216 mp.SkipMountpointCreation = true 217 } 218 219 binds[mp.Destination] = true 220 dereferenceIfExists(mp.Destination) 221 mountPoints[mp.Destination] = mp 222 } 223 224 container.Lock() 225 226 // 4. Cleanup old volumes that are about to be reassigned. 227 for _, m := range mountPoints { 228 if parser.IsBackwardCompatible(m) { 229 if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { 230 daemon.volumes.Release(ctx, mp.Volume.Name(), container.ID) 231 } 232 } 233 } 234 container.MountPoints = mountPoints 235 236 container.Unlock() 237 238 return nil 239 } 240 241 // lazyInitializeVolume initializes a mountpoint's volume if needed. 242 // This happens after a daemon restart. 243 func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volumemounts.MountPoint) error { 244 if len(m.Driver) > 0 && m.Volume == nil { 245 v, err := daemon.volumes.Get(context.TODO(), m.Name, volumeopts.WithGetDriver(m.Driver), volumeopts.WithGetReference(containerID)) 246 if err != nil { 247 return err 248 } 249 m.Volume = &volumeWrapper{v: v, s: daemon.volumes} 250 } 251 return nil 252 } 253 254 // VolumesService is used to perform volume operations 255 func (daemon *Daemon) VolumesService() *service.VolumesService { 256 return daemon.volumes 257 } 258 259 type volumeMounter interface { 260 Mount(ctx context.Context, v *volumetypes.Volume, ref string) (string, error) 261 Unmount(ctx context.Context, v *volumetypes.Volume, ref string) error 262 LiveRestoreVolume(ctx context.Context, v *volumetypes.Volume, ref string) error 263 } 264 265 type volumeWrapper struct { 266 v *volumetypes.Volume 267 s volumeMounter 268 } 269 270 func (v *volumeWrapper) Name() string { 271 return v.v.Name 272 } 273 274 func (v *volumeWrapper) DriverName() string { 275 return v.v.Driver 276 } 277 278 func (v *volumeWrapper) Path() string { 279 return v.v.Mountpoint 280 } 281 282 func (v *volumeWrapper) Mount(ref string) (string, error) { 283 return v.s.Mount(context.TODO(), v.v, ref) 284 } 285 286 func (v *volumeWrapper) Unmount(ref string) error { 287 return v.s.Unmount(context.TODO(), v.v, ref) 288 } 289 290 func (v *volumeWrapper) CreatedAt() (time.Time, error) { 291 return time.Time{}, errors.New("not implemented") 292 } 293 294 func (v *volumeWrapper) Status() map[string]interface{} { 295 return v.v.Status 296 } 297 298 func (v *volumeWrapper) LiveRestoreVolume(ctx context.Context, ref string) error { 299 return v.s.LiveRestoreVolume(ctx, v.v, ref) 300 }