}
defer parent.Close()
- return removeAllFrom(parent, base)
+ if err := removeAllFrom(parent, base); err != nil {
+ if pathErr, ok := err.(*PathError); ok {
+ pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path
+ err = pathErr
+ }
+ return err
+ }
+ return nil
}
-func removeAllFrom(parent *File, path string) error {
+func removeAllFrom(parent *File, base string) error {
parentFd := int(parent.Fd())
// Simple case: if Unlink (aka remove) works, we're done.
- err := unix.Unlinkat(parentFd, path, 0)
+ err := unix.Unlinkat(parentFd, base, 0)
if err == nil || IsNotExist(err) {
return nil
}
// whose contents need to be removed.
// Otherwise just return the error.
if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
- return err
+ return &PathError{"unlinkat", base, err}
}
// Is this a directory we need to recurse into?
var statInfo syscall.Stat_t
- statErr := unix.Fstatat(parentFd, path, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
+ statErr := unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
if statErr != nil {
if IsNotExist(statErr) {
return nil
}
- return statErr
+ return &PathError{"fstatat", base, statErr}
}
if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
- // Not a directory; return the error from the Remove.
- return err
+ // Not a directory; return the error from the unix.Unlinkat.
+ return &PathError{"unlinkat", base, err}
}
// Remove the directory's entries.
const request = 1024
// Open the directory to recurse into
- file, err := openFdAt(parentFd, path)
+ file, err := openFdAt(parentFd, base)
if err != nil {
if IsNotExist(err) {
return nil
}
- recurseErr = err
+ recurseErr = &PathError{"openfdat", base, err}
break
}
if IsNotExist(readErr) {
return nil
}
- return readErr
+ return &PathError{"readdirnames", base, readErr}
}
for _, name := range names {
err := removeAllFrom(file, name)
if err != nil {
+ if pathErr, ok := err.(*PathError); ok {
+ pathErr.Path = base + string(PathSeparator) + pathErr.Path
+ }
recurseErr = err
}
}
}
// Remove the directory itself.
- unlinkError := unix.Unlinkat(parentFd, path, unix.AT_REMOVEDIR)
+ unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
if unlinkError == nil || IsNotExist(unlinkError) {
return nil
}
if recurseErr != nil {
return recurseErr
}
- return unlinkError
+ return &PathError{"unlinkat", base, unlinkError}
}
// openFdAt opens path relative to the directory in fd.
continue
}
- return nil, &PathError{"openat", name, e}
+ return nil, e
}
if !supportsCloseOnExec {
}
// Issue #29983.
-func TestRemoveAllButReadOnly(t *testing.T) {
+func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
switch runtime.GOOS {
case "nacl", "js", "windows":
t.Skipf("skipping test on %s", runtime.GOOS)
defer Chmod(d, 0777)
}
- if err := RemoveAll(tempDir); err == nil {
+ err = RemoveAll(tempDir)
+ if err == nil {
t.Fatal("RemoveAll succeeded unexpectedly")
}
+ // The error should be of type *PathError.
+ // see issue 30491 for details.
+ if pathErr, ok := err.(*PathError); ok {
+ if g, w := pathErr.Path, filepath.Join(tempDir, "b", "y"); g != w {
+ t.Errorf("got %q, expected pathErr.path %q", g, w)
+ }
+ } else {
+ t.Errorf("got %T, expected *os.PathError", err)
+ }
+
for _, dir := range dirs {
_, err := Stat(filepath.Join(tempDir, dir))
if inReadonly(dir) {