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)