Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
| ... | ... |
@@ -10,11 +10,14 @@ import ( |
| 10 | 10 |
"os" |
| 11 | 11 |
"os/exec" |
| 12 | 12 |
"path" |
| 13 |
+ "strconv" |
|
| 13 | 14 |
"syscall" |
| 14 | 15 |
|
| 15 | 16 |
"github.com/Sirupsen/logrus" |
| 16 | 17 |
"github.com/docker/docker/daemon/graphdriver" |
| 18 |
+ "github.com/docker/docker/daemon/graphdriver/overlayutils" |
|
| 17 | 19 |
"github.com/docker/docker/pkg/archive" |
| 20 |
+ "github.com/docker/docker/pkg/fsutils" |
|
| 18 | 21 |
"github.com/docker/docker/pkg/idtools" |
| 19 | 22 |
"github.com/docker/docker/pkg/mount" |
| 20 | 23 |
"github.com/opencontainers/runc/libcontainer/label" |
| ... | ... |
@@ -89,10 +92,11 @@ func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff io.Reader) |
| 89 | 89 |
|
| 90 | 90 |
// Driver contains information about the home directory and the list of active mounts that are created using this driver. |
| 91 | 91 |
type Driver struct {
|
| 92 |
- home string |
|
| 93 |
- uidMaps []idtools.IDMap |
|
| 94 |
- gidMaps []idtools.IDMap |
|
| 95 |
- ctr *graphdriver.RefCounter |
|
| 92 |
+ home string |
|
| 93 |
+ uidMaps []idtools.IDMap |
|
| 94 |
+ gidMaps []idtools.IDMap |
|
| 95 |
+ ctr *graphdriver.RefCounter |
|
| 96 |
+ supportsDType bool |
|
| 96 | 97 |
} |
| 97 | 98 |
|
| 98 | 99 |
func init() {
|
| ... | ... |
@@ -135,11 +139,21 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap |
| 135 | 135 |
return nil, err |
| 136 | 136 |
} |
| 137 | 137 |
|
| 138 |
+ supportsDType, err := fsutils.SupportsDType(home) |
|
| 139 |
+ if err != nil {
|
|
| 140 |
+ return nil, err |
|
| 141 |
+ } |
|
| 142 |
+ if !supportsDType {
|
|
| 143 |
+ // not a fatal error until v1.16 (#27443) |
|
| 144 |
+ logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
|
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 138 | 147 |
d := &Driver{
|
| 139 |
- home: home, |
|
| 140 |
- uidMaps: uidMaps, |
|
| 141 |
- gidMaps: gidMaps, |
|
| 142 |
- ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), |
|
| 148 |
+ home: home, |
|
| 149 |
+ uidMaps: uidMaps, |
|
| 150 |
+ gidMaps: gidMaps, |
|
| 151 |
+ ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), |
|
| 152 |
+ supportsDType: supportsDType, |
|
| 143 | 153 |
} |
| 144 | 154 |
|
| 145 | 155 |
return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil |
| ... | ... |
@@ -175,6 +189,7 @@ func (d *Driver) String() string {
|
| 175 | 175 |
func (d *Driver) Status() [][2]string {
|
| 176 | 176 |
return [][2]string{
|
| 177 | 177 |
{"Backing Filesystem", backingFs},
|
| 178 |
+ {"Supports d_type", strconv.FormatBool(d.supportsDType)},
|
|
| 178 | 179 |
} |
| 179 | 180 |
} |
| 180 | 181 |
|
| ... | ... |
@@ -19,10 +19,12 @@ import ( |
| 19 | 19 |
"github.com/Sirupsen/logrus" |
| 20 | 20 |
|
| 21 | 21 |
"github.com/docker/docker/daemon/graphdriver" |
| 22 |
+ "github.com/docker/docker/daemon/graphdriver/overlayutils" |
|
| 22 | 23 |
"github.com/docker/docker/daemon/graphdriver/quota" |
| 23 | 24 |
"github.com/docker/docker/pkg/archive" |
| 24 | 25 |
"github.com/docker/docker/pkg/chrootarchive" |
| 25 | 26 |
"github.com/docker/docker/pkg/directory" |
| 27 |
+ "github.com/docker/docker/pkg/fsutils" |
|
| 26 | 28 |
"github.com/docker/docker/pkg/idtools" |
| 27 | 29 |
"github.com/docker/docker/pkg/mount" |
| 28 | 30 |
"github.com/docker/docker/pkg/parsers" |
| ... | ... |
@@ -87,13 +89,14 @@ type overlayOptions struct {
|
| 87 | 87 |
|
| 88 | 88 |
// Driver contains information about the home directory and the list of active mounts that are created using this driver. |
| 89 | 89 |
type Driver struct {
|
| 90 |
- home string |
|
| 91 |
- uidMaps []idtools.IDMap |
|
| 92 |
- gidMaps []idtools.IDMap |
|
| 93 |
- ctr *graphdriver.RefCounter |
|
| 94 |
- quotaCtl *quota.Control |
|
| 95 |
- options overlayOptions |
|
| 96 |
- naiveDiff graphdriver.DiffDriver |
|
| 90 |
+ home string |
|
| 91 |
+ uidMaps []idtools.IDMap |
|
| 92 |
+ gidMaps []idtools.IDMap |
|
| 93 |
+ ctr *graphdriver.RefCounter |
|
| 94 |
+ quotaCtl *quota.Control |
|
| 95 |
+ options overlayOptions |
|
| 96 |
+ naiveDiff graphdriver.DiffDriver |
|
| 97 |
+ supportsDType bool |
|
| 97 | 98 |
} |
| 98 | 99 |
|
| 99 | 100 |
var ( |
| ... | ... |
@@ -158,11 +161,21 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap |
| 158 | 158 |
return nil, err |
| 159 | 159 |
} |
| 160 | 160 |
|
| 161 |
+ supportsDType, err := fsutils.SupportsDType(home) |
|
| 162 |
+ if err != nil {
|
|
| 163 |
+ return nil, err |
|
| 164 |
+ } |
|
| 165 |
+ if !supportsDType {
|
|
| 166 |
+ // not a fatal error until v1.16 (#27443) |
|
| 167 |
+ logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs))
|
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 161 | 170 |
d := &Driver{
|
| 162 |
- home: home, |
|
| 163 |
- uidMaps: uidMaps, |
|
| 164 |
- gidMaps: gidMaps, |
|
| 165 |
- ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), |
|
| 171 |
+ home: home, |
|
| 172 |
+ uidMaps: uidMaps, |
|
| 173 |
+ gidMaps: gidMaps, |
|
| 174 |
+ ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), |
|
| 175 |
+ supportsDType: supportsDType, |
|
| 166 | 176 |
} |
| 167 | 177 |
|
| 168 | 178 |
d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) |
| ... | ... |
@@ -231,6 +244,7 @@ func (d *Driver) String() string {
|
| 231 | 231 |
func (d *Driver) Status() [][2]string {
|
| 232 | 232 |
return [][2]string{
|
| 233 | 233 |
{"Backing Filesystem", backingFs},
|
| 234 |
+ {"Supports d_type", strconv.FormatBool(d.supportsDType)},
|
|
| 234 | 235 |
} |
| 235 | 236 |
} |
| 236 | 237 |
|
| 237 | 238 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,18 @@ |
| 0 |
+// +build linux |
|
| 1 |
+ |
|
| 2 |
+package overlayutils |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "errors" |
|
| 6 |
+ "fmt" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// ErrDTypeNotSupported denotes that the backing filesystem doesn't support d_type. |
|
| 10 |
+func ErrDTypeNotSupported(driver, backingFs string) error {
|
|
| 11 |
+ msg := fmt.Sprintf("%s: the backing %s filesystem is formatted without d_type support, which leads to incorrect behavior.", driver, backingFs)
|
|
| 12 |
+ if backingFs == "xfs" {
|
|
| 13 |
+ msg += " Reformat the filesystem with ftype=1 to enable d_type support." |
|
| 14 |
+ } |
|
| 15 |
+ msg += " Running without d_type support will no longer be supported in Docker 1.16." |
|
| 16 |
+ return errors.New(msg) |
|
| 17 |
+} |
| 0 | 18 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,89 @@ |
| 0 |
+// +build linux |
|
| 1 |
+ |
|
| 2 |
+package fsutils |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "io/ioutil" |
|
| 7 |
+ "os" |
|
| 8 |
+ "syscall" |
|
| 9 |
+ "unsafe" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func locateDummyIfEmpty(path string) (string, error) {
|
|
| 13 |
+ children, err := ioutil.ReadDir(path) |
|
| 14 |
+ if err != nil {
|
|
| 15 |
+ return "", err |
|
| 16 |
+ } |
|
| 17 |
+ if len(children) != 0 {
|
|
| 18 |
+ return "", nil |
|
| 19 |
+ } |
|
| 20 |
+ dummyFile, err := ioutil.TempFile(path, "fsutils-dummy") |
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ return "", err |
|
| 23 |
+ } |
|
| 24 |
+ name := dummyFile.Name() |
|
| 25 |
+ if err = dummyFile.Close(); err != nil {
|
|
| 26 |
+ return name, err |
|
| 27 |
+ } |
|
| 28 |
+ return name, nil |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// SupportsDType returns whether the filesystem mounted on path supports d_type |
|
| 32 |
+func SupportsDType(path string) (bool, error) {
|
|
| 33 |
+ // locate dummy so that we have at least one dirent |
|
| 34 |
+ dummy, err := locateDummyIfEmpty(path) |
|
| 35 |
+ if err != nil {
|
|
| 36 |
+ return false, err |
|
| 37 |
+ } |
|
| 38 |
+ if dummy != "" {
|
|
| 39 |
+ defer os.Remove(dummy) |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ visited := 0 |
|
| 43 |
+ supportsDType := true |
|
| 44 |
+ fn := func(ent *syscall.Dirent) bool {
|
|
| 45 |
+ visited++ |
|
| 46 |
+ if ent.Type == syscall.DT_UNKNOWN {
|
|
| 47 |
+ supportsDType = false |
|
| 48 |
+ // stop iteration |
|
| 49 |
+ return true |
|
| 50 |
+ } |
|
| 51 |
+ // continue iteration |
|
| 52 |
+ return false |
|
| 53 |
+ } |
|
| 54 |
+ if err = iterateReadDir(path, fn); err != nil {
|
|
| 55 |
+ return false, err |
|
| 56 |
+ } |
|
| 57 |
+ if visited == 0 {
|
|
| 58 |
+ return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
|
|
| 59 |
+ } |
|
| 60 |
+ return supportsDType, nil |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
|
|
| 64 |
+ d, err := os.Open(path) |
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ return err |
|
| 67 |
+ } |
|
| 68 |
+ defer d.Close() |
|
| 69 |
+ fd := int(d.Fd()) |
|
| 70 |
+ buf := make([]byte, 4096) |
|
| 71 |
+ for {
|
|
| 72 |
+ nbytes, err := syscall.ReadDirent(fd, buf) |
|
| 73 |
+ if err != nil {
|
|
| 74 |
+ return err |
|
| 75 |
+ } |
|
| 76 |
+ if nbytes == 0 {
|
|
| 77 |
+ break |
|
| 78 |
+ } |
|
| 79 |
+ for off := 0; off < nbytes; {
|
|
| 80 |
+ ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off])) |
|
| 81 |
+ if stop := fn(ent); stop {
|
|
| 82 |
+ return nil |
|
| 83 |
+ } |
|
| 84 |
+ off += int(ent.Reclen) |
|
| 85 |
+ } |
|
| 86 |
+ } |
|
| 87 |
+ return nil |
|
| 88 |
+} |
| 0 | 89 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,91 @@ |
| 0 |
+// +build linux |
|
| 1 |
+ |
|
| 2 |
+package fsutils |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "io/ioutil" |
|
| 6 |
+ "os" |
|
| 7 |
+ "os/exec" |
|
| 8 |
+ "syscall" |
|
| 9 |
+ "testing" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func testSupportsDType(t *testing.T, expected bool, mkfsCommand string, mkfsArg ...string) {
|
|
| 13 |
+ // check whether mkfs is installed |
|
| 14 |
+ if _, err := exec.LookPath(mkfsCommand); err != nil {
|
|
| 15 |
+ t.Skipf("%s not installed: %v", mkfsCommand, err)
|
|
| 16 |
+ } |
|
| 17 |
+ |
|
| 18 |
+ // create a sparse image |
|
| 19 |
+ imageSize := int64(32 * 1024 * 1024) |
|
| 20 |
+ imageFile, err := ioutil.TempFile("", "fsutils-image")
|
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ t.Fatal(err) |
|
| 23 |
+ } |
|
| 24 |
+ imageFileName := imageFile.Name() |
|
| 25 |
+ defer os.Remove(imageFileName) |
|
| 26 |
+ if _, err = imageFile.Seek(imageSize-1, 0); err != nil {
|
|
| 27 |
+ t.Fatal(err) |
|
| 28 |
+ } |
|
| 29 |
+ if _, err = imageFile.Write([]byte{0}); err != nil {
|
|
| 30 |
+ t.Fatal(err) |
|
| 31 |
+ } |
|
| 32 |
+ if err = imageFile.Close(); err != nil {
|
|
| 33 |
+ t.Fatal(err) |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ // create a mountpoint |
|
| 37 |
+ mountpoint, err := ioutil.TempDir("", "fsutils-mountpoint")
|
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ t.Fatal(err) |
|
| 40 |
+ } |
|
| 41 |
+ defer os.RemoveAll(mountpoint) |
|
| 42 |
+ |
|
| 43 |
+ // format the image |
|
| 44 |
+ args := append(mkfsArg, imageFileName) |
|
| 45 |
+ t.Logf("Executing `%s %v`", mkfsCommand, args)
|
|
| 46 |
+ out, err := exec.Command(mkfsCommand, args...).CombinedOutput() |
|
| 47 |
+ if len(out) > 0 {
|
|
| 48 |
+ t.Log(string(out)) |
|
| 49 |
+ } |
|
| 50 |
+ if err != nil {
|
|
| 51 |
+ t.Fatal(err) |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ // loopback-mount the image. |
|
| 55 |
+ // for ease of setting up loopback device, we use os/exec rather than syscall.Mount |
|
| 56 |
+ out, err = exec.Command("mount", "-o", "loop", imageFileName, mountpoint).CombinedOutput()
|
|
| 57 |
+ if len(out) > 0 {
|
|
| 58 |
+ t.Log(string(out)) |
|
| 59 |
+ } |
|
| 60 |
+ if err != nil {
|
|
| 61 |
+ t.Skip("skipping the test because mount failed")
|
|
| 62 |
+ } |
|
| 63 |
+ defer func() {
|
|
| 64 |
+ if err := syscall.Unmount(mountpoint, 0); err != nil {
|
|
| 65 |
+ t.Fatal(err) |
|
| 66 |
+ } |
|
| 67 |
+ }() |
|
| 68 |
+ |
|
| 69 |
+ // check whether it supports d_type |
|
| 70 |
+ result, err := SupportsDType(mountpoint) |
|
| 71 |
+ if err != nil {
|
|
| 72 |
+ t.Fatal(err) |
|
| 73 |
+ } |
|
| 74 |
+ t.Logf("Supports d_type: %v", result)
|
|
| 75 |
+ if result != expected {
|
|
| 76 |
+ t.Fatalf("expected %v, got %v", expected, result)
|
|
| 77 |
+ } |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+func TestSupportsDTypeWithFType0XFS(t *testing.T) {
|
|
| 81 |
+ testSupportsDType(t, false, "mkfs.xfs", "-m", "crc=0", "-n", "ftype=0") |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func TestSupportsDTypeWithFType1XFS(t *testing.T) {
|
|
| 85 |
+ testSupportsDType(t, true, "mkfs.xfs", "-m", "crc=0", "-n", "ftype=1") |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+func TestSupportsDTypeWithExt4(t *testing.T) {
|
|
| 89 |
+ testSupportsDType(t, true, "mkfs.ext4") |
|
| 90 |
+} |