mirror of
https://github.com/golang/sys.git
synced 2026-02-08 19:56:04 +03:00
The regression test included in this change verifies that the type is, in fact, equivalent, while allowing the actual header definitions to avoid importing the (relatively heavy) "reflect" package itself. This change is loosely based on Keyan Pishdadian's draft in CL 230557. For golang/go#37805 Change-Id: I998c69cdeb852154cd66ab5fdaa542a6f19666a2 Reviewed-on: https://go-review.googlesource.com/c/sys/+/231177 Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
109 lines
2.7 KiB
Go
109 lines
2.7 KiB
Go
// Copyright 2019 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,go1.13
|
|
|
|
package unix
|
|
|
|
import (
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/internal/unsafeheader"
|
|
)
|
|
|
|
//sys closedir(dir uintptr) (err error)
|
|
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
|
|
|
|
func fdopendir(fd int) (dir uintptr, err error) {
|
|
r0, _, e1 := syscall_syscallPtr(funcPC(libc_fdopendir_trampoline), uintptr(fd), 0, 0)
|
|
dir = uintptr(r0)
|
|
if e1 != 0 {
|
|
err = errnoErr(e1)
|
|
}
|
|
return
|
|
}
|
|
|
|
func libc_fdopendir_trampoline()
|
|
|
|
//go:linkname libc_fdopendir libc_fdopendir
|
|
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
|
|
|
|
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
|
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
|
|
// We store the number of entries to skip in the seek
|
|
// offset of fd. See issue #31368.
|
|
// It's not the full required semantics, but should handle the case
|
|
// of calling Getdirentries or ReadDirent repeatedly.
|
|
// It won't handle assigning the results of lseek to *basep, or handle
|
|
// the directory being edited underfoot.
|
|
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// We need to duplicate the incoming file descriptor
|
|
// because the caller expects to retain control of it, but
|
|
// fdopendir expects to take control of its argument.
|
|
// Just Dup'ing the file descriptor is not enough, as the
|
|
// result shares underlying state. Use Openat to make a really
|
|
// new file descriptor referring to the same directory.
|
|
fd2, err := Openat(fd, ".", O_RDONLY, 0)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
d, err := fdopendir(fd2)
|
|
if err != nil {
|
|
Close(fd2)
|
|
return 0, err
|
|
}
|
|
defer closedir(d)
|
|
|
|
var cnt int64
|
|
for {
|
|
var entry Dirent
|
|
var entryp *Dirent
|
|
e := readdir_r(d, &entry, &entryp)
|
|
if e != 0 {
|
|
return n, errnoErr(e)
|
|
}
|
|
if entryp == nil {
|
|
break
|
|
}
|
|
if skip > 0 {
|
|
skip--
|
|
cnt++
|
|
continue
|
|
}
|
|
|
|
reclen := int(entry.Reclen)
|
|
if reclen > len(buf) {
|
|
// Not enough room. Return for now.
|
|
// The counter will let us know where we should start up again.
|
|
// Note: this strategy for suspending in the middle and
|
|
// restarting is O(n^2) in the length of the directory. Oh well.
|
|
break
|
|
}
|
|
|
|
// Copy entry into return buffer.
|
|
var s []byte
|
|
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&s))
|
|
hdr.Data = unsafe.Pointer(&entry)
|
|
hdr.Cap = reclen
|
|
hdr.Len = reclen
|
|
copy(buf, s)
|
|
|
|
buf = buf[reclen:]
|
|
n += reclen
|
|
cnt++
|
|
}
|
|
// Set the seek offset of the input fd to record
|
|
// how many files we've already returned.
|
|
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
return n, nil
|
|
}
|