github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/daemon/volumes_unix.go (about) 1 // +build !windows 2 3 package daemon 4 5 import ( 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "sort" 10 "strings" 11 12 "github.com/Sirupsen/logrus" 13 "github.com/docker/docker/daemon/execdriver" 14 derr "github.com/docker/docker/errors" 15 "github.com/docker/docker/pkg/system" 16 "github.com/docker/docker/runconfig" 17 "github.com/docker/docker/volume" 18 volumedrivers "github.com/docker/docker/volume/drivers" 19 "github.com/docker/docker/volume/local" 20 "github.com/opencontainers/runc/libcontainer/label" 21 ) 22 23 // copyOwnership copies the permissions and uid:gid of the source file 24 // to the destination file 25 func copyOwnership(source, destination string) error { 26 stat, err := system.Stat(source) 27 if err != nil { 28 return err 29 } 30 31 if err := os.Chown(destination, int(stat.UID()), int(stat.Gid())); err != nil { 32 return err 33 } 34 35 return os.Chmod(destination, os.FileMode(stat.Mode())) 36 } 37 38 // setupMounts iterates through each of the mount points for a container and 39 // calls Setup() on each. It also looks to see if is a network mount such as 40 // /etc/resolv.conf, and if it is not, appends it to the array of mounts. 41 func (container *Container) setupMounts() ([]execdriver.Mount, error) { 42 var mounts []execdriver.Mount 43 for _, m := range container.MountPoints { 44 path, err := m.Setup() 45 if err != nil { 46 return nil, err 47 } 48 if !container.trySetNetworkMount(m.Destination, path) { 49 mounts = append(mounts, execdriver.Mount{ 50 Source: path, 51 Destination: m.Destination, 52 Writable: m.RW, 53 }) 54 } 55 } 56 57 mounts = sortMounts(mounts) 58 return append(mounts, container.networkMounts()...), nil 59 } 60 61 // parseBindMount validates the configuration of mount information in runconfig is valid. 62 func parseBindMount(spec, volumeDriver string) (*mountPoint, error) { 63 bind := &mountPoint{ 64 RW: true, 65 } 66 arr := strings.Split(spec, ":") 67 68 switch len(arr) { 69 case 2: 70 bind.Destination = arr[1] 71 case 3: 72 bind.Destination = arr[1] 73 mode := arr[2] 74 if !volume.ValidMountMode(mode) { 75 return nil, derr.ErrorCodeVolumeInvalidMode.WithArgs(mode) 76 } 77 bind.RW = volume.ReadWrite(mode) 78 // Mode field is used by SELinux to decide whether to apply label 79 bind.Mode = mode 80 default: 81 return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec) 82 } 83 84 //validate the volumes destination path 85 if !filepath.IsAbs(bind.Destination) { 86 return nil, derr.ErrorCodeVolumeAbs.WithArgs(bind.Destination) 87 } 88 89 name, source, err := parseVolumeSource(arr[0]) 90 if err != nil { 91 return nil, err 92 } 93 94 if len(source) == 0 { 95 bind.Driver = volumeDriver 96 if len(bind.Driver) == 0 { 97 bind.Driver = volume.DefaultDriverName 98 } 99 } else { 100 bind.Source = filepath.Clean(source) 101 } 102 103 bind.Name = name 104 bind.Destination = filepath.Clean(bind.Destination) 105 return bind, nil 106 } 107 108 // sortMounts sorts an array of mounts in lexicographic order. This ensure that 109 // when mounting, the mounts don't shadow other mounts. For example, if mounting 110 // /etc and /etc/resolv.conf, /etc/resolv.conf must not be mounted first. 111 func sortMounts(m []execdriver.Mount) []execdriver.Mount { 112 sort.Sort(mounts(m)) 113 return m 114 } 115 116 type mounts []execdriver.Mount 117 118 // Len returns the number of mounts 119 func (m mounts) Len() int { 120 return len(m) 121 } 122 123 // Less returns true if the number of parts (a/b/c would be 3 parts) in the 124 // mount indexed by parameter 1 is less than that of the mount indexed by 125 // parameter 2. 126 func (m mounts) Less(i, j int) bool { 127 return m.parts(i) < m.parts(j) 128 } 129 130 // Swap swaps two items in an array of mounts. 131 func (m mounts) Swap(i, j int) { 132 m[i], m[j] = m[j], m[i] 133 } 134 135 // parts returns the number of parts in the destination of a mount. 136 func (m mounts) parts(i int) int { 137 return len(strings.Split(filepath.Clean(m[i].Destination), string(os.PathSeparator))) 138 } 139 140 // migrateVolume links the contents of a volume created pre Docker 1.7 141 // into the location expected by the local driver. 142 // It creates a symlink from DOCKER_ROOT/vfs/dir/VOLUME_ID to DOCKER_ROOT/volumes/VOLUME_ID/_container_data. 143 // It preserves the volume json configuration generated pre Docker 1.7 to be able to 144 // downgrade from Docker 1.7 to Docker 1.6 without losing volume compatibility. 145 func migrateVolume(id, vfs string) error { 146 l, err := volumedrivers.Lookup(volume.DefaultDriverName) 147 if err != nil { 148 return err 149 } 150 151 newDataPath := l.(*local.Root).DataPath(id) 152 fi, err := os.Stat(newDataPath) 153 if err != nil && !os.IsNotExist(err) { 154 return err 155 } 156 157 if fi != nil && fi.IsDir() { 158 return nil 159 } 160 161 return os.Symlink(vfs, newDataPath) 162 } 163 164 // validVolumeLayout checks whether the volume directory layout 165 // is valid to work with Docker post 1.7 or not. 166 func validVolumeLayout(files []os.FileInfo) bool { 167 if len(files) == 1 && files[0].Name() == local.VolumeDataPathName && files[0].IsDir() { 168 return true 169 } 170 171 if len(files) != 2 { 172 return false 173 } 174 175 for _, f := range files { 176 if f.Name() == "config.json" || 177 (f.Name() == local.VolumeDataPathName && f.Mode()&os.ModeSymlink == os.ModeSymlink) { 178 // Old volume configuration, we ignore it 179 continue 180 } 181 return false 182 } 183 184 return true 185 } 186 187 // verifyVolumesInfo ports volumes configured for the containers pre docker 1.7. 188 // It reads the container configuration and creates valid mount points for the old volumes. 189 func (daemon *Daemon) verifyVolumesInfo(container *Container) error { 190 // Inspect old structures only when we're upgrading from old versions 191 // to versions >= 1.7 and the MountPoints has not been populated with volumes data. 192 if len(container.MountPoints) == 0 && len(container.Volumes) > 0 { 193 for destination, hostPath := range container.Volumes { 194 vfsPath := filepath.Join(daemon.root, "vfs", "dir") 195 rw := container.VolumesRW != nil && container.VolumesRW[destination] 196 197 if strings.HasPrefix(hostPath, vfsPath) { 198 id := filepath.Base(hostPath) 199 if err := migrateVolume(id, hostPath); err != nil { 200 return err 201 } 202 container.addLocalMountPoint(id, destination, rw) 203 } else { // Bind mount 204 id, source, err := parseVolumeSource(hostPath) 205 // We should not find an error here coming 206 // from the old configuration, but who knows. 207 if err != nil { 208 return err 209 } 210 container.addBindMountPoint(id, source, destination, rw) 211 } 212 } 213 } else if len(container.MountPoints) > 0 { 214 // Volumes created with a Docker version >= 1.7. We verify integrity in case of data created 215 // with Docker 1.7 RC versions that put the information in 216 // DOCKER_ROOT/volumes/VOLUME_ID rather than DOCKER_ROOT/volumes/VOLUME_ID/_container_data. 217 l, err := volumedrivers.Lookup(volume.DefaultDriverName) 218 if err != nil { 219 return err 220 } 221 222 for _, m := range container.MountPoints { 223 if m.Driver != volume.DefaultDriverName { 224 continue 225 } 226 dataPath := l.(*local.Root).DataPath(m.Name) 227 volumePath := filepath.Dir(dataPath) 228 229 d, err := ioutil.ReadDir(volumePath) 230 if err != nil { 231 // If the volume directory doesn't exist yet it will be recreated, 232 // so we only return the error when there is a different issue. 233 if !os.IsNotExist(err) { 234 return err 235 } 236 // Do not check when the volume directory does not exist. 237 continue 238 } 239 if validVolumeLayout(d) { 240 continue 241 } 242 243 if err := os.Mkdir(dataPath, 0755); err != nil { 244 return err 245 } 246 247 // Move data inside the data directory 248 for _, f := range d { 249 oldp := filepath.Join(volumePath, f.Name()) 250 newp := filepath.Join(dataPath, f.Name()) 251 if err := os.Rename(oldp, newp); err != nil { 252 logrus.Errorf("Unable to move %s to %s\n", oldp, newp) 253 } 254 } 255 } 256 257 return container.toDiskLocking() 258 } 259 260 return nil 261 } 262 263 // parseVolumesFrom ensure that the supplied volumes-from is valid. 264 func parseVolumesFrom(spec string) (string, string, error) { 265 if len(spec) == 0 { 266 return "", "", derr.ErrorCodeVolumeFromBlank.WithArgs(spec) 267 } 268 269 specParts := strings.SplitN(spec, ":", 2) 270 id := specParts[0] 271 mode := "rw" 272 273 if len(specParts) == 2 { 274 mode = specParts[1] 275 if !volume.ValidMountMode(mode) { 276 return "", "", derr.ErrorCodeVolumeMode.WithArgs(mode) 277 } 278 } 279 return id, mode, nil 280 } 281 282 // registerMountPoints initializes the container mount points with the configured volumes and bind mounts. 283 // It follows the next sequence to decide what to mount in each final destination: 284 // 285 // 1. Select the previously configured mount points for the containers, if any. 286 // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. 287 // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. 288 func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error { 289 binds := map[string]bool{} 290 mountPoints := map[string]*mountPoint{} 291 292 // 1. Read already configured mount points. 293 for name, point := range container.MountPoints { 294 mountPoints[name] = point 295 } 296 297 // 2. Read volumes from other containers. 298 for _, v := range hostConfig.VolumesFrom { 299 containerID, mode, err := parseVolumesFrom(v) 300 if err != nil { 301 return err 302 } 303 304 c, err := daemon.Get(containerID) 305 if err != nil { 306 return err 307 } 308 309 for _, m := range c.MountPoints { 310 cp := &mountPoint{ 311 Name: m.Name, 312 Source: m.Source, 313 RW: m.RW && volume.ReadWrite(mode), 314 Driver: m.Driver, 315 Destination: m.Destination, 316 } 317 318 if len(cp.Source) == 0 { 319 v, err := daemon.createVolume(cp.Name, cp.Driver, nil) 320 if err != nil { 321 return err 322 } 323 cp.Volume = v 324 } 325 326 mountPoints[cp.Destination] = cp 327 } 328 } 329 330 // 3. Read bind mounts 331 for _, b := range hostConfig.Binds { 332 // #10618 333 bind, err := parseBindMount(b, hostConfig.VolumeDriver) 334 if err != nil { 335 return err 336 } 337 338 if binds[bind.Destination] { 339 return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination) 340 } 341 342 if len(bind.Name) > 0 && len(bind.Driver) > 0 { 343 // create the volume 344 v, err := daemon.createVolume(bind.Name, bind.Driver, nil) 345 if err != nil { 346 return err 347 } 348 bind.Volume = v 349 bind.Source = v.Path() 350 // bind.Name is an already existing volume, we need to use that here 351 bind.Driver = v.DriverName() 352 // Since this is just a named volume and not a typical bind, set to shared mode `z` 353 if bind.Mode == "" { 354 bind.Mode = "z" 355 } 356 } 357 358 shared := label.IsShared(bind.Mode) 359 if err := label.Relabel(bind.Source, container.MountLabel, shared); err != nil { 360 return err 361 } 362 binds[bind.Destination] = true 363 mountPoints[bind.Destination] = bind 364 } 365 366 // Keep backwards compatible structures 367 bcVolumes := map[string]string{} 368 bcVolumesRW := map[string]bool{} 369 for _, m := range mountPoints { 370 if m.BackwardsCompatible() { 371 bcVolumes[m.Destination] = m.Path() 372 bcVolumesRW[m.Destination] = m.RW 373 374 // This mountpoint is replacing an existing one, so the count needs to be decremented 375 if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { 376 daemon.volumes.Decrement(mp.Volume) 377 } 378 } 379 } 380 381 container.Lock() 382 container.MountPoints = mountPoints 383 container.Volumes = bcVolumes 384 container.VolumesRW = bcVolumesRW 385 container.Unlock() 386 387 return nil 388 } 389 390 // createVolume creates a volume. 391 func (daemon *Daemon) createVolume(name, driverName string, opts map[string]string) (volume.Volume, error) { 392 v, err := daemon.volumes.Create(name, driverName, opts) 393 if err != nil { 394 return nil, err 395 } 396 daemon.volumes.Increment(v) 397 return v, nil 398 } 399 400 // parseVolumeSource parses the origin sources that's mounted into the container. 401 func parseVolumeSource(spec string) (string, string, error) { 402 if !filepath.IsAbs(spec) { 403 return spec, "", nil 404 } 405 406 return "", spec, nil 407 } 408 409 // BackwardsCompatible decides whether this mount point can be 410 // used in old versions of Docker or not. 411 // Only bind mounts and local volumes can be used in old versions of Docker. 412 func (m *mountPoint) BackwardsCompatible() bool { 413 return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName 414 }