cmd/buildlet: make handleRemoveAll change permissions if needed
Fixes golang/go#32065
Change-Id: Ib2f6be05159e387374bc0255cb681c3147553973
Reviewed-on: https://go-review.googlesource.com/c/build/+/177457
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/buildlet/buildlet.go b/cmd/buildlet/buildlet.go
index 0c4a831..734334b 100644
--- a/cmd/buildlet/buildlet.go
+++ b/cmd/buildlet/buildlet.go
@@ -76,7 +76,8 @@
// 21: GO_BUILDER_SET_GOPROXY=coordinator support
// 22: TrimSpace the reverse buildlet's gobuildkey contents
// 23: revdial v2
-const buildletVersion = 23
+// 24: removeAllIncludingReadonly
+const buildletVersion = 24
func defaultListenAddr() string {
if runtime.GOOS == "darwin" {
@@ -1260,7 +1261,7 @@
for _, p := range paths {
log.Printf("Removing %s", p)
fullDir := filepath.Join(*workDir, filepath.FromSlash(p))
- err := os.RemoveAll(fullDir)
+ err := removeAllIncludingReadonly(fullDir)
if p == "." && err != nil {
// If workDir is a mountpoint and/or contains a binary
// using it, we can get a "Device or resource busy" error.
@@ -1863,6 +1864,30 @@
}
}
+// removeAllIncludingReadonly is like os.RemoveAll except that it'll
+// also try to change permissions to work around permission errors
+// when deleting.
+func removeAllIncludingReadonly(dir string) error {
+ err := os.RemoveAll(dir)
+ if err == nil || !os.IsPermission(err) ||
+ runtime.GOOS == "windows" || // different filesystem permission model; also our windows builders our emphermal single-use VMs anyway
+ runtime.GOOS == "plan9" { // untested, different enough to conservatively skip code below
+ return err
+ }
+ // Make a best effort (ignoring errors) attempt to make all
+ // files and directories writable before we try to delete them
+ // all again.
+ filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ const ownerWritable = 0200
+ if err != nil || fi.Mode().Perm()&ownerWritable != 0 {
+ return nil
+ }
+ os.Chmod(path, fi.Mode().Perm()|ownerWritable)
+ return nil
+ })
+ return os.RemoveAll(dir)
+}
+
var (
androidEmuDead = make(chan error) // closed on death
androidEmuErr error // set prior to channel close