diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 30bbfd8533..f1e7223e06 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -540,8 +540,14 @@ func xfsSupported() error { return err // error text is descriptive enough } - // Check if kernel supports xfs filesystem or not. - exec.Command("modprobe", "xfs").Run() + mountTarget, err := ioutil.TempDir("", "supportsXFS") + if err != nil { + return errors.Wrapf(err, "error checking for xfs support") + } + + /* The mounting will fail--after the module has been loaded.*/ + defer os.RemoveAll(mountTarget) + unix.Mount("none", mountTarget, "xfs", 0, "") f, err := os.Open("/proc/filesystems") if err != nil { diff --git a/daemon/graphdriver/overlay/overlay.go b/daemon/graphdriver/overlay/overlay.go index 08c05e192f..a7a53387ac 100644 --- a/daemon/graphdriver/overlay/overlay.go +++ b/daemon/graphdriver/overlay/overlay.go @@ -3,12 +3,10 @@ package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" import ( - "bufio" "fmt" "io" "io/ioutil" "os" - "os/exec" "path" "path/filepath" "strconv" @@ -124,10 +122,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } - if err := supportsOverlay(); err != nil { - return nil, graphdriver.ErrNotSupported - } - // Perform feature detection on /var/lib/docker/overlay if it's an existing directory. // This covers situations where /var/lib/docker/overlay is a mount, and on a different // filesystem than /var/lib/docker. @@ -137,18 +131,9 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap testdir = filepath.Dir(testdir) } - fsMagic, err := graphdriver.GetFSMagic(testdir) - if err != nil { - return nil, err - } - if fsName, ok := graphdriver.FsNames[fsMagic]; ok { - backingFs = fsName - } - - switch fsMagic { - case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs: - logrus.WithField("storage-driver", "overlay").Errorf("'overlay' is not supported over %s", backingFs) - return nil, graphdriver.ErrIncompatibleFS + if err := overlayutils.SupportsOverlay(testdir, false); err != nil { + logrus.WithField("storage-driver", "overlay").Error(err) + return nil, graphdriver.ErrNotSupported } supportsDType, err := fsutils.SupportsDType(testdir) @@ -200,27 +185,6 @@ func parseOptions(options []string) (*overlayOptions, error) { return o, nil } -func supportsOverlay() error { - // We can try to modprobe overlay first before looking at - // proc/filesystems for when overlay is supported - exec.Command("modprobe", "overlay").Run() - - f, err := os.Open("/proc/filesystems") - if err != nil { - return err - } - defer f.Close() - - s := bufio.NewScanner(f) - for s.Scan() { - if s.Text() == "nodev\toverlay" { - return nil - } - } - logrus.WithField("storage-driver", "overlay").Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") - return graphdriver.ErrNotSupported -} - func (d *Driver) String() string { return "overlay" } diff --git a/daemon/graphdriver/overlay2/check.go b/daemon/graphdriver/overlay2/check.go index 04c060cb72..003933be52 100644 --- a/daemon/graphdriver/overlay2/check.go +++ b/daemon/graphdriver/overlay2/check.go @@ -99,35 +99,3 @@ func doesSupportNativeDiff(d string) error { 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 { - logger.Warnf("Failed to remove check directory %v: %v", td, err) - } - }() - - for _, dir := range []string{"lower1", "lower2", "upper", workDirName, mergedDirName} { - 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, workDirName)) - if err := unix.Mount("overlay", filepath.Join(td, mergedDirName), "overlay", 0, opts); err != nil { - return errors.Wrap(err, "failed to mount overlay") - } - if err := unix.Unmount(filepath.Join(td, mergedDirName), 0); err != nil { - logger.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, mergedDirName), err) - } - return nil -} diff --git a/daemon/graphdriver/overlay2/overlay.go b/daemon/graphdriver/overlay2/overlay.go index eedf5b73ae..e203b72923 100644 --- a/daemon/graphdriver/overlay2/overlay.go +++ b/daemon/graphdriver/overlay2/overlay.go @@ -3,14 +3,12 @@ package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" import ( - "bufio" "context" "errors" "fmt" "io" "io/ioutil" "os" - "os/exec" "path" "path/filepath" "strconv" @@ -29,7 +27,6 @@ import ( "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/system" "github.com/docker/go-units" rsystem "github.com/opencontainers/runc/libcontainer/system" @@ -134,16 +131,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } - if err := supportsOverlay(); err != nil { - return nil, graphdriver.ErrNotSupported - } - - // require kernel 4.0.0 to ensure multiple lower dirs are supported - v, err := kernel.GetKernelVersion() - if err != nil { - return nil, err - } - // Perform feature detection on /var/lib/docker/overlay2 if it's an existing directory. // This covers situations where /var/lib/docker/overlay2 is a mount, and on a different // filesystem than /var/lib/docker. @@ -153,40 +140,11 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap testdir = filepath.Dir(testdir) } - fsMagic, err := graphdriver.GetFSMagic(testdir) - if err != nil { - return nil, err - } - if fsName, ok := graphdriver.FsNames[fsMagic]; ok { - backingFs = fsName + if err := overlayutils.SupportsOverlay(testdir, true); err != nil { + logger.Error(err) + return nil, graphdriver.ErrNotSupported } - switch fsMagic { - case graphdriver.FsMagicAufs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs: - logger.Errorf("'overlay2' is not supported over %s", backingFs) - return nil, graphdriver.ErrIncompatibleFS - case graphdriver.FsMagicBtrfs: - // Support for OverlayFS on BTRFS was added in kernel 4.7 - // See https://btrfs.wiki.kernel.org/index.php/Changelog - if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 7, Minor: 0}) < 0 { - if !opts.overrideKernelCheck { - logger.Errorf("'overlay2' requires kernel 4.7 to use on %s", backingFs) - return nil, graphdriver.ErrIncompatibleFS - } - logger.Warn("Using pre-4.7.0 kernel for overlay2 on btrfs, may require kernel update") - } - } - - if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 { - if opts.overrideKernelCheck { - logger.Warn("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update") - } else { - if err := supportsMultipleLowerDir(testdir); err != nil { - logger.Debugf("Multiple lower dirs not supported: %v", err) - return nil, graphdriver.ErrNotSupported - } - } - } supportsDType, err := fsutils.SupportsDType(testdir) if err != nil { return nil, err @@ -275,27 +233,6 @@ func parseOptions(options []string) (*overlayOptions, error) { return o, nil } -func supportsOverlay() error { - // We can try to modprobe overlay first before looking at - // proc/filesystems for when overlay is supported - exec.Command("modprobe", "overlay").Run() - - f, err := os.Open("/proc/filesystems") - if err != nil { - return err - } - defer f.Close() - - s := bufio.NewScanner(f) - for s.Scan() { - if s.Text() == "nodev\toverlay" { - return nil - } - } - logger.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") - return graphdriver.ErrNotSupported -} - func useNaiveDiff(home string) bool { useNaiveDiffLock.Do(func() { if err := doesSupportNativeDiff(home); err != nil { diff --git a/daemon/graphdriver/overlayutils/overlayutils.go b/daemon/graphdriver/overlayutils/overlayutils.go index 71f6d2d460..e0553a09f7 100644 --- a/daemon/graphdriver/overlayutils/overlayutils.go +++ b/daemon/graphdriver/overlayutils/overlayutils.go @@ -4,8 +4,15 @@ package overlayutils // import "github.com/docker/docker/daemon/graphdriver/over import ( "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" "github.com/docker/docker/daemon/graphdriver" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) // ErrDTypeNotSupported denotes that the backing filesystem doesn't support d_type. @@ -23,3 +30,40 @@ func ErrDTypeNotSupported(driver, backingFs string) error { return graphdriver.NotSupportedError(msg) } + +// SupportsOverlay checks if the system supports overlay filesystem +// by performing an actual overlay mount. +// +// checkMultipleLowers parameter enables check for multiple lowerdirs, +// which is required for the overlay2 driver. +func SupportsOverlay(d string, checkMultipleLowers bool) error { + td, err := ioutil.TempDir(d, "check-overlayfs-support") + 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 + } + } + + mnt := filepath.Join(td, "merged") + lowerDir := path.Join(td, "lower2") + if checkMultipleLowers { + lowerDir += ":" + path.Join(td, "lower1") + } + opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, path.Join(td, "upper"), path.Join(td, "work")) + if err := unix.Mount("overlay", mnt, "overlay", 0, opts); err != nil { + return errors.Wrap(err, "failed to mount overlay") + } + if err := unix.Unmount(mnt, 0); err != nil { + logrus.Warnf("Failed to unmount check directory %v: %v", mnt, err) + } + return nil +}