github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/daemon/volumes_unix.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package daemon // import "github.com/docker/docker/daemon" 5 6 import ( 7 "fmt" 8 "os" 9 "sort" 10 "strconv" 11 "strings" 12 13 mounttypes "github.com/docker/docker/api/types/mount" 14 "github.com/docker/docker/container" 15 "github.com/docker/docker/pkg/fileutils" 16 volumemounts "github.com/docker/docker/volume/mounts" 17 "github.com/moby/sys/mount" 18 ) 19 20 // setupMounts iterates through each of the mount points for a container and 21 // calls Setup() on each. It also looks to see if is a network mount such as 22 // /etc/resolv.conf, and if it is not, appends it to the array of mounts. 23 func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) { 24 var mounts []container.Mount 25 // TODO: tmpfs mounts should be part of Mountpoints 26 tmpfsMounts := make(map[string]bool) 27 tmpfsMountInfo, err := c.TmpfsMounts() 28 if err != nil { 29 return nil, err 30 } 31 for _, m := range tmpfsMountInfo { 32 tmpfsMounts[m.Destination] = true 33 } 34 for _, m := range c.MountPoints { 35 if tmpfsMounts[m.Destination] { 36 continue 37 } 38 if err := daemon.lazyInitializeVolume(c.ID, m); err != nil { 39 return nil, err 40 } 41 // If the daemon is being shutdown, we should not let a container start if it is trying to 42 // mount the socket the daemon is listening on. During daemon shutdown, the socket 43 // (/var/run/docker.sock by default) doesn't exist anymore causing the call to m.Setup to 44 // create at directory instead. This in turn will prevent the daemon to restart. 45 checkfunc := func(m *volumemounts.MountPoint) error { 46 if _, exist := daemon.hosts[m.Source]; exist && daemon.IsShuttingDown() { 47 return fmt.Errorf("Could not mount %q to container while the daemon is shutting down", m.Source) 48 } 49 return nil 50 } 51 52 path, err := m.Setup(c.MountLabel, daemon.idMapping.RootPair(), checkfunc) 53 if err != nil { 54 return nil, err 55 } 56 if !c.TrySetNetworkMount(m.Destination, path) { 57 mnt := container.Mount{ 58 Source: path, 59 Destination: m.Destination, 60 Writable: m.RW, 61 Propagation: string(m.Propagation), 62 } 63 if m.Spec.Type == mounttypes.TypeBind && m.Spec.BindOptions != nil { 64 mnt.NonRecursive = m.Spec.BindOptions.NonRecursive 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.idMapping.RootPair() 86 for _, mnt := 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(mnt.Source, daemon.repository) == 0 { 91 if err := os.Chown(mnt.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 *volumemounts.MountPoint) { 111 if bind.Mode == "" { 112 bind.Mode = "z" 113 } 114 } 115 116 func (daemon *Daemon) mountVolumes(container *container.Container) error { 117 mounts, err := daemon.setupMounts(container) 118 if err != nil { 119 return err 120 } 121 122 for _, m := range mounts { 123 dest, err := container.GetResourcePath(m.Destination) 124 if err != nil { 125 return err 126 } 127 128 var stat os.FileInfo 129 stat, err = os.Stat(m.Source) 130 if err != nil { 131 return err 132 } 133 if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { 134 return err 135 } 136 137 bindMode := "rbind" 138 if m.NonRecursive { 139 bindMode = "bind" 140 } 141 writeMode := "ro" 142 if m.Writable { 143 writeMode = "rw" 144 } 145 146 // mountVolumes() seems to be called for temporary mounts 147 // outside the container. Soon these will be unmounted with 148 // lazy unmount option and given we have mounted the rbind, 149 // all the submounts will propagate if these are shared. If 150 // daemon is running in host namespace and has / as shared 151 // then these unmounts will propagate and unmount original 152 // mount as well. So make all these mounts rprivate. 153 // Do not use propagation property of volume as that should 154 // apply only when mounting happens inside the container. 155 opts := strings.Join([]string{bindMode, writeMode, "rprivate"}, ",") 156 if err := mount.Mount(m.Source, dest, "", opts); err != nil { 157 return err 158 } 159 } 160 161 return nil 162 }