github.com/chenchun/docker@v1.3.2-0.20150629222414-20467faf132b/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 // TODO Windows. Factor out as not relevant (as Windows daemon support not in pre-1.7) 262 // verifyVolumesInfo ports volumes configured for the containers pre docker 1.7. 263 // It reads the container configuration and creates valid mount points for the old volumes. 264 func (daemon *Daemon) verifyVolumesInfo(container *Container) error { 265 // Inspect old structures only when we're upgrading from old versions 266 // to versions >= 1.7 and the MountPoints has not been populated with volumes data. 267 if len(container.MountPoints) == 0 && len(container.Volumes) > 0 { 268 for destination, hostPath := range container.Volumes { 269 vfsPath := filepath.Join(daemon.root, "vfs", "dir") 270 rw := container.VolumesRW != nil && container.VolumesRW[destination] 271 272 if strings.HasPrefix(hostPath, vfsPath) { 273 id := filepath.Base(hostPath) 274 if err := migrateVolume(id, hostPath); err != nil { 275 return err 276 } 277 container.addLocalMountPoint(id, destination, rw) 278 } else { // Bind mount 279 id, source, err := parseVolumeSource(hostPath) 280 // We should not find an error here coming 281 // from the old configuration, but who knows. 282 if err != nil { 283 return err 284 } 285 container.addBindMountPoint(id, source, destination, rw) 286 } 287 } 288 } else if len(container.MountPoints) > 0 { 289 // Volumes created with a Docker version >= 1.7. We verify integrity in case of data created 290 // with Docker 1.7 RC versions that put the information in 291 // DOCKER_ROOT/volumes/VOLUME_ID rather than DOCKER_ROOT/volumes/VOLUME_ID/_container_data. 292 l, err := getVolumeDriver(volume.DefaultDriverName) 293 if err != nil { 294 return err 295 } 296 297 for _, m := range container.MountPoints { 298 if m.Driver != volume.DefaultDriverName { 299 continue 300 } 301 dataPath := l.(*local.Root).DataPath(m.Name) 302 volumePath := filepath.Dir(dataPath) 303 304 d, err := ioutil.ReadDir(volumePath) 305 if err != nil { 306 // If the volume directory doesn't exist yet it will be recreated, 307 // so we only return the error when there is a different issue. 308 if !os.IsNotExist(err) { 309 return err 310 } 311 // Do not check when the volume directory does not exist. 312 continue 313 } 314 if validVolumeLayout(d) { 315 continue 316 } 317 318 if err := os.Mkdir(dataPath, 0755); err != nil { 319 return err 320 } 321 322 // Move data inside the data directory 323 for _, f := range d { 324 oldp := filepath.Join(volumePath, f.Name()) 325 newp := filepath.Join(dataPath, f.Name()) 326 if err := os.Rename(oldp, newp); err != nil { 327 logrus.Errorf("Unable to move %s to %s\n", oldp, newp) 328 } 329 } 330 } 331 332 return container.ToDisk() 333 } 334 335 return nil 336 } 337 338 func createVolume(name, driverName string) (volume.Volume, error) { 339 vd, err := getVolumeDriver(driverName) 340 if err != nil { 341 return nil, err 342 } 343 return vd.Create(name) 344 } 345 346 func removeVolume(v volume.Volume) error { 347 vd, err := getVolumeDriver(v.DriverName()) 348 if err != nil { 349 return nil 350 } 351 return vd.Remove(v) 352 }