blob: 565e02afe5f184d87491220bfefac3fec410ee5f [file] [log] [blame]
// Copyright 2020 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.
use {
crate::{
directory::{
entry::DirectoryEntry,
entry_container::{Directory, MutableDirectory},
},
filesystem::Filesystem,
},
async_trait::async_trait,
fidl_fuchsia_io as fio,
fuchsia_zircon::Status,
std::sync::Arc,
};
/// `DirectlyMutable` is a superset of `MutableDirectory` which also allows server-side management
/// of directory entries (via `add_entry` and `remove_entry`). It also provides `rename_from` and
/// `rename_to` to support global ordering in rename operations. You may wish to use
/// `filesystem::simple::SimpleFilesystem` to provide the filesystem type for this DirectlyMutable.
pub trait DirectlyMutable: Directory + Send + Sync {
/// Adds a child entry to this directory.
///
/// Possible errors are:
/// * `ZX_ERR_INVALID_ARGS` if `name` exceeds [`fidl_fuchsia_io::MAX_FILENAME`] bytes in
/// length, or if `name` includes a path separator ('/') character.
/// * `ZX_ERR_ALREADY_EXISTS` if an entry with the same name is already present in the
/// directory.
fn add_entry<Name>(&self, name: Name, entry: Arc<dyn DirectoryEntry>) -> Result<(), Status>
where
Name: Into<String>,
Self: Sized,
{
self.add_entry_impl(name.into(), entry, false)
}
/// Adds a child entry to this directory.
///
/// Possible errors are:
/// * `ZX_ERR_INVALID_ARGS` if `name` exceeds [`fidl_fuchsia_io::MAX_FILENAME`] bytes in
/// length, or if `name` includes a path separator ('/') character.
/// * `ZX_ERR_ALREADY_EXISTS` if an entry with the same name is already present in the
/// directory, and `overwrite` is false.
fn add_entry_impl(
&self,
name: String,
entry: Arc<dyn DirectoryEntry>,
overwrite: bool,
) -> Result<(), Status>;
/// Removes a child entry from this directory. In case an entry with the matching name was
/// found, the entry will be returned to the caller. If `must_be_directory` is true, an error
/// is returned if the entry is not a directory.
///
/// Possible errors are:
/// * `ZX_ERR_INVALID_ARGS` if `name` exceeds [`fidl_fuchsia_io::MAX_FILENAME`] bytes in
/// length.
fn remove_entry<Name>(
&self,
name: Name,
must_be_directory: bool,
) -> Result<Option<Arc<dyn DirectoryEntry>>, Status>
where
Name: Into<String>,
Self: Sized,
{
self.remove_entry_impl(name.into(), must_be_directory)
}
/// Removes a child entry from this directory. In case an entry with the matching name was
/// found, the entry will be returned to the caller.
///
/// Possible errors are:
/// * `ZX_ERR_INVALID_ARGS` if `name` exceeds [`fidl_fuchsia_io::MAX_FILENAME`] bytes in
/// length.
fn remove_entry_impl(
&self,
name: String,
must_be_directory: bool,
) -> Result<Option<Arc<dyn DirectoryEntry>>, Status>;
/// Renaming needs to be atomic, even accross two distinct directories. So we need a special
/// API to handle that.
///
/// As two distinct directories may mean two mutexes to lock, using it correctly is non-trivial.
/// In order to avoid a deadlock, we need to decide on a global ordering for the locks.
/// `rename_from` and [`Self::rename_to`] are used depending on the relative order of the two
/// directories involved in the operation.
///
/// Implementations are expected to lock this directory, check that the entry exists and pass a
/// reference to the entry to the `to` callback. Only if the `to` callback succeed, should the
/// entry be removed from the current directory. This will garantee atomic rename from the
/// standpoint of the client.
///
/// # Safety
///
/// This should only be called by the [`crate::filesystem::FilesystemRename::rename()`], which
/// will establish the global order and will call proper method.
///
/// See fxbug.dev/99061: unsafe is not for visibility reduction.
fn rename_from(
&self,
src: String,
to: Box<dyn FnOnce(Arc<dyn DirectoryEntry>) -> Result<(), Status>>,
) -> Result<(), Status>;
/// Renaming needs to be atomic, even accross two distinct directories. So we need a special
/// API to handle that.
///
/// See [`Self::rename_from`] comment for an explanation.
///
/// Implementations are expected to lock this dirctory, check if they can accept an entry named
/// `dst` (in case there might be any restrictions), then call the `from` callback to obtain a
/// new entry which must be added into the current directory with no errors.
///
/// # Safety
///
/// This should only be called by the [`crate::filesystem::FilesystemRename::rename()`], which
/// will establish the global order and will call proper method.
///
/// See fxbug.dev/99061: unsafe is not for visibility reduction.
fn rename_to(
&self,
dst: String,
from: Box<dyn FnOnce() -> Result<Arc<dyn DirectoryEntry>, Status>>,
) -> Result<(), Status>;
/// In case an entry is renamed within the same directory only one lock needs to be obtained.
/// This is a companion method to the [`Self::rename_from`]/[`Self::rename_to`] pair.
/// [`crate::filesystem::FilesystemRename::rename()`] will use this method to avoid locking the
/// same directory mutex twice.
///
/// It should only be used by the [`crate::filesystem::FilesystemRename::rename()`].
fn rename_within(&self, src: String, dst: String) -> Result<(), Status>;
/// Get the filesystem this directory belongs to.
fn get_filesystem(&self) -> &dyn Filesystem;
}
#[async_trait]
impl<T: DirectlyMutable> MutableDirectory for T {
async fn unlink(self: Arc<Self>, name: &str, must_be_directory: bool) -> Result<(), Status> {
match self.remove_entry_impl(name.into(), must_be_directory) {
Ok(Some(_)) => Ok(()),
Ok(None) => Err(Status::NOT_FOUND),
Err(e) => Err(e),
}
}
async fn set_attrs(
&self,
_flags: fio::NodeAttributeFlags,
_attrs: fio::NodeAttributes,
) -> Result<(), Status> {
Err(Status::NOT_SUPPORTED)
}
fn get_filesystem(&self) -> &dyn Filesystem {
(self as &dyn DirectlyMutable).get_filesystem()
}
async fn sync(&self) -> Result<(), Status> {
Err(Status::NOT_SUPPORTED)
}
}