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
5package os
6
7import (
8	"io"
9	"io/fs"
10	"syscall"
11)
12
13func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
14	// If this file has no dirinfo, create one.
15	d := file.dirinfo.Load()
16	if d == nil {
17		d = new(dirInfo)
18		file.dirinfo.Store(d)
19	}
20	d.mu.Lock()
21	defer d.mu.Unlock()
22
23	size := n
24	if size <= 0 {
25		size = 100
26		n = -1
27	}
28	for n != 0 {
29		// Refill the buffer if necessary.
30		if d.bufp >= d.nbuf {
31			nb, err := file.Read(d.buf[:])
32
33			// Update the buffer state before checking for errors.
34			d.bufp, d.nbuf = 0, nb
35
36			if err != nil {
37				if err == io.EOF {
38					break
39				}
40				return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: err}
41			}
42			if nb < syscall.STATFIXLEN {
43				return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: syscall.ErrShortStat}
44			}
45		}
46
47		// Get a record from the buffer.
48		b := d.buf[d.bufp:]
49		m := int(uint16(b[0])|uint16(b[1])<<8) + 2
50		if m < syscall.STATFIXLEN {
51			return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: syscall.ErrShortStat}
52		}
53
54		dir, err := syscall.UnmarshalDir(b[:m])
55		if err != nil {
56			return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: err}
57		}
58
59		if mode == readdirName {
60			names = append(names, dir.Name)
61		} else {
62			f := fileInfoFromStat(dir)
63			if mode == readdirDirEntry {
64				dirents = append(dirents, dirEntry{f})
65			} else {
66				infos = append(infos, f)
67			}
68		}
69		d.bufp += m
70		n--
71	}
72
73	if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
74		return nil, nil, nil, io.EOF
75	}
76	return names, dirents, infos, nil
77}
78
79type dirEntry struct {
80	fs *fileStat
81}
82
83func (de dirEntry) Name() string            { return de.fs.Name() }
84func (de dirEntry) IsDir() bool             { return de.fs.IsDir() }
85func (de dirEntry) Type() FileMode          { return de.fs.Mode().Type() }
86func (de dirEntry) Info() (FileInfo, error) { return de.fs, nil }
87
88func (de dirEntry) String() string {
89	return fs.FormatDirEntry(de)
90}
91