瀏覽代碼

Add proper refcounting to zfs graphdriver

Fixes issues with layer remounting (e.g. a running container which then
has `docker cp` used to copy files in or out) by applying the same
refcounting implementation that exists in other graphdrivers like
overlay and aufs.

Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com> (github: estesp)
Phil Estes 9 年之前
父節點
當前提交
922986b76e
共有 1 個文件被更改,包括 49 次插入4 次删除
  1. 49 4
      daemon/graphdriver/zfs/zfs.go

+ 49 - 4
daemon/graphdriver/zfs/zfs.go

@@ -22,6 +22,12 @@ import (
 	"github.com/opencontainers/runc/libcontainer/label"
 )
 
+type activeMount struct {
+	count   int
+	path    string
+	mounted bool
+}
+
 type zfsOptions struct {
 	fsName    string
 	mountPath string
@@ -103,6 +109,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
 		dataset:          rootDataset,
 		options:          options,
 		filesystemsCache: filesystemsCache,
+		active:           make(map[string]*activeMount),
 		uidMaps:          uidMaps,
 		gidMaps:          gidMaps,
 	}
@@ -159,6 +166,7 @@ type Driver struct {
 	options          zfsOptions
 	sync.Mutex       // protects filesystem cache against concurrent access
 	filesystemsCache map[string]bool
+	active           map[string]*activeMount
 	uidMaps          []idtools.IDMap
 	gidMaps          []idtools.IDMap
 }
@@ -294,6 +302,17 @@ func (d *Driver) Remove(id string) error {
 
 // Get returns the mountpoint for the given id after creating the target directories if necessary.
 func (d *Driver) Get(id, mountLabel string) (string, error) {
+	d.Lock()
+	defer d.Unlock()
+
+	mnt := d.active[id]
+	if mnt != nil {
+		mnt.count++
+		return mnt.path, nil
+	}
+
+	mnt = &activeMount{count: 1}
+
 	mountpoint := d.mountPath(id)
 	filesystem := d.zfsPath(id)
 	options := label.FormatMountLabel("", mountLabel)
@@ -316,17 +335,43 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 	if err := os.Chown(mountpoint, rootUID, rootGID); err != nil {
 		return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err)
 	}
+	mnt.path = mountpoint
+	mnt.mounted = true
+	d.active[id] = mnt
 
 	return mountpoint, nil
 }
 
 // Put removes the existing mountpoint for the given id if it exists.
 func (d *Driver) Put(id string) error {
-	mountpoint := d.mountPath(id)
-	logrus.Debugf(`[zfs] unmount("%s")`, mountpoint)
+	d.Lock()
+	defer d.Unlock()
+
+	mnt := d.active[id]
+	if mnt == nil {
+		logrus.Debugf("[zfs] Put on a non-mounted device %s", id)
+		// but it might be still here
+		if d.Exists(id) {
+			err := mount.Unmount(d.mountPath(id))
+			if err != nil {
+				logrus.Debugf("[zfs] Failed to unmount %s zfs fs: %v", id, err)
+			}
+		}
+		return nil
+	}
 
-	if err := mount.Unmount(mountpoint); err != nil {
-		return fmt.Errorf("error unmounting to %s: %v", mountpoint, err)
+	mnt.count--
+	if mnt.count > 0 {
+		return nil
+	}
+
+	defer delete(d.active, id)
+	if mnt.mounted {
+		logrus.Debugf(`[zfs] unmount("%s")`, mnt.path)
+
+		if err := mount.Unmount(mnt.path); err != nil {
+			return fmt.Errorf("error unmounting to %s: %v", mnt.path, err)
+		}
 	}
 	return nil
 }