github.com/vvnotw/moby@v1.13.1/daemon/volumes.go (about) 1 package daemon 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/Sirupsen/logrus" 11 dockererrors "github.com/docker/docker/api/errors" 12 "github.com/docker/docker/api/types" 13 containertypes "github.com/docker/docker/api/types/container" 14 mounttypes "github.com/docker/docker/api/types/mount" 15 "github.com/docker/docker/container" 16 "github.com/docker/docker/volume" 17 "github.com/docker/docker/volume/drivers" 18 "github.com/opencontainers/runc/libcontainer/label" 19 ) 20 21 var ( 22 // ErrVolumeReadonly is used to signal an error when trying to copy data into 23 // a volume mount that is not writable. 24 ErrVolumeReadonly = errors.New("mounted volume is marked read-only") 25 ) 26 27 type mounts []container.Mount 28 29 // volumeToAPIType converts a volume.Volume to the type used by the Engine API 30 func volumeToAPIType(v volume.Volume) *types.Volume { 31 tv := &types.Volume{ 32 Name: v.Name(), 33 Driver: v.DriverName(), 34 } 35 if v, ok := v.(volume.DetailedVolume); ok { 36 tv.Labels = v.Labels() 37 tv.Options = v.Options() 38 tv.Scope = v.Scope() 39 } 40 41 return tv 42 } 43 44 // Len returns the number of mounts. Used in sorting. 45 func (m mounts) Len() int { 46 return len(m) 47 } 48 49 // Less returns true if the number of parts (a/b/c would be 3 parts) in the 50 // mount indexed by parameter 1 is less than that of the mount indexed by 51 // parameter 2. Used in sorting. 52 func (m mounts) Less(i, j int) bool { 53 return m.parts(i) < m.parts(j) 54 } 55 56 // Swap swaps two items in an array of mounts. Used in sorting 57 func (m mounts) Swap(i, j int) { 58 m[i], m[j] = m[j], m[i] 59 } 60 61 // parts returns the number of parts in the destination of a mount. Used in sorting. 62 func (m mounts) parts(i int) int { 63 return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) 64 } 65 66 // registerMountPoints initializes the container mount points with the configured volumes and bind mounts. 67 // It follows the next sequence to decide what to mount in each final destination: 68 // 69 // 1. Select the previously configured mount points for the containers, if any. 70 // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. 71 // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. 72 // 4. Cleanup old volumes that are about to be reassigned. 73 func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { 74 binds := map[string]bool{} 75 mountPoints := map[string]*volume.MountPoint{} 76 defer func() { 77 // clean up the container mountpoints once return with error 78 if retErr != nil { 79 for _, m := range mountPoints { 80 if m.Volume == nil { 81 continue 82 } 83 daemon.volumes.Dereference(m.Volume, 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 := volume.ParseVolumesFrom(v) 96 if err != nil { 97 return err 98 } 99 100 c, err := daemon.GetContainer(containerID) 101 if err != nil { 102 return err 103 } 104 105 for _, m := range c.MountPoints { 106 cp := &volume.MountPoint{ 107 Name: m.Name, 108 Source: m.Source, 109 RW: m.RW && volume.ReadWrite(mode), 110 Driver: m.Driver, 111 Destination: m.Destination, 112 Propagation: m.Propagation, 113 Spec: m.Spec, 114 CopyData: false, 115 } 116 117 if len(cp.Source) == 0 { 118 v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID) 119 if err != nil { 120 return err 121 } 122 cp.Volume = v 123 } 124 125 mountPoints[cp.Destination] = cp 126 } 127 } 128 129 // 3. Read bind mounts 130 for _, b := range hostConfig.Binds { 131 bind, err := volume.ParseMountRaw(b, hostConfig.VolumeDriver) 132 if err != nil { 133 return err 134 } 135 136 // #10618 137 _, tmpfsExists := hostConfig.Tmpfs[bind.Destination] 138 if binds[bind.Destination] || tmpfsExists { 139 return fmt.Errorf("Duplicate mount point '%s'", bind.Destination) 140 } 141 142 if bind.Type == mounttypes.TypeVolume { 143 // create the volume 144 v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil) 145 if err != nil { 146 return err 147 } 148 bind.Volume = v 149 bind.Source = v.Path() 150 // bind.Name is an already existing volume, we need to use that here 151 bind.Driver = v.DriverName() 152 if bind.Driver == volume.DefaultDriverName { 153 setBindModeIfNull(bind) 154 } 155 } 156 157 binds[bind.Destination] = true 158 mountPoints[bind.Destination] = bind 159 } 160 161 for _, cfg := range hostConfig.Mounts { 162 mp, err := volume.ParseMountSpec(cfg) 163 if err != nil { 164 return dockererrors.NewBadRequestError(err) 165 } 166 167 if binds[mp.Destination] { 168 return fmt.Errorf("Duplicate mount point '%s'", cfg.Target) 169 } 170 171 if mp.Type == mounttypes.TypeVolume { 172 var v volume.Volume 173 if cfg.VolumeOptions != nil { 174 var driverOpts map[string]string 175 if cfg.VolumeOptions.DriverConfig != nil { 176 driverOpts = cfg.VolumeOptions.DriverConfig.Options 177 } 178 v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, driverOpts, cfg.VolumeOptions.Labels) 179 } else { 180 v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, nil, nil) 181 } 182 if err != nil { 183 return err 184 } 185 186 if err := label.Relabel(mp.Source, container.MountLabel, false); err != nil { 187 return err 188 } 189 mp.Volume = v 190 mp.Name = v.Name() 191 mp.Driver = v.DriverName() 192 193 // only use the cached path here since getting the path is not necessary right now and calling `Path()` may be slow 194 if cv, ok := v.(interface { 195 CachedPath() string 196 }); ok { 197 mp.Source = cv.CachedPath() 198 } 199 } 200 201 binds[mp.Destination] = true 202 mountPoints[mp.Destination] = mp 203 } 204 205 container.Lock() 206 207 // 4. Cleanup old volumes that are about to be reassigned. 208 for _, m := range mountPoints { 209 if m.BackwardsCompatible() { 210 if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { 211 daemon.volumes.Dereference(mp.Volume, container.ID) 212 } 213 } 214 } 215 container.MountPoints = mountPoints 216 217 container.Unlock() 218 219 return nil 220 } 221 222 // lazyInitializeVolume initializes a mountpoint's volume if needed. 223 // This happens after a daemon restart. 224 func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error { 225 if len(m.Driver) > 0 && m.Volume == nil { 226 v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID) 227 if err != nil { 228 return err 229 } 230 m.Volume = v 231 } 232 return nil 233 } 234 235 func backportMountSpec(container *container.Container) error { 236 for target, m := range container.MountPoints { 237 if m.Spec.Type != "" { 238 // if type is set on even one mount, no need to migrate 239 return nil 240 } 241 if m.Name != "" { 242 m.Type = mounttypes.TypeVolume 243 m.Spec.Type = mounttypes.TypeVolume 244 245 // make sure this is not an anyonmous volume before setting the spec source 246 if _, exists := container.Config.Volumes[target]; !exists { 247 m.Spec.Source = m.Name 248 } 249 if container.HostConfig.VolumeDriver != "" { 250 m.Spec.VolumeOptions = &mounttypes.VolumeOptions{ 251 DriverConfig: &mounttypes.Driver{Name: container.HostConfig.VolumeDriver}, 252 } 253 } 254 if strings.Contains(m.Mode, "nocopy") { 255 if m.Spec.VolumeOptions == nil { 256 m.Spec.VolumeOptions = &mounttypes.VolumeOptions{} 257 } 258 m.Spec.VolumeOptions.NoCopy = true 259 } 260 } else { 261 m.Type = mounttypes.TypeBind 262 m.Spec.Type = mounttypes.TypeBind 263 m.Spec.Source = m.Source 264 if m.Propagation != "" { 265 m.Spec.BindOptions = &mounttypes.BindOptions{ 266 Propagation: m.Propagation, 267 } 268 } 269 } 270 271 m.Spec.Target = m.Destination 272 if !m.RW { 273 m.Spec.ReadOnly = true 274 } 275 } 276 return container.ToDiskLocking() 277 } 278 279 func (daemon *Daemon) traverseLocalVolumes(fn func(volume.Volume) error) error { 280 localVolumeDriver, err := volumedrivers.GetDriver(volume.DefaultDriverName) 281 if err != nil { 282 return fmt.Errorf("can't retrieve local volume driver: %v", err) 283 } 284 vols, err := localVolumeDriver.List() 285 if err != nil { 286 return fmt.Errorf("can't retrieve local volumes: %v", err) 287 } 288 289 for _, v := range vols { 290 name := v.Name() 291 _, err := daemon.volumes.Get(name) 292 if err != nil { 293 logrus.Warnf("failed to retrieve volume %s from store: %v", name, err) 294 } 295 296 err = fn(v) 297 if err != nil { 298 return err 299 } 300 } 301 302 return nil 303 }