Skip to content

Commit 731ebf4

Browse files
randall77andybons
authored andcommitted
[release-branch.go1.12] syscall: avoid _getdirentries64 on darwin
Getdirentries is implemented with the __getdirentries64 function in libSystem.dylib. That function works, but it's on Apple's can't-be-used-in-an-app-store-application list. Implement Getdirentries using the underlying fdopendir/readdir_r/closedir. The simulation isn't faithful, and could be slow, but it should handle common cases. Don't use Getdirentries in the stdlib, use fdopendir/readdir_r/closedir instead (via (*os.File).readdirnames). (Incorporates CL 170837 and CL 170698, which were small fixes to the original tip CL.) Fixes #31244 Update #28984 RELNOTE=yes Change-Id: Ia6b5d003e5bfe43ba54b1e1d9cfa792cc6511717 Reviewed-on: https://go-review.googlesource.com/c/go/+/168479 Reviewed-by: Emmanuel Odeke <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Emmanuel Odeke <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> (cherry picked from commit 9da6530) Reviewed-on: https://go-review.googlesource.com/c/go/+/170640 Run-TryBot: Brad Fitzpatrick <[email protected]>
1 parent 8b086a2 commit 731ebf4

23 files changed

+300
-159
lines changed

src/internal/poll/fd_opendir_ios.go renamed to src/internal/poll/fd_opendir_darwin.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build darwin
6-
// +build arm arm64
7-
85
package poll
96

