github.com/moby/docker@v26.1.3+incompatible/daemon/snapshotter/mount.go (about)

     1  package snapshotter
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/containerd/containerd/mount"
     9  	"github.com/containerd/log"
    10  	"github.com/docker/docker/daemon/graphdriver"
    11  	"github.com/docker/docker/pkg/idtools"
    12  	"github.com/moby/locker"
    13  	"github.com/moby/sys/mountinfo"
    14  )
    15  
    16  // Mounter handles mounting/unmounting things coming in from a snapshotter
    17  // with optional reference counting if needed by the filesystem
    18  type Mounter interface {
    19  	// Mount mounts the rootfs for a container and returns the mount point
    20  	Mount(mounts []mount.Mount, containerID string) (string, error)
    21  	// Unmount unmounts the container rootfs
    22  	Unmount(target string) error
    23  	// Mounted returns a target mountpoint if it's already mounted
    24  	Mounted(containerID string) (string, error)
    25  }
    26  
    27  // NewMounter creates a new mounter for the provided snapshotter
    28  func NewMounter(home string, snapshotter string, idMap idtools.IdentityMapping) *refCountMounter {
    29  	return &refCountMounter{
    30  		base: mounter{
    31  			home:        home,
    32  			snapshotter: snapshotter,
    33  			idMap:       idMap,
    34  		},
    35  		rc:     graphdriver.NewRefCounter(checker()),
    36  		locker: locker.New(),
    37  	}
    38  }
    39  
    40  type refCountMounter struct {
    41  	rc     *graphdriver.RefCounter
    42  	locker *locker.Locker
    43  	base   mounter
    44  }
    45  
    46  func (m *refCountMounter) Mount(mounts []mount.Mount, containerID string) (target string, retErr error) {
    47  	target = m.base.target(containerID)
    48  
    49  	_, err := os.Stat(target)
    50  	if err != nil && !os.IsNotExist(err) {
    51  		return "", err
    52  	}
    53  
    54  	if count := m.rc.Increment(target); count > 1 {
    55  		return target, nil
    56  	}
    57  
    58  	m.locker.Lock(target)
    59  	defer m.locker.Unlock(target)
    60  
    61  	defer func() {
    62  		if retErr != nil {
    63  			if c := m.rc.Decrement(target); c <= 0 {
    64  				if mntErr := unmount(target); mntErr != nil {
    65  					log.G(context.TODO()).Errorf("error unmounting %s: %v", target, mntErr)
    66  				}
    67  				if rmErr := os.Remove(target); rmErr != nil && !os.IsNotExist(rmErr) {
    68  					log.G(context.TODO()).Debugf("Failed to remove %s: %v: %v", target, rmErr, err)
    69  				}
    70  			}
    71  		}
    72  	}()
    73  
    74  	return m.base.Mount(mounts, containerID)
    75  }
    76  
    77  func (m *refCountMounter) Unmount(target string) error {
    78  	if count := m.rc.Decrement(target); count > 0 {
    79  		return nil
    80  	}
    81  
    82  	m.locker.Lock(target)
    83  	defer m.locker.Unlock(target)
    84  
    85  	if err := unmount(target); err != nil {
    86  		log.G(context.TODO()).Debugf("Failed to unmount %s: %v", target, err)
    87  	}
    88  
    89  	if err := os.Remove(target); err != nil {
    90  		log.G(context.TODO()).WithError(err).WithField("dir", target).Error("failed to remove mount temp dir")
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  func (m *refCountMounter) Mounted(containerID string) (string, error) {
    97  	mounted, err := m.base.Mounted(containerID)
    98  	if err != nil || mounted == "" {
    99  		return mounted, err
   100  	}
   101  
   102  	target := m.base.target(containerID)
   103  
   104  	// Check if the refcount is non-zero.
   105  	m.rc.Increment(target)
   106  	if m.rc.Decrement(target) > 0 {
   107  		return mounted, nil
   108  	}
   109  
   110  	return "", nil
   111  }
   112  
   113  type mounter struct {
   114  	home        string
   115  	snapshotter string
   116  	idMap       idtools.IdentityMapping
   117  }
   118  
   119  func (m mounter) Mount(mounts []mount.Mount, containerID string) (string, error) {
   120  	target := m.target(containerID)
   121  
   122  	root := m.idMap.RootPair()
   123  	if err := idtools.MkdirAllAndChown(filepath.Dir(target), 0o710, idtools.Identity{
   124  		UID: idtools.CurrentIdentity().UID,
   125  		GID: root.GID,
   126  	}); err != nil {
   127  		return "", err
   128  	}
   129  	if err := idtools.MkdirAllAndChown(target, 0o710, root); err != nil {
   130  		return "", err
   131  	}
   132  
   133  	return target, mount.All(mounts, target)
   134  }
   135  
   136  func (m mounter) Unmount(target string) error {
   137  	return unmount(target)
   138  }
   139  
   140  func (m mounter) Mounted(containerID string) (string, error) {
   141  	target := m.target(containerID)
   142  
   143  	mounted, err := mountinfo.Mounted(target)
   144  	if err != nil || !mounted {
   145  		return "", err
   146  	}
   147  	return target, nil
   148  }
   149  
   150  func (m mounter) target(containerID string) string {
   151  	return filepath.Join(m.home, "rootfs", m.snapshotter, containerID)
   152  }