dir_linux.odin 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #+private
  2. package os2
  3. import "core:sys/linux"
  4. Read_Directory_Iterator_Impl :: struct {
  5. prev_fi: File_Info,
  6. dirent_backing: []u8,
  7. dirent_buflen: int,
  8. dirent_off: int,
  9. }
  10. @(require_results)
  11. _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
  12. scan_entries :: proc(it: ^Read_Directory_Iterator, dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
  13. for d in linux.dirent_iterate_buf(entries, offset) {
  14. file_name = linux.dirent_name(d)
  15. if file_name == "." || file_name == ".." {
  16. continue
  17. }
  18. file_name_cstr := cstring(raw_data(file_name))
  19. entry_fd, errno := linux.openat(dfd, file_name_cstr, {.NOFOLLOW, .PATH})
  20. if errno == .NONE {
  21. return entry_fd, file_name
  22. } else {
  23. read_directory_iterator_set_error(it, file_name, _get_platform_error(errno))
  24. }
  25. }
  26. return -1, ""
  27. }
  28. index = it.index
  29. it.index += 1
  30. dfd := linux.Fd(_fd(it.f))
  31. entries := it.impl.dirent_backing[:it.impl.dirent_buflen]
  32. entry_fd, file_name := scan_entries(it, dfd, entries, &it.impl.dirent_off)
  33. for entry_fd == -1 {
  34. if len(it.impl.dirent_backing) == 0 {
  35. it.impl.dirent_backing = make([]u8, 512, file_allocator())
  36. }
  37. loop: for {
  38. buflen, errno := linux.getdents(linux.Fd(dfd), it.impl.dirent_backing[:])
  39. #partial switch errno {
  40. case .EINVAL:
  41. delete(it.impl.dirent_backing, file_allocator())
  42. n := len(it.impl.dirent_backing) * 2
  43. it.impl.dirent_backing = make([]u8, n, file_allocator())
  44. continue
  45. case .NONE:
  46. if buflen == 0 {
  47. return
  48. }
  49. it.impl.dirent_off = 0
  50. it.impl.dirent_buflen = buflen
  51. entries = it.impl.dirent_backing[:buflen]
  52. break loop
  53. case:
  54. read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
  55. return
  56. }
  57. }
  58. entry_fd, file_name = scan_entries(it, dfd, entries, &it.impl.dirent_off)
  59. }
  60. defer linux.close(entry_fd)
  61. // PERF: reuse the fullpath string like on posix and wasi.
  62. file_info_delete(it.impl.prev_fi, file_allocator())
  63. err: Error
  64. fi, err = _fstat_internal(entry_fd, file_allocator())
  65. it.impl.prev_fi = fi
  66. if err != nil {
  67. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  68. path, _ := _get_full_path(entry_fd, temp_allocator)
  69. read_directory_iterator_set_error(it, path, err)
  70. }
  71. ok = true
  72. return
  73. }
  74. _read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
  75. // NOTE: Allow calling `init` to target a new directory with the same iterator.
  76. it.impl.dirent_buflen = 0
  77. it.impl.dirent_off = 0
  78. if f == nil || f.impl == nil {
  79. read_directory_iterator_set_error(it, "", .Invalid_File)
  80. return
  81. }
  82. stat: linux.Stat
  83. errno := linux.fstat(linux.Fd(fd(f)), &stat)
  84. if errno != .NONE {
  85. read_directory_iterator_set_error(it, name(f), _get_platform_error(errno))
  86. return
  87. }
  88. if (stat.mode & linux.S_IFMT) != linux.S_IFDIR {
  89. read_directory_iterator_set_error(it, name(f), .Invalid_Dir)
  90. return
  91. }
  92. }
  93. _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
  94. if it == nil {
  95. return
  96. }
  97. delete(it.impl.dirent_backing, file_allocator())
  98. file_info_delete(it.impl.prev_fi, file_allocator())
  99. }