107
import (

src/os/dir_ios.go renamed to src/os/dir_darwin.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build darwin
6-
// +build arm arm64
7-
85
package os
96

107
import (
@@ -47,12 +44,12 @@ func (f *File) readdirnames(n int) (names []string, err error) {
4744

4845
names = make([]string, 0, size)
4946
var dirent syscall.Dirent
50-
var entptr uintptr
51-
for len(names) < size {
52-
if res := readdir_r(d.dir, uintptr(unsafe.Pointer(&dirent)), uintptr(unsafe.Pointer(&entptr))); res != 0 {
47+
var entptr *syscall.Dirent
48+
for len(names) < size || n == -1 {
49+
if res := readdir_r(d.dir, &dirent, &entptr); res != 0 {
5350
return names, wrapSyscallError("readdir", syscall.Errno(res))
5451
}
55-
if entptr == 0 { // EOF
52+
if entptr == nil { // EOF
5653
break
5754
}
5855
if dirent.Ino == 0 {
@@ -84,4 +81,4 @@ func (f *File) readdirnames(n int) (names []string, err error) {
8481
func closedir(dir uintptr) (err error)
8582

8683
//go:linkname readdir_r syscall.readdir_r
87-
func readdir_r(dir, entry, result uintptr) (res int)
84+
func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res int)

src/os/dir_unix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build aix darwin,!arm,!arm64 dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
5+
// +build aix dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
66

77
package os
88

src/runtime/sys_darwin.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
8989
}
9090
func syscall6X()
9191

92+
//go:linkname syscall_syscallPtr syscall.syscallPtr
93+
//go:nosplit
94+
//go:cgo_unsafe_args
95+
func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
96+
entersyscallblock()
97+
libcCall(unsafe.Pointer(funcPC(syscallPtr)), unsafe.Pointer(&fn))
98+
exitsyscall()
99+
return
100+
}
101+
func syscallPtr()
102+
92103
//go:linkname syscall_rawSyscall syscall.rawSyscall
93104
//go:nosplit
94105
//go:cgo_unsafe_args

src/runtime/sys_darwin_32.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,3 @@ func syscall_syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, e
1919
return
2020
}
2121
func syscall9()
22-
23-
//go:linkname syscall_syscallPtr syscall.syscallPtr
24-
//go:nosplit
25-
//go:cgo_unsafe_args
26-
func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
27-
entersyscallblock()
28-
libcCall(unsafe.Pointer(funcPC(syscallPtr)), unsafe.Pointer(&fn))
29-
exitsyscall()
30-
return
31-
}
32-
func syscallPtr()

src/runtime/sys_darwin_386.s

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,9 +675,42 @@ ok:
675675
POPL BP
676676
RET
677677

678-
// Not used on 386.
678+
// syscallPtr is like syscall except the libc function reports an
679+
// error by returning NULL and setting errno.
679680
TEXT runtime·syscallPtr(SB),NOSPLIT,$0
680-
MOVL $0xf1, 0xf1 // crash
681+
PUSHL BP
682+
MOVL SP, BP
683+
SUBL $24, SP
684+
MOVL 32(SP), CX
685+
MOVL (0*4)(CX), AX // fn
686+
MOVL (1*4)(CX), DX // a1
687+
MOVL DX, 0(SP)
688+
MOVL (2*4)(CX), DX // a2
689+
MOVL DX, 4(SP)
690+
MOVL (3*4)(CX), DX // a3
691+
MOVL DX, 8(SP)
692+
693+
CALL AX
694+
695+
MOVL 32(SP), CX
696+
MOVL AX, (4*4)(CX) // r1
697+
MOVL DX, (5*4)(CX) // r2
698+
699+
// syscallPtr libc functions return NULL on error
700+
// and set errno.
701+
TESTL AX, AX
702+
JNE ok
703+
704+
// Get error code from libc.
705+
CALL libc_error(SB)
706+
MOVL (AX), AX
707+
MOVL 32(SP), CX
708+
MOVL AX, (6*4)(CX) // err
709+
710+
ok:
711+
XORL AX, AX // no error (it's ignored anyway)
712+
MOVL BP, SP
713+
POPL BP
681714
RET
682715

683716
// syscall6 calls a function in libc on behalf of the syscall package.

src/runtime/sys_darwin_64.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,3 @@ func syscall_syscallX(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
1919
return
2020
}
2121
func syscallX()
22-
23-
//go:linkname syscall_syscallXPtr syscall.syscallXPtr
24-
//go:nosplit
25-
//go:cgo_unsafe_args
26-
func syscall_syscallXPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
27-
entersyscallblock()
28-
libcCall(unsafe.Pointer(funcPC(syscallXPtr)), unsafe.Pointer(&fn))
29-
exitsyscall()
30-
return
31-
}
32-
func syscallXPtr()

src/runtime/sys_darwin_amd64.s

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -637,9 +637,40 @@ ok:
637637
POPQ BP
638638
RET
639639

640-
// Not used on amd64.
641-
TEXT runtime·syscallXPtr(SB),NOSPLIT,$0
642-
MOVL $0xf1, 0xf1 // crash
640+
// syscallPtr is like syscallX except that the libc function reports an
641+
// error by returning NULL and setting errno.
642+
TEXT runtime·syscallPtr(SB),NOSPLIT,$0
643+
PUSHQ BP
644+
MOVQ SP, BP
645+
SUBQ $16, SP
646+
MOVQ (0*8)(DI), CX // fn
647+
MOVQ (2*8)(DI), SI // a2
648+
MOVQ (3*8)(DI), DX // a3
649+
MOVQ DI, (SP)
650+
MOVQ (1*8)(DI), DI // a1
651+
XORL AX, AX // vararg: say "no float args"
652+
653+
CALL CX
654+
655+
MOVQ (SP), DI
656+
MOVQ AX, (4*8)(DI) // r1
657+
MOVQ DX, (5*8)(DI) // r2
658+
659+
// syscallPtr libc functions return NULL on error
660+
// and set errno.
661+
TESTQ AX, AX
662+
JNE ok
663+
664+
// Get error code from libc.
665+
CALL libc_error(SB)
666+
MOVLQSX (AX), AX
667+
MOVQ (SP), DI
668+
MOVQ AX, (6*8)(DI) // err
669+
670+
ok:
671+
XORL AX, AX // no error (it's ignored anyway)
672+
MOVQ BP, SP
673+
POPQ BP
643674
RET
644675

645676
// syscall6 calls a function in libc on behalf of the syscall package.

src/runtime/sys_darwin_arm.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ ok:
418418
RET
419419

420420
// syscallPtr is like syscall except the libc function reports an
421-
// error by returning NULL.
421+
// error by returning NULL and setting errno.
422422
TEXT runtime·syscallPtr(SB),NOSPLIT,$0
423423
MOVW.W R0, -4(R13) // push structure pointer
424424
MOVW 0(R0), R12 // fn

src/runtime/sys_darwin_arm64.s

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,9 @@ TEXT runtime·syscallX(SB),NOSPLIT,$0
465465
ok:
466466
RET
467467

468-
// syscallXPtr is like syscallX except that the libc function reports an
469-
// error by returning NULL.
470-
TEXT runtime·syscallXPtr(SB),NOSPLIT,$0
468+
// syscallPtr is like syscallX except that the libc function reports an
469+
// error by returning NULL and setting errno.
470+
TEXT runtime·syscallPtr(SB),NOSPLIT,$0
471471
SUB $16, RSP // push structure pointer
472472
MOVD R0, (RSP)
473473

src/syscall/dirent_bsd_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build darwin,!arm,!arm64 dragonfly freebsd netbsd openbsd
5+
// +build darwin dragonfly freebsd netbsd openbsd
66

77
package syscall_test
88

src/syscall/mksyscall.pl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ ($)
350350
$text .= "//go:linkname $funcname $funcname\n";
351351
# Tell the linker that funcname can be found in libSystem using varname without the libc_ prefix.
352352
my $basename = substr $funcname, 5;
353+
if($basename eq "readdir_r" && ($ENV{'GOARCH'} eq "386" || $ENV{'GOARCH'} eq "amd64")) {
354+
# Hack to make sure we get the 64-bit inode version on darwin/macOS.
355+
$basename .= "\$INODE64"
356+
}
353357
$text .= "//go:cgo_import_dynamic $funcname $basename \"/usr/lib/libSystem.B.dylib\"\n";
354358
}
355359
}

src/syscall/syscall_darwin.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
258258
//sys Chown(path string, uid int, gid int) (err error)
259259
//sys Chroot(path string) (err error)
260260
//sys Close(fd int) (err error)
261+
//sys closedir(dir uintptr) (err error)
261262
//sys Dup(fd int) (nfd int, err error)
262263
//sys Dup2(from int, to int) (err error)
263264
//sys Exchangedata(path1 string, path2 string, options int) (err error)
@@ -301,6 +302,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
301302
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
302303
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
303304
//sys read(fd int, p []byte) (n int, err error)
305+
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
304306
//sys Readlink(path string, buf []byte) (n int, err error)
305307
//sys Rename(from string, to string) (err error)
306308
//sys Revoke(path string) (err error)
@@ -363,12 +365,70 @@ func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
363365
return
364366
}
365367

