// +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)) } }