blob: a274dd126808eba7c18faf5198a40b28bcf2b547 [file] [log] [blame]
// 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) seekInvalidate() {
if f.dirinfo == nil {
return
}
// Free cached dirinfo, so we allocate a new one if we
// access this file as a directory again. See #35767.
f.dirinfo.close()
f.dirinfo = nil
}
func (f *File) readdirnames(n int) (names []string, err error) {
if f.dirinfo == nil {
dir, call, errno := f.pfd.OpenDir()
if errno != nil {
return nil, wrapSyscallError(call, errno)
}
f.dirinfo = &dirInfo{
dir: dir,
}
}
d := f.dirinfo
size := n
if size <= 0 {
size = 100
n = -1
}
names = make([]string, 0, size)
var dirent syscall.Dirent
var entptr *syscall.Dirent
for len(names) < size || n == -1 {
if res := readdir_r(d.dir, &dirent, &entptr); res != 0 {
return names, wrapSyscallError("readdir", syscall.Errno(res))
}
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
}
names = append(names, string(name))
runtime.KeepAlive(f)
}
if n >= 0 && len(names) == 0 {
return names, io.EOF
}
return names, 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 int)