Created
December 10, 2017 21:06
-
-
Save doits/08ebc7122d0eededd1384b25f444a7bb to your computer and use it in GitHub Desktop.
Docker 17.11 overlay mount patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/daemon/graphdriver/overlay/overlay.go b/daemon/graphdriver/overlay/overlay.go | |
index 853318de597..cd98d4072fe 100644 | |
--- a/daemon/graphdriver/overlay/overlay.go | |
+++ b/daemon/graphdriver/overlay/overlay.go | |
@@ -109,8 +109,10 @@ func init() { | |
} | |
// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. | |
-// If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. | |
-// If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned. | |
+// If overlay filesystem is not supported on the host, the error | |
+// graphdriver.ErrNotSupported is returned. | |
+// If an overlay filesystem is not supported over an existing filesystem then | |
+// error graphdriver.ErrIncompatibleFS is returned. | |
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { | |
if err := supportsOverlay(); err != nil { | |
@@ -199,7 +201,8 @@ func (d *Driver) Status() [][2]string { | |
} | |
} | |
-// GetMetadata returns meta data about the overlay driver such as root, LowerDir, UpperDir, WorkDir and MergeDir used to store data. | |
+// GetMetadata returns metadata about the overlay driver such as root, | |
+// LowerDir, UpperDir, WorkDir and MergeDir used to store data. | |
func (d *Driver) GetMetadata(id string) (map[string]string, error) { | |
dir := d.dir(id) | |
if _, err := os.Stat(dir); err != nil { | |
@@ -293,9 +296,6 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr | |
if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { | |
return err | |
} | |
- if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { | |
- return err | |
- } | |
if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil { | |
return err | |
} | |
@@ -326,9 +326,6 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr | |
if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { | |
return err | |
} | |
- if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { | |
- return err | |
- } | |
return copy.DirCopy(parentUpperDir, upperDir, copy.Content) | |
} | |
@@ -357,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 | |
@@ -364,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) | |
+ } | |
} | |
} | |
}() | |
@@ -372,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") | |
@@ -383,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 | |
} | |
@@ -394,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) | |
@@ -408,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 | |
} | |
diff --git a/daemon/graphdriver/overlay2/overlay.go b/daemon/graphdriver/overlay2/overlay.go | |
index c2023c72529..9e274975574 100644 | |
--- a/daemon/graphdriver/overlay2/overlay.go | |
+++ b/daemon/graphdriver/overlay2/overlay.go | |
@@ -77,7 +77,7 @@ const ( | |
maxDepth = 128 | |
// idLength represents the number of random characters | |
- // which can be used to create the unique link identifer | |
+ // which can be used to create the unique link identifier | |
// for every layer. If this value is too long then the | |
// page size limit for the mount command may be exceeded. | |
// The idLength should be selected such that following equation | |
@@ -91,7 +91,8 @@ type overlayOptions struct { | |
quota quota.Quota | |
} | |
-// Driver contains information about the home directory and the list of active mounts that are created using this driver. | |
+// Driver contains information about the home directory and the list of active | |
+// mounts that are created using this driver. | |
type Driver struct { | |
home string | |
uidMaps []idtools.IDMap | |
@@ -116,9 +117,11 @@ func init() { | |
graphdriver.Register(driverName, Init) | |
} | |
-// Init returns the a native diff driver for overlay filesystem. | |
-// If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. | |
-// If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned. | |
+// Init returns the native diff driver for overlay filesystem. | |
+// If overlay filesystem is not supported on the host, the error | |
+// graphdriver.ErrNotSupported is returned. | |
+// If an overlay filesystem is not supported over an existing filesystem then | |
+// the error graphdriver.ErrIncompatibleFS is returned. | |
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { | |
opts, err := parseOptions(options) | |
if err != nil { | |
@@ -289,8 +292,8 @@ func (d *Driver) Status() [][2]string { | |
} | |
} | |
-// GetMetadata returns meta data about the overlay driver such as | |
-// LowerDir, UpperDir, WorkDir and MergeDir used to store data. | |
+// GetMetadata returns metadata about the overlay driver such as the LowerDir, | |
+// UpperDir, WorkDir, and MergeDir used to store data. | |
func (d *Driver) GetMetadata(id string) (map[string]string, error) { | |
dir := d.dir(id) | |
if _, err := os.Stat(dir); err != nil { | |
@@ -414,9 +417,6 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr | |
if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { | |
return err | |
} | |
- if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { | |
- return err | |
- } | |
lower, err := d.getLower(parent) | |
if err != nil { | |
@@ -545,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) | |
+ } | |
} | |
} | |
}() | |
@@ -560,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 | |
@@ -594,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 | |
} | |
@@ -607,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) | |
@@ -627,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 | |
} | |
@@ -636,7 +655,8 @@ func (d *Driver) Exists(id string) bool { | |
return err == nil | |
} | |
-// isParent returns if the passed in parent is the direct parent of the passed in layer | |
+// isParent determines whether the given parent is the direct parent of the | |
+// given layer id | |
func (d *Driver) isParent(id, parent string) bool { | |
lowers, err := d.getLowerDirs(id) | |
if err != nil { | |
@@ -711,8 +731,8 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { | |
}) | |
} | |
-// Changes produces a list of changes between the specified layer | |
-// and its parent layer. If parent is "", then all changes will be ADD changes. | |
+// Changes produces a list of changes between the specified layer and its | |
+// parent layer. If parent is "", then all changes will be ADD changes. | |
func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { | |
if useNaiveDiff(d.home) || !d.isParent(id, parent) { | |
return d.naiveDiff.Changes(id, parent) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment