blob: 57607beb3575cac0a04f7cd103a691d46866dd65 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This is an implementation of "simple" pseudo directories.
//! Use [`mod@crate::directory::immutable::simple`]
//! to construct actual instances. See [`Simple`] for details.
use crate::{
common::{rights_to_posix_mode_bits, send_on_open_with_error},
directory::{
connection::io1::DerivedConnection,
dirents_sink,
entry::{DirectoryEntry, EntryInfo},
entry_container::{Directory, DirectoryWatcher},
helper::DirectlyMutable,
immutable::connection::io1::ImmutableConnection,
mutable::connection::io1::MutableConnection,
traversal_position::TraversalPosition,
watchers::{
event_producers::{SingleNameEventProducer, StaticVecEventProducer},
Watchers,
},
},
execution_scope::ExecutionScope,
filesystem::{simple::SimpleFilesystem, Filesystem},
path::Path,
MAX_NAME_LENGTH,
};
use {
async_trait::async_trait,
fidl::endpoints::ServerEnd,
fidl_fuchsia_io as fio,
fuchsia_zircon::Status,
parking_lot::Mutex,
static_assertions::assert_eq_size,
std::{
boxed::Box,
clone::Clone,
collections::{
btree_map::{self, Entry},
BTreeMap,
},
iter,
marker::PhantomData,
ops::DerefMut,
sync::Arc,
},
};
/// An implementation of a "simple" pseudo directory. This directory holds a set of entries,
/// allowing the server to add or remove entries via the
/// [`crate::directory::helper::DirectlyMutable::add_entry()`] and
/// [`crate::directory::helper::DirectlyMutable::remove_entry`] methods, and, depending on the
/// connection been used (see [`ImmutableConnection`] or [`MutableConnection`])
/// it may also allow the clients to modify the entries as well. This is a common implementation
/// for [`mod@crate::directory::immutable::simple`] and [`mod@crate::directory::mutable::simple`].
pub struct Simple<Connection>
where
Connection: DerivedConnection + 'static,
{
inner: Mutex<Inner>,
// I wish this field would not be here. A directory instance already knows the type of the
// connections that connect to it - it is in the `Connection` type. So we should just be able
// to use that. But, unfortunately, I could not write `DirectoryEntry::open()` implementation.
// The compiler is confused when it needs to convert `self` into an `Arc<dyn
// ImmutableConnectionClient>` or `Arc<dyn MutableConnectionClient>`. I have tried a few
// tricks to add the necessary constraints, but I was not able to write it correctly.
// Essentially the constraint should be something like this:
//
// impl<Connection> DirectoryEntry for Simple<Connection>
// where
// Connection: DerivedConnection + 'static,
// Arc<Self>: IsConvertibleTo<
// Type = Arc<
// <Connection as DerivedConnection>::Directory
// >
// >
//
// The problem is that I do not know how to write this `IsConvertibleTo` trait. Compiler seems
// to be following some special rules when you say `A as Arc<B>` (when `A` is an `Arc`), as it
// allows subtyping, but I do not know how to express the same constraint.
mutable: bool,
// The inode for this directory. This should either be unique within this VFS, or INO_UNKNOWN.
inode: u64,
_connection: PhantomData<Connection>,
fs: SimpleFilesystem<Self>,
not_found_handler: Mutex<Option<Box<dyn FnMut(&str) + Send + Sync + 'static>>>,
}
struct Inner {
entries: BTreeMap<String, Arc<dyn DirectoryEntry>>,
watchers: Watchers,
}
impl<Connection> Simple<Connection>
where
Connection: DerivedConnection + 'static,
{
pub(super) fn new(mutable: bool, inode: u64) -> Arc<Self> {
Arc::new(Simple {
inner: Mutex::new(Inner { entries: BTreeMap::new(), watchers: Watchers::new() }),
mutable,
_connection: PhantomData,
inode,
fs: SimpleFilesystem::new(),
not_found_handler: Mutex::new(None),
})
}
fn get_or_insert_entry(
self: Arc<Self>,
scope: ExecutionScope,
flags: fio::OpenFlags,
mode: u32,
name: &str,
path: &Path,
) -> Result<Arc<dyn DirectoryEntry>, Status> {
let mut this = self.inner.lock();
match this.entries.get(name) {
Some(entry) => {
if flags.intersects(fio::OpenFlags::CREATE_IF_ABSENT) {
return Err(Status::ALREADY_EXISTS);
}
Ok(entry.clone())
}
None => {
let entry = Connection::entry_not_found(
scope.clone(),
self.clone(),
flags,
mode,
name,
path,
)?;
let _ = this.entries.insert(name.to_string(), entry.clone());
Ok(entry)
}
}
}
/// Registers a given function to be used when an item that is not present is opened. Typically
/// used for logging.
pub fn set_not_found_handler(
self: Arc<Self>,
handler: Box<dyn FnMut(&str) + Send + Sync + 'static>,
) {
let mut this = self.not_found_handler.lock();
this.replace(handler);
}
/// Returns the entry identified by `name`.
pub fn get_entry(&self, name: &str) -> Result<Arc<dyn DirectoryEntry>, Status> {
assert_eq_size!(u64, usize);
if name.len() as u64 > MAX_NAME_LENGTH {
return Err(Status::INVALID_ARGS);
}
let this = self.inner.lock();
match this.entries.get(name) {
Some(entry) => Ok(entry.clone()),
None => Err(Status::NOT_FOUND),
}
}
}
impl<Connection> DirectoryEntry for Simple<Connection>
where
Connection: DerivedConnection + 'static,
{
fn open(
self: Arc<Self>,
scope: ExecutionScope,
flags: fio::OpenFlags,
mode: u32,
mut path: Path,
server_end: ServerEnd<fio::NodeMarker>,
) {
// See if the path has a next segment, if so we want to traverse down
// the directory. Otherwise we've arrived at the right directory.
let (name, path_ref) = match path.next_with_ref() {
(path_ref, Some(name)) => (name, path_ref),
(_, None) => {
// See comment above `Simple::mutable` as to why this selection is necessary.
if self.mutable {
MutableConnection::create_connection(scope, self, flags, server_end);
} else {
ImmutableConnection::create_connection(scope, self, flags, server_end);
}
return;
}
};
// Create copies so if this fails to open we can call the not found handler
let ref_copy = self.clone();
let name_copy = name.to_string();
// Do not hold the mutex more than necessary. Plus, [`parking_lot::Mutex`] is not
// re-entrant. So we need to make sure to release the lock before we call `open()` is it
// may turn out to be a recursive call, in case the directory contains itself directly or
// through a number of other directories. `get_entry` is responsible for locking `self`
// and it will unlock it before returning.
let found = match self.get_or_insert_entry(scope.clone(), flags, mode, name, path_ref) {
Err(status) => {
send_on_open_with_error(flags, server_end, status);
false
}
Ok(entry) => {
entry.open(scope, flags, mode, path, server_end);
true
}
};
if !found {
let mut handler = ref_copy.not_found_handler.lock();
if let Some(handler) = handler.as_mut() {
handler(&name_copy);
}
}
}
fn entry_info(&self) -> EntryInfo {
EntryInfo::new(self.inode, fio::DirentType::Directory)
}
}
#[async_trait]
impl<Connection> Directory for Simple<Connection>
where
Connection: DerivedConnection + 'static,
{
async fn read_dirents<'a>(
&'a self,
pos: &'a TraversalPosition,
sink: Box<dyn dirents_sink::Sink>,
) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status> {
use dirents_sink::AppendResult;
let this = self.inner.lock();
let (mut sink, entries_iter) = match pos {
TraversalPosition::Start => {
match sink.append(&EntryInfo::new(self.inode, fio::DirentType::Directory), ".") {
AppendResult::Ok(sink) => {
// I wonder why, but rustc can not infer T in
//
// pub fn range<T, R>(&self, range: R) -> Range<K, V>
// where
// K: Borrow<T>,
// R: RangeBounds<T>,
// T: Ord + ?Sized,
//
// for some reason here. It says:
//
// error[E0283]: type annotations required: cannot resolve `_: std::cmp::Ord`
//
// pointing to "range". Same for two the other "range()" invocations
// below.
(sink, this.entries.range::<String, _>(..))
}
AppendResult::Sealed(sealed) => {
let new_pos = match this.entries.keys().next() {
None => TraversalPosition::End,
Some(first_name) => TraversalPosition::Name(first_name.clone()),
};
return Ok((new_pos, sealed.into()));
}
}
}
TraversalPosition::Name(next_name) => {
(sink, this.entries.range::<String, _>(next_name.to_owned()..))
}
TraversalPosition::Index(_) => unreachable!(),
TraversalPosition::End => return Ok((TraversalPosition::End, sink.seal().into())),
};
for (name, entry) in entries_iter {
match sink.append(&entry.entry_info(), &name) {
AppendResult::Ok(new_sink) => sink = new_sink,
AppendResult::Sealed(sealed) => {
return Ok((TraversalPosition::Name(name.clone()), sealed.into()));
}
}
}
Ok((TraversalPosition::End, sink.seal().into()))
}
fn register_watcher(
self: Arc<Self>,
scope: ExecutionScope,
mask: fio::WatchMask,
watcher: DirectoryWatcher,
) -> Result<(), Status> {
let mut this = self.inner.lock();
let mut names = StaticVecEventProducer::existing({
let entry_names = this.entries.keys();
iter::once(&".".to_string()).chain(entry_names).cloned().collect()
});
let controller = this.watchers.add(scope, self.clone(), mask, watcher);
controller.send_event(&mut names);
controller.send_event(&mut SingleNameEventProducer::idle());
Ok(())
}
fn unregister_watcher(self: Arc<Self>, key: usize) {
let mut this = self.inner.lock();
this.watchers.remove(key);
}
async fn get_attrs(&self) -> Result<fio::NodeAttributes, Status> {
Ok(fio::NodeAttributes {
mode: fio::MODE_TYPE_DIRECTORY
| rights_to_posix_mode_bits(
/*r*/ true,
/*w*/ self.mutable,
/*x*/ true,
),
id: self.inode,
content_size: 0,
storage_size: 0,
link_count: 1,
creation_time: 0,
modification_time: 0,
})
}
fn close(&self) -> Result<(), Status> {
Ok(())
}
}
impl<Connection> DirectlyMutable for Simple<Connection>
where
Connection: DerivedConnection + 'static,
{
fn add_entry_impl(
&self,
name: String,
entry: Arc<dyn DirectoryEntry>,
overwrite: bool,
) -> Result<(), Status> {
assert_eq_size!(u64, usize);
if name.len() as u64 > MAX_NAME_LENGTH {
return Err(Status::INVALID_ARGS);
}
if name.contains('/') {
return Err(Status::INVALID_ARGS);
}
let mut this = self.inner.lock();
if !overwrite && this.entries.contains_key(&name) {
return Err(Status::ALREADY_EXISTS);
}
this.watchers.send_event(&mut SingleNameEventProducer::added(&name));
let _ = this.entries.insert(name, entry);
Ok(())
}
fn remove_entry_impl(
&self,
name: String,
must_be_directory: bool,
) -> Result<Option<Arc<dyn DirectoryEntry>>, Status> {
assert_eq_size!(u64, usize);
if name.len() as u64 >= MAX_NAME_LENGTH {
return Err(Status::INVALID_ARGS);
}
let mut this = self.inner.lock();
match this.entries.entry(name) {
Entry::Vacant(_) => Ok(None),
Entry::Occupied(occupied) => {
if must_be_directory
&& occupied.get().entry_info().type_() != fio::DirentType::Directory
{
Err(Status::NOT_DIR)
} else {
let (key, value) = occupied.remove_entry();
this.watchers.send_event(&mut SingleNameEventProducer::removed(&key));
Ok(Some(value))
}
}
}
}
fn rename_from(
&self,
src: String,
to: Box<dyn FnOnce(Arc<dyn DirectoryEntry>) -> Result<(), Status>>,
) -> Result<(), Status> {
if src.len() as u64 >= MAX_NAME_LENGTH {
return Err(Status::INVALID_ARGS);
}
let mut this = self.inner.lock();
let Inner { entries, watchers, .. } = this.deref_mut();
let map_entry = match entries.entry(src.clone()) {
btree_map::Entry::Vacant(_) => return Err(Status::NOT_FOUND),
btree_map::Entry::Occupied(map_entry) => map_entry,
};
to(map_entry.get().clone())?;
watchers.send_event(&mut SingleNameEventProducer::removed(&src));
let _ = map_entry.remove();
Ok(())
}
fn rename_to(
&self,
dst: String,
from: Box<dyn FnOnce() -> Result<Arc<dyn DirectoryEntry>, Status>>,
) -> Result<(), Status> {
if dst.len() as u64 >= MAX_NAME_LENGTH {
return Err(Status::INVALID_ARGS);
}
let mut this = self.inner.lock();
let entry = from()?;
this.watchers.send_event(&mut SingleNameEventProducer::added(&dst));
let _ = this.entries.insert(dst, entry);
Ok(())
}
fn rename_within(&self, src: String, dst: String) -> Result<(), Status> {
if src.len() as u64 >= MAX_NAME_LENGTH || dst.len() as u64 >= MAX_NAME_LENGTH {
return Err(Status::INVALID_ARGS);
}
let mut this = self.inner.lock();
// If src doesn't exist, don't do the other stuff.
if !this.entries.contains_key(&src) {
return Err(Status::NOT_FOUND);
}
// I assume we should send these events even when `src == dst`. In practice, a
// particular client may not be aware that the names match, but may still rely on the fact
// that the events occur.
//
// Watcher protocol expects to produce messages that list only one type of event. So
// we will send two independent event messages, each with one name.
this.watchers.send_event(&mut SingleNameEventProducer::removed(&src));
this.watchers.send_event(&mut SingleNameEventProducer::added(&dst));
// We acquire the lock first, as in case `src != dst`, we want to make sure that the
// recipients of these events can not see the directory in the state before the update. I
// assume that `src == dst` is unlikely case, and for the sake of reduction of code
// duplication we can lock and then immediately unlock.
//
// It also provides sequencing for the watchers, as they will always receive `removed`
// followed by `added`, and there will be no interleaving event in-between. I think it is
// not super important, as the watchers should probably be prepared to deal with all kinds
// of sequences anyways.
if src == dst {
return Ok(());
}
let entry = match this.entries.remove(&src) {
// This is truly surprising since this was checked previously, but
// we leave this in place instead of doing `unwrap` on the chance
// the earlier check is carelessly removed.
None => return Err(Status::NOT_FOUND),
Some(entry) => entry,
};
let _ = this.entries.insert(dst, entry);
Ok(())
}
fn get_filesystem(&self) -> &dyn Filesystem {
&self.fs
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::file::vmo::read_only_static;
#[test]
fn name_with_path_separator() {
let dir = crate::directory::mutable::simple();
let status = dir
.add_entry("path/with/separators", read_only_static(b"test"))
.expect_err("add entry with path separator should fail");
assert_eq!(status, Status::INVALID_ARGS);
assert_eq!(
dir.add_entry("path_without_separators", read_only_static(b"test")),
Ok(()),
"add entry with valid filename should succeed"
);
}
}