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