Skip to content

Commit 3421d5a

Browse files
committed
unix: avoid __getdirentries64 on darwin
Getdirentries is implemented with the __getdirentries64 function in libSystem.dylib on darwin/{386,amd64}. That function can't be used in an app store application. Implement Getdirentries using the underlying fdopendir/readdir_r/closedir for Go 1.13. The simulation isn't faithful, and could be slow, but it should handle common cases. For Go 1.12, fall back to raw syscalls since syscall.syscallPtr needed to use fdopendir from libSystem.dylib is not available. Follow CL 168479 and CL 170892 which did the same for syscall in the stdlib. Tested on darwin/amd64 with Go 1.11, Go 1.12 and Go 1.13 Fixes golang/go#34400 Change-Id: I631382aaea9ee7e0c4ed09e06ad5427efc620769 Reviewed-on: https://go-review.googlesource.com/c/sys/+/196478 Run-TryBot: Tobias Klauser <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 34b0ac8 commit 3421d5a

32 files changed

+466
-125
lines changed

unix/darwin_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ var darwinTests = [...]darwinTest{
139139
{"fstatfs64", libc_fstatfs64_trampoline},
140140
{"fsync", libc_fsync_trampoline},
141141
{"ftruncate", libc_ftruncate_trampoline},
142-
{"__getdirentries64", libc___getdirentries64_trampoline},
143142
{"getdtablesize", libc_getdtablesize_trampoline},
144143
{"getegid", libc_getegid_trampoline},
145144
{"geteuid", libc_geteuid_trampoline},

unix/mkall.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,11 @@ esac
212212
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ;
213213
elif [ "$GOOS" == "darwin" ]; then
214214
# pre-1.12, direct syscalls
215-
echo "$mksyscall -tags $GOOS,$GOARCH,!go1.12 $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.1_11.go";
215+
echo "$mksyscall -tags $GOOS,$GOARCH,!go1.12 $syscall_goos syscall_darwin_${GOARCH}.1_11.go $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.1_11.go";
216216
# 1.12 and later, syscalls via libSystem
217217
echo "$mksyscall -tags $GOOS,$GOARCH,go1.12 $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
218+
# 1.13 and later, syscalls via libSystem (including syscallPtr)
219+
echo "$mksyscall -tags $GOOS,$GOARCH,go1.13 syscall_darwin.1_13.go |gofmt >zsyscall_$GOOSARCH.1_13.go";
218220
else
219221
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
220222
fi

unix/mkasm_darwin.go

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,15 @@ import (
1717
"strings"
1818
)
1919

20-
func main() {
21-
in1, err := ioutil.ReadFile("syscall_darwin.go")
22-
if err != nil {
23-
log.Fatalf("can't open syscall_darwin.go: %s", err)
24-
}
25-
arch := os.Args[1]
26-
in2, err := ioutil.ReadFile(fmt.Sprintf("syscall_darwin_%s.go", arch))
27-
if err != nil {
28-
log.Fatalf("can't open syscall_darwin_%s.go: %s", arch, err)
29-
}
30-
in3, err := ioutil.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.go", arch))
31-
if err != nil {
32-
log.Fatalf("can't open zsyscall_darwin_%s.go: %s", arch, err)
33-
}
34-
in := string(in1) + string(in2) + string(in3)
35-
20+
func writeASMFile(in string, fileName string, buildTags string) {
3621
trampolines := map[string]bool{}
3722

3823
var out bytes.Buffer
3924

4025
fmt.Fprintf(&out, "// go run mkasm_darwin.go %s\n", strings.Join(os.Args[1:], " "))
4126
fmt.Fprintf(&out, "// Code generated by the command above; DO NOT EDIT.\n")
4227
fmt.Fprintf(&out, "\n")
43-
fmt.Fprintf(&out, "// +build go1.12\n")
28+
fmt.Fprintf(&out, "// +build %s\n", buildTags)
4429
fmt.Fprintf(&out, "\n")
4530
fmt.Fprintf(&out, "#include \"textflag.h\"\n")
4631
for _, line := range strings.Split(in, "\n") {
@@ -54,8 +39,40 @@ func main() {
5439
fmt.Fprintf(&out, "\tJMP\t%s(SB)\n", fn)
5540
}
5641
}
57-
err = ioutil.WriteFile(fmt.Sprintf("zsyscall_darwin_%s.s", arch), out.Bytes(), 0644)
42+
err := ioutil.WriteFile(fileName, out.Bytes(), 0644)
5843
if err != nil {
59-
log.Fatalf("can't write zsyscall_darwin_%s.s: %s", arch, err)
44+
log.Fatalf("can't write %s: %s", fileName, err)
6045
}
6146
}
47+
48+
func main() {
49+
in1, err := ioutil.ReadFile("syscall_darwin.go")
50+
if err != nil {
51+
log.Fatalf("can't open syscall_darwin.go: %s", err)
52+
}
53+
arch := os.Args[1]
54+
in2, err := ioutil.ReadFile(fmt.Sprintf("syscall_darwin_%s.go", arch))
55+
if err != nil {
56+
log.Fatalf("can't open syscall_darwin_%s.go: %s", arch, err)
57+
}
58+
in3, err := ioutil.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.go", arch))
59+
if err != nil {
60+
log.Fatalf("can't open zsyscall_darwin_%s.go: %s", arch, err)
61+
}
62+
in := string(in1) + string(in2) + string(in3)
63+
64+
writeASMFile(in, fmt.Sprintf("zsyscall_darwin_%s.s", arch), "go1.12")
65+
66+
in1, err = ioutil.ReadFile("syscall_darwin.1_13.go")
67+
if err != nil {
68+
log.Fatalf("can't open syscall_darwin.1_13.go: %s", err)
69+
}
70+
in2, err = ioutil.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.1_13.go", arch))
71+
if err != nil {
72+
log.Fatalf("can't open zsyscall_darwin_%s.1_13.go: %s", arch, err)
73+
}
74+
75+
in = string(in1) + string(in2)
76+
77+
writeASMFile(in, fmt.Sprintf("zsyscall_darwin_%s.1_13.s", arch), "go1.13")
78+
}

unix/mksyscall.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func main() {
121121
}
122122

123123
libc := false
124-
if goos == "darwin" && strings.Contains(buildTags(), ",go1.12") {
124+
if goos == "darwin" && (strings.Contains(buildTags(), ",go1.12") || strings.Contains(buildTags(), ",go1.13")) {
125125
libc = true
126126
}
127127
trampolines := map[string]bool{}
@@ -292,11 +292,6 @@ func main() {
292292
asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:] // internal syscall call
293293
sysname = strings.TrimPrefix(sysname, "SYS_") // remove SYS_
294294
sysname = strings.ToLower(sysname) // lowercase
295-
if sysname == "getdirentries64" {
296-
// Special case - libSystem name and
297-
// raw syscall name don't match.
298-
sysname = "__getdirentries64"
299-
}
300295
libcFn = sysname
301296
sysname = "funcPC(libc_" + sysname + "_trampoline)"
302297
}

unix/syscall_darwin.1_12.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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,go1.12,!go1.13
6+
7+
package unix
8+
9+
import (
10+
"unsafe"
11+
)
12+
13+
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
14+
// To implement this using libSystem we'd need syscall_syscallPtr for
15+
// fdopendir. However, syscallPtr was only added in Go 1.13, so we fall
16+
// back to raw syscalls for this func on Go 1.12.
17+
var p unsafe.Pointer
18+
if len(buf) > 0 {
19+
p = unsafe.Pointer(&buf[0])
20+
} else {
21+
p = unsafe.Pointer(&_zero)
22+
}
23+
r0, _, e1 := Syscall6(SYS_GETDIRENTRIES64, uintptr(fd), uintptr(p), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
24+
n = int(r0)
25+
if e1 != 0 {
26+
return n, errnoErr(e1)
27+
}
28+
return n, nil
29+
}

unix/syscall_darwin.1_13.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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,go1.13
6+
7+
package unix
8+
9+
import "unsafe"
10+
11+
//sys closedir(dir uintptr) (err error)
12+
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
13+
14+
func fdopendir(fd int) (dir uintptr, err error) {
15+
r0, _, e1 := syscall_syscallPtr(funcPC(libc_fdopendir_trampoline), uintptr(fd), 0, 0)
16+
dir = uintptr(r0)
17+
if e1 != 0 {
18+
err = errnoErr(e1)
19+
}
20+
return
21+
}
22+
23+
func libc_fdopendir_trampoline()
24+
25+
//go:linkname libc_fdopendir libc_fdopendir
26+
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
27+
28+
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
29+
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
30+
const ptrSize = unsafe.Sizeof(uintptr(0))
31+
32+
// We store the number of entries to skip in the seek
33+
// offset of fd. See issue #31368.
34+
// It's not the full required semantics, but should handle the case
35+
// of calling Getdirentries or ReadDirent repeatedly.
36+
// It won't handle assigning the results of lseek to *basep, or handle
37+
// the directory being edited underfoot.
38+
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
39+
if err != nil {
40+
return 0, err
41+
}
42+
43+
// We need to duplicate the incoming file descriptor
44+
// because the caller expects to retain control of it, but
45+
// fdopendir expects to take control of its argument.
46+
// Just Dup'ing the file descriptor is not enough, as the
47+
// result shares underlying state. Use Openat to make a really
48+
// new file descriptor referring to the same directory.
49+
fd2, err := Openat(fd, ".", O_RDONLY, 0)
50+
if err != nil {
51+
return 0, err
52+
}
53+
d, err := fdopendir(fd2)
54+
if err != nil {
55+
Close(fd2)
56+
return 0, err
57+
}
58+
defer closedir(d)
59+
60+
var cnt int64
61+
for {
62+
var entry Dirent
63+
var entryp *Dirent
64+
e := readdir_r(d, &entry, &entryp)
65+
if e != 0 {
66+
return n, errnoErr(e)
67+
}
68+
if entryp == nil {
69+
break
70+
}
71+
if skip > 0 {
72+
skip--
73+
cnt++
74+
continue
75+
}
76+
reclen := int(entry.Reclen)
77+
if reclen > len(buf) {
78+
// Not enough room. Return for now.
79+
// The counter will let us know where we should start up again.
80+
// Note: this strategy for suspending in the middle and
81+
// restarting is O(n^2) in the length of the directory. Oh well.
82+
break
83+
}
84+
// Copy entry into return buffer.
85+
s := struct {
86+
ptr unsafe.Pointer
87+
siz int
88+
cap int
89+
}{ptr: unsafe.Pointer(&entry), siz: reclen, cap: reclen}
90+
copy(buf, *(*[]byte)(unsafe.Pointer(&s)))
91+
buf = buf[reclen:]
92+
n += reclen
93+
cnt++
94+
}
95+
// Set the seek offset of the input fd to record
96+
// how many files we've already returned.
97+
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
98+
if err != nil {
99+
return n, err
100+
}
101+
102+
return n, nil
103+
}

unix/syscall_darwin_386.1_11.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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,386,!go1.12
6+
7+
package unix
8+
9+
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) = SYS_GETDIRENTRIES64

