github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/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 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 if ino == 0 { 93 continue 94 } 95 const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name)) 96 namlen, ok := direntNamlen(rec) 97 if !ok || namoff+namlen > uint64(len(rec)) { 98 break 99 } 100 name := rec[namoff : namoff+namlen] 101 for i, c := range name { 102 if c == 0 { 103 name = name[:i] 104 break 105 } 106 } 107 // Check for useless names before allocating a string. 108 if string(name) == "." || string(name) == ".." { 109 continue 110 } 111 if n > 0 { // see 'n == 0' comment above 112 n-- 113 } 114 if mode == readdirName { 115 names = append(names, string(name)) 116 } else if mode == readdirDirEntry { 117 de, err := newUnixDirent(f.name, string(name), direntType(rec)) 118 if IsNotExist(err) { 119 // File disappeared between readdir and stat. 120 // Treat as if it didn't exist. 121 continue 122 } 123 if err != nil { 124 return nil, dirents, nil, err 125 } 126 dirents = append(dirents, de) 127 } else { 128 info, err := lstat(f.name + "/" + string(name)) 129 if IsNotExist(err) { 130 // File disappeared between readdir + stat. 131 // Treat as if it didn't exist. 132 continue 133 } 134 if err != nil { 135 return nil, nil, infos, err 136 } 137 infos = append(infos, info) 138 } 139 } 140 141 if n > 0 && len(names)+len(dirents)+len(infos) == 0 { 142 return nil, nil, nil, io.EOF 143 } 144 return names, dirents, infos, nil 145 } 146 147 // readInt returns the size-bytes unsigned integer in native byte order at offset off. 148 func readInt(b []byte, off, size uintptr) (u uint64, ok bool) { 149 if len(b) < int(off+size) { 150 return 0, false 151 } 152 if isBigEndian { 153 return readIntBE(b[off:], size), true 154 } 155 return readIntLE(b[off:], size), true 156 } 157 158 func readIntBE(b []byte, size uintptr) uint64 { 159 switch size { 160 case 1: 161 return uint64(b[0]) 162 case 2: 163 _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 164 return uint64(b[1]) | uint64(b[0])<<8 165 case 4: 166 _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 167 return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24 168 case 8: 169 _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 170 return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | 171 uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 172 default: 173 panic("syscall: readInt with unsupported size") 174 } 175 } 176 177 func readIntLE(b []byte, size uintptr) uint64 { 178 switch size { 179 case 1: 180 return uint64(b[0]) 181 case 2: 182 _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 183 return uint64(b[0]) | uint64(b[1])<<8 184 case 4: 185 _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 186 return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 187 case 8: 188 _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 189 return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | 190 uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 191 default: 192 panic("syscall: readInt with unsupported size") 193 } 194 }