1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

graphdriver/overlay{,2}: remove 'merged' on umount

This removes and recreates the merged dir with each umount/mount
respectively.
This is done to make the impact of leaking mountpoints have less
user-visible impact.

It's fairly easy to accidentally leak mountpoints (even if moby doesn't,
other tools on linux like 'unshare' are quite able to incidentally do
so).

As of recently, overlayfs reacts to these mounts being leaked (see

One trick to force an unmount is to remove the mounted directory and
recreate it. Devicemapper now does this, overlay can follow suit.

Signed-off-by: Euan Kemp <euan.kemp@coreos.com>
This commit is contained in:
Euan Kemp 2017-11-20 21:33:11 -08:00
parent 1e214c0952
commit af0d589623
2 changed files with 52 additions and 19 deletions

View file

@ -296,9 +296,6 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr
if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil {
return err
}
if err := idtools.MkdirAndChown(path.Join(dir, "merged"), 0700, root); err != nil {
return err
}
if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil {
return err
}
@ -329,9 +326,6 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr
if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil {
return err
}
if err := idtools.MkdirAndChown(path.Join(dir, "merged"), 0700, root); err != nil {
return err
}
return copy.DirCopy(parentUpperDir, upperDir, copy.Content)
}
@ -360,6 +354,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro
if _, err := os.Stat(rootDir); err == nil {
return containerfs.NewLocalContainerFS(rootDir), nil
}
mergedDir := path.Join(dir, "merged")
if count := d.ctr.Increment(mergedDir); count > 1 {
return containerfs.NewLocalContainerFS(mergedDir), nil
@ -367,7 +362,13 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro
defer func() {
if err != nil {
if c := d.ctr.Decrement(mergedDir); c <= 0 {
unix.Unmount(mergedDir, 0)
if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil {
logrus.Debugf("Failed to unmount %s: %v: %v", id, mntErr, err)
}
// Cleanup the created merged directory; see the comment in Put's rmdir
if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) {
logrus.Warnf("Failed to remove %s: %v: %v", id, rmErr, err)
}
}
}
}()
@ -375,6 +376,13 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro
if err != nil {
return nil, err
}
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil {
return nil, err
}
if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.IDPair{rootUID, rootGID}); err != nil {
return nil, err
}
var (
lowerDir = path.Join(d.dir(string(lowerID)), "root")
upperDir = path.Join(dir, "upper")
@ -386,10 +394,6 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro
}
// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
// user namespace requires this to move a directory from lower to upper.
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil {
return nil, err
}
if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
return nil, err
}
@ -397,6 +401,8 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro
}
// Put unmounts the mount path created for the give id.
// It also removes the 'merged' directory to force the kernel to unmount the
// overlay mount in other namespaces.
func (d *Driver) Put(id string) error {
d.locker.Lock(id)
defer d.locker.Unlock(id)
@ -411,6 +417,17 @@ func (d *Driver) Put(id string) error {
if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil {
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
}
// Remove the mountpoint here. Removing the mountpoint (in newer kernels)
// will cause all other instances of this mount in other mount namespaces
// to be unmounted. This is necessary to avoid cases where an overlay mount
// that is present in another namespace will cause subsequent mounts
// operations to fail with ebusy. We ignore any errors here because this may
// fail on older kernels which don't have
// torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied.
if err := unix.Rmdir(mountpoint); err != nil {
logrus.Debugf("Failed to remove %s overlay: %v", id, err)
}
return nil
}

View file

@ -417,9 +417,6 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil {
return err
}
if err := idtools.MkdirAndChown(path.Join(dir, "merged"), 0700, root); err != nil {
return err
}
lower, err := d.getLower(parent)
if err != nil {
@ -548,6 +545,10 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil {
logrus.Errorf("error unmounting %v: %v", mergedDir, mntErr)
}
// Cleanup the created merged directory; see the comment in Put's rmdir
if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) {
logrus.Debugf("Failed to remove %s: %v: %v", id, rmErr, err)
}
}
}
}()
@ -563,6 +564,14 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
mount := unix.Mount
mountTarget := mergedDir
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil {
return nil, err
}
if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.IDPair{rootUID, rootGID}); err != nil {
return nil, err
}
pageSize := unix.Getpagesize()
// Go can return a larger page size than supported by the system
@ -597,11 +606,6 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
// user namespace requires this to move a directory from lower to upper.
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil {
return nil, err
}
if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
return nil, err
}
@ -610,6 +614,8 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
}
// Put unmounts the mount path created for the give id.
// It also removes the 'merged' directory to force the kernel to unmount the
// overlay mount in other namespaces.
func (d *Driver) Put(id string) error {
d.locker.Lock(id)
defer d.locker.Unlock(id)
@ -630,6 +636,16 @@ func (d *Driver) Put(id string) error {
if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil {
logrus.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err)
}
// Remove the mountpoint here. Removing the mountpoint (in newer kernels)
// will cause all other instances of this mount in other mount namespaces
// to be unmounted. This is necessary to avoid cases where an overlay mount
// that is present in another namespace will cause subsequent mounts
// operations to fail with ebusy. We ignore any errors here because this may
// fail on older kernels which don't have
// torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied.
if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) {
logrus.Debugf("Failed to remove %s overlay: %v", id, err)
}
return nil
}