github.com/mforkel/docker-ce-i386@v17.12.1-ce-rc2+incompatible/components/engine/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  }