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