浏览代码

idtools.MkdirAs*: error out if dir exists as file

Standard golang's `os.MkdirAll()` function returns "not a directory" error
in case a directory to be created already exists but is not a directory
(e.g. a file). Our own `idtools.MkdirAs*()` functions do not replicate
the behavior.

This is a bug since all `Mkdir()`-like functions are expected to ensure
the required directory exists and is indeed a directory, and return an
error otherwise.

As the code is using our in-house `system.Stat()` call returning a type
which is incompatible with that of golang's `os.Stat()`, I had to amend
the `system` package with `IsDir()`.

A test case is also provided.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Kir Kolyshkin 7 年之前
父节点
当前提交
2aa13f86f0
共有 3 个文件被更改,包括 23 次插入0 次删除
  1. 4 0
      pkg/idtools/idtools_unix.go
  2. 14 0
      pkg/idtools/idtools_unix_test.go
  3. 5 0
      pkg/system/stat_unix.go

+ 4 - 0
pkg/idtools/idtools_unix.go

@@ -10,6 +10,7 @@ import (
 	"path/filepath"
 	"strings"
 	"sync"
+	"syscall"
 
 	"github.com/docker/docker/pkg/system"
 	"github.com/opencontainers/runc/libcontainer/user"
@@ -29,6 +30,9 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
 
 	stat, err := system.Stat(path)
 	if err == nil {
+		if !stat.IsDir() {
+			return &os.PathError{"mkdir", path, syscall.ENOTDIR}
+		}
 		if !chownExisting {
 			return nil
 		}

+ 14 - 0
pkg/idtools/idtools_unix_test.go

@@ -378,6 +378,20 @@ func TestLookupUserAndGroupThatDoesNotExist(t *testing.T) {
 	assert.Error(t, err)
 }
 
+// TestMkdirIsNotDir checks that mkdirAs() function (used by MkdirAll...)
+// returns a correct error in case a directory which it is about to create
+// already exists but is a file (rather than a directory).
+func TestMkdirIsNotDir(t *testing.T) {
+	file, err := ioutil.TempFile("", t.Name())
+	if err != nil {
+		t.Fatalf("Couldn't create temp dir: %v", err)
+	}
+	defer os.Remove(file.Name())
+
+	err = mkdirAs(file.Name(), 0755, 0, 0, false, false)
+	assert.EqualError(t, err, "mkdir "+file.Name()+": not a directory")
+}
+
 func RequiresRoot(t *testing.T) {
 	skip.IfCondition(t, os.Getuid() != 0, "skipping test that requires root")
 }

+ 5 - 0
pkg/system/stat_unix.go

@@ -47,6 +47,11 @@ func (s StatT) Mtim() syscall.Timespec {
 	return s.mtim
 }
 
+// IsDir reports whether s describes a directory.
+func (s StatT) IsDir() bool {
+	return s.mode&syscall.S_IFDIR != 0
+}
+
 // Stat takes a path to a file and returns
 // a system.StatT type pertaining to that file.
 //