github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/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 linux && !baremetal && !wasip1
     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  }