mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
955c1f881a
The overlay2 storage-driver requires multiple lower dir support for overlayFs. Support for this feature was added in kernel 4.x, but some distros (RHEL 7.4, CentOS 7.4) ship with an older kernel with this feature backported. This patch adds feature-detection for multiple lower dirs, and will perform this feature-detection on pre-4.x kernels with overlayFS support. With this patch applied, daemons running on a kernel with multiple lower dir support will now select "overlay2" as storage-driver, instead of falling back to "overlay". Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
134 lines
4.6 KiB
Go
134 lines
4.6 KiB
Go
// +build linux
|
|
|
|
package overlay2
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/docker/docker/pkg/system"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// doesSupportNativeDiff checks whether the filesystem has a bug
|
|
// which copies up the opaque flag when copying up an opaque
|
|
// directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR.
|
|
// When these exist naive diff should be used.
|
|
func doesSupportNativeDiff(d string) error {
|
|
td, err := ioutil.TempDir(d, "opaque-bug-check")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err := os.RemoveAll(td); err != nil {
|
|
logrus.Warnf("Failed to remove check directory %v: %v", td, err)
|
|
}
|
|
}()
|
|
|
|
// Make directories l1/d, l1/d1, l2/d, l3, work, merged
|
|
if err := os.MkdirAll(filepath.Join(td, "l1", "d"), 0755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(td, "l1", "d1"), 0755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(td, "l2", "d"), 0755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Mkdir(filepath.Join(td, "l3"), 0755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Mkdir(filepath.Join(td, "work"), 0755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Mkdir(filepath.Join(td, "merged"), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Mark l2/d as opaque
|
|
if err := system.Lsetxattr(filepath.Join(td, "l2", "d"), "trusted.overlay.opaque", []byte("y"), 0); err != nil {
|
|
return errors.Wrap(err, "failed to set opaque flag on middle layer")
|
|
}
|
|
|
|
opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, "work"))
|
|
if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil {
|
|
return errors.Wrap(err, "failed to mount overlay")
|
|
}
|
|
defer func() {
|
|
if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil {
|
|
logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err)
|
|
}
|
|
}()
|
|
|
|
// Touch file in d to force copy up of opaque directory "d" from "l2" to "l3"
|
|
if err := ioutil.WriteFile(filepath.Join(td, "merged", "d", "f"), []byte{}, 0644); err != nil {
|
|
return errors.Wrap(err, "failed to write to merged directory")
|
|
}
|
|
|
|
// Check l3/d does not have opaque flag
|
|
xattrOpaque, err := system.Lgetxattr(filepath.Join(td, "l3", "d"), "trusted.overlay.opaque")
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to read opaque flag on upper layer")
|
|
}
|
|
if string(xattrOpaque) == "y" {
|
|
return errors.New("opaque flag erroneously copied up, consider update to kernel 4.8 or later to fix")
|
|
}
|
|
|
|
// rename "d1" to "d2"
|
|
if err := os.Rename(filepath.Join(td, "merged", "d1"), filepath.Join(td, "merged", "d2")); err != nil {
|
|
// if rename failed with syscall.EXDEV, the kernel doesn't have CONFIG_OVERLAY_FS_REDIRECT_DIR enabled
|
|
if err.(*os.LinkError).Err == syscall.EXDEV {
|
|
return nil
|
|
}
|
|
return errors.Wrap(err, "failed to rename dir in merged directory")
|
|
}
|
|
// get the xattr of "d2"
|
|
xattrRedirect, err := system.Lgetxattr(filepath.Join(td, "l3", "d2"), "trusted.overlay.redirect")
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to read redirect flag on upper layer")
|
|
}
|
|
|
|
if string(xattrRedirect) == "d1" {
|
|
return errors.New("kernel has CONFIG_OVERLAY_FS_REDIRECT_DIR enabled")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// supportsMultipleLowerDir checks if the system supports multiple lowerdirs,
|
|
// which is required for the overlay2 driver. On 4.x kernels, multiple lowerdirs
|
|
// are always available (so this check isn't needed), and backported to RHEL and
|
|
// CentOS 3.x kernels (3.10.0-693.el7.x86_64 and up). This function is to detect
|
|
// support on those kernels, without doing a kernel version compare.
|
|
func supportsMultipleLowerDir(d string) error {
|
|
td, err := ioutil.TempDir(d, "multiple-lowerdir-check")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err := os.RemoveAll(td); err != nil {
|
|
logrus.Warnf("Failed to remove check directory %v: %v", td, err)
|
|
}
|
|
}()
|
|
|
|
for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
|
|
if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "lower2"), path.Join(td, "lower1"), path.Join(td, "upper"), path.Join(td, "work"))
|
|
if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil {
|
|
return errors.Wrap(err, "failed to mount overlay")
|
|
}
|
|
if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil {
|
|
logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err)
|
|
}
|
|
return nil
|
|
}
|