xref: /drstd/dlibc/src/unix/header/dirent/mod.rs (revision 0fe3ff0054d3aec7fbf9bddecfecb10bc7d23a51)
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