github.com/dinever/docker@v1.11.1/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 "github.com/opencontainers/runc/libcontainer/label" 15 ) 16 17 var ( 18 // ErrVolumeReadonly is used to signal an error when trying to copy data into 19 // a volume mount that is not writable. 20 ErrVolumeReadonly = errors.New("mounted volume is marked read-only") 21 ) 22 23 type mounts []container.Mount 24 25 // volumeToAPIType converts a volume.Volume to the type used by the remote API 26 func volumeToAPIType(v volume.Volume) *types.Volume { 27 tv := &types.Volume{ 28 Name: v.Name(), 29 Driver: v.DriverName(), 30 Mountpoint: v.Path(), 31 } 32 if v, ok := v.(interface { 33 Labels() map[string]string 34 }); ok { 35 tv.Labels = v.Labels() 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) error { 70 binds := map[string]bool{} 71 mountPoints := map[string]*volume.MountPoint{} 72 73 // 1. Read already configured mount points. 74 for name, point := range container.MountPoints { 75 mountPoints[name] = point 76 } 77 78 // 2. Read volumes from other containers. 79 for _, v := range hostConfig.VolumesFrom { 80 containerID, mode, err := volume.ParseVolumesFrom(v) 81 if err != nil { 82 return err 83 } 84 85 c, err := daemon.GetContainer(containerID) 86 if err != nil { 87 return err 88 } 89 90 for _, m := range c.MountPoints { 91 cp := &volume.MountPoint{ 92 Name: m.Name, 93 Source: m.Source, 94 RW: m.RW && volume.ReadWrite(mode), 95 Driver: m.Driver, 96 Destination: m.Destination, 97 Propagation: m.Propagation, 98 Named: m.Named, 99 } 100 101 if len(cp.Source) == 0 { 102 v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID) 103 if err != nil { 104 return err 105 } 106 cp.Volume = v 107 } 108 109 mountPoints[cp.Destination] = cp 110 } 111 } 112 113 // 3. Read bind mounts 114 for _, b := range hostConfig.Binds { 115 // #10618 116 bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver) 117 if err != nil { 118 return err 119 } 120 121 if binds[bind.Destination] { 122 return fmt.Errorf("Duplicate mount point '%s'", bind.Destination) 123 } 124 125 if len(bind.Name) > 0 { 126 // create the volume 127 v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil) 128 if err != nil { 129 return err 130 } 131 bind.Volume = v 132 bind.Source = v.Path() 133 // bind.Name is an already existing volume, we need to use that here 134 bind.Driver = v.DriverName() 135 bind.Named = true 136 if bind.Driver == "local" { 137 bind = setBindModeIfNull(bind) 138 } 139 } 140 141 if label.RelabelNeeded(bind.Mode) { 142 if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil { 143 return err 144 } 145 } 146 binds[bind.Destination] = true 147 mountPoints[bind.Destination] = bind 148 } 149 150 container.Lock() 151 152 // 4. Cleanup old volumes that are about to be reassigned. 153 for _, m := range mountPoints { 154 if m.BackwardsCompatible() { 155 if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { 156 daemon.volumes.Dereference(mp.Volume, container.ID) 157 } 158 } 159 } 160 container.MountPoints = mountPoints 161 162 container.Unlock() 163 164 return nil 165 } 166 167 // lazyInitializeVolume initializes a mountpoint's volume if needed. 168 // This happens after a daemon restart. 169 func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error { 170 if len(m.Driver) > 0 && m.Volume == nil { 171 v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID) 172 if err != nil { 173 return err 174 } 175 m.Volume = v 176 } 177 return nil 178 }