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