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