1use crate::directory::common::encode_dirent;
9use crate::directory::dirents_sink::{self, AppendResult};
10use crate::directory::entry::EntryInfo;
11use crate::directory::traversal_position::TraversalPosition;
12
13use fidl_fuchsia_io as fio;
14use std::any::Any;
15use std::convert::TryInto as _;
16use zx_status::Status;
17
18pub struct Sink {
22 buf: Vec<u8>,
23 max_bytes: u64,
24 state: SinkState,
25}
26
27pub struct Done {
30 pub(super) buf: Vec<u8>,
31 pub(super) status: Status,
32}
33
34#[derive(PartialEq, Eq)]
35enum SinkState {
36 NotCalled,
37 DidNotFit,
38 FitOne,
39}
40
41impl Sink {
42 pub(super) fn new(max_bytes: u64) -> Box<Sink> {
44 Box::new(Sink { buf: vec![], max_bytes, state: SinkState::NotCalled })
45 }
46}
47
48impl dirents_sink::Sink for Sink {
49 fn append(mut self: Box<Self>, entry: &EntryInfo, name: &str) -> AppendResult {
50 if !encode_dirent(&mut self.buf, self.max_bytes, entry, name) {
51 if self.state == SinkState::NotCalled {
52 self.state = SinkState::DidNotFit;
53 }
54 AppendResult::Sealed(self.seal())
55 } else {
56 if self.state == SinkState::NotCalled {
57 self.state = SinkState::FitOne;
58 }
59 AppendResult::Ok(self)
60 }
61 }
62
63 fn seal(self: Box<Self>) -> Box<dyn dirents_sink::Sealed> {
64 Box::new(Done {
65 buf: self.buf,
66 status: match self.state {
67 SinkState::NotCalled | SinkState::FitOne => Status::OK,
68 SinkState::DidNotFit => Status::BUFFER_TOO_SMALL,
69 },
70 })
71 }
72}
73
74impl dirents_sink::Sealed for Done {
75 fn open(self: Box<Self>) -> Box<dyn Any> {
76 self
77 }
78}
79
80pub async fn read_dirents<'a>(
86 entries: &'a [(EntryInfo, String)],
87 pos: &'a TraversalPosition,
88 mut sink: Box<(dyn dirents_sink::Sink + 'static)>,
89) -> Result<(TraversalPosition, Box<(dyn dirents_sink::Sealed + 'static)>), Status> {
90 let starting_position = match pos {
91 TraversalPosition::Start => {
92 match sink.append(&EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), ".") {
93 AppendResult::Ok(new_sink) => sink = new_sink,
94 AppendResult::Sealed(sealed) => {
95 return Ok((TraversalPosition::Start, sealed));
96 }
97 };
98 0usize
99 }
100 TraversalPosition::Index(i) => u64_to_usize_safe(*i),
101 TraversalPosition::End => {
102 return Ok((TraversalPosition::End, sink.seal()));
103 }
104 TraversalPosition::Name(_) => {
105 unreachable!("the VFS should never send this to us, since we never return it here");
106 }
107 };
108
109 for i in starting_position..entries.len() {
110 let (info, name) = &entries[i];
111 match sink.append(info, name) {
112 AppendResult::Ok(new_sink) => sink = new_sink,
113 AppendResult::Sealed(sealed) => {
114 return Ok((TraversalPosition::Index(usize_to_u64_safe(i)), sealed));
115 }
116 }
117 }
118 Ok((TraversalPosition::End, sink.seal()))
119}
120
121fn usize_to_u64_safe(u: usize) -> u64 {
122 let ret: u64 = u.try_into().unwrap();
123 static_assertions::assert_eq_size_val!(u, ret);
124 ret
125}
126
127fn u64_to_usize_safe(u: u64) -> usize {
128 let ret: usize = u.try_into().unwrap();
129 static_assertions::assert_eq_size_val!(u, ret);
130 ret
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn usize_to_u64_safe_does_not_panic() {
139 assert_eq!(usize_to_u64_safe(usize::MAX), usize::MAX as u64);
140 }
141
142 #[test]
143 fn u64_to_usize_safe_does_not_panic() {
144 assert_eq!(u64_to_usize_safe(u64::MAX), u64::MAX as usize);
145 }
146
147 #[derive(Clone)]
151 pub(crate) struct FakeSink {
152 max_entries: usize,
153 entries: Vec<(EntryInfo, String)>,
154 sealed: bool,
155 }
156
157 impl FakeSink {
158 fn new(max_entries: usize) -> Self {
159 FakeSink { max_entries, entries: Vec::with_capacity(max_entries), sealed: false }
160 }
161
162 fn from_sealed(sealed: Box<dyn dirents_sink::Sealed>) -> Box<FakeSink> {
163 sealed.into()
164 }
165 }
166
167 impl From<Box<dyn dirents_sink::Sealed>> for Box<FakeSink> {
168 fn from(sealed: Box<dyn dirents_sink::Sealed>) -> Self {
169 sealed.open().downcast::<FakeSink>().unwrap()
170 }
171 }
172
173 impl dirents_sink::Sink for FakeSink {
174 fn append(mut self: Box<Self>, entry: &EntryInfo, name: &str) -> AppendResult {
175 assert!(!self.sealed);
176 if self.entries.len() == self.max_entries {
177 AppendResult::Sealed(self.seal())
178 } else {
179 self.entries.push((entry.clone(), name.to_owned()));
180 AppendResult::Ok(self)
181 }
182 }
183
184 fn seal(mut self: Box<Self>) -> Box<dyn dirents_sink::Sealed> {
185 self.sealed = true;
186 self
187 }
188 }
189
190 impl dirents_sink::Sealed for FakeSink {
191 fn open(self: Box<Self>) -> Box<dyn Any> {
192 self
193 }
194 }
195
196 #[fuchsia::test]
197 async fn read_dirents_start() {
198 let entries = vec![
199 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), "dir".into()),
200 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File), "file".into()),
201 ];
202
203 let (pos, sealed) =
205 read_dirents(&entries, &TraversalPosition::Start, Box::new(FakeSink::new(0)))
206 .await
207 .expect("read_dirents failed");
208 assert_eq!(pos, TraversalPosition::Start);
209 assert_eq!(FakeSink::from_sealed(sealed).entries, vec![]);
210
211 let (pos, sealed) =
213 read_dirents(&entries, &TraversalPosition::Start, Box::new(FakeSink::new(2)))
214 .await
215 .expect("read_dirents failed");
216 assert_eq!(pos, TraversalPosition::Index(1));
217 assert_eq!(
218 FakeSink::from_sealed(sealed).entries,
219 vec![
220 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), ".".into()),
221 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), "dir".into()),
222 ]
223 );
224
225 let (pos, sealed) =
227 read_dirents(&entries, &TraversalPosition::Start, Box::new(FakeSink::new(3)))
228 .await
229 .expect("read_dirents failed");
230 assert_eq!(pos, TraversalPosition::End);
231 assert_eq!(
232 FakeSink::from_sealed(sealed).entries,
233 vec![
234 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), ".".into()),
235 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), "dir".into()),
236 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File), "file".into())
237 ]
238 );
239 }
240
241 #[fuchsia::test]
242 async fn read_dirents_index() {
243 let entries = vec![
244 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), "dir".into()),
245 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File), "file".into()),
246 ];
247
248 let (pos, sealed) =
250 read_dirents(&entries, &TraversalPosition::Index(0), Box::new(FakeSink::new(0)))
251 .await
252 .expect("read_dirents failed");
253 assert_eq!(pos, TraversalPosition::Index(0));
254 assert_eq!(FakeSink::from_sealed(sealed).entries, vec![]);
255
256 let (pos, sealed) =
258 read_dirents(&entries, &TraversalPosition::Index(0), Box::new(FakeSink::new(1)))
259 .await
260 .expect("read_dirents failed");
261 assert_eq!(pos, TraversalPosition::Index(1));
262 assert_eq!(
263 FakeSink::from_sealed(sealed).entries,
264 vec![(EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), "dir".into()),]
265 );
266
267 let (pos, sealed) =
269 read_dirents(&entries, &TraversalPosition::Index(0), Box::new(FakeSink::new(2)))
270 .await
271 .expect("read_dirents failed");
272 assert_eq!(pos, TraversalPosition::End);
273 assert_eq!(
274 FakeSink::from_sealed(sealed).entries,
275 vec![
276 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), "dir".into()),
277 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File), "file".into()),
278 ]
279 );
280 }
281
282 #[fuchsia::test]
283 async fn read_dirents_end() {
284 let entries = vec![
285 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), "dir".into()),
286 (EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File), "file".into()),
287 ];
288
289 let (pos, sealed) =
290 read_dirents(&entries, &TraversalPosition::End, Box::new(FakeSink::new(3)))
291 .await
292 .expect("read_dirents failed");
293 assert_eq!(pos, TraversalPosition::End);
294 assert_eq!(FakeSink::from_sealed(sealed).entries, vec![]);
295 }
296}