x/tools/internal/fastwalk: fixes "interrupted system call" error

According to https://golang.org/doc/go1.14#runtime
A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS
systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases.

This causes syscall.Open and syscall.ReadDirent sometimes fail with EINTR errors.
We need to retry in this case.

Fixes golang/go#44478

Change-Id: I0b0291471e47e8682fac791e1ed024b5a42a56f8
Reviewed-on: https://go-review.googlesource.com/c/tools/+/294730
Reviewed-by: Heschi Kreinick <heschi@google.com>
Trust: Heschi Kreinick <heschi@google.com>
Trust: Peter Weinberger <pjw@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/internal/fastwalk/fastwalk_unix.go b/internal/fastwalk/fastwalk_unix.go
index e4edb5c..58bd878 100644
--- a/internal/fastwalk/fastwalk_unix.go
+++ b/internal/fastwalk/fastwalk_unix.go
@@ -22,7 +22,7 @@
 const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
 
 func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
-	fd, err := syscall.Open(dirName, 0, 0)
+	fd, err := open(dirName, 0, 0)
 	if err != nil {
 		return &os.PathError{Op: "open", Path: dirName, Err: err}
 	}
@@ -36,7 +36,7 @@
 	for {
 		if bufp >= nbuf {
 			bufp = 0
-			nbuf, err = syscall.ReadDirent(fd, buf)
+			nbuf, err = readDirent(fd, buf)
 			if err != nil {
 				return os.NewSyscallError("readdirent", err)
 			}
@@ -127,3 +127,27 @@
 	}
 	return
 }
+
+// According to https://golang.org/doc/go1.14#runtime
+// A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS
+// systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases.
+//
+// This causes syscall.Open and syscall.ReadDirent sometimes fail with EINTR errors.
+// We need to retry in this case.
+func open(path string, mode int, perm uint32) (fd int, err error) {
+	for {
+		fd, err := syscall.Open(path, mode, perm)
+		if err != syscall.EINTR {
+			return fd, err
+		}
+	}
+}
+
+func readDirent(fd int, buf []byte) (n int, err error) {
+	for {
+		nbuf, err := syscall.ReadDirent(fd, buf)
+		if err != syscall.EINTR {
+			return nbuf, err
+		}
+	}
+}