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