2023-05-15 15:42:37 +00:00
|
|
|
package snapshotter
|
|
|
|
|
|
|
|
import (
|
2023-06-23 00:33:17 +00:00
|
|
|
"context"
|
2023-05-15 15:42:37 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/containerd/containerd/mount"
|
2023-09-13 15:41:45 +00:00
|
|
|
"github.com/containerd/log"
|
2023-05-15 15:42:37 +00:00
|
|
|
"github.com/docker/docker/daemon/graphdriver"
|
|
|
|
"github.com/docker/docker/pkg/idtools"
|
|
|
|
"github.com/moby/locker"
|
2023-11-03 12:46:15 +00:00
|
|
|
"github.com/moby/sys/mountinfo"
|
2023-05-15 15:42:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Mounter handles mounting/unmounting things coming in from a snapshotter
|
|
|
|
// with optional reference counting if needed by the filesystem
|
|
|
|
type Mounter interface {
|
|
|
|
// Mount mounts the rootfs for a container and returns the mount point
|
|
|
|
Mount(mounts []mount.Mount, containerID string) (string, error)
|
|
|
|
// Unmount unmounts the container rootfs
|
|
|
|
Unmount(target string) error
|
2023-11-03 12:46:15 +00:00
|
|
|
// Mounted returns a target mountpoint if it's already mounted
|
|
|
|
Mounted(containerID string) (string, error)
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewMounter creates a new mounter for the provided snapshotter
|
2024-01-18 13:23:32 +00:00
|
|
|
func NewMounter(home string, snapshotter string, idMap idtools.IdentityMapping) *refCountMounter {
|
|
|
|
return &refCountMounter{
|
|
|
|
base: mounter{
|
|
|
|
home: home,
|
|
|
|
snapshotter: snapshotter,
|
|
|
|
idMap: idMap,
|
|
|
|
},
|
|
|
|
rc: graphdriver.NewRefCounter(checker()),
|
|
|
|
locker: locker.New(),
|
2023-10-30 13:40:45 +00:00
|
|
|
}
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type refCountMounter struct {
|
2023-10-30 13:40:45 +00:00
|
|
|
rc *graphdriver.RefCounter
|
|
|
|
locker *locker.Locker
|
|
|
|
base mounter
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *refCountMounter) Mount(mounts []mount.Mount, containerID string) (target string, retErr error) {
|
2023-10-30 13:40:45 +00:00
|
|
|
target = m.base.target(containerID)
|
2023-05-15 15:42:37 +00:00
|
|
|
|
|
|
|
_, err := os.Stat(target)
|
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if count := m.rc.Increment(target); count > 1 {
|
|
|
|
return target, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
m.locker.Lock(target)
|
|
|
|
defer m.locker.Unlock(target)
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if retErr != nil {
|
|
|
|
if c := m.rc.Decrement(target); c <= 0 {
|
|
|
|
if mntErr := unmount(target); mntErr != nil {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).Errorf("error unmounting %s: %v", target, mntErr)
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
if rmErr := os.Remove(target); rmErr != nil && !os.IsNotExist(rmErr) {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).Debugf("Failed to remove %s: %v: %v", target, rmErr, err)
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2023-10-30 13:40:45 +00:00
|
|
|
return m.base.Mount(mounts, containerID)
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *refCountMounter) Unmount(target string) error {
|
|
|
|
if count := m.rc.Decrement(target); count > 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
m.locker.Lock(target)
|
|
|
|
defer m.locker.Unlock(target)
|
|
|
|
|
|
|
|
if err := unmount(target); err != nil {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).Debugf("Failed to unmount %s: %v", target, err)
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Remove(target); err != nil {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).WithError(err).WithField("dir", target).Error("failed to remove mount temp dir")
|
2023-05-15 15:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-03 12:46:15 +00:00
|
|
|
func (m *refCountMounter) Mounted(containerID string) (string, error) {
|
|
|
|
mounted, err := m.base.Mounted(containerID)
|
|
|
|
if err != nil || mounted == "" {
|
|
|
|
return mounted, err
|
|
|
|
}
|
|
|
|
|
|
|
|
target := m.base.target(containerID)
|
|
|
|
|
|
|
|
// Check if the refcount is non-zero.
|
|
|
|
m.rc.Increment(target)
|
|
|
|
if m.rc.Decrement(target) > 0 {
|
|
|
|
return mounted, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2023-05-15 15:42:37 +00:00
|
|
|
type mounter struct {
|
|
|
|
home string
|
|
|
|
snapshotter string
|
|
|
|
idMap idtools.IdentityMapping
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m mounter) Mount(mounts []mount.Mount, containerID string) (string, error) {
|
2023-10-30 13:40:45 +00:00
|
|
|
target := m.target(containerID)
|
2023-05-15 15:42:37 +00:00
|
|
|
|
|
|
|
root := m.idMap.RootPair()
|
2023-10-30 13:40:45 +00:00
|
|
|
if err := idtools.MkdirAllAndChown(filepath.Dir(target), 0o710, idtools.Identity{
|
|
|
|
UID: idtools.CurrentIdentity().UID,
|
|
|
|
GID: root.GID,
|
|
|
|
}); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if err := idtools.MkdirAllAndChown(target, 0o710, root); err != nil {
|
2023-05-15 15:42:37 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return target, mount.All(mounts, target)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m mounter) Unmount(target string) error {
|
|
|
|
return unmount(target)
|
|
|
|
}
|
2023-10-30 13:40:45 +00:00
|
|
|
|
2023-11-03 12:46:15 +00:00
|
|
|
func (m mounter) Mounted(containerID string) (string, error) {
|
|
|
|
target := m.target(containerID)
|
|
|
|
|
|
|
|
mounted, err := mountinfo.Mounted(target)
|
|
|
|
if err != nil || !mounted {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return target, nil
|
|
|
|
}
|
|
|
|
|
2023-10-30 13:40:45 +00:00
|
|
|
func (m mounter) target(containerID string) string {
|
|
|
|
return filepath.Join(m.home, "rootfs", m.snapshotter, containerID)
|
|
|
|
}
|