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)
This commit is contained in:
Phil Estes 2016-02-11 01:00:54 -05:00
parent 2658341b5f
commit 922986b76e
1 changed files with 49 additions and 4 deletions

View File

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