diff --git a/user/idtools_test.go b/user/idtools_test.go new file mode 100644 index 00000000..5e47950f --- /dev/null +++ b/user/idtools_test.go @@ -0,0 +1,60 @@ +package user_test + +import ( + "errors" + "os" + "path/filepath" + "syscall" + "testing" + + "github.com/moby/sys/user" +) + +// TestMkdirAndChownNonDir checks that MkdirAndChown 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 TestMkdirAndChownNonDir(t *testing.T) { + file, err := os.CreateTemp(t.TempDir(), t.Name()) + if err != nil { + t.Fatalf("Couldn't create temp dir: %v", err) + } + _ = file.Close() + + expected := syscall.ENOTDIR + err = user.MkdirAndChown(file.Name(), 0o755, 0, 0) + if !errors.Is(err, expected) { + t.Fatalf("expected error: %v, got: %v", expected, err) + } +} + +// TestMkdirAndChownExistingDir checks that MkdirAndChown does not return an error +// if the target directory already exists. +func TestMkdirAndChownExistingDir(t *testing.T) { + dirName := t.TempDir() + err := user.MkdirAndChown(dirName, 0, 0, 0, user.WithOnlyNew) + if err != nil { + t.Fatal(err) + } +} + +// TestMkdirAndChownMissingParent checks that MkdirAndChown errors if the parent +// directory doesn't exist and doesn't create any of the parent directories. +func TestMkdirAndChownMissingParent(t *testing.T) { + dirName := t.TempDir() + if err := user.MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0, 0, 0, user.WithOnlyNew); err == nil { + t.Fatal("Trying to create a directory with Mkdir where the parent doesn't exist should have failed") + } + + _, err := os.Stat(filepath.Join(dirName, "usr")) + if err == nil || !os.IsNotExist(err) { + t.Fatal("parent directory should not have been created", err) + } + _, err = os.Stat(filepath.Join(dirName, "usr", "bin")) + if err == nil || !os.IsNotExist(err) { + t.Fatal("parent directory should not have been created", err) + } + _, err = os.Stat(filepath.Join(dirName, "usr", "bin", "subdir")) + if err == nil || !os.IsNotExist(err) { + t.Fatal("directory should not have been created", err) + } +} diff --git a/user/idtools_unix_test.go b/user/idtools_unix_test.go index 5e0bcce5..5482aa4e 100644 --- a/user/idtools_unix_test.go +++ b/user/idtools_unix_test.go @@ -376,22 +376,6 @@ func TestToContainer(t *testing.T) { } } -// TestMkdirIsNotDir checks that MkdirAndChown 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 := os.CreateTemp(t.TempDir(), t.Name()) - if err != nil { - t.Fatalf("Couldn't create temp dir: %v", err) - } - - expected := "mkdir " + file.Name() + ": not a directory" - err = MkdirAndChown(file.Name(), 0o755, 0, 0) - if err == nil || err.Error() != expected { - t.Fatalf("expected error: %v, got: %v", expected, err) - } -} - func requiresRoot(t *testing.T) { if os.Getuid() != 0 { t.Skip("skipping test that requires root") diff --git a/user/idtools_windows.go b/user/idtools_windows.go index d83ec902..3973d559 100644 --- a/user/idtools_windows.go +++ b/user/idtools_windows.go @@ -2,12 +2,25 @@ package user import ( "os" + "syscall" ) -// This is currently a wrapper around [os.MkdirAll] since currently -// permissions aren't set through this path, the identity isn't utilized. -// Ownership is handled elsewhere, but in the future could be support here -// too. -func mkdirAs(path string, _ os.FileMode, _, _ int, _ bool, _ ...MkdirOpt) error { - return os.MkdirAll(path, 0) +// mkdirAs creates path, optionally creating any missing parent directories. +// +// On Windows this is currently a thin wrapper around os.Mkdir and +// os.MkdirAll. Unlike the Unix implementation, ownership and permission +// bits are not applied. +func mkdirAs(path string, _ os.FileMode, _, _ int, mkAll bool, _ ...MkdirOpt) error { + if mkAll { + return os.MkdirAll(path, 0) + } + stat, err := os.Stat(path) + if err == nil { + if !stat.IsDir() { + return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} + } + return nil + } + + return os.Mkdir(path, 0) }