Skip to content

Commit 56c9f8e

Browse files
os: treat EACCES as a permission error in RemoveAll
Fixes #29983 Change-Id: I24077bde991e621c23d00973b2a77bb3a18e4ae7 Reviewed-on: https://go-review.googlesource.com/c/160180 Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Damien Neil <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent ea27cd3 commit 56c9f8e

File tree

2 files changed

+91
-6
lines changed

2 files changed

+91
-6
lines changed

src/os/removeall_at.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,13 @@ func removeAllFrom(parent *File, path string) error {
5757
return nil
5858
}
5959

60-
// If not a "is directory" error, we have a problem
61-
if err != syscall.EISDIR && err != syscall.EPERM {
60+
// EISDIR means that we have a directory, and we need to
61+
// remove its contents.
62+
// EPERM or EACCES means that we don't have write permission on
63+
// the parent directory, but this entry might still be a directory
64+
// whose contents need to be removed.
65+
// Otherwise just return the error.
66+
if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
6267
return err
6368
}
6469

@@ -69,11 +74,11 @@ func removeAllFrom(parent *File, path string) error {
6974
return statErr
7075
}
7176
if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
72-
// Not a directory; return the error from the Remove
77+
// Not a directory; return the error from the Remove.
7378
return err
7479
}
7580

76-
// Remove the directory's entries
81+
// Remove the directory's entries.
7782
var recurseErr error
7883
for {
7984
const request = 1024
@@ -88,7 +93,7 @@ func removeAllFrom(parent *File, path string) error {
8893
}
8994

9095
names, readErr := file.Readdirnames(request)
91-
// Errors other than EOF should stop us from continuing
96+
// Errors other than EOF should stop us from continuing.
9297
if readErr != nil && readErr != io.EOF {
9398
file.Close()
9499
if IsNotExist(readErr) {
@@ -117,7 +122,7 @@ func removeAllFrom(parent *File, path string) error {
117122
}
118123
}
119124

120-
// Remove the directory itself
125+
// Remove the directory itself.
121126
unlinkError := unix.Unlinkat(parentFd, path, unix.AT_REMOVEDIR)
122127
if unlinkError == nil || IsNotExist(unlinkError) {
123128
return nil

src/os/removeall_test.go

+80
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,83 @@ func TestRemoveReadOnlyDir(t *testing.T) {
292292
t.Error("subdirectory was not removed")
293293
}
294294
}
295+
296+
// Issue #29983.
297+
func TestRemoveAllButReadOnly(t *testing.T) {
298+
switch runtime.GOOS {
299+
case "nacl", "js", "windows":
300+
t.Skipf("skipping test on %s", runtime.GOOS)
301+
}
302+
303+
if Getuid() == 0 {
304+
t.Skip("skipping test when running as root")
305+
}
306+
307+
t.Parallel()
308+
309+
tempDir, err := ioutil.TempDir("", "TestRemoveAllButReadOnly-")
310+
if err != nil {
311+
t.Fatal(err)
312+
}
313+
defer RemoveAll(tempDir)
314+
315+
dirs := []string{
316+
"a",
317+
"a/x",
318+
"a/x/1",
319+
"b",
320+
"b/y",
321+
"b/y/2",
322+
"c",
323+
"c/z",
324+
"c/z/3",
325+
}
326+
readonly := []string{
327+
"b",
328+
}
329+
inReadonly := func(d string) bool {
330+
for _, ro := range readonly {
331+
if d == ro {
332+
return true
333+
}
334+
dd, _ := filepath.Split(d)
335+
if filepath.Clean(dd) == ro {
336+
return true
337+
}
338+
}
339+
return false
340+
}
341+
342+
for _, dir := range dirs {
343+
if err := Mkdir(filepath.Join(tempDir, dir), 0777); err != nil {
344+
t.Fatal(err)
345+
}
346+
}
347+
for _, dir := range readonly {
348+
d := filepath.Join(tempDir, dir)
349+
if err := Chmod(d, 0555); err != nil {
350+
t.Fatal(err)
351+
}
352+
353+
// Defer changing the mode back so that the deferred
354+
// RemoveAll(tempDir) can succeed.
355+
defer Chmod(d, 0777)
356+
}
357+
358+
if err := RemoveAll(tempDir); err == nil {
359+
t.Fatal("RemoveAll succeeded unexpectedly")
360+
}
361+
362+
for _, dir := range dirs {
363+
_, err := Stat(filepath.Join(tempDir, dir))
364+
if inReadonly(dir) {
365+
if err != nil {
366+
t.Errorf("file %q was deleted but should still exist", dir)
367+
}
368+
} else {
369+
if err == nil {
370+
t.Errorf("file %q still exists but should have been deleted", dir)
371+
}
372+
}
373+
}
374+
}

0 commit comments

Comments
 (0)