mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
When building programs to GOOS=wasip1, the program does not have the guarantees that the underlying directories will come from a file system where a zero inode value indicates that the entry was deleted but not yet removed from the directory. The host runtime may be running on windows or may be exposing virtual user-space file systems that do not have the concept of inodes. In those setup, we assume that the host runtime is in charge of dealing with edge cases such as skipping directory entries with zero inodes when needed, and the guest application should trust the list of entries that it sees; therefore, we disable skipping over zero inodes on wasip1. Change-Id: I99aa562441cdb4182965f270af054cf3cf7f8f20 Reviewed-on: https://go-review.googlesource.com/c/go/+/507915 Reviewed-by: Ian Lance Taylor <iant@google.com> Run-TryBot: Ian Lance Taylor <iant@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
198 lines
5.2 KiB
Go
198 lines
5.2 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build aix || dragonfly || freebsd || (js && wasm) || wasip1 || linux || netbsd || openbsd || solaris
|
|
|
|
package os
|
|
|
|
import (
|
|
"io"
|
|
"runtime"
|
|
"sync"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// Auxiliary information if the File describes a directory
|
|
type dirInfo struct {
|
|
buf *[]byte // buffer for directory I/O
|
|
nbuf int // length of buf; return value from Getdirentries
|
|
bufp int // location of next record in buf.
|
|
}
|
|
|
|
const (
|
|
// More than 5760 to work around https://golang.org/issue/24015.
|
|
blockSize = 8192
|
|
)
|
|
|
|
var dirBufPool = sync.Pool{
|
|
New: func() any {
|
|
// The buffer must be at least a block long.
|
|
buf := make([]byte, blockSize)
|
|
return &buf
|
|
},
|
|
}
|
|
|
|
func (d *dirInfo) close() {
|
|
if d.buf != nil {
|
|
dirBufPool.Put(d.buf)
|
|
d.buf = nil
|
|
}
|
|
}
|
|
|
|
func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
|
|
// If this file has no dirinfo, create one.
|
|
if f.dirinfo == nil {
|
|
f.dirinfo = new(dirInfo)
|
|
f.dirinfo.buf = dirBufPool.Get().(*[]byte)
|
|
}
|
|
d := f.dirinfo
|
|
|
|
// Change the meaning of n for the implementation below.
|
|
//
|
|
// The n above was for the public interface of "if n <= 0,
|
|
// Readdir returns all the FileInfo from the directory in a
|
|
// single slice".
|
|
//
|
|
// But below, we use only negative to mean looping until the
|
|
// end and positive to mean bounded, with positive
|
|
// terminating at 0.
|
|
if n == 0 {
|
|
n = -1
|
|
}
|
|
|
|
for n != 0 {
|
|
// Refill the buffer if necessary
|
|
if d.bufp >= d.nbuf {
|
|
d.bufp = 0
|
|
var errno error
|
|
d.nbuf, errno = f.pfd.ReadDirent(*d.buf)
|
|
runtime.KeepAlive(f)
|
|
if errno != nil {
|
|
return names, dirents, infos, &PathError{Op: "readdirent", Path: f.name, Err: errno}
|
|
}
|
|
if d.nbuf <= 0 {
|
|
break // EOF
|
|
}
|
|
}
|
|
|
|
// Drain the buffer
|
|
buf := (*d.buf)[d.bufp:d.nbuf]
|
|
reclen, ok := direntReclen(buf)
|
|
if !ok || reclen > uint64(len(buf)) {
|
|
break
|
|
}
|
|
rec := buf[:reclen]
|
|
d.bufp += int(reclen)
|
|
ino, ok := direntIno(rec)
|
|
if !ok {
|
|
break
|
|
}
|
|
// When building to wasip1, the host runtime might be running on Windows
|
|
// or might expose a remote file system which does not have the concept
|
|
// of inodes. Therefore, we cannot make the assumption that it is safe
|
|
// to skip entries with zero inodes.
|
|
if ino == 0 && runtime.GOOS != "wasip1" {
|
|
continue
|
|
}
|
|
const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
|
|
namlen, ok := direntNamlen(rec)
|
|
if !ok || namoff+namlen > uint64(len(rec)) {
|
|
break
|
|
}
|
|
name := rec[namoff : namoff+namlen]
|
|
for i, c := range name {
|
|
if c == 0 {
|
|
name = name[:i]
|
|
break
|
|
}
|
|
}
|
|
// Check for useless names before allocating a string.
|
|
if string(name) == "." || string(name) == ".." {
|
|
continue
|
|
}
|
|
if n > 0 { // see 'n == 0' comment above
|
|
n--
|
|
}
|
|
if mode == readdirName {
|
|
names = append(names, string(name))
|
|
} else if mode == readdirDirEntry {
|
|
de, err := newUnixDirent(f.name, string(name), direntType(rec))
|
|
if IsNotExist(err) {
|
|
// File disappeared between readdir and stat.
|
|
// Treat as if it didn't exist.
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return nil, dirents, nil, err
|
|
}
|
|
dirents = append(dirents, de)
|
|
} else {
|
|
info, err := lstat(f.name + "/" + string(name))
|
|
if IsNotExist(err) {
|
|
// File disappeared between readdir + stat.
|
|
// Treat as if it didn't exist.
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return nil, nil, infos, err
|
|
}
|
|
infos = append(infos, info)
|
|
}
|
|
}
|
|
|
|
if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
|
|
return nil, nil, nil, io.EOF
|
|
}
|
|
return names, dirents, infos, nil
|
|
}
|
|
|
|
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
|
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
|
|
if len(b) < int(off+size) {
|
|
return 0, false
|
|
}
|
|
if isBigEndian {
|
|
return readIntBE(b[off:], size), true
|
|
}
|
|
return readIntLE(b[off:], size), true
|
|
}
|
|
|
|
func readIntBE(b []byte, size uintptr) uint64 {
|
|
switch size {
|
|
case 1:
|
|
return uint64(b[0])
|
|
case 2:
|
|
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
|
return uint64(b[1]) | uint64(b[0])<<8
|
|
case 4:
|
|
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
|
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
|
|
case 8:
|
|
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
|
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
|
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
|
default:
|
|
panic("syscall: readInt with unsupported size")
|
|
}
|
|
}
|
|
|
|
func readIntLE(b []byte, size uintptr) uint64 {
|
|
switch size {
|
|
case 1:
|
|
return uint64(b[0])
|
|
case 2:
|
|
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
|
return uint64(b[0]) | uint64(b[1])<<8
|
|
case 4:
|
|
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
|
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
|
|
case 8:
|
|
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
|
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
|
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
|
default:
|
|
panic("syscall: readInt with unsupported size")
|
|
}
|
|
}
|