Skip to content

Commit ff04803

Browse files
oioojbradfitz
authored andcommitted
[release-branch.go1.12] os: consistently return PathError from RemoveAll
Fixes golang#30859 Updates golang#30491 Change-Id: If4070e5d39d8649643d7e90f6f3eb499642e25ab Reviewed-on: https://go-review.googlesource.com/c/go/+/164720 Run-TryBot: Baokun Lee <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]> (cherry picked from commit d039e12) Reviewed-on: https://go-review.googlesource.com/c/go/+/167739 Run-TryBot: Brad Fitzpatrick <[email protected]> Reviewed-by: Baokun Lee <[email protected]>
1 parent c1d8d9d commit ff04803

File tree

4 files changed

+39
-17
lines changed

4 files changed

+39
-17
lines changed

src/os/path.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ func MkdirAll(path string, perm FileMode) error {
6262
// It removes everything it can but returns the first error
6363
// it encounters. If the path does not exist, RemoveAll
6464
// returns nil (no error).
65+
// If there is an error, it will be of type *PathError.
6566
func RemoveAll(path string) error {
6667
return removeAll(path)
6768
}

src/os/path_unix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func splitPath(path string) (string, string) {
5151
// Remove leading directory path
5252
for i--; i >= 0; i-- {
5353
if path[i] == '/' {
54-
dirname = path[:i+1]
54+
dirname = path[:i]
5555
basename = path[i+1:]
5656
break
5757
}

src/os/removeall_at.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,20 @@ func removeAll(path string) error {
4646
}
4747
defer parent.Close()
4848

49-
return removeAllFrom(parent, base)
49+
if err := removeAllFrom(parent, base); err != nil {
50+
if pathErr, ok := err.(*PathError); ok {
51+
pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path
52+
err = pathErr
53+
}
54+
return err
55+
}
56+
return nil
5057
}
5158

52-
func removeAllFrom(parent *File, path string) error {
59+
func removeAllFrom(parent *File, base string) error {
5360
parentFd := int(parent.Fd())
5461
// Simple case: if Unlink (aka remove) works, we're done.
55-
err := unix.Unlinkat(parentFd, path, 0)
62+
err := unix.Unlinkat(parentFd, base, 0)
5663
if err == nil || IsNotExist(err) {
5764
return nil
5865
}
@@ -64,21 +71,21 @@ func removeAllFrom(parent *File, path string) error {
6471
// whose contents need to be removed.
6572
// Otherwise just return the error.
6673
if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
67-
return err
74+
return &PathError{"unlinkat", base, err}
6875
}
6976

7077
// Is this a directory we need to recurse into?
7178
var statInfo syscall.Stat_t
72-
statErr := unix.Fstatat(parentFd, path, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
79+
statErr := unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
7380
if statErr != nil {
7481
if IsNotExist(statErr) {
7582
return nil
7683
}
77-
return statErr
84+
return &PathError{"fstatat", base, statErr}
7885
}
7986
if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
80-
// Not a directory; return the error from the Remove.
81-
return err
87+
// Not a directory; return the error from the unix.Unlinkat.
88+
return &PathError{"unlinkat", base, err}
8289
}
8390

8491
// Remove the directory's entries.
@@ -87,12 +94,12 @@ func removeAllFrom(parent *File, path string) error {
8794
const request = 1024
8895

8996
// Open the directory to recurse into
90-
file, err := openFdAt(parentFd, path)
97+
file, err := openFdAt(parentFd, base)
9198
if err != nil {
9299
if IsNotExist(err) {
93100
return nil
94101
}
95-
recurseErr = err
102+
recurseErr = &PathError{"openfdat", base, err}
96103
break
97104
}
98105

@@ -103,12 +110,15 @@ func removeAllFrom(parent *File, path string) error {
103110
if IsNotExist(readErr) {
104111
return nil
105112
}
106-
return readErr
113+
return &PathError{"readdirnames", base, readErr}
107114
}
108115

109116
for _, name := range names {
110117
err := removeAllFrom(file, name)
111118
if err != nil {
119+
if pathErr, ok := err.(*PathError); ok {
120+
pathErr.Path = base + string(PathSeparator) + pathErr.Path
121+
}
112122
recurseErr = err
113123
}
114124
}
@@ -127,15 +137,15 @@ func removeAllFrom(parent *File, path string) error {
127137
}
128138

129139
// Remove the directory itself.
130-
unlinkError := unix.Unlinkat(parentFd, path, unix.AT_REMOVEDIR)
140+
unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
131141
if unlinkError == nil || IsNotExist(unlinkError) {
132142
return nil
133143
}
134144

135145
if recurseErr != nil {
136146
return recurseErr
137147
}
138-
return unlinkError
148+
return &PathError{"unlinkat", base, unlinkError}
139149
}
140150

141151
// openFdAt opens path relative to the directory in fd.
@@ -157,7 +167,7 @@ func openFdAt(dirfd int, name string) (*File, error) {
157167
continue
158168
}
159169

160-
return nil, &PathError{"openat", name, e}
170+
return nil, e
161171
}
162172

163173
if !supportsCloseOnExec {

src/os/removeall_test.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ func TestRemoveReadOnlyDir(t *testing.T) {
294294
}
295295

296296
// Issue #29983.
297-
func TestRemoveAllButReadOnly(t *testing.T) {
297+
func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
298298
switch runtime.GOOS {
299299
case "nacl", "js", "windows":
300300
t.Skipf("skipping test on %s", runtime.GOOS)
@@ -355,10 +355,21 @@ func TestRemoveAllButReadOnly(t *testing.T) {
355355
defer Chmod(d, 0777)
356356
}
357357

358-
if err := RemoveAll(tempDir); err == nil {
358+
err = RemoveAll(tempDir)
359+
if err == nil {
359360
t.Fatal("RemoveAll succeeded unexpectedly")
360361
}
361362

363+
// The error should be of type *PathError.
364+
// see issue 30491 for details.
365+
if pathErr, ok := err.(*PathError); ok {
366+
if g, w := pathErr.Path, filepath.Join(tempDir, "b", "y"); g != w {
367+
t.Errorf("got %q, expected pathErr.path %q", g, w)
368+
}
369+
} else {
370+
t.Errorf("got %T, expected *os.PathError", err)
371+
}
372+
362373
for _, dir := range dirs {
363374
_, err := Stat(filepath.Join(tempDir, dir))
364375
if inReadonly(dir) {

0 commit comments

Comments
 (0)