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