dir_unix.mx raw
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 && !wasip2 && !wasm_unknown && !nintendoswitch
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 }
182