-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
overlay: warn if overlay backing fs doesn't support d_type
Signed-off-by: Akihiro Suda <[email protected]>
- Loading branch information
1 parent
64a42d6
commit 2e20e63
Showing
5 changed files
with
246 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} |