mod.rs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. //! ::dirent implementation following http://pubs.opengroup.org/onlinepubs/009695399/basedefs/dirent.h.html
  2. use alloc::boxed::Box;
  3. use core::{mem, ptr};
  4. use crate::unix::{
  5. c_str::CStr,
  6. c_vec::CVec,
  7. fs::File,
  8. header::{errno, fcntl, stdlib, string},
  9. io::{Seek, SeekFrom},
  10. platform,
  11. };
  12. const DIR_BUF_SIZE: usize = mem::size_of::<::dirent>() * 3;
  13. // No repr(C) needed, C won't see the content
  14. pub struct DIR {
  15. file: File,
  16. buf: [::c_char; DIR_BUF_SIZE],
  17. // index and len are specified in bytes
  18. index: usize,
  19. len: usize,
  20. // The last value of d_off, used by telldir
  21. offset: usize,
  22. }
  23. #[no_mangle]
  24. pub unsafe extern "C" fn opendir(path: *const ::c_char) -> *mut DIR {
  25. let path = CStr::from_ptr(path);
  26. let file = match File::open(
  27. path,
  28. fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
  29. ) {
  30. Ok(file) => file,
  31. Err(_) => return ptr::null_mut(),
  32. };
  33. Box::into_raw(Box::new(DIR {
  34. file,
  35. buf: [0; DIR_BUF_SIZE],
  36. index: 0,
  37. len: 0,
  38. offset: 0,
  39. }))
  40. }
  41. #[no_mangle]
  42. pub unsafe extern "C" fn closedir(dir: *mut DIR) -> ::c_int {
  43. let mut dir = Box::from_raw(dir);
  44. let ret = platform::pal::close(*dir.file);
  45. // Reference files aren't closed when dropped
  46. dir.file.reference = true;
  47. ret
  48. }
  49. #[no_mangle]
  50. pub unsafe extern "C" fn dirfd(dir: *mut DIR) -> ::c_int {
  51. *((*dir).file)
  52. }
  53. #[no_mangle]
  54. pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut ::dirent {
  55. if (*dir).index >= (*dir).len {
  56. let read = platform::pal::getdents(
  57. *(*dir).file,
  58. (*dir).buf.as_mut_ptr() as *mut ::dirent,
  59. (*dir).buf.len(),
  60. );
  61. if read <= 0 {
  62. if read != 0 && read != -errno::ENOENT {
  63. platform::errno = -read;
  64. }
  65. return ptr::null_mut();
  66. }
  67. (*dir).index = 0;
  68. (*dir).len = read as usize;
  69. }
  70. let ptr = (*dir).buf.as_mut_ptr().add((*dir).index) as *mut ::dirent;
  71. (*dir).offset = (*ptr).d_off as usize;
  72. (*dir).index += (*ptr).d_reclen as usize;
  73. ptr
  74. }
  75. // #[no_mangle]
  76. pub extern "C" fn readdir_r(
  77. _dir: *mut DIR,
  78. _entry: *mut ::dirent,
  79. _result: *mut *mut ::dirent,
  80. ) -> *mut ::dirent {
  81. unimplemented!(); // plus, deprecated
  82. }
  83. #[no_mangle]
  84. pub unsafe extern "C" fn telldir(dir: *mut DIR) -> ::c_long {
  85. (*dir).offset as ::c_long
  86. }
  87. #[no_mangle]
  88. pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: ::c_long) {
  89. let _ = (*dir).file.seek(SeekFrom::Start(off as u64));
  90. (*dir).offset = off as usize;
  91. (*dir).index = 0;
  92. (*dir).len = 0;
  93. }
  94. #[no_mangle]
  95. pub unsafe extern "C" fn rewinddir(dir: *mut DIR) {
  96. seekdir(dir, 0)
  97. }
  98. #[no_mangle]
  99. pub unsafe extern "C" fn alphasort(first: *mut *const ::dirent, second: *mut *const ::dirent) -> ::c_int {
  100. string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr())
  101. }
  102. #[no_mangle]
  103. pub unsafe extern "C" fn scandir(
  104. dirp: *const ::c_char,
  105. namelist: *mut *mut *mut ::dirent,
  106. filter: Option<extern "C" fn(_: *const ::dirent) -> ::c_int>,
  107. compare: Option<extern "C" fn(_: *mut *const ::dirent, _: *mut *const ::dirent) -> ::c_int>,
  108. ) -> ::c_int {
  109. let dir = opendir(dirp);
  110. if dir.is_null() {
  111. return -1;
  112. }
  113. let mut vec = match CVec::with_capacity(4) {
  114. Ok(vec) => vec,
  115. Err(err) => return -1,
  116. };
  117. let old_errno = platform::errno;
  118. platform::errno = 0;
  119. loop {
  120. let entry: *mut ::dirent = readdir(dir);
  121. if entry.is_null() {
  122. break;
  123. }
  124. if let Some(filter) = filter {
  125. if filter(entry) == 0 {
  126. continue;
  127. }
  128. }
  129. let copy = platform::alloc(mem::size_of::<::dirent>()) as *mut ::dirent;
  130. if copy.is_null() {
  131. break;
  132. }
  133. ptr::write(copy, (*entry).clone());
  134. if let Err(_) = vec.push(copy) {
  135. break;
  136. }
  137. }
  138. closedir(dir);
  139. let len = vec.len();
  140. if let Err(_) = vec.shrink_to_fit() {
  141. return -1;
  142. }
  143. if platform::errno != 0 {
  144. for ptr in &mut vec {
  145. platform::free(*ptr as *mut ::c_void);
  146. }
  147. -1
  148. } else {
  149. *namelist = vec.leak();
  150. platform::errno = old_errno;
  151. stdlib::qsort(
  152. *namelist as *mut ::c_void,
  153. len as ::size_t,
  154. mem::size_of::<*mut ::dirent>(),
  155. mem::transmute(compare),
  156. );
  157. len as ::c_int
  158. }
  159. }