
The stargz snapshotter cannot be re-mounted, so the reference-counted
path must be used.
Co-authored-by: Djordje Lukic <djordje.lukic@docker.com>
Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
(cherry picked from commit 21c0a54a6b
)
Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
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{"fuse-overlayfs", "overlayfs", "stargz", "zfs"}
|
|
|
|
// 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)
|
|
|
|
}
|