github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/daemon/volumes_unix.go (about)

     1  // +build !windows
     2  
     3  package daemon
     4  
     5  import (
     6  	"encoding/json"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/docker/docker/container"
    14  	"github.com/docker/docker/volume"
    15  	"github.com/docker/docker/volume/drivers"
    16  	"github.com/docker/docker/volume/local"
    17  	"github.com/pkg/errors"
    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  	for _, m := range c.TmpfsMounts() {
    28  		tmpfsMounts[m.Destination] = true
    29  	}
    30  	for _, m := range c.MountPoints {
    31  		if tmpfsMounts[m.Destination] {
    32  			continue
    33  		}
    34  		if err := daemon.lazyInitializeVolume(c.ID, m); err != nil {
    35  			return nil, err
    36  		}
    37  		rootUID, rootGID := daemon.GetRemappedUIDGID()
    38  		path, err := m.Setup(c.MountLabel, rootUID, rootGID)
    39  		if err != nil {
    40  			return nil, err
    41  		}
    42  		if !c.TrySetNetworkMount(m.Destination, path) {
    43  			mnt := container.Mount{
    44  				Source:      path,
    45  				Destination: m.Destination,
    46  				Writable:    m.RW,
    47  				Propagation: string(m.Propagation),
    48  			}
    49  			if m.Volume != nil {
    50  				attributes := map[string]string{
    51  					"driver":      m.Volume.DriverName(),
    52  					"container":   c.ID,
    53  					"destination": m.Destination,
    54  					"read/write":  strconv.FormatBool(m.RW),
    55  					"propagation": string(m.Propagation),
    56  				}
    57  				daemon.LogVolumeEvent(m.Volume.Name(), "mount", attributes)
    58  			}
    59  			mounts = append(mounts, mnt)
    60  		}
    61  	}
    62  
    63  	mounts = sortMounts(mounts)
    64  	netMounts := c.NetworkMounts()
    65  	// if we are going to mount any of the network files from container
    66  	// metadata, the ownership must be set properly for potential container
    67  	// remapped root (user namespaces)
    68  	rootUID, rootGID := daemon.GetRemappedUIDGID()
    69  	for _, mount := range netMounts {
    70  		if err := os.Chown(mount.Source, rootUID, rootGID); err != nil {
    71  			return nil, err
    72  		}
    73  	}
    74  	return append(mounts, netMounts...), nil
    75  }
    76  
    77  // sortMounts sorts an array of mounts in lexicographic order. This ensure that
    78  // when mounting, the mounts don't shadow other mounts. For example, if mounting
    79  // /etc and /etc/resolv.conf, /etc/resolv.conf must not be mounted first.
    80  func sortMounts(m []container.Mount) []container.Mount {
    81  	sort.Sort(mounts(m))
    82  	return m
    83  }
    84  
    85  // setBindModeIfNull is platform specific processing to ensure the
    86  // shared mode is set to 'z' if it is null. This is called in the case
    87  // of processing a named volume and not a typical bind.
    88  func setBindModeIfNull(bind *volume.MountPoint) {
    89  	if bind.Mode == "" {
    90  		bind.Mode = "z"
    91  	}
    92  }
    93  
    94  // migrateVolume links the contents of a volume created pre Docker 1.7
    95  // into the location expected by the local driver.
    96  // It creates a symlink from DOCKER_ROOT/vfs/dir/VOLUME_ID to DOCKER_ROOT/volumes/VOLUME_ID/_container_data.
    97  // It preserves the volume json configuration generated pre Docker 1.7 to be able to
    98  // downgrade from Docker 1.7 to Docker 1.6 without losing volume compatibility.
    99  func migrateVolume(id, vfs string) error {
   100  	l, err := volumedrivers.GetDriver(volume.DefaultDriverName)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	newDataPath := l.(*local.Root).DataPath(id)
   106  	fi, err := os.Stat(newDataPath)
   107  	if err != nil && !os.IsNotExist(err) {
   108  		return err
   109  	}
   110  
   111  	if fi != nil && fi.IsDir() {
   112  		return nil
   113  	}
   114  
   115  	return os.Symlink(vfs, newDataPath)
   116  }
   117  
   118  // verifyVolumesInfo ports volumes configured for the containers pre docker 1.7.
   119  // It reads the container configuration and creates valid mount points for the old volumes.
   120  func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
   121  	// Inspect old structures only when we're upgrading from old versions
   122  	// to versions >= 1.7 and the MountPoints has not been populated with volumes data.
   123  	type volumes struct {
   124  		Volumes   map[string]string
   125  		VolumesRW map[string]bool
   126  	}
   127  	cfgPath, err := container.ConfigPath()
   128  	if err != nil {
   129  		return err
   130  	}
   131  	f, err := os.Open(cfgPath)
   132  	if err != nil {
   133  		return errors.Wrap(err, "could not open container config")
   134  	}
   135  	var cv volumes
   136  	if err := json.NewDecoder(f).Decode(&cv); err != nil {
   137  		return errors.Wrap(err, "could not decode container config")
   138  	}
   139  
   140  	if len(container.MountPoints) == 0 && len(cv.Volumes) > 0 {
   141  		for destination, hostPath := range cv.Volumes {
   142  			vfsPath := filepath.Join(daemon.root, "vfs", "dir")
   143  			rw := cv.VolumesRW != nil && cv.VolumesRW[destination]
   144  
   145  			if strings.HasPrefix(hostPath, vfsPath) {
   146  				id := filepath.Base(hostPath)
   147  				v, err := daemon.volumes.CreateWithRef(id, volume.DefaultDriverName, container.ID, nil, nil)
   148  				if err != nil {
   149  					return err
   150  				}
   151  				if err := migrateVolume(id, hostPath); err != nil {
   152  					return err
   153  				}
   154  				container.AddMountPointWithVolume(destination, v, true)
   155  			} else { // Bind mount
   156  				m := volume.MountPoint{Source: hostPath, Destination: destination, RW: rw}
   157  				container.MountPoints[destination] = &m
   158  			}
   159  		}
   160  		return container.ToDisk()
   161  	}
   162  	return nil
   163  }