github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/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/docker/docker/container" 11 "github.com/docker/docker/volume" 12 "github.com/docker/engine-api/types" 13 containertypes "github.com/docker/engine-api/types/container" 14 ) 15 16 var ( 17 // ErrVolumeReadonly is used to signal an error when trying to copy data into 18 // a volume mount that is not writable. 19 ErrVolumeReadonly = errors.New("mounted volume is marked read-only") 20 ) 21 22 type mounts []container.Mount 23 24 // volumeToAPIType converts a volume.Volume to the type used by the remote API 25 func volumeToAPIType(v volume.Volume) *types.Volume { 26 tv := &types.Volume{ 27 Name: v.Name(), 28 Driver: v.DriverName(), 29 } 30 if v, ok := v.(volume.LabeledVolume); ok { 31 tv.Labels = v.Labels() 32 } 33 34 if v, ok := v.(volume.ScopedVolume); ok { 35 tv.Scope = v.Scope() 36 } 37 return tv 38 } 39 40 // Len returns the number of mounts. Used in sorting. 41 func (m mounts) Len() int { 42 return len(m) 43 } 44 45 // Less returns true if the number of parts (a/b/c would be 3 parts) in the 46 // mount indexed by parameter 1 is less than that of the mount indexed by 47 // parameter 2. Used in sorting. 48 func (m mounts) Less(i, j int) bool { 49 return m.parts(i) < m.parts(j) 50 } 51 52 // Swap swaps two items in an array of mounts. Used in sorting 53 func (m mounts) Swap(i, j int) { 54 m[i], m[j] = m[j], m[i] 55 } 56 57 // parts returns the number of parts in the destination of a mount. Used in sorting. 58 func (m mounts) parts(i int) int { 59 return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) 60 } 61 62 // registerMountPoints initializes the container mount points with the configured volumes and bind mounts. 63 // It follows the next sequence to decide what to mount in each final destination: 64 // 65 // 1. Select the previously configured mount points for the containers, if any. 66 // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. 67 // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. 68 // 4. Cleanup old volumes that are about to be reassigned. 69 func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { 70 binds := map[string]bool{} 71 mountPoints := map[string]*volume.MountPoint{} 72 defer func() { 73 // clean up the container mountpoints once return with error 74 if retErr != nil { 75 for _, m := range mountPoints { 76 if m.Volume == nil { 77 continue 78 } 79 daemon.volumes.Dereference(m.Volume, container.ID) 80 } 81 } 82 }() 83 84 // 1. Read already configured mount points. 85 for name, point := range container.MountPoints { 86 mountPoints[name] = point 87 } 88 89 // 2. Read volumes from other containers. 90 for _, v := range hostConfig.VolumesFrom { 91 containerID, mode, err := volume.ParseVolumesFrom(v) 92 if err != nil { 93 return err 94 } 95 96 c, err := daemon.GetContainer(containerID) 97 if err != nil { 98 return err 99 } 100 101 for _, m := range c.MountPoints { 102 cp := &volume.MountPoint{ 103 Name: m.Name, 104 Source: m.Source, 105 RW: m.RW && volume.ReadWrite(mode), 106 Driver: m.Driver, 107 Destination: m.Destination, 108 Propagation: m.Propagation, 109 Named: m.Named, 110 } 111 112 if len(cp.Source) == 0 { 113 v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID) 114 if err != nil { 115 return err 116 } 117 cp.Volume = v 118 } 119 120 mountPoints[cp.Destination] = cp 121 } 122 } 123 124 // 3. Read bind mounts 125 for _, b := range hostConfig.Binds { 126 // #10618 127 bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver) 128 if err != nil { 129 return err 130 } 131 132 _, tmpfsExists := hostConfig.Tmpfs[bind.Destination] 133 if binds[bind.Destination] || tmpfsExists { 134 return fmt.Errorf("Duplicate mount point '%s'", bind.Destination) 135 } 136 137 if len(bind.Name) > 0 { 138 // create the volume 139 v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil) 140 if err != nil { 141 return err 142 } 143 bind.Volume = v 144 bind.Source = v.Path() 145 // bind.Name is an already existing volume, we need to use that here 146 bind.Driver = v.DriverName() 147 bind.Named = true 148 if bind.Driver == "local" { 149 bind = setBindModeIfNull(bind) 150 } 151 } 152 153 binds[bind.Destination] = true 154 mountPoints[bind.Destination] = bind 155 } 156 157 container.Lock() 158 159 // 4. Cleanup old volumes that are about to be reassigned. 160 for _, m := range mountPoints { 161 if m.BackwardsCompatible() { 162 if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { 163 daemon.volumes.Dereference(mp.Volume, container.ID) 164 } 165 } 166 } 167 container.MountPoints = mountPoints 168 169 container.Unlock() 170 171 return nil 172 } 173 174 // lazyInitializeVolume initializes a mountpoint's volume if needed. 175 // This happens after a daemon restart. 176 func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error { 177 if len(m.Driver) > 0 && m.Volume == nil { 178 v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID) 179 if err != nil { 180 return err 181 } 182 m.Volume = v 183 } 184 return nil 185 }