368+
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
369+
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
370+
const ptrSize = unsafe.Sizeof(uintptr(0))
371+
fd2, err := Dup(fd)
372+
if err != nil {
373+
return 0, err
374+
}
375+
d, err := fdopendir(fd2)
376+
if err != nil {
377+
Close(fd2)
378+
return 0, err
379+
}
380+
defer closedir(d)
381+
// We keep the number of records already returned in *basep.
382+
// It's not the full required semantics, but should handle the case
383+
// of calling Getdirentries repeatedly.
384+
// It won't handle assigning the results of lseek to *basep, or handle
385+
// the directory being edited underfoot.
386+
skip := *basep
387+
*basep = 0
388+
for {
389+
var entry Dirent
390+
var entryp *Dirent
391+
e := readdir_r(d, &entry, &entryp)
392+
if e != 0 {
393+
return n, errnoErr(e)
394+
}
395+
if entryp == nil {
396+
break
397+
}
398+
if skip > 0 {
399+
skip--
400+
*basep++
401+
continue
402+
}
403+
reclen := int(entry.Reclen)
404+
if reclen > len(buf) {
405+
// Not enough room. Return for now.
406+
// *basep will let us know where we should start up again.
407+
// Note: this strategy for suspending in the middle and
408+
// restarting is O(n^2) in the length of the directory. Oh well.
409+
break
410+
}
411+
// Copy entry into return buffer.
412+
s := struct {
413+
ptr unsafe.Pointer
414+
siz int
415+
cap int
416+
}{ptr: unsafe.Pointer(&entry), siz: reclen, cap: reclen}
417+
copy(buf, *(*[]byte)(unsafe.Pointer(&s)))
418+
buf = buf[reclen:]
419+
n += reclen
420+
*basep++
421+
}
422+
return n, nil
423+
}
424+
366425
// Implemented in the runtime package (runtime/sys_darwin.go)
367426
func syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
368427
func syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
369428
func syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
370429
func rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
371430
func rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
431+
func syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
372432

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

src/syscall/syscall_darwin_386.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ func setTimeval(sec, usec int64) Timeval {
1616

1717
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_fstat64
1818
//sys Fstatfs(fd int, stat *Statfs_t) (err error) = SYS_fstatfs64
19-
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) = SYS___getdirentries64
2019
//sysnb Gettimeofday(tp *Timeval) (err error)
2120
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_lstat64
2221
//sys Stat(path string, stat *Stat_t) (err error) = SYS_stat64
@@ -59,6 +58,20 @@ func libc_sendfile_trampoline()
5958
//go:linkname libc_sendfile libc_sendfile
6059
//go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib"
6160

61+
func fdopendir(fd int) (dir uintptr, err error) {
62+
r0, _, e1 := syscallPtr(funcPC(libc_fdopendir_trampoline), uintptr(fd), 0, 0)
63+
dir = uintptr(r0)
64+
if e1 != 0 {
65+
err = errnoErr(e1)
66+
}
67+
return
68+
}
69+
70+
func libc_fdopendir_trampoline()
71+
72+
//go:linkname libc_fdopendir libc_fdopendir
73+
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
74+
6275
// Implemented in the runtime package (runtime/sys_darwin_32.go)
6376
func syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
6477

src/syscall/syscall_darwin_amd64.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ func setTimeval(sec, usec int64) Timeval {
1616

1717
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_fstat64
1818
//sys Fstatfs(fd int, stat *Statfs_t) (err error) = SYS_fstatfs64
19-
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) = SYS___getdirentries64
2019
//sysnb Gettimeofday(tp *Timeval) (err error)
2120
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_lstat64
2221
//sys Stat(path string, stat *Stat_t) (err error) = SYS_stat64
@@ -59,6 +58,20 @@ func libc_sendfile_trampoline()
5958
//go:linkname libc_sendfile libc_sendfile
6059
//go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib"
6160

61+
func fdopendir(fd int) (dir uintptr, err error) {
62+
r0, _, e1 := syscallPtr(funcPC(libc_fdopendir_trampoline), uintptr(fd), 0, 0)
63+
dir = uintptr(r0)
64+
if e1 != 0 {
65+
err = errnoErr(e1)
66+
}
67+
return
68+
}
69+
70+
func libc_fdopendir_trampoline()
71+
72+
//go:linkname libc_fdopendir libc_fdopendir
73+
//go:cgo_import_dynamic libc_fdopendir fdopendir$INODE64 "/usr/lib/libSystem.B.dylib"
74+
6275
// Implemented in the runtime package (runtime/sys_darwin_64.go)
6376
func syscallX(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
6477

0 commit comments

Comments
 (0)