Merge pull request #815 from AkihiroSuda/dtype
overlay: abort if backing fs lacks support for d_type
This commit is contained in:
commit
a1173ce712
87
fs/fsutils/fsutils_linux.go
Normal file
87
fs/fsutils/fsutils_linux.go
Normal file
@ -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
|
||||||
|
}
|
91
fs/fsutils/fsutils_linux_test.go
Normal file
91
fs/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.Skip("skipping the test because %s failed. This is probably your %s is an unsupported version.", mkfsCommand, mkfsCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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", "-F")
|
||||||
|
}
|
@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/fs"
|
"github.com/containerd/containerd/fs"
|
||||||
|
"github.com/containerd/containerd/fs/fsutils"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/containerd/containerd/snapshot"
|
"github.com/containerd/containerd/snapshot"
|
||||||
@ -47,6 +48,13 @@ func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
|
|||||||
if err := os.MkdirAll(root, 0700); err != nil {
|
if err := os.MkdirAll(root, 0700); err != nil {
|
||||||
return nil, err
|
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"))
|
ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user