internal/fastwalk: don't cross Dirent.Reclen boundry while looking for NULL in parseDirEnt

Crossing Dirent.Reclen boundry was manifested in golang/go#28131 as garbaled filenames,
when Dirent.Name was not NULL terminated on FreeBSD due to a bug (parseDirEnt would find a NULL
in the following Dirent's Fileno/Reclen fields).

Only search for NULL on linux, when the Namlen field is available use it directly instead.

Updates golang/go#28131

Change-Id: I64090576c8bad2bd246d1561432bf73d5caee2a9
Reviewed-on: https://go-review.googlesource.com/c/141801
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/internal/fastwalk/fastwalk_dirent_namlen_bsd.go b/internal/fastwalk/fastwalk_dirent_namlen_bsd.go
new file mode 100644
index 0000000..a3b26a7
--- /dev/null
+++ b/internal/fastwalk/fastwalk_dirent_namlen_bsd.go
@@ -0,0 +1,13 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd openbsd netbsd
+
+package fastwalk
+
+import "syscall"
+
+func direntNamlen(dirent *syscall.Dirent) uint64 {
+	return uint64(dirent.Namlen)
+}
diff --git a/internal/fastwalk/fastwalk_dirent_namlen_linux.go b/internal/fastwalk/fastwalk_dirent_namlen_linux.go
new file mode 100644
index 0000000..61896ff
--- /dev/null
+++ b/internal/fastwalk/fastwalk_dirent_namlen_linux.go
@@ -0,0 +1,24 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build !appengine
+
+package fastwalk
+
+import (
+	"bytes"
+	"syscall"
+	"unsafe"
+)
+
+func direntNamlen(dirent *syscall.Dirent) uint64 {
+	const fixedHdr = uint16(unsafe.Offsetof(syscall.Dirent{}.Name))
+	nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
+	nameLen := bytes.IndexByte(nameBuf[:dirent.Reclen-fixedHdr], 0)
+	if nameLen < 0 {
+		panic("failed to find terminating 0 byte in dirent")
+	}
+	return uint64(nameLen)
+}
diff --git a/internal/fastwalk/fastwalk_unix.go b/internal/fastwalk/fastwalk_unix.go
index e541c57..3369b1a 100644
--- a/internal/fastwalk/fastwalk_unix.go
+++ b/internal/fastwalk/fastwalk_unix.go
@@ -8,7 +8,6 @@
 package fastwalk
 
 import (
-	"bytes"
 	"fmt"
 	"os"
 	"syscall"
@@ -114,10 +113,7 @@
 	}
 
 	nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
-	nameLen := bytes.IndexByte(nameBuf[:], 0)
-	if nameLen < 0 {
-		panic("failed to find terminating 0 byte in dirent")
-	}
+	nameLen := direntNamlen(dirent)
 
 	// Special cases for common things:
 	if nameLen == 1 && nameBuf[0] == '.' {