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