github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/dir_darwin.go (about)

     1  // Copyright 2009 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  package os
     6  
     7  import (
     8  	"io"
     9  	"runtime"
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  // Auxiliary information if the File describes a directory
    15  type dirInfo struct {
    16  	dir uintptr // Pointer to DIR structure from dirent.h
    17  }
    18  
    19  func (d *dirInfo) close() {
    20  	if d.dir == 0 {
    21  		return
    22  	}
    23  	closedir(d.dir)
    24  	d.dir = 0
    25  }
    26  
    27  func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
    28  	if f.dirinfo == nil {
    29  		dir, call, errno := darwinOpenDir(syscallFd(f.handle.(unixFileHandle)))
    30  		if errno != nil {
    31  			return nil, nil, nil, &PathError{Op: call, Path: f.name, Err: errno}
    32  		}
    33  		f.dirinfo = &dirInfo{
    34  			dir: dir,
    35  		}
    36  	}
    37  	d := f.dirinfo
    38  
    39  	size := n
    40  	if size <= 0 {
    41  		size = 100
    42  		n = -1
    43  	}
    44  
    45  	var dirent syscall.Dirent
    46  	var entptr *syscall.Dirent
    47  	for len(names)+len(dirents)+len(infos) < size || n == -1 {
    48  		if errno := readdir_r(d.dir, &dirent, &entptr); errno != 0 {
    49  			if errno == syscall.EINTR {
    50  				continue
    51  			}
    52  			return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno}
    53  		}
    54  		if entptr == nil { // EOF
    55  			break
    56  		}
    57  		if dirent.Ino == 0 {
    58  			continue
    59  		}
    60  		name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
    61  		for i, c := range name {
    62  			if c == 0 {
    63  				name = name[:i]
    64  				break
    65  			}
    66  		}
    67  		// Check for useless names before allocating a string.
    68  		if string(name) == "." || string(name) == ".." {
    69  			continue
    70  		}
    71  		if mode == readdirName {
    72  			names = append(names, string(name))
    73  		} else if mode == readdirDirEntry {
    74  			de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type))
    75  			if IsNotExist(err) {
    76  				// File disappeared between readdir and stat.
    77  				// Treat as if it didn't exist.
    78  				continue
    79  			}
    80  			if err != nil {
    81  				return nil, dirents, nil, err
    82  			}
    83  			dirents = append(dirents, de)
    84  		} else {
    85  			info, err := lstat(f.name + "/" + string(name))
    86  			if IsNotExist(err) {
    87  				// File disappeared between readdir + stat.
    88  				// Treat as if it didn't exist.
    89  				continue
    90  			}
    91  			if err != nil {
    92  				return nil, nil, infos, err
    93  			}
    94  			infos = append(infos, info)
    95  		}
    96  		runtime.KeepAlive(f)
    97  	}
    98  
    99  	if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
   100  		return nil, nil, nil, io.EOF
   101  	}
   102  	return names, dirents, infos, nil
   103  }
   104  
   105  func dtToType(typ uint8) FileMode {
   106  	switch typ {
   107  	case syscall.DT_BLK:
   108  		return ModeDevice
   109  	case syscall.DT_CHR:
   110  		return ModeDevice | ModeCharDevice
   111  	case syscall.DT_DIR:
   112  		return ModeDir
   113  	case syscall.DT_FIFO:
   114  		return ModeNamedPipe
   115  	case syscall.DT_LNK:
   116  		return ModeSymlink
   117  	case syscall.DT_REG:
   118  		return 0
   119  	case syscall.DT_SOCK:
   120  		return ModeSocket
   121  	}
   122  	return ^FileMode(0)
   123  }
   124  
   125  // darwinOpenDir returns a pointer to a DIR structure suitable for
   126  // ReadDir. In case of an error, the name of the failed
   127  // syscall is returned along with a syscall.Errno.
   128  // Borrowed from upstream's internal/poll/fd_opendir_darwin.go
   129  func darwinOpenDir(fd syscallFd) (uintptr, string, error) {
   130  	// fdopendir(3) takes control of the file descriptor,
   131  	// so use a dup.
   132  	fd2, err := syscall.Dup(fd)
   133  	if err != nil {
   134  		return 0, "dup", err
   135  	}
   136  	var dir uintptr
   137  	for {
   138  		dir, err = syscall.Fdopendir(fd2)
   139  		if err != syscall.EINTR {
   140  			break
   141  		}
   142  	}
   143  	if err != nil {
   144  		syscall.Close(fd2)
   145  		return 0, "fdopendir", err
   146  	}
   147  	return dir, "", nil
   148  }
   149  
   150  // Implemented in syscall/syscall_libc_darwin_*.go.
   151  
   152  //go:linkname closedir syscall.closedir
   153  func closedir(dir uintptr) (err error)
   154  
   155  //go:linkname readdir_r syscall.readdir_r
   156  func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res syscall.Errno)