mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	overlay: warn if overlay backing fs doesn't support d_type
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
		
							parent
							
								
									64a42d656b
								
							
						
					
					
						commit
						2e20e63da2
					
				
					 5 changed files with 246 additions and 19 deletions
				
			
		| 
						 | 
				
			
			@ -10,11 +10,14 @@ import (
 | 
			
		|||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/docker/docker/daemon/graphdriver"
 | 
			
		||||
	"github.com/docker/docker/daemon/graphdriver/overlayutils"
 | 
			
		||||
	"github.com/docker/docker/pkg/archive"
 | 
			
		||||
	"github.com/docker/docker/pkg/fsutils"
 | 
			
		||||
	"github.com/docker/docker/pkg/idtools"
 | 
			
		||||
	"github.com/docker/docker/pkg/mount"
 | 
			
		||||
	"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.
 | 
			
		||||
type Driver struct {
 | 
			
		||||
	home    string
 | 
			
		||||
	uidMaps []idtools.IDMap
 | 
			
		||||
	gidMaps []idtools.IDMap
 | 
			
		||||
	ctr     *graphdriver.RefCounter
 | 
			
		||||
	home          string
 | 
			
		||||
	uidMaps       []idtools.IDMap
 | 
			
		||||
	gidMaps       []idtools.IDMap
 | 
			
		||||
	ctr           *graphdriver.RefCounter
 | 
			
		||||
	supportsDType bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
| 
						 | 
				
			
			@ -135,11 +139,21 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 | 
			
		|||
		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{
 | 
			
		||||
		home:    home,
 | 
			
		||||
		uidMaps: uidMaps,
 | 
			
		||||
		gidMaps: gidMaps,
 | 
			
		||||
		ctr:     graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
 | 
			
		||||
		home:          home,
 | 
			
		||||
		uidMaps:       uidMaps,
 | 
			
		||||
		gidMaps:       gidMaps,
 | 
			
		||||
		ctr:           graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
 | 
			
		||||
		supportsDType: supportsDType,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
 | 
			
		||||
| 
						 | 
				
			
			@ -175,6 +189,7 @@ func (d *Driver) String() string {
 | 
			
		|||
func (d *Driver) Status() [][2]string {
 | 
			
		||||
	return [][2]string{
 | 
			
		||||
		{"Backing Filesystem", backingFs},
 | 
			
		||||
		{"Supports d_type", strconv.FormatBool(d.supportsDType)},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,10 +19,12 @@ import (
 | 
			
		|||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/daemon/graphdriver"
 | 
			
		||||
	"github.com/docker/docker/daemon/graphdriver/overlayutils"
 | 
			
		||||
	"github.com/docker/docker/daemon/graphdriver/quota"
 | 
			
		||||
	"github.com/docker/docker/pkg/archive"
 | 
			
		||||
	"github.com/docker/docker/pkg/chrootarchive"
 | 
			
		||||
	"github.com/docker/docker/pkg/directory"
 | 
			
		||||
	"github.com/docker/docker/pkg/fsutils"
 | 
			
		||||
	"github.com/docker/docker/pkg/idtools"
 | 
			
		||||
	"github.com/docker/docker/pkg/mount"
 | 
			
		||||
	"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.
 | 
			
		||||
type Driver struct {
 | 
			
		||||
	home      string
 | 
			
		||||
	uidMaps   []idtools.IDMap
 | 
			
		||||
	gidMaps   []idtools.IDMap
 | 
			
		||||
	ctr       *graphdriver.RefCounter
 | 
			
		||||
	quotaCtl  *quota.Control
 | 
			
		||||
	options   overlayOptions
 | 
			
		||||
	naiveDiff graphdriver.DiffDriver
 | 
			
		||||
	home          string
 | 
			
		||||
	uidMaps       []idtools.IDMap
 | 
			
		||||
	gidMaps       []idtools.IDMap
 | 
			
		||||
	ctr           *graphdriver.RefCounter
 | 
			
		||||
	quotaCtl      *quota.Control
 | 
			
		||||
	options       overlayOptions
 | 
			
		||||
	naiveDiff     graphdriver.DiffDriver
 | 
			
		||||
	supportsDType bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			@ -158,11 +161,21 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 | 
			
		|||
		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{
 | 
			
		||||
		home:    home,
 | 
			
		||||
		uidMaps: uidMaps,
 | 
			
		||||
		gidMaps: gidMaps,
 | 
			
		||||
		ctr:     graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
 | 
			
		||||
		home:          home,
 | 
			
		||||
		uidMaps:       uidMaps,
 | 
			
		||||
		gidMaps:       gidMaps,
 | 
			
		||||
		ctr:           graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
 | 
			
		||||
		supportsDType: supportsDType,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps)
 | 
			
		||||
| 
						 | 
				
			
			@ -231,6 +244,7 @@ func (d *Driver) String() string {
 | 
			
		|||
func (d *Driver) Status() [][2]string {
 | 
			
		||||
	return [][2]string{
 | 
			
		||||
		{"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
		Add a link
		
	
		Reference in a new issue