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