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