github.com/Shopify/docker@v1.13.1/daemon/volumes_unix.go (about) 1 // +build !windows 2 3 // TODO(amitkris): We need to split this file for solaris. 4 5 package daemon 6 7 import ( 8 "encoding/json" 9 "os" 10 "path/filepath" 11 "sort" 12 "strconv" 13 "strings" 14 15 "github.com/docker/docker/container" 16 "github.com/docker/docker/pkg/fileutils" 17 "github.com/docker/docker/pkg/mount" 18 "github.com/docker/docker/volume" 19 "github.com/docker/docker/volume/drivers" 20 "github.com/docker/docker/volume/local" 21 "github.com/pkg/errors" 22 ) 23 24 // setupMounts iterates through each of the mount points for a container and 25 // calls Setup() on each. It also looks to see if is a network mount such as 26 // /etc/resolv.conf, and if it is not, appends it to the array of mounts. 27 func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) { 28 var mounts []container.Mount 29 // TODO: tmpfs mounts should be part of Mountpoints 30 tmpfsMounts := make(map[string]bool) 31 tmpfsMountInfo, err := c.TmpfsMounts() 32 if err != nil { 33 return nil, err 34 } 35 for _, m := range tmpfsMountInfo { 36 tmpfsMounts[m.Destination] = true 37 } 38 for _, m := range c.MountPoints { 39 if tmpfsMounts[m.Destination] { 40 continue 41 } 42 if err := daemon.lazyInitializeVolume(c.ID, m); err != nil { 43 return nil, err 44 } 45 rootUID, rootGID := daemon.GetRemappedUIDGID() 46 path, err := m.Setup(c.MountLabel, rootUID, rootGID) 47 if err != nil { 48 return nil, err 49 } 50 if !c.TrySetNetworkMount(m.Destination, path) { 51 mnt := container.Mount{ 52 Source: path, 53 Destination: m.Destination, 54 Writable: m.RW, 55 Propagation: string(m.Propagation), 56 } 57 if m.Volume != nil { 58 attributes := map[string]string{ 59 "driver": m.Volume.DriverName(), 60 "container": c.ID, 61 "destination": m.Destination, 62 "read/write": strconv.FormatBool(m.RW), 63 "propagation": string(m.Propagation), 64 } 65 daemon.LogVolumeEvent(m.Volume.Name(), "mount", attributes) 66 } 67 mounts = append(mounts, mnt) 68 } 69 } 70 71 mounts = sortMounts(mounts) 72 netMounts := c.NetworkMounts() 73 // if we are going to mount any of the network files from container 74 // metadata, the ownership must be set properly for potential container 75 // remapped root (user namespaces) 76 rootUID, rootGID := daemon.GetRemappedUIDGID() 77 for _, mount := range netMounts { 78 if err := os.Chown(mount.Source, rootUID, rootGID); err != nil { 79 return nil, err 80 } 81 } 82 return append(mounts, netMounts...), nil 83 } 84 85 // sortMounts sorts an array of mounts in lexicographic order. This ensure that 86 // when mounting, the mounts don't shadow other mounts. For example, if mounting 87 // /etc and /etc/resolv.conf, /etc/resolv.conf must not be mounted first. 88 func sortMounts(m []container.Mount) []container.Mount { 89 sort.Sort(mounts(m)) 90 return m 91 } 92 93 // setBindModeIfNull is platform specific processing to ensure the 94 // shared mode is set to 'z' if it is null. This is called in the case 95 // of processing a named volume and not a typical bind. 96 func setBindModeIfNull(bind *volume.MountPoint) { 97 if bind.Mode == "" { 98 bind.Mode = "z" 99 } 100 } 101 102 // migrateVolume links the contents of a volume created pre Docker 1.7 103 // into the location expected by the local driver. 104 // It creates a symlink from DOCKER_ROOT/vfs/dir/VOLUME_ID to DOCKER_ROOT/volumes/VOLUME_ID/_container_data. 105 // It preserves the volume json configuration generated pre Docker 1.7 to be able to 106 // downgrade from Docker 1.7 to Docker 1.6 without losing volume compatibility. 107 func migrateVolume(id, vfs string) error { 108 l, err := volumedrivers.GetDriver(volume.DefaultDriverName) 109 if err != nil { 110 return err 111 } 112 113 newDataPath := l.(*local.Root).DataPath(id) 114 fi, err := os.Stat(newDataPath) 115 if err != nil && !os.IsNotExist(err) { 116 return err 117 } 118 119 if fi != nil && fi.IsDir() { 120 return nil 121 } 122 123 return os.Symlink(vfs, newDataPath) 124 } 125 126 // verifyVolumesInfo ports volumes configured for the containers pre docker 1.7. 127 // It reads the container configuration and creates valid mount points for the old volumes. 128 func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { 129 // Inspect old structures only when we're upgrading from old versions 130 // to versions >= 1.7 and the MountPoints has not been populated with volumes data. 131 type volumes struct { 132 Volumes map[string]string 133 VolumesRW map[string]bool 134 } 135 cfgPath, err := container.ConfigPath() 136 if err != nil { 137 return err 138 } 139 f, err := os.Open(cfgPath) 140 if err != nil { 141 return errors.Wrap(err, "could not open container config") 142 } 143 defer f.Close() 144 var cv volumes 145 if err := json.NewDecoder(f).Decode(&cv); err != nil { 146 return errors.Wrap(err, "could not decode container config") 147 } 148 149 if len(container.MountPoints) == 0 && len(cv.Volumes) > 0 { 150 for destination, hostPath := range cv.Volumes { 151 vfsPath := filepath.Join(daemon.root, "vfs", "dir") 152 rw := cv.VolumesRW != nil && cv.VolumesRW[destination] 153 154 if strings.HasPrefix(hostPath, vfsPath) { 155 id := filepath.Base(hostPath) 156 v, err := daemon.volumes.CreateWithRef(id, volume.DefaultDriverName, container.ID, nil, nil) 157 if err != nil { 158 return err 159 } 160 if err := migrateVolume(id, hostPath); err != nil { 161 return err 162 } 163 container.AddMountPointWithVolume(destination, v, true) 164 } else { // Bind mount 165 m := volume.MountPoint{Source: hostPath, Destination: destination, RW: rw} 166 container.MountPoints[destination] = &m 167 } 168 } 169 return container.ToDisk() 170 } 171 return nil 172 } 173 174 func (daemon *Daemon) mountVolumes(container *container.Container) error { 175 mounts, err := daemon.setupMounts(container) 176 if err != nil { 177 return err 178 } 179 180 for _, m := range mounts { 181 dest, err := container.GetResourcePath(m.Destination) 182 if err != nil { 183 return err 184 } 185 186 var stat os.FileInfo 187 stat, err = os.Stat(m.Source) 188 if err != nil { 189 return err 190 } 191 if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { 192 return err 193 } 194 195 opts := "rbind,ro" 196 if m.Writable { 197 opts = "rbind,rw" 198 } 199 200 if err := mount.Mount(m.Source, dest, bindMountType, opts); err != nil { 201 return err 202 } 203 204 // mountVolumes() seems to be called for temporary mounts 205 // outside the container. Soon these will be unmounted with 206 // lazy unmount option and given we have mounted the rbind, 207 // all the submounts will propagate if these are shared. If 208 // daemon is running in host namespace and has / as shared 209 // then these unmounts will propagate and unmount original 210 // mount as well. So make all these mounts rprivate. 211 // Do not use propagation property of volume as that should 212 // apply only when mounting happen inside the container. 213 if err := mount.MakeRPrivate(dest); err != nil { 214 return err 215 } 216 } 217 218 return nil 219 }