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 std::collections::btree_map::Entry;
29use std::collections::BTreeMap;
30use std::iter;
31use std::sync::{Arc, Mutex};
32use zx_status::Status;
33
34use super::entry::GetEntryInfo;
35
36/// An implementation of a "simple" pseudo directory.  This directory holds a set of entries,
37/// allowing the server to add or remove entries via the
38/// [`crate::directory::helper::DirectlyMutable::add_entry()`] and
39/// [`crate::directory::helper::DirectlyMutable::remove_entry`] methods.
40pub struct Simple {
41    inner: Mutex<Inner>,
42
43    // The inode for this directory. This should either be unique within this VFS, or INO_UNKNOWN.
44    inode: u64,
45
46    not_found_handler: Mutex<Option<Box<dyn FnMut(&str) + Send + Sync + 'static>>>,
47}
48
49struct Inner {
50    entries: BTreeMap<Name, Arc<dyn DirectoryEntry>>,
51
52    watchers: Watchers,
53}
54
55impl Simple {
56    pub fn new() -> Arc<Self> {
57        Self::new_with_inode(fio::INO_UNKNOWN)
58    }
59
60    pub(crate) fn new_with_inode(inode: u64) -> Arc<Self> {
61        Arc::new(Simple {
62            inner: Mutex::new(Inner { entries: BTreeMap::new(), watchers: Watchers::new() }),
63            inode,
64            not_found_handler: Mutex::new(None),
65        })
66    }
67
68    /// The provided function will be called whenever this VFS receives an open request for a path
69    /// that is not present in the VFS. The function is invoked with the full path of the missing
70    /// entry, relative to the root of this VFS. Typically this function is used for logging.
71    pub fn set_not_found_handler(
72        self: Arc<Self>,
73        handler: Box<dyn FnMut(&str) + Send + Sync + 'static>,
74    ) {
75        let mut this = self.not_found_handler.lock().unwrap();
76        this.replace(handler);
77    }
78
79    /// Returns the entry identified by `name`.
80    pub fn get_entry(&self, name: &str) -> Result<Arc<dyn DirectoryEntry>, Status> {
81        crate::name::validate_name(name)?;
82
83        let this = self.inner.lock().unwrap();
84        match this.entries.get(name) {
85            Some(entry) => Ok(entry.clone()),
86            None => Err(Status::NOT_FOUND),
87        }
88    }
89
90    /// Gets or inserts an entry (as supplied by the callback `f`).
91    pub fn get_or_insert<T: DirectoryEntry>(
92        &self,
93        name: Name,
94        f: impl FnOnce() -> Arc<T>,
95    ) -> Arc<dyn DirectoryEntry> {
96        let mut guard = self.inner.lock().unwrap();
97        let inner = &mut *guard;
98        match inner.entries.entry(name) {
99            Entry::Vacant(slot) => {
100                inner.watchers.send_event(&mut SingleNameEventProducer::added(""));
101                slot.insert(f()).clone()
102            }
103            Entry::Occupied(entry) => entry.get().clone(),
104        }
105    }
106
107    /// Removes all entries from the directory.
108    pub fn remove_all_entries(&self) {
109        let mut inner = self.inner.lock().unwrap();
110        if !inner.entries.is_empty() {
111            let names = std::mem::take(&mut inner.entries)
112                .into_keys()
113                .map(String::from)
114                .collect::<Vec<String>>();
115            inner.watchers.send_event(&mut StaticVecEventProducer::removed(names));
116        }
117    }
118
119    fn open_impl<'a, P: ProtocolsExt + ToRequestFlags>(
120        self: Arc<Self>,
121        scope: ExecutionScope,
122        mut path: Path,
123        protocols: P,
124        object_request: ObjectRequestRef<'_>,
125    ) -> Result<(), Status> {
126        // See if the path has a next segment, if so we want to traverse down the directory.
127        // Otherwise we've arrived at the right directory.
128        let (name, path_ref) = match path.next_with_ref() {
129            (path_ref, Some(name)) => (name, path_ref),
130            (_, None) => {
131                if protocols.create_unnamed_temporary_in_directory_path() {
132                    // Creating an entry is not supported.
133                    return Err(Status::NOT_SUPPORTED);
134                }
135                object_request
136                    .take()
137                    .create_connection_sync::<ImmutableConnection<_>, _>(scope, self, protocols);
138                return Ok(());
139            }
140        };
141
142        // Don't hold the inner lock while opening the entry in case the directory contains itself.
143        let entry = self.inner.lock().unwrap().entries.get(name).cloned();
144        match (entry, path_ref.is_empty(), protocols.creation_mode()) {
145            (None, false, _) | (None, true, CreationMode::Never) => {
146                // Either:
147                //   - we're at an intermediate directory and the next entry doesn't exist, or
148                //   - we're at the last directory and the next entry doesn't exist and creating the
149                //     entry wasn't requested.
150                if let Some(not_found_handler) = &mut *self.not_found_handler.lock().unwrap() {
151                    not_found_handler(path_ref.as_str());
152                }
153                Err(Status::NOT_FOUND)
154            }
155            (
156                None,
157                true,
158                CreationMode::Always
159                | CreationMode::AllowExisting
160                | CreationMode::UnnamedTemporary
161                | CreationMode::UnlinkableUnnamedTemporary,
162            ) => {
163                // We're at the last directory and the entry doesn't exist and creating the entry
164                // was requested which isn't supported.
165                Err(Status::NOT_SUPPORTED)
166            }
167            (
168                Some(_),
169                true,
170                CreationMode::UnnamedTemporary | CreationMode::UnlinkableUnnamedTemporary,
171            ) => {
172                // We're at the last directory and the entry exists and it was requested to create
173                // an unnamed temporary object in this entry (this is not supported for simple
174                // pseudo directory).
175                Err(Status::NOT_SUPPORTED)
176            }
177            (Some(_), true, CreationMode::Always) => {
178                // We're at the last directory and the entry exists but creating the entry is
179                // required.
180                Err(Status::ALREADY_EXISTS)
181            }
182            (Some(entry), _, _) => entry.open_entry(OpenRequest::new(
183                scope,
184                protocols.to_request_flags(),
185                path,
186                object_request,
187            )),
188        }
189    }
190}
191
192impl GetEntryInfo for Simple {
193    fn entry_info(&self) -> EntryInfo {
194        EntryInfo::new(self.inode, fio::DirentType::Directory)
195    }
196}
197
198impl DirectoryEntry for Simple {
199    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
200        request.open_dir(self)
201    }
202}
203
204impl Node for Simple {
205    async fn get_attributes(
206        &self,
207        requested_attributes: fio::NodeAttributesQuery,
208    ) -> Result<fio::NodeAttributes2, Status> {
209        Ok(immutable_attributes!(
210            requested_attributes,
211            Immutable {
212                protocols: fio::NodeProtocolKinds::DIRECTORY,
213                abilities: fio::Operations::GET_ATTRIBUTES
214                    | fio::Operations::ENUMERATE
215                    | fio::Operations::TRAVERSE,
216                id: self.inode,
217            }
218        ))
219    }
220}
221
222impl Directory for Simple {
223    fn open(
224        self: Arc<Self>,
225        scope: ExecutionScope,
226        flags: fio::OpenFlags,
227        path: Path,
228        server_end: ServerEnd<fio::NodeMarker>,
229    ) {
230        flags
231            .to_object_request(server_end)
232            .handle(|object_request| self.open_impl(scope, path, flags, object_request));
233    }
234
235    fn open3(
236        self: Arc<Self>,
237        scope: ExecutionScope,
238        path: Path,
239        flags: fio::Flags,
240        object_request: ObjectRequestRef<'_>,
241    ) -> Result<(), Status> {
242        self.open_impl(scope, path, flags, object_request)
243    }
244
245    async fn read_dirents<'a>(
246        &'a self,
247        pos: &'a TraversalPosition,
248        sink: Box<dyn dirents_sink::Sink>,
249    ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status> {
250        use dirents_sink::AppendResult;
251
252        let this = self.inner.lock().unwrap();
253
254        let (mut sink, entries_iter) = match pos {
255            TraversalPosition::Start => {
256                match sink.append(&EntryInfo::new(self.inode, fio::DirentType::Directory), ".") {
257                    AppendResult::Ok(sink) => {
258                        // I wonder why, but rustc can not infer T in
259                        //
260                        //   pub fn range<T, R>(&self, range: R) -> Range<K, V>
261                        //   where
262                        //     K: Borrow<T>,
263                        //     R: RangeBounds<T>,
264                        //     T: Ord + Sized:?,
265                        //
266                        // for some reason here.  It says:
267                        //
268                        //   error[E0283]: type annotations required: cannot resolve `_: std::cmp::Ord`
269                        //
270                        // pointing to "range".  Same for two the other "range()" invocations
271                        // below.
272                        (sink, this.entries.range::<Name, _>(..))
273                    }
274                    AppendResult::Sealed(sealed) => {
275                        let new_pos = match this.entries.keys().next() {
276                            None => TraversalPosition::End,
277                            Some(first_name) => TraversalPosition::Name(first_name.clone().into()),
278                        };
279                        return Ok((new_pos, sealed));
280                    }
281                }
282            }
283
284            TraversalPosition::Name(next_name) => {
285                // The only way to get a `TraversalPosition::Name` is if we returned it in the
286                // `AppendResult::Sealed` code path above. Therefore, the conversion from
287                // `next_name` to `Name` will never fail in practice.
288                let next: Name = next_name.to_owned().try_into().unwrap();
289                (sink, this.entries.range::<Name, _>(next..))
290            }
291
292            TraversalPosition::Index(_) => unreachable!(),
293
294            TraversalPosition::End => return Ok((TraversalPosition::End, sink.seal())),
295        };
296
297        for (name, entry) in entries_iter {
298            match sink.append(&entry.entry_info(), &name) {
299                AppendResult::Ok(new_sink) => sink = new_sink,
300                AppendResult::Sealed(sealed) => {
301                    return Ok((TraversalPosition::Name(name.clone().into()), sealed));
302                }
303            }
304        }
305
306        Ok((TraversalPosition::End, sink.seal()))
307    }
308
309    fn register_watcher(
310        self: Arc<Self>,
311        scope: ExecutionScope,
312        mask: fio::WatchMask,
313        watcher: DirectoryWatcher,
314    ) -> Result<(), Status> {
315        let mut this = self.inner.lock().unwrap();
316
317        let mut names = StaticVecEventProducer::existing({
318            let entry_names = this.entries.keys();
319            iter::once(".".to_string()).chain(entry_names.map(|x| x.to_owned().into())).collect()
320        });
321
322        let controller = this.watchers.add(scope, self.clone(), mask, watcher);
323        controller.send_event(&mut names);
324        controller.send_event(&mut SingleNameEventProducer::idle());
325
326        Ok(())
327    }
328
329    fn unregister_watcher(self: Arc<Self>, key: usize) {
330        let mut this = self.inner.lock().unwrap();
331        this.watchers.remove(key);
332    }
333}
334
335impl DirectlyMutable for Simple {
336    fn add_entry_impl(
337        &self,
338        name: Name,
339        entry: Arc<dyn DirectoryEntry>,
340        overwrite: bool,
341    ) -> Result<(), AlreadyExists> {
342        let mut this = self.inner.lock().unwrap();
343
344        if !overwrite && this.entries.contains_key(&name) {
345            return Err(AlreadyExists);
346        }
347
348        this.watchers.send_event(&mut SingleNameEventProducer::added(&name));
349
350        let _ = this.entries.insert(name, entry);
351        Ok(())
352    }
353
354    fn remove_entry_impl(
355        &self,
356        name: Name,
357        must_be_directory: bool,
358    ) -> Result<Option<Arc<dyn DirectoryEntry>>, NotDirectory> {
359        let mut this = self.inner.lock().unwrap();
360
361        match this.entries.entry(name) {
362            Entry::Vacant(_) => Ok(None),
363            Entry::Occupied(occupied) => {
364                if must_be_directory
365                    && occupied.get().entry_info().type_() != fio::DirentType::Directory
366                {
367                    Err(NotDirectory)
368                } else {
369                    let (key, value) = occupied.remove_entry();
370                    this.watchers.send_event(&mut SingleNameEventProducer::removed(&key));
371                    Ok(Some(value))
372                }
373            }
374        }
375    }
376}
377
378trait ToRequestFlags {
379    fn to_request_flags(&self) -> RequestFlags;
380}
381
382impl ToRequestFlags for fio::OpenFlags {
383    fn to_request_flags(&self) -> RequestFlags {
384        RequestFlags::Open1(*self)
385    }
386}
387
388impl ToRequestFlags for fio::Flags {
389    fn to_request_flags(&self) -> RequestFlags {
390        RequestFlags::Open3(*self)
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397    use crate::directory::immutable::Simple;
398    use crate::{assert_event, file};
399    use fidl::endpoints::create_proxy;
400
401    #[test]
402    fn add_entry_success() {
403        let dir = Simple::new();
404        assert_eq!(
405            dir.add_entry("path_without_separators", file::read_only(b"test")),
406            Ok(()),
407            "add entry with valid filename should succeed"
408        );
409    }
410
411    #[test]
412    fn add_entry_error_name_with_path_separator() {
413        let dir = Simple::new();
414        let status = dir
415            .add_entry("path/with/separators", file::read_only(b"test"))
416            .expect_err("add entry with path separator should fail");
417        assert_eq!(status, Status::INVALID_ARGS);
418    }
419
420    #[test]
421    fn add_entry_error_name_too_long() {
422        let dir = Simple::new();
423        let status = dir
424            .add_entry("a".repeat(10000), file::read_only(b"test"))
425            .expect_err("add entry whose name is too long should fail");
426        assert_eq!(status, Status::BAD_PATH);
427    }
428
429    #[fuchsia::test]
430    async fn not_found_handler() {
431        let dir = Simple::new();
432        let path_mutex = Arc::new(Mutex::new(None));
433        let path_mutex_clone = path_mutex.clone();
434        dir.clone().set_not_found_handler(Box::new(move |path| {
435            *path_mutex_clone.lock().unwrap() = Some(path.to_string());
436        }));
437
438        let sub_dir = Simple::new();
439        let path_mutex_clone = path_mutex.clone();
440        sub_dir.clone().set_not_found_handler(Box::new(move |path| {
441            *path_mutex_clone.lock().unwrap() = Some(path.to_string());
442        }));
443        dir.add_entry("dir", sub_dir).expect("add entry with valid filename should succeed");
444
445        dir.add_entry("file", file::read_only(b"test"))
446            .expect("add entry with valid filename should succeed");
447
448        let scope = ExecutionScope::new();
449
450        for (path, expectation) in vec![
451            (".", None),
452            ("does-not-exist", Some("does-not-exist".to_string())),
453            ("file", None),
454            ("dir", None),
455            ("dir/does-not-exist", Some("dir/does-not-exist".to_string())),
456        ] {
457            let (proxy, server_end) = create_proxy::<fio::NodeMarker>();
458            let flags = fio::OpenFlags::NODE_REFERENCE | fio::OpenFlags::DESCRIBE;
459            let path = Path::validate_and_split(path).unwrap();
460            dir.clone().open(scope.clone(), flags, path, server_end.into_channel().into());
461            assert_event!(proxy, fio::NodeEvent::OnOpen_ { .. }, {});
462
463            assert_eq!(expectation, path_mutex.lock().unwrap().take());
464        }
465    }
466
467    #[test]
468    fn remove_all_entries() {
469        let dir = Simple::new();
470
471        dir.add_entry("file", file::read_only(""))
472            .expect("add entry with valid filename should succeed");
473
474        dir.remove_all_entries();
475        assert_eq!(
476            dir.get_entry("file").err().expect("file should no longer exist"),
477            Status::NOT_FOUND
478        );
479    }
480}