gopkg.in/docker/docker.v20@v20.10.27/volume/mounts/mounts.go (about)

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