xref: /aosp_15_r20/build/soong/finder/fs/readdir.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage fs
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Worker// This is based on the readdir implementation from Go 1.9:
18*333d2b36SAndroid Build Coastguard Worker// Copyright 2009 The Go Authors. All rights reserved.
19*333d2b36SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style
20*333d2b36SAndroid Build Coastguard Worker// license that can be found in the LICENSE file.
21*333d2b36SAndroid Build Coastguard Worker
22*333d2b36SAndroid Build Coastguard Workerimport (
23*333d2b36SAndroid Build Coastguard Worker	"os"
24*333d2b36SAndroid Build Coastguard Worker	"syscall"
25*333d2b36SAndroid Build Coastguard Worker	"unsafe"
26*333d2b36SAndroid Build Coastguard Worker)
27*333d2b36SAndroid Build Coastguard Worker
28*333d2b36SAndroid Build Coastguard Workerconst (
29*333d2b36SAndroid Build Coastguard Worker	blockSize = 4096
30*333d2b36SAndroid Build Coastguard Worker)
31*333d2b36SAndroid Build Coastguard Worker
32*333d2b36SAndroid Build Coastguard Workerfunc readdir(path string) ([]DirEntryInfo, error) {
33*333d2b36SAndroid Build Coastguard Worker	f, err := os.Open(path)
34*333d2b36SAndroid Build Coastguard Worker	defer f.Close()
35*333d2b36SAndroid Build Coastguard Worker
36*333d2b36SAndroid Build Coastguard Worker	if err != nil {
37*333d2b36SAndroid Build Coastguard Worker		return nil, err
38*333d2b36SAndroid Build Coastguard Worker	}
39*333d2b36SAndroid Build Coastguard Worker	// This implicitly switches the fd to non-blocking mode, which is less efficient than what
40*333d2b36SAndroid Build Coastguard Worker	// file.ReadDir does since it will keep a thread blocked and not just a goroutine.
41*333d2b36SAndroid Build Coastguard Worker	fd := int(f.Fd())
42*333d2b36SAndroid Build Coastguard Worker
43*333d2b36SAndroid Build Coastguard Worker	buf := make([]byte, blockSize)
44*333d2b36SAndroid Build Coastguard Worker	entries := make([]*dirEntryInfo, 0, 100)
45*333d2b36SAndroid Build Coastguard Worker
46*333d2b36SAndroid Build Coastguard Worker	for {
47*333d2b36SAndroid Build Coastguard Worker		n, errno := syscall.ReadDirent(fd, buf)
48*333d2b36SAndroid Build Coastguard Worker		if errno != nil {
49*333d2b36SAndroid Build Coastguard Worker			err = os.NewSyscallError("readdirent", errno)
50*333d2b36SAndroid Build Coastguard Worker			break
51*333d2b36SAndroid Build Coastguard Worker		}
52*333d2b36SAndroid Build Coastguard Worker		if n <= 0 {
53*333d2b36SAndroid Build Coastguard Worker			break // EOF
54*333d2b36SAndroid Build Coastguard Worker		}
55*333d2b36SAndroid Build Coastguard Worker
56*333d2b36SAndroid Build Coastguard Worker		entries = parseDirent(buf[:n], entries)
57*333d2b36SAndroid Build Coastguard Worker	}
58*333d2b36SAndroid Build Coastguard Worker
59*333d2b36SAndroid Build Coastguard Worker	ret := make([]DirEntryInfo, 0, len(entries))
60*333d2b36SAndroid Build Coastguard Worker
61*333d2b36SAndroid Build Coastguard Worker	for _, entry := range entries {
62*333d2b36SAndroid Build Coastguard Worker		if !entry.modeExists {
63*333d2b36SAndroid Build Coastguard Worker			mode, lerr := lstatFileMode(path + "/" + entry.name)
64*333d2b36SAndroid Build Coastguard Worker			if os.IsNotExist(lerr) {
65*333d2b36SAndroid Build Coastguard Worker				// File disappeared between readdir + stat.
66*333d2b36SAndroid Build Coastguard Worker				// Just treat it as if it didn't exist.
67*333d2b36SAndroid Build Coastguard Worker				continue
68*333d2b36SAndroid Build Coastguard Worker			}
69*333d2b36SAndroid Build Coastguard Worker			if lerr != nil {
70*333d2b36SAndroid Build Coastguard Worker				return ret, lerr
71*333d2b36SAndroid Build Coastguard Worker			}
72*333d2b36SAndroid Build Coastguard Worker			entry.mode = mode
73*333d2b36SAndroid Build Coastguard Worker			entry.modeExists = true
74*333d2b36SAndroid Build Coastguard Worker		}
75*333d2b36SAndroid Build Coastguard Worker		ret = append(ret, entry)
76*333d2b36SAndroid Build Coastguard Worker	}
77*333d2b36SAndroid Build Coastguard Worker
78*333d2b36SAndroid Build Coastguard Worker	return ret, err
79*333d2b36SAndroid Build Coastguard Worker}
80*333d2b36SAndroid Build Coastguard Worker
81*333d2b36SAndroid Build Coastguard Workerfunc parseDirent(buf []byte, entries []*dirEntryInfo) []*dirEntryInfo {
82*333d2b36SAndroid Build Coastguard Worker	for len(buf) > 0 {
83*333d2b36SAndroid Build Coastguard Worker		reclen, ok := direntReclen(buf)
84*333d2b36SAndroid Build Coastguard Worker		if !ok || reclen > uint64(len(buf)) {
85*333d2b36SAndroid Build Coastguard Worker			return entries
86*333d2b36SAndroid Build Coastguard Worker		}
87*333d2b36SAndroid Build Coastguard Worker		rec := buf[:reclen]
88*333d2b36SAndroid Build Coastguard Worker		buf = buf[reclen:]
89*333d2b36SAndroid Build Coastguard Worker		ino, ok := direntIno(rec)
90*333d2b36SAndroid Build Coastguard Worker		if !ok {
91*333d2b36SAndroid Build Coastguard Worker			break
92*333d2b36SAndroid Build Coastguard Worker		}
93*333d2b36SAndroid Build Coastguard Worker		if ino == 0 { // File absent in directory.
94*333d2b36SAndroid Build Coastguard Worker			continue
95*333d2b36SAndroid Build Coastguard Worker		}
96*333d2b36SAndroid Build Coastguard Worker		typ, ok := direntType(rec)
97*333d2b36SAndroid Build Coastguard Worker		if !ok {
98*333d2b36SAndroid Build Coastguard Worker			break
99*333d2b36SAndroid Build Coastguard Worker		}
100*333d2b36SAndroid Build Coastguard Worker		const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
101*333d2b36SAndroid Build Coastguard Worker		namlen, ok := direntNamlen(rec)
102*333d2b36SAndroid Build Coastguard Worker		if !ok || namoff+namlen > uint64(len(rec)) {
103*333d2b36SAndroid Build Coastguard Worker			break
104*333d2b36SAndroid Build Coastguard Worker		}
105*333d2b36SAndroid Build Coastguard Worker		name := rec[namoff : namoff+namlen]
106*333d2b36SAndroid Build Coastguard Worker
107*333d2b36SAndroid Build Coastguard Worker		for i, c := range name {
108*333d2b36SAndroid Build Coastguard Worker			if c == 0 {
109*333d2b36SAndroid Build Coastguard Worker				name = name[:i]
110*333d2b36SAndroid Build Coastguard Worker				break
111*333d2b36SAndroid Build Coastguard Worker			}
112*333d2b36SAndroid Build Coastguard Worker		}
113*333d2b36SAndroid Build Coastguard Worker		// Check for useless names before allocating a string.
114*333d2b36SAndroid Build Coastguard Worker		if string(name) == "." || string(name) == ".." {
115*333d2b36SAndroid Build Coastguard Worker			continue
116*333d2b36SAndroid Build Coastguard Worker		}
117*333d2b36SAndroid Build Coastguard Worker
118*333d2b36SAndroid Build Coastguard Worker		mode, modeExists := direntTypeToFileMode(typ)
119*333d2b36SAndroid Build Coastguard Worker
120*333d2b36SAndroid Build Coastguard Worker		entries = append(entries, &dirEntryInfo{string(name), mode, modeExists})
121*333d2b36SAndroid Build Coastguard Worker	}
122*333d2b36SAndroid Build Coastguard Worker	return entries
123*333d2b36SAndroid Build Coastguard Worker}
124*333d2b36SAndroid Build Coastguard Worker
125*333d2b36SAndroid Build Coastguard Workerfunc direntIno(buf []byte) (uint64, bool) {
126*333d2b36SAndroid Build Coastguard Worker	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Ino), unsafe.Sizeof(syscall.Dirent{}.Ino))
127*333d2b36SAndroid Build Coastguard Worker}
128*333d2b36SAndroid Build Coastguard Worker
129*333d2b36SAndroid Build Coastguard Workerfunc direntType(buf []byte) (uint64, bool) {
130*333d2b36SAndroid Build Coastguard Worker	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Type), unsafe.Sizeof(syscall.Dirent{}.Type))
131*333d2b36SAndroid Build Coastguard Worker}
132*333d2b36SAndroid Build Coastguard Worker
133*333d2b36SAndroid Build Coastguard Workerfunc direntReclen(buf []byte) (uint64, bool) {
134*333d2b36SAndroid Build Coastguard Worker	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
135*333d2b36SAndroid Build Coastguard Worker}
136*333d2b36SAndroid Build Coastguard Worker
137*333d2b36SAndroid Build Coastguard Workerfunc direntNamlen(buf []byte) (uint64, bool) {
138*333d2b36SAndroid Build Coastguard Worker	reclen, ok := direntReclen(buf)
139*333d2b36SAndroid Build Coastguard Worker	if !ok {
140*333d2b36SAndroid Build Coastguard Worker		return 0, false
141*333d2b36SAndroid Build Coastguard Worker	}
142*333d2b36SAndroid Build Coastguard Worker	return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true
143*333d2b36SAndroid Build Coastguard Worker}
144*333d2b36SAndroid Build Coastguard Worker
145*333d2b36SAndroid Build Coastguard Worker// readInt returns the size-bytes unsigned integer in native byte order at offset off.
146*333d2b36SAndroid Build Coastguard Workerfunc readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
147*333d2b36SAndroid Build Coastguard Worker	if len(b) < int(off+size) {
148*333d2b36SAndroid Build Coastguard Worker		return 0, false
149*333d2b36SAndroid Build Coastguard Worker	}
150*333d2b36SAndroid Build Coastguard Worker	return readIntLE(b[off:], size), true
151*333d2b36SAndroid Build Coastguard Worker}
152*333d2b36SAndroid Build Coastguard Worker
153*333d2b36SAndroid Build Coastguard Workerfunc readIntLE(b []byte, size uintptr) uint64 {
154*333d2b36SAndroid Build Coastguard Worker	switch size {
155*333d2b36SAndroid Build Coastguard Worker	case 1:
156*333d2b36SAndroid Build Coastguard Worker		return uint64(b[0])
157*333d2b36SAndroid Build Coastguard Worker	case 2:
158*333d2b36SAndroid Build Coastguard Worker		_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
159*333d2b36SAndroid Build Coastguard Worker		return uint64(b[0]) | uint64(b[1])<<8
160*333d2b36SAndroid Build Coastguard Worker	case 4:
161*333d2b36SAndroid Build Coastguard Worker		_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
162*333d2b36SAndroid Build Coastguard Worker		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
163*333d2b36SAndroid Build Coastguard Worker	case 8:
164*333d2b36SAndroid Build Coastguard Worker		_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
165*333d2b36SAndroid Build Coastguard Worker		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
166*333d2b36SAndroid Build Coastguard Worker			uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
167*333d2b36SAndroid Build Coastguard Worker	default:
168*333d2b36SAndroid Build Coastguard Worker		panic("syscall: readInt with unsupported size")
169*333d2b36SAndroid Build Coastguard Worker	}
170*333d2b36SAndroid Build Coastguard Worker}
171*333d2b36SAndroid Build Coastguard Worker
172*333d2b36SAndroid Build Coastguard Worker// If the directory entry doesn't specify the type, fall back to using lstat to get the type.
173*333d2b36SAndroid Build Coastguard Workerfunc lstatFileMode(name string) (os.FileMode, error) {
174*333d2b36SAndroid Build Coastguard Worker	stat, err := os.Lstat(name)
175*333d2b36SAndroid Build Coastguard Worker	if err != nil {
176*333d2b36SAndroid Build Coastguard Worker		return 0, err
177*333d2b36SAndroid Build Coastguard Worker	}
178*333d2b36SAndroid Build Coastguard Worker
179*333d2b36SAndroid Build Coastguard Worker	return stat.Mode() & (os.ModeType | os.ModeCharDevice), nil
180*333d2b36SAndroid Build Coastguard Worker}
181*333d2b36SAndroid Build Coastguard Worker
182*333d2b36SAndroid Build Coastguard Worker// from Linux and Darwin dirent.h
183*333d2b36SAndroid Build Coastguard Workerconst (
184*333d2b36SAndroid Build Coastguard Worker	DT_UNKNOWN = 0
185*333d2b36SAndroid Build Coastguard Worker	DT_FIFO    = 1
186*333d2b36SAndroid Build Coastguard Worker	DT_CHR     = 2
187*333d2b36SAndroid Build Coastguard Worker	DT_DIR     = 4
188*333d2b36SAndroid Build Coastguard Worker	DT_BLK     = 6
189*333d2b36SAndroid Build Coastguard Worker	DT_REG     = 8
190*333d2b36SAndroid Build Coastguard Worker	DT_LNK     = 10
191*333d2b36SAndroid Build Coastguard Worker	DT_SOCK    = 12
192*333d2b36SAndroid Build Coastguard Worker)
193*333d2b36SAndroid Build Coastguard Worker
194*333d2b36SAndroid Build Coastguard Workerfunc direntTypeToFileMode(typ uint64) (os.FileMode, bool) {
195*333d2b36SAndroid Build Coastguard Worker	exists := true
196*333d2b36SAndroid Build Coastguard Worker	var mode os.FileMode
197*333d2b36SAndroid Build Coastguard Worker	switch typ {
198*333d2b36SAndroid Build Coastguard Worker	case DT_UNKNOWN:
199*333d2b36SAndroid Build Coastguard Worker		exists = false
200*333d2b36SAndroid Build Coastguard Worker	case DT_FIFO:
201*333d2b36SAndroid Build Coastguard Worker		mode = os.ModeNamedPipe
202*333d2b36SAndroid Build Coastguard Worker	case DT_CHR:
203*333d2b36SAndroid Build Coastguard Worker		mode = os.ModeDevice | os.ModeCharDevice
204*333d2b36SAndroid Build Coastguard Worker	case DT_DIR:
205*333d2b36SAndroid Build Coastguard Worker		mode = os.ModeDir
206*333d2b36SAndroid Build Coastguard Worker	case DT_BLK:
207*333d2b36SAndroid Build Coastguard Worker		mode = os.ModeDevice
208*333d2b36SAndroid Build Coastguard Worker	case DT_REG:
209*333d2b36SAndroid Build Coastguard Worker		mode = 0
210*333d2b36SAndroid Build Coastguard Worker	case DT_LNK:
211*333d2b36SAndroid Build Coastguard Worker		mode = os.ModeSymlink
212*333d2b36SAndroid Build Coastguard Worker	case DT_SOCK:
213*333d2b36SAndroid Build Coastguard Worker		mode = os.ModeSocket
214*333d2b36SAndroid Build Coastguard Worker	default:
215*333d2b36SAndroid Build Coastguard Worker		exists = false
216*333d2b36SAndroid Build Coastguard Worker	}
217*333d2b36SAndroid Build Coastguard Worker
218*333d2b36SAndroid Build Coastguard Worker	return mode, exists
219*333d2b36SAndroid Build Coastguard Worker}
220