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 // We store the number of entries to skip in the seek 31 // offset of fd. See issue #31368. 32 // It's not the full required semantics, but should handle the case 33 // of calling Getdirentries or ReadDirent repeatedly. 34 // It won't handle assigning the results of lseek to *basep, or handle 35 // the directory being edited underfoot. 36 skip, err := Seek(fd, 0, 1 /* SEEK_CUR */) 37 if err != nil { 38 return 0, err 39 } 40 41 // We need to duplicate the incoming file descriptor 42 // because the caller expects to retain control of it, but 43 // fdopendir expects to take control of its argument. 44 // Just Dup'ing the file descriptor is not enough, as the 45 // result shares underlying state. Use Openat to make a really 46 // new file descriptor referring to the same directory. 47 fd2, err := Openat(fd, ".", O_RDONLY, 0) 48 if err != nil { 49 return 0, err 50 } 51 d, err := fdopendir(fd2) 52 if err != nil { 53 Close(fd2) 54 return 0, err 55 } 56 defer closedir(d) 57 58 var cnt int64 59 for { 60 var entry Dirent 61 var entryp *Dirent 62 e := readdir_r(d, &entry, &entryp) 63 if e != 0 { 64 return n, errnoErr(e) 65 } 66 if entryp == nil { 67 break 68 } 69 if skip > 0 { 70 skip-- 71 cnt++ 72 continue 73 } 74 reclen := int(entry.Reclen) 75 if reclen > len(buf) { 76 // Not enough room. Return for now. 77 // The counter will let us know where we should start up again. 78 // Note: this strategy for suspending in the middle and 79 // restarting is O(n^2) in the length of the directory. Oh well. 80 break 81 } 82 // Copy entry into return buffer. 83 s := struct { 84 ptr unsafe.Pointer 85 siz int 86 cap int 87 }{ptr: unsafe.Pointer(&entry), siz: reclen, cap: reclen} 88 copy(buf, *(*[]byte)(unsafe.Pointer(&s))) 89 buf = buf[reclen:] 90 n += reclen 91 cnt++ 92 } 93 // Set the seek offset of the input fd to record 94 // how many files we've already returned. 95 _, err = Seek(fd, cnt, 0 /* SEEK_SET */) 96 if err != nil { 97 return n, err 98 } 99 100 return n, nil 101} 102