github.com/circular-dark/docker@v1.7.0/daemon/volumes.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/pkg/chrootarchive" 12 "github.com/docker/docker/runconfig" 13 "github.com/docker/docker/volume" 14 "github.com/docker/docker/volume/local" 15 "github.com/docker/libcontainer/label" 16 ) 17 18 type mountPoint struct { 19 Name string 20 Destination string 21 Driver string 22 RW bool 23 Volume volume.Volume `json:"-"` 24 Source string 25 Relabel string 26 } 27 28 func (m *mountPoint) Setup() (string, error) { 29 if m.Volume != nil { 30 return m.Volume.Mount() 31 } 32 33 if len(m.Source) > 0 { 34 if _, err := os.Stat(m.Source); err != nil { 35 if !os.IsNotExist(err) { 36 return "", err 37 } 38 if err := os.MkdirAll(m.Source, 0755); err != nil { 39 return "", err 40 } 41 } 42 return m.Source, nil 43 } 44 45 return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined") 46 } 47 48 func (m *mountPoint) Path() string { 49 if m.Volume != nil { 50 return m.Volume.Path() 51 } 52 53 return m.Source 54 } 55 56 // BackwardsCompatible decides whether this mount point can be 57 // used in old versions of Docker or not. 58 // Only bind mounts and local volumes can be used in old versions of Docker. 59 func (m *mountPoint) BackwardsCompatible() bool { 60 return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName 61 } 62 63 func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*mountPoint, error) { 64 bind := &mountPoint{ 65 RW: true, 66 } 67 arr := strings.Split(spec, ":") 68 69 switch len(arr) { 70 case 2: 71 bind.Destination = arr[1] 72 case 3: 73 bind.Destination = arr[1] 74 mode := arr[2] 75 if !validMountMode(mode) { 76 return nil, fmt.Errorf("invalid mode for volumes-from: %s", mode) 77 } 78 bind.RW = rwModes[mode] 79 // Relabel will apply a SELinux label, if necessary 80 bind.Relabel = mode 81 default: 82 return nil, fmt.Errorf("Invalid volume specification: %s", spec) 83 } 84 85 name, source, err := parseVolumeSource(arr[0]) 86 if err != nil { 87 return nil, err 88 } 89 90 if len(source) == 0 { 91 bind.Driver = config.VolumeDriver 92 if len(bind.Driver) == 0 { 93 bind.Driver = volume.DefaultDriverName 94 } 95 } else { 96 bind.Source = filepath.Clean(source) 97 } 98 99 bind.Name = name 100 bind.Destination = filepath.Clean(bind.Destination) 101 return bind, nil 102 } 103 104 func parseVolumesFrom(spec string) (string, string, error) { 105 if len(spec) == 0 { 106 return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec) 107 } 108 109 specParts := strings.SplitN(spec, ":", 2) 110 id := specParts[0] 111 mode := "rw" 112 113 if len(specParts) == 2 { 114 mode = specParts[1] 115 if !validMountMode(mode) { 116 return "", "", fmt.Errorf("invalid mode for volumes-from: %s", mode) 117 } 118 } 119 return id, mode, nil 120 } 121 122 // read-write modes 123 var rwModes = map[string]bool{ 124 "rw": true, 125 "rw,Z": true, 126 "rw,z": true, 127 "z,rw": true, 128 "Z,rw": true, 129 "Z": true, 130 "z": true, 131 } 132 133 // read-only modes 134 var roModes = map[string]bool{ 135 "ro": true, 136 "ro,Z": true, 137 "ro,z": true, 138 "z,ro": true, 139 "Z,ro": true, 140 } 141 142 func validMountMode(mode string) bool { 143 return roModes[mode] || rwModes[mode] 144 } 145 146 func copyExistingContents(source, destination string) error { 147 volList, err := ioutil.ReadDir(source) 148 if err != nil { 149 return err 150 } 151 if len(volList) > 0 { 152 srcList, err := ioutil.ReadDir(destination) 153 if err != nil { 154 return err 155 } 156 if len(srcList) == 0 { 157 // If the source volume is empty copy files from the root into the volume 158 if err := chrootarchive.CopyWithTar(source, destination); err != nil { 159 return err 160 } 161 } 162 } 163 return copyOwnership(source, destination) 164 } 165 166 // registerMountPoints initializes the container mount points with the configured volumes and bind mounts. 167 // It follows the next sequence to decide what to mount in each final destination: 168 // 169 // 1. Select the previously configured mount points for the containers, if any. 170 // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. 171 // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. 172 func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error { 173 binds := map[string]bool{} 174 mountPoints := map[string]*mountPoint{} 175 176 // 1. Read already configured mount points. 177 for name, point := range container.MountPoints { 178 mountPoints[name] = point 179 } 180 181 // 2. Read volumes from other containers. 182 for _, v := range hostConfig.VolumesFrom { 183 containerID, mode, err := parseVolumesFrom(v) 184 if err != nil { 185 return err 186 } 187 188 c, err := daemon.Get(containerID) 189 if err != nil { 190 return err 191 } 192 193 for _, m := range c.MountPoints { 194 cp := m 195 cp.RW = m.RW && mode != "ro" 196 197 if len(m.Source) == 0 { 198 v, err := createVolume(m.Name, m.Driver) 199 if err != nil { 200 return err 201 } 202 cp.Volume = v 203 } 204 205 mountPoints[cp.Destination] = cp 206 } 207 } 208 209 // 3. Read bind mounts 210 for _, b := range hostConfig.Binds { 211 // #10618 212 bind, err := parseBindMount(b, container.MountLabel, container.Config) 213 if err != nil { 214 return err 215 } 216 217 if binds[bind.Destination] { 218 return fmt.Errorf("Duplicate bind mount %s", bind.Destination) 219 } 220 221 if len(bind.Name) > 0 && len(bind.Driver) > 0 { 222 // create the volume 223 v, err := createVolume(bind.Name, bind.Driver) 224 if err != nil { 225 return err 226 } 227 bind.Volume = v 228 bind.Source = v.Path() 229 // Since this is just a named volume and not a typical bind, set to shared mode `z` 230 if bind.Relabel == "" { 231 bind.Relabel = "z" 232 } 233 } 234 235 if err := label.Relabel(bind.Source, container.MountLabel, bind.Relabel); err != nil { 236 return err 237 } 238 binds[bind.Destination] = true 239 mountPoints[bind.Destination] = bind 240 } 241 242 // Keep backwards compatible structures 243 bcVolumes := map[string]string{} 244 bcVolumesRW := map[string]bool{} 245 for _, m := range mountPoints { 246 if m.BackwardsCompatible() { 247 bcVolumes[m.Destination] = m.Path() 248 bcVolumesRW[m.Destination] = m.RW 249 } 250 } 251 252 container.Lock() 253 container.MountPoints = mountPoints 254 container.Volumes = bcVolumes 255 container.VolumesRW = bcVolumesRW 256 container.Unlock() 257 258 return nil 259 } 260 261 // verifyVolumesInfo ports volumes configured for the containers pre docker 1.7. 262 // It reads the container configuration and creates valid mount points for the old volumes. 263 func (daemon *Daemon) verifyVolumesInfo(container *Container) error { 264 // Inspect old structures only when we're upgrading from old versions 265 // to versions >= 1.7 and the MountPoints has not been populated with volumes data. 266 if len(container.MountPoints) == 0 && len(container.Volumes) > 0 { 267 for destination, hostPath := range container.Volumes { 268 vfsPath := filepath.Join(daemon.root, "vfs", "dir") 269 rw := container.VolumesRW != nil && container.VolumesRW[destination] 270 271 if strings.HasPrefix(hostPath, vfsPath) { 272 id := filepath.Base(hostPath) 273 if err := migrateVolume(id, hostPath); err != nil { 274 return err 275 } 276 container.addLocalMountPoint(id, destination, rw) 277 } else { // Bind mount 278 id, source, err := parseVolumeSource(hostPath) 279 // We should not find an error here coming 280 // from the old configuration, but who knows. 281 if err != nil { 282 return err 283 } 284 container.addBindMountPoint(id, source, destination, rw) 285 } 286 } 287 } else if len(container.MountPoints) > 0 { 288 // Volumes created with a Docker version >= 1.7. We verify integrity in case of data created 289 // with Docker 1.7 RC versions that put the information in 290 // DOCKER_ROOT/volumes/VOLUME_ID rather than DOCKER_ROOT/volumes/VOLUME_ID/_container_data. 291 l, err := getVolumeDriver(volume.DefaultDriverName) 292 if err != nil { 293 return err 294 } 295 296 for _, m := range container.MountPoints { 297 if m.Driver != volume.DefaultDriverName { 298 continue 299 } 300 dataPath := l.(*local.Root).DataPath(m.Name) 301 volumePath := filepath.Dir(dataPath) 302 303 d, err := ioutil.ReadDir(volumePath) 304 if err != nil { 305 // If the volume directory doesn't exist yet it will be recreated, 306 // so we only return the error when there is a different issue. 307 if !os.IsNotExist(err) { 308 return err 309 } 310 // Do not check when the volume directory does not exist. 311 continue 312 } 313 if validVolumeLayout(d) { 314 continue 315 } 316 317 if err := os.Mkdir(dataPath, 0755); err != nil { 318 return err 319 } 320 321 // Move data inside the data directory 322 for _, f := range d { 323 oldp := filepath.Join(volumePath, f.Name()) 324 newp := filepath.Join(dataPath, f.Name()) 325 if err := os.Rename(oldp, newp); err != nil { 326 logrus.Errorf("Unable to move %s to %s\n", oldp, newp) 327 } 328 } 329 } 330 331 return container.ToDisk() 332 } 333 334 return nil 335 } 336 337 func createVolume(name, driverName string) (volume.Volume, error) { 338 vd, err := getVolumeDriver(driverName) 339 if err != nil { 340 return nil, err 341 } 342 return vd.Create(name) 343 } 344 345 func removeVolume(v volume.Volume) error { 346 vd, err := getVolumeDriver(v.DriverName()) 347 if err != nil { 348 return nil 349 } 350 return vd.Remove(v) 351 }