blob: cfedfbe356228f9438f736cd122f7feed041bde3 [file] [log] [blame]
// Copyright 2022 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::{
capability::CapabilityProvider,
model::{component::WeakComponentInstance, routing::router_ext::WeakComponentTokenExt},
sandbox_util::DictExt,
},
::routing::error::RoutingError,
async_trait::async_trait,
clonable_error::ClonableError,
cm_rust::Availability,
cm_types::{Name, OPEN_FLAGS_MAX_POSSIBLE_RIGHTS},
cm_util::TaskGroup,
errors::{CapabilityProviderError, OpenError},
router_error::RouterError,
sandbox::Request,
sandbox::WeakComponentToken,
std::sync::Arc,
vfs::{directory::entry::OpenRequest, path::Path as VfsPath, remote::remote_dir},
};
/// The default provider for a ComponentCapability.
/// This provider will start the source component instance and open the capability `name` at
/// `path` under the source component's outgoing namespace.
pub struct DefaultComponentCapabilityProvider {
target: WeakComponentInstance,
source: WeakComponentInstance,
name: Name,
}
impl DefaultComponentCapabilityProvider {
pub fn new(target: WeakComponentInstance, source: WeakComponentInstance, name: Name) -> Self {
DefaultComponentCapabilityProvider { target, source, name }
}
}
#[async_trait]
impl CapabilityProvider for DefaultComponentCapabilityProvider {
async fn open(
self: Box<Self>,
_task_group: TaskGroup,
open_request: OpenRequest<'_>,
) -> Result<(), CapabilityProviderError> {
let source = self.source.upgrade()?;
let capability = source
.get_program_output_dict()
.await?
.get_with_request(
&self.name,
// Routers in `program_output_dict` do not check availability but we need a
// request to run hooks.
Request {
availability: Availability::Transitional,
target: WeakComponentToken::new(self.target.clone()),
},
)
.await?
.ok_or_else(|| RoutingError::BedrockNotPresentInDictionary {
name: self.name.to_string(),
})
.map_err(RouterError::from)?;
let entry = capability
.try_into_directory_entry()
.map_err(OpenError::DoesNotSupportOpen)
.map_err(RouterError::from)?;
entry.open_entry(open_request).map_err(|err| CapabilityProviderError::VfsOpenError(err))
}
}
/// The default provider for a Namespace Capability.
pub struct NamespaceCapabilityProvider {
pub path: cm_types::Path,
pub is_directory_like: bool,
}
#[async_trait]
impl CapabilityProvider for NamespaceCapabilityProvider {
async fn open(
self: Box<Self>,
_task_group: TaskGroup,
mut open_request: OpenRequest<'_>,
) -> Result<(), CapabilityProviderError> {
let path = self.path.to_path_buf();
let (dir, base) = if self.is_directory_like {
(path.to_str().ok_or(CapabilityProviderError::BadPath)?, VfsPath::dot())
} else {
(
match path.parent() {
None => "/",
Some(p) => p.to_str().ok_or(CapabilityProviderError::BadPath)?,
},
path.file_name()
.and_then(|f| f.to_str())
.ok_or(CapabilityProviderError::BadPath)?
.try_into()
.map_err(|_| CapabilityProviderError::BadPath)?,
)
};
open_request.prepend_path(&base);
open_request
.open_remote(remote_dir(
fuchsia_fs::directory::open_in_namespace(dir, OPEN_FLAGS_MAX_POSSIBLE_RIGHTS)
.map_err(|e| CapabilityProviderError::CmNamespaceError {
err: ClonableError::from(anyhow::Error::from(e)),
})?,
))
.map_err(|e| CapabilityProviderError::CmNamespaceError {
err: ClonableError::from(anyhow::Error::from(e)),
})
}
}
/// A `CapabilityProvider` that serves a pseudo directory entry.
#[derive(Clone)]
pub struct DirectoryEntryCapabilityProvider {
/// The pseudo directory that backs this capability.
pub entry: Arc<vfs::directory::immutable::simple::Simple>,
}
#[async_trait]
impl CapabilityProvider for DirectoryEntryCapabilityProvider {
async fn open(
self: Box<Self>,
_task_group: TaskGroup,
open_request: OpenRequest<'_>,
) -> Result<(), CapabilityProviderError> {
open_request
.open_dir(self.entry.clone())
.map_err(|e| CapabilityProviderError::VfsOpenError(e))
}
}