|
@@ -0,0 +1,141 @@
|
|
|
+package snapshotter
|
|
|
+
|
|
|
+import (
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
+
|
|
|
+ "github.com/containerd/containerd/mount"
|
|
|
+ "github.com/docker/docker/daemon/graphdriver"
|
|
|
+ "github.com/docker/docker/pkg/idtools"
|
|
|
+ "github.com/moby/locker"
|
|
|
+ "github.com/sirupsen/logrus"
|
|
|
+)
|
|
|
+
|
|
|
+const mountsDir = "rootfs"
|
|
|
+
|
|
|
+// List of known filesystems that can't be re-mounted or have shared layers
|
|
|
+var refCountedFileSystems = []string{"overlayfs", "zfs", "fuse-overlayfs"}
|
|
|
+
|
|
|
+// 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
|
|
|
+}
|
|
|
+
|
|
|
+// inSlice tests whether a string is contained in a slice of strings or not.
|
|
|
+// Comparison is case sensitive
|
|
|
+func inSlice(slice []string, s string) bool {
|
|
|
+ for _, ss := range slice {
|
|
|
+ if s == ss {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+// NewMounter creates a new mounter for the provided snapshotter
|
|
|
+func NewMounter(home string, snapshotter string, idMap idtools.IdentityMapping) Mounter {
|
|
|
+ if inSlice(refCountedFileSystems, snapshotter) {
|
|
|
+ return &refCountMounter{
|
|
|
+ home: home,
|
|
|
+ snapshotter: snapshotter,
|
|
|
+ rc: graphdriver.NewRefCounter(checker()),
|
|
|
+ locker: locker.New(),
|
|
|
+ idMap: idMap,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return mounter{
|
|
|
+ home: home,
|
|
|
+ snapshotter: snapshotter,
|
|
|
+ idMap: idMap,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type refCountMounter struct {
|
|
|
+ home string
|
|
|
+ snapshotter string
|
|
|
+ rc *graphdriver.RefCounter
|
|
|
+ locker *locker.Locker
|
|
|
+ idMap idtools.IdentityMapping
|
|
|
+}
|
|
|
+
|
|
|
+func (m *refCountMounter) Mount(mounts []mount.Mount, containerID string) (target string, retErr error) {
|
|
|
+ target = filepath.Join(m.home, mountsDir, m.snapshotter, containerID)
|
|
|
+
|
|
|
+ _, 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 {
|
|
|
+ logrus.Errorf("error unmounting %s: %v", target, mntErr)
|
|
|
+ }
|
|
|
+ if rmErr := os.Remove(target); rmErr != nil && !os.IsNotExist(rmErr) {
|
|
|
+ logrus.Debugf("Failed to remove %s: %v: %v", target, rmErr, err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ root := m.idMap.RootPair()
|
|
|
+ if err := idtools.MkdirAllAndChown(target, 0700, root); err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ return target, mount.All(mounts, target)
|
|
|
+}
|
|
|
+
|
|
|
+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 {
|
|
|
+ logrus.Debugf("Failed to unmount %s: %v", target, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := os.Remove(target); err != nil {
|
|
|
+ logrus.WithError(err).WithField("dir", target).Error("failed to remove mount temp dir")
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+type mounter struct {
|
|
|
+ home string
|
|
|
+ snapshotter string
|
|
|
+ idMap idtools.IdentityMapping
|
|
|
+}
|
|
|
+
|
|
|
+func (m mounter) Mount(mounts []mount.Mount, containerID string) (string, error) {
|
|
|
+ target := filepath.Join(m.home, mountsDir, m.snapshotter, containerID)
|
|
|
+
|
|
|
+ root := m.idMap.RootPair()
|
|
|
+ if err := idtools.MkdirAndChown(target, 0700, root); err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ return target, mount.All(mounts, target)
|
|
|
+}
|
|
|
+
|
|
|
+func (m mounter) Unmount(target string) error {
|
|
|
+ return unmount(target)
|
|
|
+
|
|
|
+}
|