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] == '.' {