-
Notifications
You must be signed in to change notification settings - Fork 867
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
os: implement readdir for darwin and linux
readdir is disabled on linux for 386 and arm until syscall.seek is implemented there. windows is hard, so leaving that for later. File src/os/dir_other_go115.go can be deleted when we drop support for go 1.15.
- Loading branch information
1 parent
4681d14
commit baff248
Showing
21 changed files
with
971 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// +build go1.16 | ||
|
||
// Copyright 2016 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. | ||
|
||
package os | ||
|
||
import ( | ||
"io/fs" | ||
"sort" | ||
) | ||
|
||
type readdirMode int | ||
|
||
const ( | ||
readdirName readdirMode = iota | ||
readdirDirEntry | ||
readdirFileInfo | ||
) | ||
|
||
// Readdir reads the contents of the directory associated with file and | ||
// returns a slice of up to n FileInfo values, as would be returned | ||
// by Lstat, in directory order. Subsequent calls on the same file will yield | ||
// further FileInfos. | ||
// | ||
// If n > 0, Readdir returns at most n FileInfo structures. In this case, if | ||
// Readdir returns an empty slice, it will return a non-nil error | ||
// explaining why. At the end of a directory, the error is io.EOF. | ||
// | ||
// If n <= 0, Readdir returns all the FileInfo from the directory in | ||
// a single slice. In this case, if Readdir succeeds (reads all | ||
// the way to the end of the directory), it returns the slice and a | ||
// nil error. If it encounters an error before the end of the | ||
// directory, Readdir returns the FileInfo read until that point | ||
// and a non-nil error. | ||
// | ||
// Most clients are better served by the more efficient ReadDir method. | ||
func (f *File) Readdir(n int) ([]FileInfo, error) { | ||
if f == nil { | ||
return nil, ErrInvalid | ||
} | ||
_, _, infos, err := f.readdir(n, readdirFileInfo) | ||
if infos == nil { | ||
// Readdir has historically always returned a non-nil empty slice, never nil, | ||
// even on error (except misuse with nil receiver above). | ||
// Keep it that way to avoid breaking overly sensitive callers. | ||
infos = []FileInfo{} | ||
} | ||
return infos, err | ||
} | ||
|
||
// Readdirnames reads the contents of the directory associated with file | ||
// and returns a slice of up to n names of files in the directory, | ||
// in directory order. Subsequent calls on the same file will yield | ||
// further names. | ||
// | ||
// If n > 0, Readdirnames returns at most n names. In this case, if | ||
// Readdirnames returns an empty slice, it will return a non-nil error | ||
// explaining why. At the end of a directory, the error is io.EOF. | ||
// | ||
// If n <= 0, Readdirnames returns all the names from the directory in | ||
// a single slice. In this case, if Readdirnames succeeds (reads all | ||
// the way to the end of the directory), it returns the slice and a | ||
// nil error. If it encounters an error before the end of the | ||
// directory, Readdirnames returns the names read until that point and | ||
// a non-nil error. | ||
func (f *File) Readdirnames(n int) (names []string, err error) { | ||
if f == nil { | ||
return nil, ErrInvalid | ||
} | ||
names, _, _, err = f.readdir(n, readdirName) | ||
if names == nil { | ||
// Readdirnames has historically always returned a non-nil empty slice, never nil, | ||
// even on error (except misuse with nil receiver above). | ||
// Keep it that way to avoid breaking overly sensitive callers. | ||
names = []string{} | ||
} | ||
return names, err | ||
} | ||
|
||
// A DirEntry is an entry read from a directory | ||
// (using the ReadDir function or a File's ReadDir method). | ||
type DirEntry = fs.DirEntry | ||
|
||
// ReadDir reads the contents of the directory associated with the file f | ||
// and returns a slice of DirEntry values in directory order. | ||
// Subsequent calls on the same file will yield later DirEntry records in the directory. | ||
// | ||
// If n > 0, ReadDir returns at most n DirEntry records. | ||
// In this case, if ReadDir returns an empty slice, it will return an error explaining why. | ||
// At the end of a directory, the error is io.EOF. | ||
// | ||
// If n <= 0, ReadDir returns all the DirEntry records remaining in the directory. | ||
// When it succeeds, it returns a nil error (not io.EOF). | ||
func (f *File) ReadDir(n int) ([]DirEntry, error) { | ||
if f == nil { | ||
return nil, ErrInvalid | ||
} | ||
_, dirents, _, err := f.readdir(n, readdirDirEntry) | ||
if dirents == nil { | ||
// Match Readdir and Readdirnames: don't return nil slices. | ||
dirents = []DirEntry{} | ||
} | ||
return dirents, err | ||
} | ||
|
||
// testingForceReadDirLstat forces ReadDir to call Lstat, for testing that code path. | ||
// This can be difficult to provoke on some Unix systems otherwise. | ||
var testingForceReadDirLstat bool | ||
|
||
// ReadDir reads the named directory, | ||
// returning all its directory entries sorted by filename. | ||
// If an error occurs reading the directory, | ||
// ReadDir returns the entries it was able to read before the error, | ||
// along with the error. | ||
func ReadDir(name string) ([]DirEntry, error) { | ||
f, err := Open(name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer f.Close() | ||
|
||
dirs, err := f.ReadDir(-1) | ||
sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) | ||
return dirs, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// 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. | ||
|
||
package os | ||
|
||
import ( | ||
"io" | ||
"runtime" | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
// Auxiliary information if the File describes a directory | ||
type dirInfo struct { | ||
dir uintptr // Pointer to DIR structure from dirent.h | ||
} | ||
|
||
func (d *dirInfo) close() { | ||
if d.dir == 0 { | ||
return | ||
} | ||
closedir(d.dir) | ||
d.dir = 0 | ||
} | ||
|
||
func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { | ||
if f.dirinfo == nil { | ||
dir, call, errno := darwinOpenDir(syscallFd(f.handle.(unixFileHandle))) | ||
if errno != nil { | ||
return nil, nil, nil, &PathError{Op: call, Path: f.name, Err: errno} | ||
} | ||
f.dirinfo = &dirInfo{ | ||
dir: dir, | ||
} | ||
} | ||
d := f.dirinfo | ||
|
||
size := n | ||
if size <= 0 { | ||
size = 100 | ||
n = -1 | ||
} | ||
|
||
var dirent syscall.Dirent | ||
var entptr *syscall.Dirent | ||
for len(names)+len(dirents)+len(infos) < size || n == -1 { | ||
if errno := readdir_r(d.dir, &dirent, &entptr); errno != 0 { | ||
if errno == syscall.EINTR { | ||
continue | ||
} | ||
return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno} | ||
} | ||
if entptr == nil { // EOF | ||
break | ||
} | ||
if dirent.Ino == 0 { | ||
continue | ||
} | ||
name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] | ||
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 mode == readdirName { | ||
names = append(names, string(name)) | ||
} else if mode == readdirDirEntry { | ||
de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type)) | ||
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) | ||
} | ||
runtime.KeepAlive(f) | ||
} | ||
|
||
if n > 0 && len(names)+len(dirents)+len(infos) == 0 { | ||
return nil, nil, nil, io.EOF | ||
} | ||
return names, dirents, infos, nil | ||
} | ||
|
||
func dtToType(typ uint8) FileMode { | ||
switch typ { | ||
case syscall.DT_BLK: | ||
return ModeDevice | ||
case syscall.DT_CHR: | ||
return ModeDevice | ModeCharDevice | ||
case syscall.DT_DIR: | ||
return ModeDir | ||
case syscall.DT_FIFO: | ||
return ModeNamedPipe | ||
case syscall.DT_LNK: | ||
return ModeSymlink | ||
case syscall.DT_REG: | ||
return 0 | ||
case syscall.DT_SOCK: | ||
return ModeSocket | ||
} | ||
return ^FileMode(0) | ||
} | ||
|
||
// darwinOpenDir returns a pointer to a DIR structure suitable for | ||
// ReadDir. In case of an error, the name of the failed | ||
// syscall is returned along with a syscall.Errno. | ||
// Borrowed from upstream's internal/poll/fd_opendir_darwin.go | ||
func darwinOpenDir(fd syscallFd) (uintptr, string, error) { | ||
// fdopendir(3) takes control of the file descriptor, | ||
// so use a dup. | ||
fd2, err := syscall.Dup(fd) | ||
if err != nil { | ||
return 0, "dup", err | ||
} | ||
var dir uintptr | ||
for { | ||
dir, err = syscall.Fdopendir(fd2) | ||
if err != syscall.EINTR { | ||
break | ||
} | ||
} | ||
if err != nil { | ||
syscall.Close(fd2) | ||
return 0, "fdopendir", err | ||
} | ||
return dir, "", nil | ||
} | ||
|
||
// Implemented in syscall/syscall_darwin.go. | ||
|
||
//go:linkname closedir syscall.closedir | ||
func closedir(dir uintptr) (err error) | ||
|
||
//go:linkname readdir_r syscall.readdir_r | ||
func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res syscall.Errno) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// +build go1.16,baremetal go1.16,js go1.16,wasi go1.16,windows | ||
|
||
// 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. | ||
|
||
package os | ||
|
||
import ( | ||
"syscall" | ||
) | ||
|
||
type dirInfo struct { | ||
} | ||
|
||
func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { | ||
return nil, nil, nil, &PathError{Op: "readdir unimplemented", Err: syscall.ENOTDIR} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// +build !go1.16 | ||
|
||
// 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. | ||
|
||
package os | ||
|
||
func (f *File) Readdirnames(n int) (names []string, err error) { | ||
return nil, ErrInvalid | ||
} |
Oops, something went wrong.