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