github.com/nibnait/go-learn@v0.0.0-20220227013611-dfa47ea6d2da/src/pkg/mod/golang.org/x/sys@v0.0.0-20210630005230-0f9fa26af87c/unix/syscall_darwin.1_13.go (about)

     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  }