blob: 35a67e6ce0e84713280e18fb3291cefaf47dfeb7 [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.
//! The storage admin protocol is a FIDL protocol that is hosted by the framework for clients to
//! perform privileged operations on isolated storage. Clients can perform tasks such as opening a
//! component's storage or outright deleting it.
//!
//! This API allows clients to perform a limited set of mutable operations on storage, without
//! direct access to the backing directory, with the goal of making it easier for clients to work
//! with isolated storage without needing to understand component_manager's storage layout.
use {
crate::{
capability::{CapabilityProvider, CapabilitySource, ComponentCapability},
channel,
model::{
component::{BindReason, WeakComponentInstance},
error::ModelError,
hooks::{Event, EventPayload, EventType, Hook, HooksRegistration},
routing::{self, CapabilityState},
storage,
},
},
anyhow::{format_err, Error},
async_trait::async_trait,
cm_rust::{CapabilityName, ExposeDecl, OfferDecl, StorageDecl, UseDecl},
fidl::endpoints::{ServerEnd, ServiceMarker},
fidl_fuchsia_component as fcomponent,
fidl_fuchsia_io::{MODE_TYPE_SERVICE, OPEN_RIGHT_READABLE, OPEN_RIGHT_WRITABLE},
fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync, fuchsia_zircon as zx,
futures::TryStreamExt,
lazy_static::lazy_static,
log::*,
moniker::AbsoluteMoniker,
moniker::ExtendedMoniker,
std::{
convert::TryInto,
path::PathBuf,
sync::{Arc, Weak},
},
};
lazy_static! {
pub static ref STORAGE_ADMIN_PROTOCOL_NAME: CapabilityName =
fsys::StorageAdminMarker::NAME.into();
}
struct StorageAdminProtocolProvider {
storage_decl: StorageDecl,
component: WeakComponentInstance,
storage_admin: Arc<StorageAdmin>,
}
impl StorageAdminProtocolProvider {
pub fn new(
storage_decl: StorageDecl,
component: WeakComponentInstance,
storage_admin: Arc<StorageAdmin>,
) -> Self {
Self { storage_decl, component, storage_admin }
}
}
#[async_trait]
impl CapabilityProvider for StorageAdminProtocolProvider {
async fn open(
self: Box<Self>,
flags: u32,
open_mode: u32,
in_relative_path: PathBuf,
server_end: &mut zx::Channel,
) -> Result<(), ModelError> {
let server_end = channel::take_channel(server_end);
if (flags & (OPEN_RIGHT_READABLE | OPEN_RIGHT_WRITABLE))
!= (OPEN_RIGHT_READABLE | OPEN_RIGHT_WRITABLE)
{
warn!("open request for the storage admin protocol rejected: access denied");
return Ok(());
}
if 0 == (open_mode & MODE_TYPE_SERVICE) {
warn!("open request for the storage admin protocol rejected: incorrect mode");
return Ok(());
}
if in_relative_path != PathBuf::from("") {
warn!("open request for the storage admin protocol rejected: invalid path");
return Ok(());
}
let storage_decl = self.storage_decl.clone();
let component = self.component.clone();
let storage_admin = self.storage_admin.clone();
fasync::Task::spawn(async move {
if let Err(e) = storage_admin.serve(storage_decl, component, server_end).await {
warn!("failed to serve storage admin protocol: {:?}", e);
}
})
.detach();
Ok(())
}
}
pub struct StorageAdmin {}
// `StorageAdmin` is a `Hook` that serves the `StorageAdmin` FIDL protocol.
impl StorageAdmin {
pub fn new() -> Self {
Self {}
}
pub fn hooks(self: &Arc<Self>) -> Vec<HooksRegistration> {
vec![HooksRegistration::new(
"StorageAdmin",
vec![EventType::CapabilityRouted],
Arc::downgrade(self) as Weak<dyn Hook>,
)]
}
async fn extract_storage_decl(
source_capability: &ComponentCapability,
component: WeakComponentInstance,
) -> Result<Option<StorageDecl>, ModelError> {
match source_capability {
ComponentCapability::Offer(OfferDecl::Protocol(_))
| ComponentCapability::Expose(ExposeDecl::Protocol(_))
| ComponentCapability::Use(UseDecl::Protocol(_)) => (),
_ => return Ok(None),
}
if source_capability.source_name() != Some(&fsys::StorageAdminMarker::NAME.into()) {
return Ok(None);
}
let source_capability_name = source_capability.source_capability_name();
if source_capability_name.is_none() {
return Ok(None);
}
let source_component = component.upgrade()?;
let source_component_state = source_component.lock_resolved_state().await?;
let decl = source_component_state.decl();
Ok(decl.find_storage_source(source_capability_name.unwrap()).cloned())
}
async fn on_scoped_framework_capability_routed_async<'a>(
self: Arc<Self>,
source_capability: &'a ComponentCapability,
component: WeakComponentInstance,
capability_provider: Option<Box<dyn CapabilityProvider>>,
) -> Result<Option<Box<dyn CapabilityProvider>>, ModelError> {
// If some other capability has already been installed, then there's nothing to
// do here.
if capability_provider.is_some() {
return Ok(capability_provider);
}
// Find the storage decl, if it exists we're good to go
let storage_decl = Self::extract_storage_decl(source_capability, component.clone()).await?;
if let Some(storage_decl) = storage_decl {
return Ok(Some(Box::new(StorageAdminProtocolProvider::new(
storage_decl,
component,
self.clone(),
)) as Box<dyn CapabilityProvider>));
}
// The declaration referenced either a nonexistent capability, or a capability that isn't a
// storage capability. We can't be the provider for this.
Ok(None)
}
async fn serve(
self: Arc<Self>,
storage_decl: StorageDecl,
component: WeakComponentInstance,
server_end: zx::Channel,
) -> Result<(), Error> {
let component = component.upgrade().map_err(|e| {
format_err!(
"unable to serve storage admin protocol, model reference is no longer valid: {:?}",
e,
)
})?;
let storage_moniker = component.abs_moniker.clone();
let capability = ComponentCapability::Storage(storage_decl.clone());
let cap_state = CapabilityState::new(&capability);
let storage_capability_source_info =
routing::route_storage_backing_directory(storage_decl, component.clone(), cap_state)
.await?;
let mut stream = ServerEnd::<fsys::StorageAdminMarker>::new(server_end)
.into_stream()
.expect("could not convert channel into stream");
while let Some(request) = stream.try_next().await? {
match request {
fsys::StorageAdminRequest::OpenComponentStorage {
relative_moniker,
flags,
mode,
object,
control_handle: _,
} => {
let relative_moniker = relative_moniker.as_str().try_into()?;
let abs_moniker =
AbsoluteMoniker::from_relative(&component.abs_moniker, &relative_moniker)?;
let instance_id = component
.try_get_context()?
.component_id_index()
.look_up_moniker(&abs_moniker)
.cloned();
let dir_proxy = storage::open_isolated_storage(
storage_capability_source_info.clone(),
relative_moniker,
instance_id.as_ref(),
mode,
&BindReason::AccessCapability {
target: ExtendedMoniker::ComponentInstance(storage_moniker.clone()),
path: storage_capability_source_info.backing_directory_path.clone(),
},
)
.await?;
dir_proxy.clone(flags, object)?;
}
fsys::StorageAdminRequest::DeleteComponentStorage {
relative_moniker,
responder,
} => {
let err_code = match relative_moniker.as_str().try_into() {
Err(e) => {
warn!("couldn't parse string as relative moniker for storage admin protocol: {:?}", e);
Err(fcomponent::Error::InvalidArguments)
}
Ok(relative_moniker) => {
let abs_moniker = AbsoluteMoniker::from_relative(
&component.abs_moniker,
&relative_moniker,
)?;
let instance_id = component
.try_get_context()?
.component_id_index()
.look_up_moniker(&abs_moniker)
.cloned();
let res = storage::delete_isolated_storage(
storage_capability_source_info.clone(),
relative_moniker,
instance_id.as_ref(),
)
.await;
match res {
Err(e) => {
warn!(
"couldn't delete storage for storage admin protocol: {:?}",
e
);
Err(fcomponent::Error::Internal)
}
Ok(()) => Ok(()),
}
}
};
match err_code {
Err(e) => responder.send(&mut Err(e))?,
Ok(()) => responder.send(&mut Ok(()))?,
}
}
}
}
Ok(())
}
}
#[async_trait]
impl Hook for StorageAdmin {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError> {
match &event.result {
Ok(EventPayload::CapabilityRouted {
source: CapabilitySource::Capability { source_capability, component },
capability_provider,
}) => {
let mut capability_provider = capability_provider.lock().await;
*capability_provider = self
.on_scoped_framework_capability_routed_async(
source_capability,
component.clone(),
capability_provider.take(),
)
.await?;
Ok(())
}
_ => Ok(()),
}
}
}