Skip to content

Commit 06cff11

Browse files
committed
syscall: use openat instead of dup to make a really new file descriptor
Update #31269 Change-Id: I0e7184420055b8dfd23688dab9f9d8cba1fa2485 Reviewed-on: https://go-review.googlesource.com/c/go/+/170892 Run-TryBot: Keith Randall <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 68d89bb commit 06cff11

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

src/syscall/getdirentries_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin freebsd netbsd openbsd
6+
7+
package syscall_test
8+
9+
import (
10+
"fmt"
11+
"io/ioutil"
12+
"os"
13+
"path/filepath"
14+
"sort"
15+
"strings"
16+
"syscall"
17+
"testing"
18+
"unsafe"
19+
)
20+
21+
func TestGetdirentries(t *testing.T) {
22+
for _, count := range []int{10, 1000} {
23+
t.Run(fmt.Sprintf("n=%d", count), func(t *testing.T) {
24+
testGetdirentries(t, count)
25+
})
26+
}
27+
}
28+
func testGetdirentries(t *testing.T, count int) {
29+
d, err := ioutil.TempDir("", "getdirentries-test")
30+
if err != nil {
31+
t.Fatalf("Tempdir: %v", err)
32+
}
33+
defer os.RemoveAll(d)
34+
var names []string
35+
for i := 0; i < count; i++ {
36+
names = append(names, fmt.Sprintf("file%03d", i))
37+
}
38+
39+
// Make files in the temp directory
40+
for _, name := range names {
41+
err := ioutil.WriteFile(filepath.Join(d, name), []byte("data"), 0)
42+
if err != nil {
43+
t.Fatalf("WriteFile: %v", err)
44+
}
45+
}
46+
47+
// Read files using Getdirentries
48+
var names2 []string
49+
fd, err := syscall.Open(d, syscall.O_RDONLY, 0)
50+
if err != nil {
51+
t.Fatalf("Open: %v", err)
52+
}
53+
defer syscall.Close(fd)
54+
var base uintptr
55+
var buf [2048]byte
56+
for {
57+
n, err := syscall.Getdirentries(fd, buf[:], &base)
58+
if err != nil {
59+
t.Fatalf("Getdirentries: %v", err)
60+
}
61+
if n == 0 {
62+
break
63+
}
64+
data := buf[:n]
65+
for len(data) > 0 {
66+
dirent := (*syscall.Dirent)(unsafe.Pointer(&data[0]))
67+
data = data[dirent.Reclen:]
68+
name := make([]byte, dirent.Namlen)
69+
for i := 0; i < int(dirent.Namlen); i++ {
70+
name[i] = byte(dirent.Name[i])
71+
}
72+
names2 = append(names2, string(name))
73+
}
74+
}
75+
76+
names = append(names, ".", "..") // Getdirentries returns these also
77+
sort.Strings(names)
78+
sort.Strings(names2)
79+
if strings.Join(names, ":") != strings.Join(names2, ":") {
80+
t.Errorf("names don't match\n names: %q\nnames2: %q", names, names2)
81+
}
82+
}

src/syscall/syscall_darwin.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,13 @@ func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
368368
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
369369
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
370370
const ptrSize = unsafe.Sizeof(uintptr(0))
371-
fd2, err := Dup(fd)
371+
// We need to duplicate the incoming file descriptor
372+
// because the caller expects to retain control of it, but
373+
// fdopendir expects to take control of its argument.
374+
// Just Dup'ing the file descriptor is not enough, as the
375+
// result shares underlying state. Use openat to make a really
376+
// new file descriptor referring to the same directory.
377+
fd2, err := openat(fd, ".", O_RDONLY, 0)
372378
if err != nil {
373379
return 0, err
374380
}

0 commit comments

Comments
 (0)