blob: b9ce6d8bd0b35d155ba81d44426c4ca6dc7b67a1 [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::{
capability::{CapabilityProvider, CapabilitySource, InternalCapability},
channel,
model::{
error::ModelError,
hooks::{Event, EventPayload, EventType, Hook, HooksRegistration},
rights,
testing::{routing_test_helpers::*, test_helpers::*},
},
},
async_trait::async_trait,
cm_rust::*,
fidl::endpoints::ServerEnd,
fidl_fuchsia_io::{self as fio, DirectoryProxy},
fuchsia_zircon as zx,
std::{
convert::TryFrom,
path::PathBuf,
sync::{Arc, Weak},
},
};
#[fuchsia_async::run_singlethreaded(test)]
async fn offer_increasing_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(
DirectoryDeclBuilder::new("foo_data")
.rights(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS)
.build(),
)
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS),
subdir: None,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn offer_incompatible_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::WRITE_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(
DirectoryDeclBuilder::new("foo_data")
.rights(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS)
.build(),
)
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS),
subdir: None,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn expose_increasing_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(
DirectoryDeclBuilder::new("foo_data")
.rights(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS)
.build(),
)
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS),
subdir: None,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn expose_incompatible_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(
DirectoryDeclBuilder::new("foo_data")
.rights(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS)
.build(),
)
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::WRITE_RIGHTS),
subdir: None,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn capability_increasing_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(
DirectoryDeclBuilder::new("foo_data")
.rights(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS)
.build(),
)
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn capability_incompatible_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(
DirectoryDeclBuilder::new("foo_data").rights(*rights::WRITE_RIGHTS).build(),
)
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS | *rights::WRITE_RIGHTS),
subdir: None,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
}
struct MockFrameworkDirectoryProvider {
test_dir_proxy: DirectoryProxy,
}
struct MockFrameworkDirectoryHost {
test_dir_proxy: DirectoryProxy,
}
#[async_trait]
impl CapabilityProvider for MockFrameworkDirectoryProvider {
async fn open(
self: Box<Self>,
flags: u32,
open_mode: u32,
relative_path: PathBuf,
server_end: &mut zx::Channel,
) -> Result<(), ModelError> {
let relative_path = relative_path.to_str().unwrap();
let server_end = channel::take_channel(server_end);
let server_end = ServerEnd::<fio::NodeMarker>::new(server_end);
self.test_dir_proxy
.open(flags, open_mode, relative_path, server_end)
.expect("failed to open test dir");
Ok(())
}
}
#[async_trait]
impl Hook for MockFrameworkDirectoryHost {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError> {
if let Ok(EventPayload::CapabilityRouted {
source:
CapabilitySource::Framework {
capability: InternalCapability::Directory(source_name),
..
},
capability_provider,
}) = &event.result
{
let mut capability_provider = capability_provider.lock().await;
if source_name.str() == "foo_data" {
let test_dir_proxy =
io_util::clone_directory(&self.test_dir_proxy, fio::CLONE_FLAG_SAME_RIGHTS)
.expect("failed to clone test dir");
*capability_provider =
Some(Box::new(MockFrameworkDirectoryProvider { test_dir_proxy }));
}
}
Ok(())
}
}
#[fuchsia_async::run_singlethreaded(test)]
async fn framework_directory_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Framework,
source_name: "foo_data".into(),
target_name: "foo_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: None,
subdir: Some("foo".into()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
let test_dir_proxy =
io_util::clone_directory(&test.test_dir_proxy, fio::CLONE_FLAG_SAME_RIGHTS)
.expect("failed to clone test dir");
let directory_host = Arc::new(MockFrameworkDirectoryHost { test_dir_proxy });
test.model
.root_realm
.hooks
.install(vec![HooksRegistration::new(
"MockFrameworkDirectoryHost",
vec![EventType::CapabilityRouted],
Arc::downgrade(&directory_host) as Weak<dyn Hook>,
)])
.await;
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn framework_directory_incompatible_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Framework,
source_name: "foo_data".into(),
target_name: "foo_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: None,
subdir: Some("foo".into()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS | *rights::WRITE_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
let test_dir_proxy =
io_util::clone_directory(&test.test_dir_proxy, fio::CLONE_FLAG_SAME_RIGHTS)
.expect("failed to clone test dir");
let directory_host = Arc::new(MockFrameworkDirectoryHost { test_dir_proxy });
test.model
.root_realm
.hooks
.install(vec![HooksRegistration::new(
"MockFrameworkDirectoryHost",
vec![EventType::CapabilityRouted],
Arc::downgrade(&directory_host) as Weak<dyn Hook>,
)])
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
}
/// component manager's namespace
/// |
/// a
/// \
/// b
///
/// a: offers directory /offer_from_cm_namespace/data/foo from realm as bar_data
/// b: uses directory bar_data as /data/hippo, but the rights don't match
#[fuchsia_async::run_singlethreaded(test)]
async fn offer_from_component_manager_namespace_directory_incompatible_rights() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Parent,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: None,
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "bar_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let namespace_capabilities = vec![CapabilityDecl::Directory(
DirectoryDeclBuilder::new("foo_data")
.path("/offer_from_cm_namespace/data/foo")
.rights(*rights::WRITE_RIGHTS)
.build(),
)];
let test = RoutingTestBuilder::new("a", components)
.set_namespace_capabilities(namespace_capabilities)
.build()
.await;
let _ns_dir = ScopedNamespaceDir::new(&test, "/offer_from_cm_namespace");
test.check_use(
vec!["b:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
}