132 lines
3.5 KiB
Go
132 lines
3.5 KiB
Go
|
// +build !windows
|
||
|
|
||
|
package godirwalk
|
||
|
|
||
|
import (
|
||
|
"os"
|
||
|
"syscall"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
// MinimumScratchBufferSize specifies the minimum size of the scratch buffer
|
||
|
// that ReadDirents, ReadDirnames, Scanner, and Walk will use when reading file
|
||
|
// entries from the operating system. During program startup it is initialized
|
||
|
// to the result from calling `os.Getpagesize()` for non Windows environments,
|
||
|
// and 0 for Windows.
|
||
|
var MinimumScratchBufferSize = os.Getpagesize()
|
||
|
|
||
|
func newScratchBuffer() []byte { return make([]byte, MinimumScratchBufferSize) }
|
||
|
|
||
|
func readDirents(osDirname string, scratchBuffer []byte) ([]*Dirent, error) {
|
||
|
var entries []*Dirent
|
||
|
var workBuffer []byte
|
||
|
|
||
|
dh, err := os.Open(osDirname)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
fd := int(dh.Fd())
|
||
|
|
||
|
if len(scratchBuffer) < MinimumScratchBufferSize {
|
||
|
scratchBuffer = newScratchBuffer()
|
||
|
}
|
||
|
|
||
|
var sde syscall.Dirent
|
||
|
for {
|
||
|
if len(workBuffer) == 0 {
|
||
|
n, err := syscall.ReadDirent(fd, scratchBuffer)
|
||
|
// n, err := unix.ReadDirent(fd, scratchBuffer)
|
||
|
if err != nil {
|
||
|
if err == syscall.EINTR /* || err == unix.EINTR */ {
|
||
|
continue
|
||
|
}
|
||
|
_ = dh.Close()
|
||
|
return nil, err
|
||
|
}
|
||
|
if n <= 0 { // end of directory: normal exit
|
||
|
if err = dh.Close(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return entries, nil
|
||
|
}
|
||
|
workBuffer = scratchBuffer[:n] // trim work buffer to number of bytes read
|
||
|
}
|
||
|
|
||
|
copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(&sde))[:], workBuffer)
|
||
|
workBuffer = workBuffer[reclen(&sde):] // advance buffer for next iteration through loop
|
||
|
|
||
|
if inoFromDirent(&sde) == 0 {
|
||
|
continue // inode set to 0 indicates an entry that was marked as deleted
|
||
|
}
|
||
|
|
||
|
nameSlice := nameFromDirent(&sde)
|
||
|
nameLength := len(nameSlice)
|
||
|
|
||
|
if nameLength == 0 || (nameSlice[0] == '.' && (nameLength == 1 || (nameLength == 2 && nameSlice[1] == '.'))) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
childName := string(nameSlice)
|
||
|
mt, err := modeTypeFromDirent(&sde, osDirname, childName)
|
||
|
if err != nil {
|
||
|
_ = dh.Close()
|
||
|
return nil, err
|
||
|
}
|
||
|
entries = append(entries, &Dirent{name: childName, path: osDirname, modeType: mt})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func readDirnames(osDirname string, scratchBuffer []byte) ([]string, error) {
|
||
|
var entries []string
|
||
|
var workBuffer []byte
|
||
|
var sde *syscall.Dirent
|
||
|
|
||
|
dh, err := os.Open(osDirname)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
fd := int(dh.Fd())
|
||
|
|
||
|
if len(scratchBuffer) < MinimumScratchBufferSize {
|
||
|
scratchBuffer = newScratchBuffer()
|
||
|
}
|
||
|
|
||
|
for {
|
||
|
if len(workBuffer) == 0 {
|
||
|
n, err := syscall.ReadDirent(fd, scratchBuffer)
|
||
|
// n, err := unix.ReadDirent(fd, scratchBuffer)
|
||
|
if err != nil {
|
||
|
if err == syscall.EINTR /* || err == unix.EINTR */ {
|
||
|
continue
|
||
|
}
|
||
|
_ = dh.Close()
|
||
|
return nil, err
|
||
|
}
|
||
|
if n <= 0 { // end of directory: normal exit
|
||
|
if err = dh.Close(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return entries, nil
|
||
|
}
|
||
|
workBuffer = scratchBuffer[:n] // trim work buffer to number of bytes read
|
||
|
}
|
||
|
|
||
|
sde = (*syscall.Dirent)(unsafe.Pointer(&workBuffer[0])) // point entry to first syscall.Dirent in buffer
|
||
|
// Handle first entry in the work buffer.
|
||
|
workBuffer = workBuffer[reclen(sde):] // advance buffer for next iteration through loop
|
||
|
|
||
|
if inoFromDirent(sde) == 0 {
|
||
|
continue // inode set to 0 indicates an entry that was marked as deleted
|
||
|
}
|
||
|
|
||
|
nameSlice := nameFromDirent(sde)
|
||
|
nameLength := len(nameSlice)
|
||
|
|
||
|
if nameLength == 0 || (nameSlice[0] == '.' && (nameLength == 1 || (nameLength == 2 && nameSlice[1] == '.'))) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
entries = append(entries, string(nameSlice))
|
||
|
}
|
||
|
}
|