1use crate::common::CreationMode;
10use crate::directory::dirents_sink;
11use crate::directory::entry::{DirectoryEntry, EntryInfo, OpenRequest, RequestFlags};
12use crate::directory::entry_container::{Directory, DirectoryWatcher};
13use crate::directory::helper::{AlreadyExists, DirectlyMutable, NotDirectory};
14use crate::directory::immutable::connection::ImmutableConnection;
15use crate::directory::traversal_position::TraversalPosition;
16use crate::directory::watchers::Watchers;
17use crate::directory::watchers::event_producers::{
18 SingleNameEventProducer, StaticVecEventProducer,
19};
20use crate::execution_scope::ExecutionScope;
21use crate::name::Name;
22use crate::node::Node;
23use crate::path::Path;
24use crate::protocols::ProtocolsExt;
25use crate::{ObjectRequestRef, ToObjectRequest};
26use fidl::endpoints::ServerEnd;
27use fidl_fuchsia_io as fio;
28use fuchsia_sync::Mutex;
29use std::collections::BTreeMap;
30use std::collections::btree_map::Entry;
31use std::iter;
32use std::sync::Arc;
33use zx_status::Status;
34
35use super::entry::GetEntryInfo;
36
37pub struct Simple {
42 inner: Mutex<Inner>,
43
44 inode: u64,
46
47 not_found_handler: Mutex<Option<Box<dyn FnMut(&str) + Send + Sync + 'static>>>,
48}
49
50struct Inner {
51 entries: BTreeMap<Name, Arc<dyn DirectoryEntry>>,
52
53 watchers: Watchers,
54}
55
56impl Simple {
57 pub fn new() -> Arc<Self> {
58 Self::new_with_inode(fio::INO_UNKNOWN)
59 }
60
61 pub(crate) fn new_with_inode(inode: u64) -> Arc<Self> {
62 Arc::new(Simple {
63 inner: Mutex::new(Inner { entries: BTreeMap::new(), watchers: Watchers::new() }),
64 inode,
65 not_found_handler: Mutex::new(None),
66 })
67 }
68
69 pub fn set_not_found_handler(
73 self: Arc<Self>,
74 handler: Box<dyn FnMut(&str) + Send + Sync + 'static>,
75 ) {
76 let mut this = self.not_found_handler.lock();
77 this.replace(handler);
78 }
79
80 pub fn get_entry(&self, name: &str) -> Result<Arc<dyn DirectoryEntry>, Status> {
82 crate::name::validate_name(name)?;
83
84 let this = self.inner.lock();
85 match this.entries.get(name) {
86 Some(entry) => Ok(entry.clone()),
87 None => Err(Status::NOT_FOUND),
88 }
89 }
90
91 pub fn get_or_insert<T: DirectoryEntry>(
93 &self,
94 name: Name,
95 f: impl FnOnce() -> Arc<T>,
96 ) -> Arc<dyn DirectoryEntry> {
97 let mut guard = self.inner.lock();
98 let inner = &mut *guard;
99 match inner.entries.entry(name) {
100 Entry::Vacant(slot) => {
101 inner.watchers.send_event(&mut SingleNameEventProducer::added(""));
102 slot.insert(f()).clone()
103 }
104 Entry::Occupied(entry) => entry.get().clone(),
105 }
106 }
107
108 pub fn remove_all_entries(&self) {
110 let mut inner = self.inner.lock();
111 if !inner.entries.is_empty() {
112 let names = std::mem::take(&mut inner.entries)
113 .into_keys()
114 .map(String::from)
115 .collect::<Vec<String>>();
116 inner.watchers.send_event(&mut StaticVecEventProducer::removed(names));
117 }
118 }
119
120 fn open_impl<'a, P: ProtocolsExt + ToRequestFlags>(
121 self: Arc<Self>,
122 mut scope: ExecutionScope,
123 mut path: Path,
124 protocols: P,
125 object_request: ObjectRequestRef<'_>,
126 ) -> Result<(), Status> {
127 let (name, path_ref) = match path.next_with_ref() {
130 (path_ref, Some(name)) => (name, path_ref),
131 (_, None) => {
132 if protocols.create_unnamed_temporary_in_directory_path() {
133 return Err(Status::NOT_SUPPORTED);
135 }
136 object_request
137 .take()
138 .create_connection_sync::<ImmutableConnection<_>, _>(scope, self, protocols);
139 return Ok(());
140 }
141 };
142
143 let _guard;
145 let entry = match self.inner.lock().entries.get(name) {
146 Some(entry) => {
147 if let Some(s) = entry.scope() {
149 let Some(g) = s.try_active_guard() else {
151 return Err(Status::PEER_CLOSED);
152 };
153 scope = s;
154 _guard = g;
155 }
156 Some(entry.clone())
157 }
158 None => None,
159 };
160
161 match (entry, path_ref.is_empty(), protocols.creation_mode()) {
162 (None, false, _) | (None, true, CreationMode::Never) => {
163 if let Some(not_found_handler) = &mut *self.not_found_handler.lock() {
168 not_found_handler(path_ref.as_str());
169 }
170 Err(Status::NOT_FOUND)
171 }
172 (
173 None,
174 true,
175 CreationMode::Always
176 | CreationMode::AllowExisting
177 | CreationMode::UnnamedTemporary
178 | CreationMode::UnlinkableUnnamedTemporary,
179 ) => {
180 Err(Status::NOT_SUPPORTED)
183 }
184 (
185 Some(_),
186 true,
187 CreationMode::UnnamedTemporary | CreationMode::UnlinkableUnnamedTemporary,
188 ) => {
189 Err(Status::NOT_SUPPORTED)
193 }
194 (Some(_), true, CreationMode::Always) => {
195 Err(Status::ALREADY_EXISTS)
198 }
199 (Some(entry), _, _) => entry.open_entry(OpenRequest::new(
200 scope,
201 protocols.to_request_flags(),
202 path,
203 object_request,
204 )),
205 }
206 }
207}
208
209impl GetEntryInfo for Simple {
210 fn entry_info(&self) -> EntryInfo {
211 EntryInfo::new(self.inode, fio::DirentType::Directory)
212 }
213}
214
215impl DirectoryEntry for Simple {
216 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
217 request.open_dir(self)
218 }
219}
220
221impl Node for Simple {
222 async fn get_attributes(
223 &self,
224 requested_attributes: fio::NodeAttributesQuery,
225 ) -> Result<fio::NodeAttributes2, Status> {
226 Ok(immutable_attributes!(
227 requested_attributes,
228 Immutable {
229 protocols: fio::NodeProtocolKinds::DIRECTORY,
230 abilities: fio::Operations::GET_ATTRIBUTES
231 | fio::Operations::ENUMERATE
232 | fio::Operations::TRAVERSE,
233 id: self.inode,
234 }
235 ))
236 }
237}
238
239impl Directory for Simple {
240 fn deprecated_open(
241 self: Arc<Self>,
242 scope: ExecutionScope,
243 flags: fio::OpenFlags,
244 path: Path,
245 server_end: ServerEnd<fio::NodeMarker>,
246 ) {
247 flags
248 .to_object_request(server_end)
249 .handle(|object_request| self.open_impl(scope, path, flags, object_request));
250 }
251
252 fn open(
253 self: Arc<Self>,
254 scope: ExecutionScope,
255 path: Path,
256 flags: fio::Flags,
257 object_request: ObjectRequestRef<'_>,
258 ) -> Result<(), Status> {
259 self.open_impl(scope, path, flags, object_request)
260 }
261
262 async fn read_dirents(
263 &self,
264 pos: &TraversalPosition,
265 sink: Box<dyn dirents_sink::Sink>,
266 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status> {
267 use dirents_sink::AppendResult;
268
269 let this = self.inner.lock();
270
271 let (mut sink, entries_iter) = match pos {
272 TraversalPosition::Start => {
273 match sink.append(&EntryInfo::new(self.inode, fio::DirentType::Directory), ".") {
274 AppendResult::Ok(sink) => (sink, this.entries.range::<Name, _>(..)),
275 AppendResult::Sealed(sealed) => {
276 return Ok((TraversalPosition::Start, sealed));
277 }
278 }
279 }
280
281 TraversalPosition::Name(next_name) => {
282 let next: Name = next_name.to_owned().try_into().unwrap();
286 (sink, this.entries.range::<Name, _>(next..))
287 }
288
289 TraversalPosition::Bytes(_) | TraversalPosition::Index(_) => unreachable!(),
290
291 TraversalPosition::End => return Ok((TraversalPosition::End, sink.seal())),
292 };
293
294 for (name, entry) in entries_iter {
295 match sink.append(&entry.entry_info(), &name) {
296 AppendResult::Ok(new_sink) => sink = new_sink,
297 AppendResult::Sealed(sealed) => {
298 return Ok((TraversalPosition::Name(name.clone().into()), sealed));
299 }
300 }
301 }
302
303 Ok((TraversalPosition::End, sink.seal()))
304 }
305
306 fn register_watcher(
307 self: Arc<Self>,
308 scope: ExecutionScope,
309 mask: fio::WatchMask,
310 watcher: DirectoryWatcher,
311 ) -> Result<(), Status> {
312 let mut this = self.inner.lock();
313
314 let mut names = StaticVecEventProducer::existing({
315 let entry_names = this.entries.keys();
316 iter::once(".".to_string()).chain(entry_names.map(|x| x.to_owned().into())).collect()
317 });
318
319 let controller = this.watchers.add(scope, self.clone(), mask, watcher);
320 controller.send_event(&mut names);
321 controller.send_event(&mut SingleNameEventProducer::idle());
322
323 Ok(())
324 }
325
326 fn unregister_watcher(self: Arc<Self>, key: usize) {
327 let mut this = self.inner.lock();
328 this.watchers.remove(key);
329 }
330}
331
332impl DirectlyMutable for Simple {
333 fn add_entry_impl(
334 &self,
335 name: Name,
336 entry: Arc<dyn DirectoryEntry>,
337 overwrite: bool,
338 ) -> Result<(), AlreadyExists> {
339 let mut this = self.inner.lock();
340
341 if !overwrite && this.entries.contains_key(&name) {
342 return Err(AlreadyExists);
343 }
344
345 this.watchers.send_event(&mut SingleNameEventProducer::added(&name));
346
347 let _ = this.entries.insert(name, entry);
348 Ok(())
349 }
350
351 fn remove_entry_impl(
352 &self,
353 name: Name,
354 must_be_directory: bool,
355 ) -> Result<Option<Arc<dyn DirectoryEntry>>, NotDirectory> {
356 let mut this = self.inner.lock();
357
358 match this.entries.entry(name) {
359 Entry::Vacant(_) => Ok(None),
360 Entry::Occupied(occupied) => {
361 if must_be_directory
362 && occupied.get().entry_info().type_() != fio::DirentType::Directory
363 {
364 Err(NotDirectory)
365 } else {
366 let (key, value) = occupied.remove_entry();
367 this.watchers.send_event(&mut SingleNameEventProducer::removed(&key));
368 Ok(Some(value))
369 }
370 }
371 }
372 }
373}
374
375trait ToRequestFlags {
376 fn to_request_flags(&self) -> RequestFlags;
377}
378
379impl ToRequestFlags for fio::OpenFlags {
380 fn to_request_flags(&self) -> RequestFlags {
381 RequestFlags::Open1(*self)
382 }
383}
384
385impl ToRequestFlags for fio::Flags {
386 fn to_request_flags(&self) -> RequestFlags {
387 RequestFlags::Open3(*self)
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394 use crate::directory::immutable::Simple;
395 use crate::file;
396 use crate::object_request::ObjectRequest;
397 use fidl::endpoints::create_endpoints;
398
399 #[test]
400 fn add_entry_success() {
401 let dir = Simple::new();
402 assert_eq!(
403 dir.add_entry("path_without_separators", file::read_only(b"test")),
404 Ok(()),
405 "add entry with valid filename should succeed"
406 );
407 }
408
409 #[test]
410 fn add_entry_error_name_with_path_separator() {
411 let dir = Simple::new();
412 let status = dir
413 .add_entry("path/with/separators", file::read_only(b"test"))
414 .expect_err("add entry with path separator should fail");
415 assert_eq!(status, Status::INVALID_ARGS);
416 }
417
418 #[test]
419 fn add_entry_error_name_too_long() {
420 let dir = Simple::new();
421 let status = dir
422 .add_entry("a".repeat(10000), file::read_only(b"test"))
423 .expect_err("add entry whose name is too long should fail");
424 assert_eq!(status, Status::BAD_PATH);
425 }
426
427 #[fuchsia::test]
428 async fn not_found_handler() {
429 let dir = Simple::new();
430 let path_mutex = Arc::new(Mutex::new(None));
431 let path_mutex_clone = path_mutex.clone();
432 dir.clone().set_not_found_handler(Box::new(move |path| {
433 *path_mutex_clone.lock() = Some(path.to_string());
434 }));
435
436 let sub_dir = Simple::new();
437 let path_mutex_clone = path_mutex.clone();
438 sub_dir.clone().set_not_found_handler(Box::new(move |path| {
439 *path_mutex_clone.lock() = Some(path.to_string());
440 }));
441 dir.add_entry("dir", sub_dir).expect("add entry with valid filename should succeed");
442
443 dir.add_entry("file", file::read_only(b"test"))
444 .expect("add entry with valid filename should succeed");
445
446 let scope = ExecutionScope::new();
447
448 for (path, expectation) in vec![
449 (".", None),
450 ("does-not-exist", Some("does-not-exist".to_string())),
451 ("file", None),
452 ("dir", None),
453 ("dir/does-not-exist", Some("dir/does-not-exist".to_string())),
454 ] {
455 log::info!("{path}");
456 let (_proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
457 let flags = fio::Flags::PROTOCOL_NODE | fio::Flags::FLAG_SEND_REPRESENTATION;
458 let path = Path::validate_and_split(path).unwrap();
459 ObjectRequest::new(flags, &fio::Options::default(), server_end.into())
460 .handle(|request| dir.clone().open(scope.clone(), path, flags, request));
461
462 assert_eq!(expectation, path_mutex.lock().take());
463 }
464 }
465
466 #[test]
467 fn remove_all_entries() {
468 let dir = Simple::new();
469
470 dir.add_entry("file", file::read_only(""))
471 .expect("add entry with valid filename should succeed");
472
473 dir.remove_all_entries();
474 assert_eq!(
475 dir.get_entry("file").err().expect("file should no longer exist"),
476 Status::NOT_FOUND
477 );
478 }
479
480 #[fuchsia::test]
481 async fn test_alternate_scope() {
482 struct MockEntry(ExecutionScope);
483
484 impl DirectoryEntry for MockEntry {
485 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
486 assert_eq!(request.scope(), &self.0);
487 Ok(())
488 }
489
490 fn scope(&self) -> Option<ExecutionScope> {
491 Some(self.0.clone())
492 }
493 }
494
495 impl GetEntryInfo for MockEntry {
496 fn entry_info(&self) -> EntryInfo {
497 EntryInfo::new(1, fio::DirentType::Directory)
498 }
499 }
500
501 let dir = Simple::new();
502
503 dir.add_entry("foo", Arc::new(MockEntry(ExecutionScope::new()))).expect("add_entry failed");
504
505 let (_client, server) = create_endpoints::<fio::DirectoryMarker>();
506 let mut request =
507 ObjectRequest::new(fio::Flags::empty(), &fio::Options::default(), server.into());
508 dir.open(ExecutionScope::new(), Path::dot(), fio::Flags::empty(), &mut request)
509 .expect("open succeeded");
510 }
511}