| // 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 { |
| anyhow::{self, Context}, |
| base_resolver_config::Config, |
| fidl::endpoints::{ClientEnd, Proxy}, |
| fidl_fuchsia_component_decl as fdecl, |
| fidl_fuchsia_component_resolution::{ |
| self as fresolution, ResolverRequest, ResolverRequestStream, |
| }, |
| fidl_fuchsia_io as fio, |
| fidl_fuchsia_pkg::PackageCacheMarker, |
| fidl_fuchsia_pkg_ext::BasePackageIndex, |
| fuchsia_component::{client::connect_to_protocol, server::ServiceFs}, |
| fuchsia_pkg::{ |
| transitional::{context_bytes_from_subpackages_map, subpackages_map_from_context_bytes}, |
| PackageDirectory, |
| }, |
| fuchsia_url::{ComponentUrl, PackageUrl}, |
| futures::prelude::*, |
| log::*, |
| }; |
| |
| pub(crate) async fn main() -> anyhow::Result<()> { |
| info!("started"); |
| |
| // Record configuration to inspect |
| let config = Config::take_from_startup_handle(); |
| let inspector = fuchsia_inspect::component::inspector(); |
| inspector.root().record_child("config", |config_node| config.record_inspect(config_node)); |
| |
| let mut service_fs = ServiceFs::new_local(); |
| service_fs.dir("svc").add_fidl_service(Services::BaseResolver); |
| service_fs.take_and_serve_directory_handle().context("failed to serve outgoing namespace")?; |
| let () = service_fs |
| .for_each_concurrent(None, |request| async { |
| match request { |
| Services::BaseResolver(stream) => { |
| serve(stream, &config) |
| .unwrap_or_else(|e| { |
| error!("failed to serve base resolver request: {:#}", e) |
| }) |
| .await |
| } |
| } |
| }) |
| .await; |
| |
| Ok(()) |
| } |
| |
| enum Services { |
| BaseResolver(ResolverRequestStream), |
| } |
| |
| async fn serve(mut stream: ResolverRequestStream, config: &Config) -> anyhow::Result<()> { |
| let pkg_cache = |
| connect_to_protocol::<PackageCacheMarker>().context("error connecting to package cache")?; |
| let base_package_index = BasePackageIndex::from_proxy(&pkg_cache) |
| .await |
| .context("failed to load base package index")?; |
| let packages_dir = fuchsia_fs::open_directory_in_namespace( |
| "/pkgfs/packages", |
| fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE, |
| ) |
| .context("failed to open /pkgfs/packages")?; |
| while let Some(request) = |
| stream.try_next().await.context("failed to read request from FIDL stream")? |
| { |
| match request { |
| ResolverRequest::Resolve { component_url, responder } => { |
| let mut result = |
| resolve_component(&component_url, &packages_dir).await.map_err(|err| { |
| let fidl_err = (&err).into(); |
| error!( |
| "failed to resolve component URL {}: {:#}", |
| &component_url, |
| anyhow::anyhow!(err) |
| ); |
| fidl_err |
| }); |
| responder.send(&mut result).context("failed sending response")?; |
| } |
| ResolverRequest::ResolveWithContext { component_url, context, responder } => { |
| if config.enable_subpackages { |
| let mut result = resolve_component_with_context( |
| &component_url, |
| &context, |
| &packages_dir, |
| &base_package_index, |
| ) |
| .await |
| .map_err(|err| { |
| let fidl_err = (&err).into(); |
| error!( |
| "failed to resolve component URL {} with context {:?}: {:#}", |
| &component_url, |
| &context, |
| anyhow::anyhow!(err) |
| ); |
| fidl_err |
| }); |
| responder.send(&mut result).context("failed sending response")?; |
| } else { |
| error!( |
| "base-resolver ResolveWithContext is disabled. Config value `enable_subpackages` is false. Cannot resolve component URL {:?} with context {:?}", |
| component_url, |
| context |
| ); |
| responder |
| .send(&mut Err(fresolution::ResolverError::Internal)) |
| .context("failed sending response")?; |
| } |
| } |
| }; |
| } |
| Ok(()) |
| } |
| |
| async fn resolve_component( |
| component_url: &str, |
| packages_dir: &fio::DirectoryProxy, |
| ) -> Result<fresolution::Component, crate::ResolverError> { |
| resolve_component_async(component_url, None, packages_dir, None).await |
| } |
| |
| async fn resolve_component_with_context( |
| component_url: &str, |
| context: &Vec<u8>, |
| packages_dir: &fio::DirectoryProxy, |
| base_package_index: &BasePackageIndex, |
| ) -> Result<fresolution::Component, crate::ResolverError> { |
| resolve_component_async(component_url, Some(context), packages_dir, Some(base_package_index)) |
| .await |
| } |
| |
| async fn resolve_component_async( |
| component_url_str: &str, |
| some_incoming_context: Option<&Vec<u8>>, |
| packages_dir: &fio::DirectoryProxy, |
| some_base_package_index: Option<&BasePackageIndex>, |
| ) -> Result<fresolution::Component, crate::ResolverError> { |
| let component_url = ComponentUrl::parse(component_url_str)?; |
| let package = resolve_package_async( |
| component_url.package_url(), |
| some_incoming_context, |
| packages_dir, |
| some_base_package_index, |
| ) |
| .await?; |
| |
| let data = mem_util::open_file_data(&package.dir, &component_url.resource()) |
| .await |
| .map_err(crate::ResolverError::ComponentNotFound)?; |
| let raw_bytes = mem_util::bytes_from_data(&data).map_err(crate::ResolverError::ReadManifest)?; |
| let decl: fdecl::Component = fidl::encoding::decode_persistent(&raw_bytes[..]) |
| .map_err(crate::ResolverError::ParsingManifest)?; |
| |
| let config_values = if let Some(config_decl) = decl.config.as_ref() { |
| // if we have a config declaration, we need to read the value file from the package dir |
| let strategy = |
| config_decl.value_source.as_ref().ok_or(crate::ResolverError::InvalidConfigSource)?; |
| let config_path = match strategy { |
| fdecl::ConfigValueSource::PackagePath(path) => path, |
| other => return Err(crate::ResolverError::UnsupportedConfigSource(other.to_owned())), |
| }; |
| Some( |
| mem_util::open_file_data(&package.dir, &config_path) |
| .await |
| .map_err(crate::ResolverError::ConfigValuesNotFound)?, |
| ) |
| } else { |
| None |
| }; |
| |
| let package_dir = ClientEnd::new( |
| package.dir.into_channel().expect("could not convert proxy to channel").into_zx_channel(), |
| ); |
| Ok(fresolution::Component { |
| url: Some(component_url_str.to_string()), |
| resolution_context: Some(package.context), |
| decl: Some(data), |
| package: Some(fresolution::Package { |
| url: Some(component_url.package_url().to_string()), |
| directory: Some(package_dir), |
| ..fresolution::Package::EMPTY |
| }), |
| config_values, |
| ..fresolution::Component::EMPTY |
| }) |
| } |
| |
| #[derive(Debug)] |
| struct ResolvedPackage { |
| dir: fio::DirectoryProxy, |
| context: Vec<u8>, |
| } |
| |
| async fn resolve_package_async( |
| package_url: &PackageUrl, |
| some_incoming_context: Option<&Vec<u8>>, |
| packages_dir: &fio::DirectoryProxy, |
| some_base_package_index: Option<&BasePackageIndex>, |
| ) -> Result<ResolvedPackage, crate::ResolverError> { |
| let (package_name, some_variant) = match package_url { |
| PackageUrl::Relative(relative) => { |
| let context = some_incoming_context.ok_or_else(|| { |
| crate::ResolverError::RelativeUrlMissingContext(package_url.to_string()) |
| })?; |
| // TODO(fxbug.dev/101492): Update base-resolver to use blobfs directly, |
| // and allow subpackage lookup to resolve subpackages from blobfs via |
| // the blobid (package hash). Then base-resolver will no longer need |
| // access to pkgfs-packages or to the package index (from PackageCache). |
| let base_package_index = |
| some_base_package_index.ok_or_else(|| crate::ResolverError::Internal)?; |
| let subpackage_hashes = subpackages_map_from_context_bytes(context) |
| .map_err(|err| crate::ResolverError::ReadingContext(err))?; |
| let hash = subpackage_hashes.get(relative).ok_or_else(|| { |
| crate::ResolverError::SubpackageNotFound(anyhow::format_err!( |
| "Subpackage '{}' not found in context: {:?}", |
| relative, |
| subpackage_hashes |
| )) |
| })?; |
| let absolute = base_package_index.get_url(&(*hash).into()).ok_or_else(|| { |
| crate::ResolverError::SubpackageNotInBase(anyhow::format_err!( |
| "Subpackage '{}' with hash '{}' is not in base", |
| relative, |
| hash |
| )) |
| })?; |
| (absolute.name(), absolute.variant()) |
| } |
| PackageUrl::Absolute(absolute) => { |
| if absolute.host() != "fuchsia.com" { |
| return Err(crate::ResolverError::UnsupportedRepo); |
| } |
| if absolute.hash().is_some() { |
| return Err(crate::ResolverError::PackageHashNotSupported); |
| } |
| (absolute.name(), absolute.variant()) |
| } |
| }; |
| // Package contents are available at `packages/$PACKAGE_NAME/0`. |
| let dir = fuchsia_fs::directory::open_directory( |
| packages_dir, |
| &format!("{}/{}", package_name.as_ref(), some_variant.map(|v| v.as_ref()).unwrap_or("0")), |
| fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE, |
| ) |
| .await |
| .map_err(crate::ResolverError::PackageNotFound)?; |
| let package_dir = PackageDirectory::from_proxy(dir); |
| let context = fabricate_package_context(&package_dir) |
| .map_err(|err| crate::ResolverError::CreatingContext(anyhow::anyhow!(err))) |
| .await?; |
| Ok(ResolvedPackage { dir: package_dir.into_proxy(), context }) |
| } |
| |
| async fn fabricate_package_context(package_dir: &PackageDirectory) -> anyhow::Result<Vec<u8>> { |
| let meta = package_dir.meta_subpackages().await?; |
| Ok(context_bytes_from_subpackages_map(&meta.into_subpackages())?.unwrap_or(vec![])) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| assert_matches::assert_matches, |
| fidl::encoding::encode_persistent_with_context, |
| fidl::endpoints::{create_proxy, ServerEnd}, |
| fidl::prelude::*, |
| fidl_fuchsia_component_config as fconfig, fidl_fuchsia_component_decl as fdecl, |
| fidl_fuchsia_mem as fmem, |
| fidl_fuchsia_pkg_ext::BlobId, |
| fuchsia_hash::Hash, |
| fuchsia_pkg::MetaSubpackages, |
| fuchsia_url::{RelativePackageUrl, UnpinnedAbsolutePackageUrl}, |
| fuchsia_zircon::Status, |
| maplit::hashmap, |
| std::{iter::FromIterator, str::FromStr, sync::Arc}, |
| }; |
| |
| const SUBPACKAGE_NAME: &'static str = "my_subpackage"; |
| const SUBPACKAGE_HASH: &'static str = |
| "facefacefacefacefacefacefacefacefacefacefacefacefacefacefaceface"; |
| const OTHER_PACKAGE_HASH: &'static str = |
| "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; |
| |
| /// A DirectoryEntry implementation that checks whether an expected set of flags |
| /// are set in the Open request. |
| struct FlagVerifier(fio::OpenFlags); |
| |
| impl vfs::directory::entry::DirectoryEntry for FlagVerifier { |
| fn open( |
| self: Arc<Self>, |
| _scope: vfs::execution_scope::ExecutionScope, |
| flags: fio::OpenFlags, |
| _mode: u32, |
| _path: vfs::path::Path, |
| server_end: ServerEnd<fio::NodeMarker>, |
| ) { |
| let status = if flags & self.0 != self.0 { Status::INVALID_ARGS } else { Status::OK }; |
| let stream = server_end.into_stream().expect("failed to create stream"); |
| let control_handle = stream.control_handle(); |
| control_handle |
| .send_on_open_( |
| status.into_raw(), |
| Some(&mut fio::NodeInfo::Directory(fio::DirectoryObject {})), |
| ) |
| .expect("failed to send OnOpen event"); |
| control_handle.shutdown_with_epitaph(status); |
| } |
| |
| fn entry_info(&self) -> vfs::directory::entry::EntryInfo { |
| vfs::directory::entry::EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory) |
| } |
| } |
| |
| /// Serve a pseudo_dir with `RIGHT_EXECUTABLE` permissions, which can |
| /// emulate `/pkgfs/packages` for tests. |
| fn serve_executable_dir( |
| pseudo_dir: Arc<dyn vfs::directory::entry::DirectoryEntry>, |
| ) -> fio::DirectoryProxy { |
| let (proxy, server_end) = create_proxy::<fio::DirectoryMarker>() |
| .expect("failed to create DirectoryProxy/Server pair"); |
| let () = pseudo_dir.open( |
| vfs::execution_scope::ExecutionScope::new(), |
| fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE, |
| 0, |
| vfs::path::Path::dot(), |
| ServerEnd::new(server_end.into_channel()), |
| ); |
| proxy |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_package_unsupported_repo() { |
| let packages_dir = serve_executable_dir(vfs::pseudo_directory! {}); |
| assert_matches!( |
| resolve_component("fuchsia-pkg://fuchsia.ca/test-package#meta/foo.cm", &packages_dir) |
| .await, |
| Err(crate::ResolverError::UnsupportedRepo) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_component_invalid_url() { |
| let packages_dir = serve_executable_dir(vfs::pseudo_directory! {}); |
| assert_matches!( |
| resolve_component("fuchsia://fuchsia.com/foo#meta/bar.cm", &packages_dir).await, |
| Err(crate::ResolverError::InvalidUrl(_)) |
| ); |
| assert_matches!( |
| resolve_component("fuchsia-pkg://fuchsia.com/foo", &packages_dir).await, |
| Err(crate::ResolverError::InvalidUrl(_)) |
| ); |
| assert_matches!( |
| resolve_component("fuchsia-pkg://fuchsia.com/#meta/bar.cm", &packages_dir).await, |
| Err(crate::ResolverError::InvalidUrl(_)) |
| ); |
| assert_matches!( |
| resolve_component("fuchsia-pkg://fuchsia.ca/foo#meta/bar.cm", &packages_dir).await, |
| Err(crate::ResolverError::UnsupportedRepo) |
| ); |
| |
| let url_with_hash = concat!( |
| "fuchsia-pkg://fuchsia.com/test-package", |
| "?hash=f241b31d5913b66c90a44d44537d6bec62672e1f05dbc4c4f22b863b01c68749", |
| "#meta/test.cm" |
| ); |
| assert_matches!( |
| resolve_component(url_with_hash, &packages_dir).await, |
| Err(crate::ResolverError::PackageHashNotSupported) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_component_package_not_found() { |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| assert_matches!( |
| resolve_component( |
| "fuchsia-pkg://fuchsia.com/missing-package#meta/foo.cm", |
| &packages_dir |
| ) |
| .await, |
| Err(crate::ResolverError::PackageNotFound(_)) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_component_missing_manifest() { |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| assert_matches!( |
| resolve_component("fuchsia-pkg://fuchsia.com/test-package#meta/bar.cm", &packages_dir) |
| .await, |
| Err(crate::ResolverError::ComponentNotFound(_)) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn resolves_component_vmo_manifest() { |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| assert_matches!( |
| resolve_component("fuchsia-pkg://fuchsia.com/test-package#meta/vmo.cm", &packages_dir) |
| .await, |
| Ok(fresolution::Component { decl: Some(fmem::Data::Buffer(_)), .. }) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn resolves_component_file_manifest() { |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| assert_matches!( |
| resolve_component("fuchsia-pkg://fuchsia.com/test-package#meta/foo.cm", &packages_dir) |
| .await, |
| Ok(fresolution::Component { |
| decl: Some(fidl_fuchsia_mem::Data::Buffer(fidl_fuchsia_mem::Buffer { .. })), |
| .. |
| }) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn resolves_component_in_subpackage() { |
| let parent_component_url = "fuchsia-pkg://fuchsia.com/test-package#meta/foo.cm"; |
| let subpackaged_component_url = SUBPACKAGE_NAME.to_string() + "#meta/subfoo.cm"; |
| |
| // Set up the base package index with the subpackage's hash that will |
| // be requested |
| let subpackage_as_base_package_url: UnpinnedAbsolutePackageUrl = |
| "fuchsia-pkg://fuchsia.com/toplevel-subpackage".parse().unwrap(); |
| let subpackage_blob_id = BlobId::parse(SUBPACKAGE_HASH).unwrap(); |
| let other_package_as_base_package_url: UnpinnedAbsolutePackageUrl = |
| "fuchsia-pkg://fuchsia.com/some-other-package".parse().unwrap(); |
| let other_package_blob_id = BlobId::parse(OTHER_PACKAGE_HASH).unwrap(); |
| let index = hashmap! { |
| subpackage_as_base_package_url => subpackage_blob_id, |
| other_package_as_base_package_url => other_package_blob_id, |
| }; |
| let base_package_index = BasePackageIndex::create_mock(index); |
| |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| let parent_component = resolve_component(parent_component_url, &packages_dir) |
| .await |
| .expect("failed to resolve parent_component"); |
| |
| assert_matches!(parent_component.resolution_context, Some(..)); |
| assert_matches!( |
| resolve_component_with_context( |
| &subpackaged_component_url, |
| parent_component.resolution_context.as_ref().unwrap(), |
| &packages_dir, |
| &base_package_index, |
| ) |
| .await, |
| Ok(fresolution::Component { decl: Some(..), .. }), |
| "Could not resolve subpackaged component '{}' from context '{:?}'", |
| subpackaged_component_url, |
| parent_component.resolution_context |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_component_in_subpackage_not_in_base() { |
| let parent_component_url = "fuchsia-pkg://fuchsia.com/test-package#meta/foo.cm"; |
| let subpackaged_component_url = SUBPACKAGE_NAME.to_string() + "#meta/other_subfoo.cm"; |
| |
| // Set up the base package index WITHOUT the subpackage's hash that will |
| // be requested |
| let other_package_as_base_package_url: UnpinnedAbsolutePackageUrl = |
| "fuchsia-pkg://fuchsia.com/some-other-package".parse().unwrap(); |
| let other_package_blob_id = BlobId::parse(OTHER_PACKAGE_HASH).unwrap(); |
| let index = hashmap! { |
| other_package_as_base_package_url => other_package_blob_id, |
| }; |
| let base_package_index = BasePackageIndex::create_mock(index); |
| assert!(base_package_index.contains_package(&other_package_blob_id)); |
| |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| let parent_component = resolve_component(parent_component_url, &packages_dir) |
| .await |
| .expect("failed to resolve parent_component"); |
| |
| assert_matches!(parent_component.resolution_context, Some(..)); |
| assert_matches!( |
| resolve_component_with_context( |
| &subpackaged_component_url, |
| parent_component.resolution_context.as_ref().unwrap(), |
| &packages_dir, |
| &base_package_index, |
| ) |
| .await, |
| Err(crate::ResolverError::SubpackageNotInBase(..)) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_subpackage_name_not_in_parent_subpackages() { |
| let parent_component_url = "fuchsia-pkg://fuchsia.com/test-package#meta/foo.cm"; |
| let subpackaged_component_url = "subpackage_not_in_parent#meta/other_subfoo.cm"; |
| |
| // Set up the base package index WITHOUT the subpackage's hash that will |
| // be requested |
| let other_package_as_base_package_url: UnpinnedAbsolutePackageUrl = |
| "fuchsia-pkg://fuchsia.com/some-other-package".parse().unwrap(); |
| let other_package_blob_id = BlobId::parse(OTHER_PACKAGE_HASH).unwrap(); |
| let index = hashmap! { |
| other_package_as_base_package_url => other_package_blob_id, |
| }; |
| let base_package_index = BasePackageIndex::create_mock(index); |
| assert!(base_package_index.contains_package(&other_package_blob_id)); |
| |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| let parent_component = resolve_component(parent_component_url, &packages_dir) |
| .await |
| .expect("failed to resolve parent_component"); |
| |
| assert_matches!(parent_component.resolution_context, Some(..)); |
| assert_matches!( |
| resolve_component_with_context( |
| &subpackaged_component_url, |
| parent_component.resolution_context.as_ref().unwrap(), |
| &packages_dir, |
| &base_package_index, |
| ) |
| .await, |
| Err(crate::ResolverError::SubpackageNotFound(..)) |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn resolves_component_with_config() { |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| let component = resolve_component( |
| "fuchsia-pkg://fuchsia.com/test-package#meta/foo-with-config.cm", |
| &packages_dir, |
| ) |
| .await |
| .unwrap(); |
| assert_matches!( |
| component, |
| fresolution::Component { decl: Some(..), config_values: Some(..), .. } |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_component_missing_config_values() { |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| let error = resolve_component( |
| "fuchsia-pkg://fuchsia.com/test-package#meta/foo-without-config.cm", |
| &packages_dir, |
| ) |
| .await |
| .unwrap_err(); |
| assert_matches!(error, crate::ResolverError::ConfigValuesNotFound(..)); |
| } |
| |
| #[fuchsia::test] |
| async fn fails_to_resolve_component_bad_config_source() { |
| let packages_dir = serve_executable_dir(build_fake_packages_dir()); |
| let error = resolve_component( |
| "fuchsia-pkg://fuchsia.com/test-package#meta/foo-with-bad-config.cm", |
| &packages_dir, |
| ) |
| .await |
| .unwrap_err(); |
| assert_matches!(error, crate::ResolverError::InvalidConfigSource); |
| } |
| |
| fn build_fake_packages_dir() -> Arc<dyn vfs::directory::entry::DirectoryEntry> { |
| let subpackage = build_fake_subpackage_dir(); |
| let parent_package = build_fake_package_dir(); |
| vfs::pseudo_directory! { |
| "test-package" => vfs::pseudo_directory! { |
| "0" => parent_package, |
| }, |
| "toplevel-subpackage" => vfs::pseudo_directory! { |
| "0" => subpackage, |
| }, |
| } |
| } |
| |
| fn build_fake_package_dir() -> Arc<dyn vfs::directory::entry::DirectoryEntry> { |
| let cm_bytes = encode_persistent_with_context( |
| &fidl::encoding::Context { wire_format_version: fidl::encoding::WireFormatVersion::V2 }, |
| &mut fdecl::Component::EMPTY.clone(), |
| ) |
| .expect("failed to encode ComponentDecl FIDL"); |
| |
| let subpackages = MetaSubpackages::from_iter(vec![( |
| RelativePackageUrl::parse(SUBPACKAGE_NAME).unwrap(), |
| Hash::from_str(SUBPACKAGE_HASH).unwrap(), |
| )]); |
| |
| vfs::pseudo_directory! { |
| "meta" => vfs::pseudo_directory! { |
| "fuchsia.pkg" => vfs::pseudo_directory! { |
| "subpackages" => vfs::file::vmo::asynchronous::read_only_const(&serde_json::to_vec(&subpackages).unwrap()), |
| }, |
| "foo.cm" => vfs::file::vmo::asynchronous::read_only_const(&cm_bytes), |
| "foo-with-config.cm" => vfs::file::vmo::asynchronous::read_only_const( |
| &encode_persistent_with_context( |
| &fidl::encoding::Context { |
| wire_format_version: fidl::encoding::WireFormatVersion::V2 |
| }, |
| &mut fdecl::Component { |
| config: Some(fdecl::ConfigSchema { |
| value_source: Some( |
| fdecl::ConfigValueSource::PackagePath( |
| "meta/foo-with-config.cvf".to_string(), |
| ), |
| ), |
| ..fdecl::ConfigSchema::EMPTY |
| }), |
| ..fdecl::Component::EMPTY |
| } |
| ).unwrap() |
| ), |
| "foo-with-config.cvf" => vfs::file::vmo::asynchronous::read_only_const( |
| &encode_persistent_with_context( |
| &fidl::encoding::Context { |
| wire_format_version: fidl::encoding::WireFormatVersion::V2 |
| }, |
| &mut fconfig::ValuesData { |
| ..fconfig::ValuesData::EMPTY |
| } |
| ).unwrap() |
| ), |
| "foo-with-bad-config.cm" => vfs::file::vmo::asynchronous::read_only_const( |
| &encode_persistent_with_context( |
| &fidl::encoding::Context { |
| wire_format_version: fidl::encoding::WireFormatVersion::V2 |
| }, |
| &mut fdecl::Component { |
| config: Some(fdecl::ConfigSchema { |
| ..fdecl::ConfigSchema::EMPTY |
| }), |
| ..fdecl::Component::EMPTY |
| } |
| ).unwrap() |
| ), |
| "foo-without-config.cm" => vfs::file::vmo::asynchronous::read_only_const( |
| &encode_persistent_with_context( |
| &fidl::encoding::Context { |
| wire_format_version: fidl::encoding::WireFormatVersion::V2 |
| }, |
| &mut fdecl::Component { |
| config: Some(fdecl::ConfigSchema { |
| value_source: Some( |
| fdecl::ConfigValueSource::PackagePath( |
| "doesnt-exist.cvf".to_string(), |
| ), |
| ), |
| ..fdecl::ConfigSchema::EMPTY |
| }), |
| ..fdecl::Component::EMPTY |
| } |
| ).unwrap() |
| ), |
| "vmo.cm" => vfs::file::vmo::asynchronous::read_only_const(&cm_bytes), |
| } |
| } |
| } |
| |
| fn build_fake_subpackage_dir() -> Arc<dyn vfs::directory::entry::DirectoryEntry> { |
| let cm_bytes = encode_persistent_with_context( |
| &fidl::encoding::Context { wire_format_version: fidl::encoding::WireFormatVersion::V2 }, |
| &mut fdecl::Component::EMPTY.clone(), |
| ) |
| .expect("failed to encode ComponentDecl FIDL"); |
| |
| vfs::pseudo_directory! { |
| "meta" => vfs::pseudo_directory! { |
| "subfoo.cm" => vfs::file::vmo::asynchronous::read_only_const(&cm_bytes), |
| } |
| } |
| } |
| } |