github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/volume/mounts/mounts.go (about)

     1  package mounts // import "github.com/demonoid81/moby/volume/mounts"
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"syscall"
     8  
     9  	mounttypes "github.com/demonoid81/moby/api/types/mount"
    10  	"github.com/demonoid81/moby/pkg/idtools"
    11  	"github.com/demonoid81/moby/pkg/stringid"
    12  	"github.com/demonoid81/moby/volume"
    13  	"github.com/opencontainers/selinux/go-selinux/label"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // MountPoint is the intersection point between a volume and a container. It
    18  // specifies which volume is to be used and where inside a container it should
    19  // be mounted.
    20  //
    21  // Note that this type is embedded in `container.Container` object and persisted to disk.
    22  // Changes to this struct need to by synced with on disk state.
    23  type MountPoint struct {
    24  	// Source is the source path of the mount.
    25  	// E.g. `mount --bind /foo /bar`, `/foo` is the `Source`.
    26  	Source string
    27  	// Destination is the path relative to the container root (`/`) to the mount point
    28  	// It is where the `Source` is mounted to
    29  	Destination string
    30  	// RW is set to true when the mountpoint should be mounted as read-write
    31  	RW bool
    32  	// Name is the name reference to the underlying data defined by `Source`
    33  	// e.g., the volume name
    34  	Name string
    35  	// Driver is the volume driver used to create the volume (if it is a volume)
    36  	Driver string
    37  	// Type of mount to use, see `Type<foo>` definitions in github.com/demonoid81/moby/api/types/mount
    38  	Type mounttypes.Type `json:",omitempty"`
    39  	// Volume is the volume providing data to this mountpoint.
    40  	// This is nil unless `Type` is set to `TypeVolume`
    41  	Volume volume.Volume `json:"-"`
    42  
    43  	// Mode is the comma separated list of options supplied by the user when creating
    44  	// the bind/volume mount.
    45  	// Note Mode is not used on Windows
    46  	Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`"
    47  
    48  	// Propagation describes how the mounts are propagated from the host into the
    49  	// mount point, and vice-versa.
    50  	// See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
    51  	// Note Propagation is not used on Windows
    52  	Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string
    53  
    54  	// Specifies if data should be copied from the container before the first mount
    55  	// Use a pointer here so we can tell if the user set this value explicitly
    56  	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
    57  	CopyData bool `json:"-"`
    58  	// ID is the opaque ID used to pass to the volume driver.
    59  	// This should be set by calls to `Mount` and unset by calls to `Unmount`
    60  	ID string `json:",omitempty"`
    61  
    62  	// Sepc is a copy of the API request that created this mount.
    63  	Spec mounttypes.Mount
    64  
    65  	// Some bind mounts should not be automatically created.
    66  	// (Some are auto-created for backwards-compatibility)
    67  	// This is checked on the API but setting this here prevents race conditions.
    68  	// where a bind dir existed during validation was removed before reaching the setup code.
    69  	SkipMountpointCreation bool
    70  
    71  	// Track usage of this mountpoint
    72  	// Specifically needed for containers which are running and calls to `docker cp`
    73  	// because both these actions require mounting the volumes.
    74  	active int
    75  }
    76  
    77  // Cleanup frees resources used by the mountpoint
    78  func (m *MountPoint) Cleanup() error {
    79  	if m.Volume == nil || m.ID == "" {
    80  		return nil
    81  	}
    82  
    83  	if err := m.Volume.Unmount(m.ID); err != nil {
    84  		return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name())
    85  	}
    86  
    87  	m.active--
    88  	if m.active == 0 {
    89  		m.ID = ""
    90  	}
    91  	return nil
    92  }
    93  
    94  // Setup sets up a mount point by either mounting the volume if it is
    95  // configured, or creating the source directory if supplied.
    96  // The, optional, checkFun parameter allows doing additional checking
    97  // before creating the source directory on the host.
    98  func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun func(m *MountPoint) error) (path string, err error) {
    99  	if m.SkipMountpointCreation {
   100  		return m.Source, nil
   101  	}
   102  
   103  	defer func() {
   104  		if err != nil || !label.RelabelNeeded(m.Mode) {
   105  			return
   106  		}
   107  
   108  		var sourcePath string
   109  		sourcePath, err = filepath.EvalSymlinks(m.Source)
   110  		if err != nil {
   111  			path = ""
   112  			err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source)
   113  			return
   114  		}
   115  		err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode))
   116  		if errors.Is(err, syscall.ENOTSUP) {
   117  			err = nil
   118  		}
   119  		if err != nil {
   120  			path = ""
   121  			err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath)
   122  		}
   123  	}()
   124  
   125  	if m.Volume != nil {
   126  		id := m.ID
   127  		if id == "" {
   128  			id = stringid.GenerateRandomID()
   129  		}
   130  		path, err := m.Volume.Mount(id)
   131  		if err != nil {
   132  			return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
   133  		}
   134  
   135  		m.ID = id
   136  		m.active++
   137  		return path, nil
   138  	}
   139  
   140  	if len(m.Source) == 0 {
   141  		return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
   142  	}
   143  
   144  	if m.Type == mounttypes.TypeBind {
   145  		// Before creating the source directory on the host, invoke checkFun if it's not nil. One of
   146  		// the use case is to forbid creating the daemon socket as a directory if the daemon is in
   147  		// the process of shutting down.
   148  		if checkFun != nil {
   149  			if err := checkFun(m); err != nil {
   150  				return "", err
   151  			}
   152  		}
   153  
   154  		// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
   155  		// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
   156  		if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil {
   157  			if perr, ok := err.(*os.PathError); ok {
   158  				if perr.Err != syscall.ENOTDIR {
   159  					return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)
   160  				}
   161  			}
   162  		}
   163  	}
   164  	return m.Source, nil
   165  }
   166  
   167  // Path returns the path of a volume in a mount point.
   168  func (m *MountPoint) Path() string {
   169  	if m.Volume != nil {
   170  		return m.Volume.Path()
   171  	}
   172  	return m.Source
   173  }
   174  
   175  func errInvalidMode(mode string) error {
   176  	return errors.Errorf("invalid mode: %v", mode)
   177  }
   178  
   179  func errInvalidSpec(spec string) error {
   180  	return errors.Errorf("invalid volume specification: '%s'", spec)
   181  }