mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #27433 from AkihiroSuda/fix-ovl-xfs-ftype0
overlay: warn if overlay backing fs doesn't support d_type
This commit is contained in:
commit
643ac2f804
5 changed files with 246 additions and 19 deletions
|
@ -10,11 +10,14 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
|
"github.com/docker/docker/daemon/graphdriver/overlayutils"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/fsutils"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/opencontainers/runc/libcontainer/label"
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
|
@ -89,10 +92,11 @@ func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff io.Reader)
|
||||||
|
|
||||||
// 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 {
|
type Driver struct {
|
||||||
home string
|
home string
|
||||||
uidMaps []idtools.IDMap
|
uidMaps []idtools.IDMap
|
||||||
gidMaps []idtools.IDMap
|
gidMaps []idtools.IDMap
|
||||||
ctr *graphdriver.RefCounter
|
ctr *graphdriver.RefCounter
|
||||||
|
supportsDType bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -135,11 +139,21 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
supportsDType, err := fsutils.SupportsDType(home)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !supportsDType {
|
||||||
|
// not a fatal error until v1.16 (#27443)
|
||||||
|
logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
|
||||||
|
}
|
||||||
|
|
||||||
d := &Driver{
|
d := &Driver{
|
||||||
home: home,
|
home: home,
|
||||||
uidMaps: uidMaps,
|
uidMaps: uidMaps,
|
||||||
gidMaps: gidMaps,
|
gidMaps: gidMaps,
|
||||||
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
|
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
|
||||||
|
supportsDType: supportsDType,
|
||||||
}
|
}
|
||||||
|
|
||||||
return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
|
return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
|
||||||
|
@ -175,6 +189,7 @@ func (d *Driver) String() string {
|
||||||
func (d *Driver) Status() [][2]string {
|
func (d *Driver) Status() [][2]string {
|
||||||
return [][2]string{
|
return [][2]string{
|
||||||
{"Backing Filesystem", backingFs},
|
{"Backing Filesystem", backingFs},
|
||||||
|
{"Supports d_type", strconv.FormatBool(d.supportsDType)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,12 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
|
"github.com/docker/docker/daemon/graphdriver/overlayutils"
|
||||||
"github.com/docker/docker/daemon/graphdriver/quota"
|
"github.com/docker/docker/daemon/graphdriver/quota"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/chrootarchive"
|
"github.com/docker/docker/pkg/chrootarchive"
|
||||||
"github.com/docker/docker/pkg/directory"
|
"github.com/docker/docker/pkg/directory"
|
||||||
|
"github.com/docker/docker/pkg/fsutils"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/docker/docker/pkg/parsers"
|
"github.com/docker/docker/pkg/parsers"
|
||||||
|
@ -87,13 +89,14 @@ type overlayOptions struct {
|
||||||
|
|
||||||
// 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 {
|
type Driver struct {
|
||||||
home string
|
home string
|
||||||
uidMaps []idtools.IDMap
|
uidMaps []idtools.IDMap
|
||||||
gidMaps []idtools.IDMap
|
gidMaps []idtools.IDMap
|
||||||
ctr *graphdriver.RefCounter
|
ctr *graphdriver.RefCounter
|
||||||
quotaCtl *quota.Control
|
quotaCtl *quota.Control
|
||||||
options overlayOptions
|
options overlayOptions
|
||||||
naiveDiff graphdriver.DiffDriver
|
naiveDiff graphdriver.DiffDriver
|
||||||
|
supportsDType bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -158,11 +161,21 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
supportsDType, err := fsutils.SupportsDType(home)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !supportsDType {
|
||||||
|
// not a fatal error until v1.16 (#27443)
|
||||||
|
logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs))
|
||||||
|
}
|
||||||
|
|
||||||
d := &Driver{
|
d := &Driver{
|
||||||
home: home,
|
home: home,
|
||||||
uidMaps: uidMaps,
|
uidMaps: uidMaps,
|
||||||
gidMaps: gidMaps,
|
gidMaps: gidMaps,
|
||||||
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
|
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
|
||||||
|
supportsDType: supportsDType,
|
||||||
}
|
}
|
||||||
|
|
||||||
d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps)
|
d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps)
|
||||||
|
@ -231,6 +244,7 @@ func (d *Driver) String() string {
|
||||||
func (d *Driver) Status() [][2]string {
|
func (d *Driver) Status() [][2]string {
|
||||||
return [][2]string{
|
return [][2]string{
|
||||||
{"Backing Filesystem", backingFs},
|
{"Backing Filesystem", backingFs},
|
||||||
|
{"Supports d_type", strconv.FormatBool(d.supportsDType)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
daemon/graphdriver/overlayutils/overlayutils.go
Normal file
18
daemon/graphdriver/overlayutils/overlayutils.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package overlayutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrDTypeNotSupported denotes that the backing filesystem doesn't support d_type.
|
||||||
|
func ErrDTypeNotSupported(driver, backingFs string) error {
|
||||||
|
msg := fmt.Sprintf("%s: the backing %s filesystem is formatted without d_type support, which leads to incorrect behavior.", driver, backingFs)
|
||||||
|
if backingFs == "xfs" {
|
||||||
|
msg += " Reformat the filesystem with ftype=1 to enable d_type support."
|
||||||
|
}
|
||||||
|
msg += " Running without d_type support will no longer be supported in Docker 1.16."
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
89
pkg/fsutils/fsutils_linux.go
Normal file
89
pkg/fsutils/fsutils_linux.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package fsutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func locateDummyIfEmpty(path string) (string, error) {
|
||||||
|
children, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(children) != 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
dummyFile, err := ioutil.TempFile(path, "fsutils-dummy")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name := dummyFile.Name()
|
||||||
|
if err = dummyFile.Close(); err != nil {
|
||||||
|
return name, err
|
||||||
|
}
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsDType returns whether the filesystem mounted on path supports d_type
|
||||||
|
func SupportsDType(path string) (bool, error) {
|
||||||
|
// locate dummy so that we have at least one dirent
|
||||||
|
dummy, err := locateDummyIfEmpty(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if dummy != "" {
|
||||||
|
defer os.Remove(dummy)
|
||||||
|
}
|
||||||
|
|
||||||
|
visited := 0
|
||||||
|
supportsDType := true
|
||||||
|
fn := func(ent *syscall.Dirent) bool {
|
||||||
|
visited++
|
||||||
|
if ent.Type == syscall.DT_UNKNOWN {
|
||||||
|
supportsDType = false
|
||||||
|
// stop iteration
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// continue iteration
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err = iterateReadDir(path, fn); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if visited == 0 {
|
||||||
|
return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
|
||||||
|
}
|
||||||
|
return supportsDType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
|
||||||
|
d, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
fd := int(d.Fd())
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
nbytes, err := syscall.ReadDirent(fd, buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if nbytes == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for off := 0; off < nbytes; {
|
||||||
|
ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
|
||||||
|
if stop := fn(ent); stop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
off += int(ent.Reclen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
91
pkg/fsutils/fsutils_linux_test.go
Normal file
91
pkg/fsutils/fsutils_linux_test.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package fsutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testSupportsDType(t *testing.T, expected bool, mkfsCommand string, mkfsArg ...string) {
|
||||||
|
// check whether mkfs is installed
|
||||||
|
if _, err := exec.LookPath(mkfsCommand); err != nil {
|
||||||
|
t.Skipf("%s not installed: %v", mkfsCommand, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a sparse image
|
||||||
|
imageSize := int64(32 * 1024 * 1024)
|
||||||
|
imageFile, err := ioutil.TempFile("", "fsutils-image")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
imageFileName := imageFile.Name()
|
||||||
|
defer os.Remove(imageFileName)
|
||||||
|
if _, err = imageFile.Seek(imageSize-1, 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err = imageFile.Write([]byte{0}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = imageFile.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a mountpoint
|
||||||
|
mountpoint, err := ioutil.TempDir("", "fsutils-mountpoint")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(mountpoint)
|
||||||
|
|
||||||
|
// format the image
|
||||||
|
args := append(mkfsArg, imageFileName)
|
||||||
|
t.Logf("Executing `%s %v`", mkfsCommand, args)
|
||||||
|
out, err := exec.Command(mkfsCommand, args...).CombinedOutput()
|
||||||
|
if len(out) > 0 {
|
||||||
|
t.Log(string(out))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loopback-mount the image.
|
||||||
|
// for ease of setting up loopback device, we use os/exec rather than syscall.Mount
|
||||||
|
out, err = exec.Command("mount", "-o", "loop", imageFileName, mountpoint).CombinedOutput()
|
||||||
|
if len(out) > 0 {
|
||||||
|
t.Log(string(out))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("skipping the test because mount failed")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := syscall.Unmount(mountpoint, 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// check whether it supports d_type
|
||||||
|
result, err := SupportsDType(mountpoint)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("Supports d_type: %v", result)
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSupportsDTypeWithFType0XFS(t *testing.T) {
|
||||||
|
testSupportsDType(t, false, "mkfs.xfs", "-m", "crc=0", "-n", "ftype=0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSupportsDTypeWithFType1XFS(t *testing.T) {
|
||||||
|
testSupportsDType(t, true, "mkfs.xfs", "-m", "crc=0", "-n", "ftype=1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSupportsDTypeWithExt4(t *testing.T) {
|
||||||
|
testSupportsDType(t, true, "mkfs.ext4")
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue