vfs/directory/
simple.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! This is an implementation of "simple" pseudo directories.
6//! Use [`crate::directory::immutable::Simple::new()`]
7//! to construct actual instances.  See [`Simple`] for details.
8
9use 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::event_producers::{
17    SingleNameEventProducer, StaticVecEventProducer,
18};
19use crate::directory::watchers::Watchers;
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::btree_map::Entry;
30use std::collections::BTreeMap;
31use std::iter;
32use std::sync::Arc;
33use zx_status::Status;
34
35use super::entry::GetEntryInfo;
36
37/// An implementation of a "simple" pseudo directory.  This directory holds a set of entries,
38/// allowing the server to add or remove entries via the
39/// [`crate::directory::helper::DirectlyMutable::add_entry()`] and
40/// [`crate::directory::helper::DirectlyMutable::remove_entry`] methods.
41pub struct Simple {
42    inner: Mutex<Inner>,
43
44    // The inode for this directory. This should either be unique within this VFS, or INO_UNKNOWN.
45    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    /// The provided function will be called whenever this VFS receives an open request for a path
70    /// that is not present in the VFS. The function is invoked with the full path of the missing
71    /// entry, relative to the root of this VFS. Typically this function is used for logging.
72    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    /// Returns the entry identified by `name`.
81    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    /// Gets or inserts an entry (as supplied by the callback `f`).
92    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    /// Removes all entries from the directory.
109    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        // See if the path has a next segment, if so we want to traverse down the directory.
128        // Otherwise we've arrived at the right directory.
129        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                    // Creating an entry is not supported.
134                    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        // Don't hold the inner lock while opening the entry in case the directory contains itself.
144        let _guard;
145        let entry = match self.inner.lock().entries.get(name) {
146            Some(entry) => {
147                // Whilst we are holding the lock, see if an alternative scope should be used.
148                if let Some(s) = entry.scope() {
149                    // Make sure we can get an active guard.
150                    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                // Either:
164                //   - we're at an intermediate directory and the next entry doesn't exist, or
165                //   - we're at the last directory and the next entry doesn't exist and creating the
166                //     entry wasn't requested.
167                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                // We're at the last directory and the entry doesn't exist and creating the entry
181                // was requested which isn't supported.
182                Err(Status::NOT_SUPPORTED)
183            }
184            (
185                Some(_),
186                true,
187                CreationMode::UnnamedTemporary | CreationMode::UnlinkableUnnamedTemporary,
188            ) => {
189                // We're at the last directory and the entry exists and it was requested to create
190                // an unnamed temporary object in this entry (this is not supported for simple
191                // pseudo directory).
192                Err(Status::NOT_SUPPORTED)
193            }
194            (Some(_), true, CreationMode::Always) => {
195                // We're at the last directory and the entry exists but creating the entry is
196                // required.
197                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<'a>(
263        &'a self,
264        pos: &'a 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) => {
275                        // I wonder why, but rustc can not infer T in
276                        //
277                        //   pub fn range<T, R>(&self, range: R) -> Range<K, V>
278                        //   where
279                        //     K: Borrow<T>,
280                        //     R: RangeBounds<T>,
281                        //     T: Ord + Sized:?,
282                        //
283                        // for some reason here.  It says:
284                        //
285                        //   error[E0283]: type annotations required: cannot resolve `_: std::cmp::Ord`
286                        //
287                        // pointing to "range".  Same for two the other "range()" invocations
288                        // below.
289                        (sink, this.entries.range::<Name, _>(..))
290                    }
291                    AppendResult::Sealed(sealed) => {
292                        let new_pos = match this.entries.keys().next() {
293                            None => TraversalPosition::End,
294                            Some(first_name) => TraversalPosition::Name(first_name.clone().into()),
295                        };
296                        return Ok((new_pos, sealed));
297                    }
298                }
299            }
300
301            TraversalPosition::Name(next_name) => {
302                // The only way to get a `TraversalPosition::Name` is if we returned it in the
303                // `AppendResult::Sealed` code path above. Therefore, the conversion from
304                // `next_name` to `Name` will never fail in practice.
305                let next: Name = next_name.to_owned().try_into().unwrap();
306                (sink, this.entries.range::<Name, _>(next..))
307            }
308
309            TraversalPosition::Index(_) => unreachable!(),
310
311            TraversalPosition::End => return Ok((TraversalPosition::End, sink.seal())),
312        };
313
314        for (name, entry) in entries_iter {
315            match sink.append(&entry.entry_info(), &name) {
316                AppendResult::Ok(new_sink) => sink = new_sink,
317                AppendResult::Sealed(sealed) => {
318                    return Ok((TraversalPosition::Name(name.clone().into()), sealed));
319                }
320            }
321        }
322
323        Ok((TraversalPosition::End, sink.seal()))
324    }
325
326    fn register_watcher(
327        self: Arc<Self>,
328        scope: ExecutionScope,
329        mask: fio::WatchMask,
330        watcher: DirectoryWatcher,
331    ) -> Result<(), Status> {
332        let mut this = self.inner.lock();
333
334        let mut names = StaticVecEventProducer::existing({
335            let entry_names = this.entries.keys();
336            iter::once(".".to_string()).chain(entry_names.map(|x| x.to_owned().into())).collect()
337        });
338
339        let controller = this.watchers.add(scope, self.clone(), mask, watcher);
340        controller.send_event(&mut names);
341        controller.send_event(&mut SingleNameEventProducer::idle());
342
343        Ok(())
344    }
345
346    fn unregister_watcher(self: Arc<Self>, key: usize) {
347        let mut this = self.inner.lock();
348        this.watchers.remove(key);
349    }
350}
351
352impl DirectlyMutable for Simple {
353    fn add_entry_impl(
354        &self,
355        name: Name,
356        entry: Arc<dyn DirectoryEntry>,
357        overwrite: bool,
358    ) -> Result<(), AlreadyExists> {
359        let mut this = self.inner.lock();
360
361        if !overwrite && this.entries.contains_key(&name) {
362            return Err(AlreadyExists);
363        }
364
365        this.watchers.send_event(&mut SingleNameEventProducer::added(&name));
366
367        let _ = this.entries.insert(name, entry);
368        Ok(())
369    }
370
371    fn remove_entry_impl(
372        &self,
373        name: Name,
374        must_be_directory: bool,
375    ) -> Result<Option<Arc<dyn DirectoryEntry>>, NotDirectory> {
376        let mut this = self.inner.lock();
377
378        match this.entries.entry(name) {
379            Entry::Vacant(_) => Ok(None),
380            Entry::Occupied(occupied) => {
381                if must_be_directory
382                    && occupied.get().entry_info().type_() != fio::DirentType::Directory
383                {
384                    Err(NotDirectory)
385                } else {
386                    let (key, value) = occupied.remove_entry();
387                    this.watchers.send_event(&mut SingleNameEventProducer::removed(&key));
388                    Ok(Some(value))
389                }
390            }
391        }
392    }
393}
394
395trait ToRequestFlags {
396    fn to_request_flags(&self) -> RequestFlags;
397}
398
399impl ToRequestFlags for fio::OpenFlags {
400    fn to_request_flags(&self) -> RequestFlags {
401        RequestFlags::Open1(*self)
402    }
403}
404
405impl ToRequestFlags for fio::Flags {
406    fn to_request_flags(&self) -> RequestFlags {
407        RequestFlags::Open3(*self)
408    }
409}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414    use crate::directory::immutable::Simple;
415    use crate::file;
416    use crate::object_request::ObjectRequest;
417    use fidl::endpoints::create_endpoints;
418
419    #[test]
420    fn add_entry_success() {
421        let dir = Simple::new();
422        assert_eq!(
423            dir.add_entry("path_without_separators", file::read_only(b"test")),
424            Ok(()),
425            "add entry with valid filename should succeed"
426        );
427    }
428
429    #[test]
430    fn add_entry_error_name_with_path_separator() {
431        let dir = Simple::new();
432        let status = dir
433            .add_entry("path/with/separators", file::read_only(b"test"))
434            .expect_err("add entry with path separator should fail");
435        assert_eq!(status, Status::INVALID_ARGS);
436    }
437
438    #[test]
439    fn add_entry_error_name_too_long() {
440        let dir = Simple::new();
441        let status = dir
442            .add_entry("a".repeat(10000), file::read_only(b"test"))
443            .expect_err("add entry whose name is too long should fail");
444        assert_eq!(status, Status::BAD_PATH);
445    }
446
447    #[fuchsia::test]
448    async fn not_found_handler() {
449        let dir = Simple::new();
450        let path_mutex = Arc::new(Mutex::new(None));
451        let path_mutex_clone = path_mutex.clone();
452        dir.clone().set_not_found_handler(Box::new(move |path| {
453            *path_mutex_clone.lock() = Some(path.to_string());
454        }));
455
456        let sub_dir = Simple::new();
457        let path_mutex_clone = path_mutex.clone();
458        sub_dir.clone().set_not_found_handler(Box::new(move |path| {
459            *path_mutex_clone.lock() = Some(path.to_string());
460        }));
461        dir.add_entry("dir", sub_dir).expect("add entry with valid filename should succeed");
462
463        dir.add_entry("file", file::read_only(b"test"))
464            .expect("add entry with valid filename should succeed");
465
466        let scope = ExecutionScope::new();
467
468        for (path, expectation) in vec![
469            (".", None),
470            ("does-not-exist", Some("does-not-exist".to_string())),
471            ("file", None),
472            ("dir", None),
473            ("dir/does-not-exist", Some("dir/does-not-exist".to_string())),
474        ] {
475            log::info!("{path}");
476            let (_proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
477            let flags = fio::Flags::PROTOCOL_NODE | fio::Flags::FLAG_SEND_REPRESENTATION;
478            let path = Path::validate_and_split(path).unwrap();
479            ObjectRequest::new(flags, &fio::Options::default(), server_end.into())
480                .handle(|request| dir.clone().open(scope.clone(), path, flags, request));
481
482            assert_eq!(expectation, path_mutex.lock().take());
483        }
484    }
485
486    #[test]
487    fn remove_all_entries() {
488        let dir = Simple::new();
489
490        dir.add_entry("file", file::read_only(""))
491            .expect("add entry with valid filename should succeed");
492
493        dir.remove_all_entries();
494        assert_eq!(
495            dir.get_entry("file").err().expect("file should no longer exist"),
496            Status::NOT_FOUND
497        );
498    }
499
500    #[fuchsia::test]
501    async fn test_alternate_scope() {
502        struct MockEntry(ExecutionScope);
503
504        impl DirectoryEntry for MockEntry {
505            fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
506                assert_eq!(request.scope(), &self.0);
507                Ok(())
508            }
509
510            fn scope(&self) -> Option<ExecutionScope> {
511                Some(self.0.clone())
512            }
513        }
514
515        impl GetEntryInfo for MockEntry {
516            fn entry_info(&self) -> EntryInfo {
517                EntryInfo::new(1, fio::DirentType::Directory)
518            }
519        }
520
521        let dir = Simple::new();
522
523        dir.add_entry("foo", Arc::new(MockEntry(ExecutionScope::new()))).expect("add_entry failed");
524
525        let (_client, server) = create_endpoints::<fio::DirectoryMarker>();
526        let mut request =
527            ObjectRequest::new(fio::Flags::empty(), &fio::Options::default(), server.into());
528        dir.open(ExecutionScope::new(), Path::dot(), fio::Flags::empty(), &mut request)
529            .expect("open succeeded");
530    }
531}