Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/os/root_noopenat.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ func rootMkdir(r *Root, name string, perm FileMode) error {
if err := checkPathEscapes(r, name); err != nil {
return &PathError{Op: "mkdirat", Path: name, Err: err}
}
if name == "" {
return &PathError{Op: "mkdirat", Path: name, Err: syscall.ENOENT}
}
if err := Mkdir(joinPath(r.root.name, name), perm); err != nil {
return &PathError{Op: "mkdirat", Path: name, Err: underlyingError(err)}
}
Expand All @@ -158,6 +161,9 @@ func rootMkdirAll(r *Root, name string, perm FileMode) error {
if err := checkPathEscapes(r, name); err == errPathEscapes {
return &PathError{Op: "mkdirat", Path: name, Err: err}
}
if name == "" {
return &PathError{Op: "mkdirat", Path: name, Err: syscall.ENOENT}
}
prefix := r.root.name + string(PathSeparator)
if err := MkdirAll(prefix+name, perm); err != nil {
if pe, ok := err.(*PathError); ok {
Expand Down
19 changes: 15 additions & 4 deletions src/os/root_openat.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func rootMkdir(r *Root, name string, perm FileMode) error {
// (POSIX.1-2024 4.16 says that the trailing slash should cause
// resolution to follow the symlink, but we're trying to match
// platform semantics, not implement POSIX.)
flags = doInRootNoHandleTerminalSlash
flags |= doInRootNoHandleTerminalSlash
}
_, err := doInRoot(r, name, flags, nil, func(parent sysfdType, name string, endsInSlash bool) (struct{}, error) {
return struct{}{}, mkdirat(parent, name, perm)
Expand Down Expand Up @@ -167,6 +167,12 @@ func rootMkdirAll(r *Root, fullname string, perm FileMode) error {
fi, e := r.Stat(fullname)
if e == nil && fi.Mode().IsDir() {
err = nil
} else if e == nil {
err = syscall.ENOTDIR
} else if !IsNotExist(e) {
// EPERM, ELOOP, etc.,
// probably more useful than EEXIST.
err = e
}
}
}
Expand All @@ -177,7 +183,12 @@ func rootMkdirAll(r *Root, fullname string, perm FileMode) error {
}
return struct{}{}, &PathError{Op: "mkdirat", Err: err}
}
_, err := doInRoot(r, fullname, 0, openDirFunc, openLastComponentFunc)
flags := uint(doInRootCreatingDirectory)
switch runtime.GOOS {
case "linux", "windows":
flags |= doInRootNoHandleTerminalSlash // see rootMkdir
}
_, err := doInRoot(r, fullname, flags, openDirFunc, openLastComponentFunc)
if err != nil {
if _, ok := err.(*PathError); !ok {
err = &PathError{Op: "mkdirat", Path: fullname, Err: err}
Expand Down Expand Up @@ -232,7 +243,7 @@ func rootRename(r *Root, oldname, newname string) error {
_, err := doInRoot(r, oldname, 0, nil, func(oldparent sysfdType, oldname string, oldEndsInSlash bool) (struct{}, error) {
flags := uint(doInRootCreatingDirectory)
if runtime.GOOS == "windows" {
flags = doInRootNoHandleTerminalSlash
flags |= doInRootNoHandleTerminalSlash
}
_, err := doInRoot(r, newname, flags, nil, func(newparent sysfdType, newname string, newEndsInSlash bool) (struct{}, error) {
if runtime.GOOS != "windows" && newEndsInSlash {
Expand Down Expand Up @@ -268,7 +279,7 @@ func rootLink(r *Root, oldname, newname string) error {
flags := uint(0)
if runtime.GOOS == "windows" {
// Windows doesn't pay attention to trailing slashes in the link target.
flags = doInRootNoHandleTerminalSlash
flags |= doInRootNoHandleTerminalSlash
}
_, err := doInRoot(r, newname, flags, nil, func(newparent sysfdType, newname string, newEndsInSlash bool) (struct{}, error) {
return struct{}{}, linkat(oldparent, oldname, newparent, newname)
Expand Down
57 changes: 56 additions & 1 deletion src/os/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,11 @@ func TestRootMkdir(t *testing.T) {

func TestRootMkdirAll(t *testing.T) {
for _, test := range rootTestCases {
if test.name == "directory does not exist" {
// Test expects error, mkdirall creates the missing directory.
// TestRootMultiMkdirAll covers this case better anyway, just skip.
continue
}
test.run(t, func(t *testing.T, target string, root *os.Root) {
wantError := test.wantError
if test.ltarget != "" {
Expand All @@ -593,7 +598,7 @@ func TestRootMkdirAll(t *testing.T) {
wantError = true
}

err := root.Mkdir(test.open, 0o777)
err := root.MkdirAll(test.open, 0o777)
if errEndsTest(t, err, wantError, "root.MkdirAll(%q)", test.open) {
return
}
Expand Down Expand Up @@ -3133,6 +3138,52 @@ func TestRootMultiMkdir(t *testing.T) {
})
}

func TestRootMultiMkdirAllShallow(t *testing.T) {
runRootMultiTest(t, func(t *testing.T, test *rootMultiTest) (string, error) {
return testRootMultiMkdirAll(t, test, test.targetPath)
})
}

func TestRootMultiMkdirAllDeep(t *testing.T) {
runRootMultiTest(t, func(t *testing.T, test *rootMultiTest) (string, error) {
targetPath := test.targetPath
if len(targetPath) > 0 && os.IsPathSeparator(targetPath[len(targetPath)-1]) {
targetPath += "a/b/"
} else {
targetPath += "/a/b"
}
return testRootMultiMkdirAll(t, test, targetPath)
})
}

func testRootMultiMkdirAll(t *testing.T, test *rootMultiTest, targetPath string) (string, error) {
var mkdirAll = os.MkdirAll
if test.root != nil {
mkdirAll = test.root.MkdirAll
}

test.setOp("MkdirAll(%q, 0o777)", targetPath)
gotErr := mkdirAll(targetPath, 0o777)

switch {
case test.root != nil && test.target.lescapes():
// "mkdir ../target", or equivalent escaping path.
test.wantError(t, gotErr, os.ErrPathEscapes)
case test.root != nil && test.target.escapes():
// "mkdir ../target", or equivalent escaping path.
test.wantError(t, gotErr, errAny)
return "", errSkipRootConsistencyCheck
case test.root != nil && test.target.kind == testFileSymlink && test.target.target.kind == testFileAbsent && targetPath != test.targetPath:
// A minor inconsistency between Root.MkdirAll and os.MkdirAll:
// When an intermediate component of the tree being constructed is a
// dangling symlink, Root.MkdirAll will follow the symlink and create
// its target directory, while os.MkdirAll will fail with an error.
return "", errSkipRootConsistencyCheck
default:
}
return "", gotErr
}

func TestRootMultiRename(t *testing.T) {
if runtime.GOOS == "wasip1" {
switch os.Getenv("GOWASIRUNTIME") {
Expand Down Expand Up @@ -3228,6 +3279,10 @@ func TestRootMultiReadFile(t *testing.T) {
case runtime.GOOS == "plan9":
// Plan9 lets you read from directories.
// Just rely on consistency checks.
case runtime.GOOS == "netbsd":
// See https://go.dev/issue/80322:
// NetBSD builder appears to be succeeding on read-from-dir as well.
return "", gotErr
case test.target.finalKind() == testFileDir:
test.wantError(t, gotErr, errAny)
case test.target.anySlashSuffix():
Expand Down
Loading