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 7package unix 8 9import "unsafe" 10 11//sys closedir(dir uintptr) (err error) 12//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) 13 14func 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 23func 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 28func 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} 104