1// +build !windows 2 3package daemon // import "github.com/docker/docker/daemon" 4 5import ( 6 "fmt" 7 "os" 8 "sort" 9 "strconv" 10 "strings" 11 12 "github.com/docker/docker/container" 13 "github.com/docker/docker/pkg/fileutils" 14 "github.com/docker/docker/pkg/mount" 15 volumemounts "github.com/docker/docker/volume/mounts" 16) 17 18// setupMounts iterates through each of the mount points for a container and 19// calls Setup() on each. It also looks to see if is a network mount such as 20// /etc/resolv.conf, and if it is not, appends it to the array of mounts. 21func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) { 22 var mounts []container.Mount 23 // TODO: tmpfs mounts should be part of Mountpoints 24 tmpfsMounts := make(map[string]bool) 25 tmpfsMountInfo, err := c.TmpfsMounts() 26 if err != nil { 27 return nil, err 28 } 29 for _, m := range tmpfsMountInfo { 30 tmpfsMounts[m.Destination] = true 31 } 32 for _, m := range c.MountPoints { 33 if tmpfsMounts[m.Destination] { 34 continue 35 } 36 if err := daemon.lazyInitializeVolume(c.ID, m); err != nil { 37 return nil, err 38 } 39 // If the daemon is being shutdown, we should not let a container start if it is trying to 40 // mount the socket the daemon is listening on. During daemon shutdown, the socket 41 // (/var/run/docker.sock by default) doesn't exist anymore causing the call to m.Setup to 42 // create at directory instead. This in turn will prevent the daemon to restart. 43 checkfunc := func(m *volumemounts.MountPoint) error { 44 if _, exist := daemon.hosts[m.Source]; exist && daemon.IsShuttingDown() { 45 return fmt.Errorf("Could not mount %q to container while the daemon is shutting down", m.Source) 46 } 47 return nil 48 } 49 50 path, err := m.Setup(c.MountLabel, daemon.idMappings.RootPair(), checkfunc) 51 if err != nil { 52 return nil, err 53 } 54 if !c.TrySetNetworkMount(m.Destination, path) { 55 mnt := container.Mount{ 56 Source: path, 57 Destination: m.Destination, 58 Writable: m.RW, 59 Propagation: string(m.Propagation), 60 } 61 if m.Volume != nil { 62 attributes := map[string]string{ 63 "driver": m.Volume.DriverName(), 64 "container": c.ID, 65 "destination": m.Destination, 66 "read/write": strconv.FormatBool(m.RW), 67 "propagation": string(m.Propagation), 68 } 69 daemon.LogVolumeEvent(m.Volume.Name(), "mount", attributes) 70 } 71 mounts = append(mounts, mnt) 72 } 73 } 74 75 mounts = sortMounts(mounts) 76 netMounts := c.NetworkMounts() 77 // if we are going to mount any of the network files from container 78 // metadata, the ownership must be set properly for potential container 79 // remapped root (user namespaces) 80 rootIDs := daemon.idMappings.RootPair() 81 for _, mount := range netMounts { 82 // we should only modify ownership of network files within our own container 83 // metadata repository. If the user specifies a mount path external, it is 84 // up to the user to make sure the file has proper ownership for userns 85 if strings.Index(mount.Source, daemon.repository) == 0 { 86 if err := os.Chown(mount.Source, rootIDs.UID, rootIDs.GID); err != nil { 87 return nil, err 88 } 89 } 90 } 91 return append(mounts, netMounts...), nil 92} 93 94// sortMounts sorts an array of mounts in lexicographic order. This ensure that 95// when mounting, the mounts don't shadow other mounts. For example, if mounting 96// /etc and /etc/resolv.conf, /etc/resolv.conf must not be mounted first. 97func sortMounts(m []container.Mount) []container.Mount { 98 sort.Sort(mounts(m)) 99 return m 100} 101 102// setBindModeIfNull is platform specific processing to ensure the 103// shared mode is set to 'z' if it is null. This is called in the case 104// of processing a named volume and not a typical bind. 105func setBindModeIfNull(bind *volumemounts.MountPoint) { 106 if bind.Mode == "" { 107 bind.Mode = "z" 108 } 109} 110 111func (daemon *Daemon) mountVolumes(container *container.Container) error { 112 mounts, err := daemon.setupMounts(container) 113 if err != nil { 114 return err 115 } 116 117 for _, m := range mounts { 118 dest, err := container.GetResourcePath(m.Destination) 119 if err != nil { 120 return err 121 } 122 123 var stat os.FileInfo 124 stat, err = os.Stat(m.Source) 125 if err != nil { 126 return err 127 } 128 if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { 129 return err 130 } 131 132 opts := "rbind,ro" 133 if m.Writable { 134 opts = "rbind,rw" 135 } 136 137 if err := mount.Mount(m.Source, dest, bindMountType, opts); err != nil { 138 return err 139 } 140 141 // mountVolumes() seems to be called for temporary mounts 142 // outside the container. Soon these will be unmounted with 143 // lazy unmount option and given we have mounted the rbind, 144 // all the submounts will propagate if these are shared. If 145 // daemon is running in host namespace and has / as shared 146 // then these unmounts will propagate and unmount original 147 // mount as well. So make all these mounts rprivate. 148 // Do not use propagation property of volume as that should 149 // apply only when mounting happen inside the container. 150 if err := mount.MakeRPrivate(dest); err != nil { 151 return err 152 } 153 } 154 155 return nil 156} 157