unix/syscall_darwin_386.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ const SYS___SYSCTL = SYS_SYSCTL
6363
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
6464
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
6565
//sys Fstatfs(fd int, stat *Statfs_t) (err error) = SYS_FSTATFS64
66-
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) = SYS_GETDIRENTRIES64
6766
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64
6867
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
6968
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64

unix/syscall_darwin_amd64.1_11.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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,amd64,!go1.12
6+
7+
package unix
8+
9+
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) = SYS_GETDIRENTRIES64

unix/syscall_darwin_amd64.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ const SYS___SYSCTL = SYS_SYSCTL
6363
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
6464
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
6565
//sys Fstatfs(fd int, stat *Statfs_t) (err error) = SYS_FSTATFS64
66-
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) = SYS_GETDIRENTRIES64
6766
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64
6867
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
6968
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64

unix/syscall_darwin_arm.1_11.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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,386,!go1.12
6+
7+
package unix
8+
9+
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
10+
return 0, ENOSYS
11+
}

unix/syscall_darwin_arm.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,3 @@ const SYS___SYSCTL = SYS_SYSCTL
7070
//sys Lstat(path string, stat *Stat_t) (err error)
7171
//sys Stat(path string, stat *Stat_t) (err error)
7272
//sys Statfs(path string, stat *Statfs_t) (err error)
73-
74-
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
75-
return 0, ENOSYS
76-
}

