github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/dir_unix.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 //go:build linux && !baremetal && !wasip1 6 7 package os 8 9 import ( 10 "io" 11 "syscall" 12 "unsafe" 13 ) 14 15 // Auxiliary information if the File describes a directory 16 type dirInfo struct { 17 nbuf int // length of buf; return value from Getdirentries 18 bufp int // location of next record in buf. 19 buf [blockSize]byte // buffer for directory I/O 20 } 21 22 const ( 23 // More than 5760 to work around https://golang.org/issue/24015. 24 blockSize = 8192 - 2*unsafe.Sizeof(int(0)) 25 ) 26 27 func (d *dirInfo) close() { 28 } 29 30 func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { 31 // If this file has no dirinfo, create one. 32 if f.dirinfo == nil { 33 f.dirinfo = new(dirInfo) 34 } 35 d := f.dirinfo 36 37 // Change the meaning of n for the implementation below. 38 // 39 // The n above was for the public interface of "if n <= 0, 40 // Readdir returns all the FileInfo from the directory in a 41 // single slice". 42 // 43 // But below, we use only negative to mean looping until the 44 // end and positive to mean bounded, with positive 45 // terminating at 0. 46 if n == 0 { 47 n = -1 48 } 49 50 for n != 0 { 51 // Refill the buffer if necessary 52 if d.bufp >= d.nbuf { 53 d.bufp = 0 54 var errno error 55 d.nbuf, errno = syscall.ReadDirent(syscallFd(f.handle.(unixFileHandle)), d.buf[:]) 56 if d.nbuf < 0 { 57 errno = handleSyscallError(errno) 58 } 59 if errno != nil { 60 return names, dirents, infos, &PathError{Op: "readdirent", Path: f.name, Err: errno} 61 } 62 if d.nbuf <= 0 { 63 break // EOF 64 } 65 } 66 67 // Drain the buffer 68 buf := d.buf[d.bufp:d.nbuf] 69 reclen, ok := direntReclen(buf) 70 if !ok || reclen > uint64(len(buf)) { 71 break 72 } 73 rec := buf[:reclen] 74 d.bufp += int(reclen) 75 ino, ok := direntIno(rec) 76 if !ok { 77 break 78 } 79 if ino == 0 { 80 continue 81 } 82 const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name)) 83 namlen, ok := direntNamlen(rec) 84 if !ok || namoff+namlen > uint64(len(rec)) { 85 break 86 } 87 name := rec[namoff : namoff+namlen] 88 for i, c := range name { 89 if c == 0 { 90 name = name[:i] 91 break 92 } 93 } 94 // Check for useless names before allocating a string. 95 if string(name) == "." || string(name) == ".." { 96 continue 97 } 98 if n > 0 { // see 'n == 0' comment above 99 n-- 100 } 101 if mode == readdirName { 102 names = append(names, string(name)) 103 } else if mode == readdirDirEntry { 104 de, err := newUnixDirent(f.name, string(name), direntType(rec)) 105 if IsNotExist(err) { 106 // File disappeared between readdir and stat. 107 // Treat as if it didn't exist. 108 continue 109 } 110 if err != nil { 111 return nil, dirents, nil, err 112 } 113 dirents = append(dirents, de) 114 } else { 115 info, err := lstat(f.name + "/" + string(name)) 116 if IsNotExist(err) { 117 // File disappeared between readdir + stat. 118 // Treat as if it didn't exist. 119 continue 120 } 121 if err != nil { 122 return nil, nil, infos, err 123 } 124 infos = append(infos, info) 125 } 126 } 127 128 if n > 0 && len(names)+len(dirents)+len(infos) == 0 { 129 return nil, nil, nil, io.EOF 130 } 131 return names, dirents, infos, nil 132 } 133 134 // readInt returns the size-bytes unsigned integer in native byte order at offset off. 135 func readInt(b []byte, off, size uintptr) (u uint64, ok bool) { 136 if len(b) < int(off+size) { 137 return 0, false 138 } 139 if isBigEndian { 140 return readIntBE(b[off:], size), true 141 } 142 return readIntLE(b[off:], size), true 143 } 144 145 func readIntBE(b []byte, size uintptr) uint64 { 146 switch size { 147 case 1: 148 return uint64(b[0]) 149 case 2: 150 _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 151 return uint64(b[1]) | uint64(b[0])<<8 152 case 4: 153 _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 154 return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24 155 case 8: 156 _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 157 return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | 158 uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 159 default: 160 panic("syscall: readInt with unsupported size") 161 } 162 } 163 164 func readIntLE(b []byte, size uintptr) uint64 { 165 switch size { 166 case 1: 167 return uint64(b[0]) 168 case 2: 169 _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 170 return uint64(b[0]) | uint64(b[1])<<8 171 case 4: 172 _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 173 return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 174 case 8: 175 _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 176 return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | 177 uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 178 default: 179 panic("syscall: readInt with unsupported size") 180 } 181 }