readdir_unix.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // +build !windows
  2. package godirwalk
  3. import (
  4. "os"
  5. "syscall"
  6. "unsafe"
  7. )
  8. // MinimumScratchBufferSize specifies the minimum size of the scratch buffer
  9. // that ReadDirents, ReadDirnames, Scanner, and Walk will use when reading file
  10. // entries from the operating system. During program startup it is initialized
  11. // to the result from calling `os.Getpagesize()` for non Windows environments,
  12. // and 0 for Windows.
  13. var MinimumScratchBufferSize = os.Getpagesize()
  14. func newScratchBuffer() []byte { return make([]byte, MinimumScratchBufferSize) }
  15. func readDirents(osDirname string, scratchBuffer []byte) ([]*Dirent, error) {
  16. var entries []*Dirent
  17. var workBuffer []byte
  18. dh, err := os.Open(osDirname)
  19. if err != nil {
  20. return nil, err
  21. }
  22. fd := int(dh.Fd())
  23. if len(scratchBuffer) < MinimumScratchBufferSize {
  24. scratchBuffer = newScratchBuffer()
  25. }
  26. var sde syscall.Dirent
  27. for {
  28. if len(workBuffer) == 0 {
  29. n, err := syscall.ReadDirent(fd, scratchBuffer)
  30. // n, err := unix.ReadDirent(fd, scratchBuffer)
  31. if err != nil {
  32. if err == syscall.EINTR /* || err == unix.EINTR */ {
  33. continue
  34. }
  35. _ = dh.Close()
  36. return nil, err
  37. }
  38. if n <= 0 { // end of directory: normal exit
  39. if err = dh.Close(); err != nil {
  40. return nil, err
  41. }
  42. return entries, nil
  43. }
  44. workBuffer = scratchBuffer[:n] // trim work buffer to number of bytes read
  45. }
  46. copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(&sde))[:], workBuffer)
  47. workBuffer = workBuffer[reclen(&sde):] // advance buffer for next iteration through loop
  48. if inoFromDirent(&sde) == 0 {
  49. continue // inode set to 0 indicates an entry that was marked as deleted
  50. }
  51. nameSlice := nameFromDirent(&sde)
  52. nameLength := len(nameSlice)
  53. if nameLength == 0 || (nameSlice[0] == '.' && (nameLength == 1 || (nameLength == 2 && nameSlice[1] == '.'))) {
  54. continue
  55. }
  56. childName := string(nameSlice)
  57. mt, err := modeTypeFromDirent(&sde, osDirname, childName)
  58. if err != nil {
  59. _ = dh.Close()
  60. return nil, err
  61. }
  62. entries = append(entries, &Dirent{name: childName, path: osDirname, modeType: mt})
  63. }
  64. }
  65. func readDirnames(osDirname string, scratchBuffer []byte) ([]string, error) {
  66. var entries []string
  67. var workBuffer []byte
  68. var sde *syscall.Dirent
  69. dh, err := os.Open(osDirname)
  70. if err != nil {
  71. return nil, err
  72. }
  73. fd := int(dh.Fd())
  74. if len(scratchBuffer) < MinimumScratchBufferSize {
  75. scratchBuffer = newScratchBuffer()
  76. }
  77. for {
  78. if len(workBuffer) == 0 {
  79. n, err := syscall.ReadDirent(fd, scratchBuffer)
  80. // n, err := unix.ReadDirent(fd, scratchBuffer)
  81. if err != nil {
  82. if err == syscall.EINTR /* || err == unix.EINTR */ {
  83. continue
  84. }
  85. _ = dh.Close()
  86. return nil, err
  87. }
  88. if n <= 0 { // end of directory: normal exit
  89. if err = dh.Close(); err != nil {
  90. return nil, err
  91. }
  92. return entries, nil
  93. }
  94. workBuffer = scratchBuffer[:n] // trim work buffer to number of bytes read
  95. }
  96. sde = (*syscall.Dirent)(unsafe.Pointer(&workBuffer[0])) // point entry to first syscall.Dirent in buffer
  97. // Handle first entry in the work buffer.
  98. workBuffer = workBuffer[reclen(sde):] // advance buffer for next iteration through loop
  99. if inoFromDirent(sde) == 0 {
  100. continue // inode set to 0 indicates an entry that was marked as deleted
  101. }
  102. nameSlice := nameFromDirent(sde)
  103. nameLength := len(nameSlice)
  104. if nameLength == 0 || (nameSlice[0] == '.' && (nameLength == 1 || (nameLength == 2 && nameSlice[1] == '.'))) {
  105. continue
  106. }
  107. entries = append(entries, string(nameSlice))
  108. }
  109. }