unix/syscall_darwin_arm64.1_11.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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,arm64,!go1.12
6+
7+
package unix
8+
9+
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
10+
return 0, ENOSYS
11+
}

unix/syscall_darwin_arm64.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,3 @@ const SYS___SYSCTL = SYS_SYSCTL
7272
//sys Lstat(path string, stat *Stat_t) (err error)
7373
//sys Stat(path string, stat *Stat_t) (err error)
7474
//sys Statfs(path string, stat *Statfs_t) (err error)
75-
76-
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
77-
return 0, ENOSYS
78-
}

unix/syscall_darwin_libSystem.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err
1515
func syscall_syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // 32-bit only
1616
func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
1717
func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
18+
func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
1819

1920
//go:linkname syscall_syscall syscall.syscall
2021
//go:linkname syscall_syscall6 syscall.syscall6
2122
//go:linkname syscall_syscall6X syscall.syscall6X
2223
//go:linkname syscall_syscall9 syscall.syscall9
2324
//go:linkname syscall_rawSyscall syscall.rawSyscall
2425
//go:linkname syscall_rawSyscall6 syscall.rawSyscall6
26+
//go:linkname syscall_syscallPtr syscall.syscallPtr
2527

2628
// Find the entry point for f. See comments in runtime/proc.go for the
2729
// function of the same name.

unix/zsyscall_darwin_386.1_11.go

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)