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