From 6e2457f186133944f7d30724bd4810254f2d20ea Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Mon, 8 May 2017 06:46:55 +0000 Subject: [PATCH 1/3] import github.com/moby/moby/pkg/fsutils (v17.05.0-ce) as fs/fsutils Signed-off-by: Akihiro Suda --- fs/fsutils/fsutils_linux.go | 87 ++++++++++++++++++++++++++++++ fs/fsutils/fsutils_linux_test.go | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 fs/fsutils/fsutils_linux.go create mode 100644 fs/fsutils/fsutils_linux_test.go diff --git a/fs/fsutils/fsutils_linux.go b/fs/fsutils/fsutils_linux.go new file mode 100644 index 000000000..c8cac3072 --- /dev/null +++ b/fs/fsutils/fsutils_linux.go @@ -0,0 +1,87 @@ +// +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() + err = dummyFile.Close() + return name, err +} + +// 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 +} diff --git a/fs/fsutils/fsutils_linux_test.go b/fs/fsutils/fsutils_linux_test.go new file mode 100644 index 000000000..4a648239c --- /dev/null +++ b/fs/fsutils/fsutils_linux_test.go @@ -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") +} From 825c2ce3cc47557caf2dbaa5fac59a768d78f28c Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Mon, 8 May 2017 06:53:35 +0000 Subject: [PATCH 2/3] overlay: abort if backing fs lacks support for d_type Signed-off-by: Akihiro Suda --- snapshot/overlay/overlay.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go index c60c15212..7aaa92026 100644 --- a/snapshot/overlay/overlay.go +++ b/snapshot/overlay/overlay.go @@ -12,6 +12,7 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/fs" + "github.com/containerd/containerd/fs/fsutils" "github.com/containerd/containerd/log" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/snapshot" @@ -47,6 +48,13 @@ func NewSnapshotter(root string) (snapshot.Snapshotter, error) { if err := os.MkdirAll(root, 0700); err != nil { return nil, err } + supportsDType, err := fsutils.SupportsDType(root) + if err != nil { + return nil, err + } + if !supportsDType { + return nil, fmt.Errorf("%s does not support d_type. If the backing filesystem is xfs, please reformat with ftype=1 to enable d_type support.", root) + } ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db")) if err != nil { return nil, err From e64757b6574c9f09ae61f0dbd908ba7d9111d480 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 10 May 2017 05:23:28 +0000 Subject: [PATCH 3/3] fsutils: refactor test Signed-off-by: Akihiro Suda --- fs/fsutils/fsutils_linux_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/fsutils/fsutils_linux_test.go b/fs/fsutils/fsutils_linux_test.go index 4a648239c..221e4f00b 100644 --- a/fs/fsutils/fsutils_linux_test.go +++ b/fs/fsutils/fsutils_linux_test.go @@ -49,7 +49,7 @@ func testSupportsDType(t *testing.T, expected bool, mkfsCommand string, mkfsArg t.Log(string(out)) } if err != nil { - t.Fatal(err) + t.Skip("skipping the test because %s failed. This is probably your %s is an unsupported version.", mkfsCommand, mkfsCommand) } // loopback-mount the image. @@ -87,5 +87,5 @@ func TestSupportsDTypeWithFType1XFS(t *testing.T) { } func TestSupportsDTypeWithExt4(t *testing.T) { - testSupportsDType(t, true, "mkfs.ext4") + testSupportsDType(t, true, "mkfs.ext4", "-F") }