blob: 1df467d43754c881db3b9c88ed4d7460a7e363f0 [file] [log] [blame]
// Copyright 2021 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::{policy::PolicyError, rights::Rights},
bedrock_error::{BedrockError, Explain},
clonable_error::ClonableError,
cm_rust::CapabilityTypeName,
cm_types::Name,
fidl_fuchsia_component as fcomponent, fuchsia_zircon_status as zx,
moniker::{ChildName, Moniker, MonikerError},
std::sync::Arc,
thiserror::Error,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Errors produced by `ComponentInstanceInterface`.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
#[derive(Debug, Error, Clone)]
pub enum ComponentInstanceError {
#[error("component instance {} not found", moniker)]
InstanceNotFound { moniker: Moniker },
#[error("component manager instance unavailable")]
ComponentManagerInstanceUnavailable {},
#[error("malformed url {} for component instance {}", url, moniker)]
MalformedUrl { url: String, moniker: Moniker },
#[error("url {} for component {} does not resolve to an absolute url", url, moniker)]
NoAbsoluteUrl { url: String, moniker: Moniker },
// The capability routing static analyzer never produces this error subtype, so we don't need
// to serialize it.
#[cfg_attr(feature = "serde", serde(skip))]
#[error("Failed to resolve `{}`: {}", moniker, err)]
ResolveFailed {
moniker: Moniker,
#[source]
err: ClonableError,
},
}
impl ComponentInstanceError {
pub fn as_zx_status(&self) -> zx::Status {
match self {
ComponentInstanceError::ResolveFailed { .. }
| ComponentInstanceError::InstanceNotFound { .. }
| ComponentInstanceError::ComponentManagerInstanceUnavailable {}
| ComponentInstanceError::NoAbsoluteUrl { .. } => zx::Status::NOT_FOUND,
ComponentInstanceError::MalformedUrl { .. } => zx::Status::INTERNAL,
}
}
pub fn instance_not_found(moniker: Moniker) -> ComponentInstanceError {
ComponentInstanceError::InstanceNotFound { moniker }
}
pub fn cm_instance_unavailable() -> ComponentInstanceError {
ComponentInstanceError::ComponentManagerInstanceUnavailable {}
}
pub fn resolve_failed(moniker: Moniker, err: impl Into<anyhow::Error>) -> Self {
Self::ResolveFailed { moniker, err: err.into().into() }
}
}
// Custom implementation of PartialEq in which two ComponentInstanceError::ResolveFailed errors are
// never equal.
impl PartialEq for ComponentInstanceError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::InstanceNotFound { moniker: self_moniker },
Self::InstanceNotFound { moniker: other_moniker },
) => self_moniker.eq(other_moniker),
(
Self::ComponentManagerInstanceUnavailable {},
Self::ComponentManagerInstanceUnavailable {},
) => true,
(Self::ResolveFailed { .. }, Self::ResolveFailed { .. }) => false,
_ => false,
}
}
}
/// Errors produced during routing.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
#[derive(Debug, Error, Clone, PartialEq)]
pub enum RoutingError {
#[error(
"Backing directory `{}` was not exposed to `{}` from child `#{}`.",
capability_id,
moniker,
child_moniker
)]
StorageFromChildExposeNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_id: String,
},
#[error(
"`{}` tried to use a storage capability from `{}` but it is not in the component id index. \
See: https://fuchsia.dev/go/components/instance-id.",
target_moniker, source_moniker
)]
ComponentNotInIdIndex { source_moniker: Moniker, target_moniker: Moniker },
#[error("`{}` is not a built-in capability.", capability_id)]
UseFromComponentManagerNotFound { capability_id: String },
#[error("`{}` is not a built-in capability.", capability_id)]
RegisterFromComponentManagerNotFound { capability_id: String },
#[error("`{}` is not a built-in capability.", capability_id)]
OfferFromComponentManagerNotFound { capability_id: String },
#[error("`{}` was not offered to `{}` by parent.", capability_id, moniker)]
UseFromParentNotFound { moniker: Moniker, capability_id: String },
#[error("`{}` was not declared as a capability by `{}`.", capability_id, moniker)]
UseFromSelfNotFound { moniker: Moniker, capability_id: String },
#[error("`{}` does not have child `#{}`.", moniker, child_moniker)]
UseFromChildInstanceNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_id: String,
},
#[error(
"{} `{}` was not registered in environment of `{}`.",
capability_type,
capability_name,
moniker
)]
UseFromEnvironmentNotFound { moniker: Moniker, capability_type: String, capability_name: Name },
#[error(
"`{}` tried to use {} `{}` from the root environment. This is not allowed.",
moniker,
capability_type,
capability_name
)]
UseFromRootEnvironmentNotAllowed {
moniker: Moniker,
capability_type: String,
capability_name: Name,
},
#[error("`{}` was not offered to `{}` by parent.", capability_name, moniker)]
EnvironmentFromParentNotFound {
moniker: Moniker,
capability_type: String,
capability_name: Name,
},
#[error(
"`{}` was not exposed to `{}` from child `#{}`.",
capability_name,
moniker,
child_moniker
)]
EnvironmentFromChildExposeNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_type: String,
capability_name: Name,
},
#[error("`{}` does not have child `#{}`.", moniker, child_moniker)]
EnvironmentFromChildInstanceNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_name: Name,
capability_type: String,
},
#[error(
"`{}` was not offered to `{}` by parent. For more, run `ffx component doctor {moniker}`.",
capability_id,
moniker
)]
OfferFromParentNotFound { moniker: Moniker, capability_id: String },
#[error(
"Unable to offer `{}` because was not declared as a capability by `{}`. For more, run `ffx component doctor {moniker}`.",
capability_id,
moniker
)]
OfferFromSelfNotFound { moniker: Moniker, capability_id: String },
#[error(
"`{}` was not offered to `{}` by parent. For more, run `ffx component doctor {moniker}`.",
capability_id,
moniker
)]
StorageFromParentNotFound { moniker: Moniker, capability_id: String },
#[error("`{}` does not have child `#{}`.", moniker, child_moniker)]
OfferFromChildInstanceNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_id: String,
},
#[error("`{}` does not have collection `#{}`.", moniker, collection)]
OfferFromCollectionNotFound { collection: String, moniker: Moniker, capability: Name },
#[error(
"`{}` was not exposed to `{}` from child `#{}`. For more, run `ffx component doctor {moniker}`.",
capability_id,
moniker,
child_moniker
)]
OfferFromChildExposeNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_id: String,
},
// TODO: Could this be distinguished by use/offer/expose?
#[error("`{}` is not a framework capability.", capability_id)]
CapabilityFromFrameworkNotFound { moniker: Moniker, capability_id: String },
#[error(
"A capability was sourced to a base capability `{}` from `{}`, but this is unsupported.",
capability_id,
moniker
)]
CapabilityFromCapabilityNotFound { moniker: Moniker, capability_id: String },
// TODO: Could this be distinguished by use/offer/expose?
#[error("`{}` is not a framework capability.", capability_id)]
CapabilityFromComponentManagerNotFound { capability_id: String },
#[error(
"Unable to expose `{}` because it was not declared as a capability by `{}`.",
capability_id,
moniker
)]
ExposeFromSelfNotFound { moniker: Moniker, capability_id: String },
#[error("`{}` does not have child `#{}`.", moniker, child_moniker)]
ExposeFromChildInstanceNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_id: String,
},
#[error("`{}` does not have collection `#{}`.", moniker, collection)]
ExposeFromCollectionNotFound { collection: String, moniker: Moniker, capability: Name },
#[error(
"`{}` was not exposed to `{}` from child `#{}`. For more, run `ffx component doctor {moniker}`.",
capability_id,
moniker,
child_moniker
)]
ExposeFromChildExposeNotFound {
child_moniker: ChildName,
moniker: Moniker,
capability_id: String,
},
#[error(
"`{}` tried to expose `{}` from the framework, but no such framework capability was found.",
moniker,
capability_id
)]
ExposeFromFrameworkNotFound { moniker: Moniker, capability_id: String },
#[error(
"`{}` was not exposed to `{}` from child `#{}`. For more, run `ffx component doctor {moniker}`.",
capability_id,
moniker,
child_moniker
)]
UseFromChildExposeNotFound { child_moniker: ChildName, moniker: Moniker, capability_id: String },
#[error("Routing a capability from an unsupported source type: {}.", source_type)]
UnsupportedRouteSource { source_type: String },
#[error("Routing a capability of an unsupported type: {}", type_name)]
UnsupportedCapabilityType { type_name: CapabilityTypeName },
#[error("Dictionaries are not yet supported for {cap_type} capabilities")]
DictionariesNotSupported { cap_type: CapabilityTypeName },
#[error("The capability does not support member access")]
BedrockMemberAccessUnsupported,
#[error("Item {name} is not present in dictionary")]
BedrockNotPresentInDictionary { name: String },
#[error("Source dictionary was not found in child's exposes")]
BedrockSourceDictionaryExposeNotFound,
#[error(
"A capability in a dictionary extended from a source dictionary collides with \
a capability in the source dictionary that has the same key"
)]
BedrockSourceDictionaryCollision,
#[error(transparent)]
ComponentInstanceError(#[from] ComponentInstanceError),
#[error(transparent)]
EventsRoutingError(#[from] EventsRoutingError),
#[error(transparent)]
RightsRoutingError(#[from] RightsRoutingError),
#[error(transparent)]
AvailabilityRoutingError(#[from] AvailabilityRoutingError),
#[error(transparent)]
PolicyError(#[from] PolicyError),
#[error(transparent)]
MonikerError(#[from] MonikerError),
#[error(
"source capability is void. \
If the offer/expose declaration has `source_availability` set to `unknown`, \
the source component instance likely isn't defined in the component declaration"
)]
SourceCapabilityIsVoid,
}
impl Explain for RoutingError {
/// Convert this error into its approximate `zx::Status` equivalent.
fn as_zx_status(&self) -> zx::Status {
match self {
RoutingError::UseFromRootEnvironmentNotAllowed { .. } => zx::Status::ACCESS_DENIED,
RoutingError::StorageFromChildExposeNotFound { .. }
| RoutingError::ComponentNotInIdIndex { .. }
| RoutingError::UseFromComponentManagerNotFound { .. }
| RoutingError::RegisterFromComponentManagerNotFound { .. }
| RoutingError::OfferFromComponentManagerNotFound { .. }
| RoutingError::UseFromParentNotFound { .. }
| RoutingError::UseFromSelfNotFound { .. }
| RoutingError::UseFromChildInstanceNotFound { .. }
| RoutingError::UseFromEnvironmentNotFound { .. }
| RoutingError::EnvironmentFromParentNotFound { .. }
| RoutingError::EnvironmentFromChildExposeNotFound { .. }
| RoutingError::EnvironmentFromChildInstanceNotFound { .. }
| RoutingError::OfferFromParentNotFound { .. }
| RoutingError::OfferFromSelfNotFound { .. }
| RoutingError::StorageFromParentNotFound { .. }
| RoutingError::OfferFromChildInstanceNotFound { .. }
| RoutingError::OfferFromCollectionNotFound { .. }
| RoutingError::OfferFromChildExposeNotFound { .. }
| RoutingError::CapabilityFromFrameworkNotFound { .. }
| RoutingError::CapabilityFromCapabilityNotFound { .. }
| RoutingError::CapabilityFromComponentManagerNotFound { .. }
| RoutingError::ExposeFromSelfNotFound { .. }
| RoutingError::ExposeFromChildInstanceNotFound { .. }
| RoutingError::ExposeFromCollectionNotFound { .. }
| RoutingError::ExposeFromChildExposeNotFound { .. }
| RoutingError::ExposeFromFrameworkNotFound { .. }
| RoutingError::UseFromChildExposeNotFound { .. }
| RoutingError::UnsupportedRouteSource { .. }
| RoutingError::UnsupportedCapabilityType { .. }
| RoutingError::EventsRoutingError(_)
| RoutingError::BedrockNotPresentInDictionary { .. }
| RoutingError::BedrockSourceDictionaryExposeNotFound { .. }
| RoutingError::BedrockSourceDictionaryCollision { .. }
| RoutingError::AvailabilityRoutingError(_) => zx::Status::NOT_FOUND,
RoutingError::BedrockMemberAccessUnsupported { .. }
| RoutingError::DictionariesNotSupported { .. } => zx::Status::NOT_SUPPORTED,
RoutingError::MonikerError(_) => zx::Status::INTERNAL,
RoutingError::ComponentInstanceError(err) => err.as_zx_status(),
RoutingError::RightsRoutingError(err) => err.as_zx_status(),
RoutingError::PolicyError(err) => err.as_zx_status(),
RoutingError::SourceCapabilityIsVoid => zx::Status::NOT_FOUND,
}
}
}
impl From<RoutingError> for BedrockError {
fn from(value: RoutingError) -> Self {
BedrockError::RoutingError(Arc::new(value))
}
}
impl RoutingError {
/// Convert this error into its approximate `fuchsia.component.Error` equivalent.
pub fn as_fidl_error(&self) -> fcomponent::Error {
fcomponent::Error::ResourceUnavailable
}
pub fn storage_from_child_expose_not_found(
child_moniker: &ChildName,
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::StorageFromChildExposeNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn use_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
Self::UseFromComponentManagerNotFound { capability_id: capability_id.into() }
}
pub fn register_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
Self::RegisterFromComponentManagerNotFound { capability_id: capability_id.into() }
}
pub fn offer_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
Self::OfferFromComponentManagerNotFound { capability_id: capability_id.into() }
}
pub fn use_from_parent_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
Self::UseFromParentNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn use_from_self_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
Self::UseFromSelfNotFound { moniker: moniker.clone(), capability_id: capability_id.into() }
}
pub fn use_from_child_instance_not_found(
child_moniker: &ChildName,
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::UseFromChildInstanceNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn use_from_environment_not_found(
moniker: &Moniker,
capability_type: impl Into<String>,
capability_name: &Name,
) -> Self {
Self::UseFromEnvironmentNotFound {
moniker: moniker.clone(),
capability_type: capability_type.into(),
capability_name: capability_name.clone(),
}
}
pub fn offer_from_parent_not_found(
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::OfferFromParentNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn offer_from_self_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
Self::OfferFromSelfNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn storage_from_parent_not_found(
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::StorageFromParentNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn offer_from_child_instance_not_found(
child_moniker: &ChildName,
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::OfferFromChildInstanceNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn offer_from_child_expose_not_found(
child_moniker: &ChildName,
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::OfferFromChildExposeNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn use_from_child_expose_not_found(
child_moniker: &ChildName,
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::UseFromChildExposeNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn expose_from_self_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
Self::ExposeFromSelfNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn expose_from_child_instance_not_found(
child_moniker: &ChildName,
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::ExposeFromChildInstanceNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn expose_from_child_expose_not_found(
child_moniker: &ChildName,
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::ExposeFromChildExposeNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn capability_from_framework_not_found(
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::CapabilityFromFrameworkNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn capability_from_capability_not_found(
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::CapabilityFromCapabilityNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn capability_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
Self::CapabilityFromComponentManagerNotFound { capability_id: capability_id.into() }
}
pub fn expose_from_framework_not_found(
moniker: &Moniker,
capability_id: impl Into<String>,
) -> Self {
Self::ExposeFromFrameworkNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn unsupported_route_source(source: impl Into<String>) -> Self {
Self::UnsupportedRouteSource { source_type: source.into() }
}
pub fn unsupported_capability_type(type_name: impl Into<CapabilityTypeName>) -> Self {
Self::UnsupportedCapabilityType { type_name: type_name.into() }
}
}
/// Errors produced during routing specific to events.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
#[derive(Error, Debug, Clone, PartialEq)]
pub enum EventsRoutingError {
#[error("Filter is not a subset")]
InvalidFilter,
#[error("Event routes must end at source with a filter declaration")]
MissingFilter,
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
#[derive(Debug, Error, Clone, PartialEq)]
pub enum RightsRoutingError {
#[error("Requested rights ({requested}) greater than provided rights ({provided})")]
Invalid { requested: Rights, provided: Rights },
#[error("Directory routes must end at source with a rights declaration")]
MissingRightsSource,
}
impl RightsRoutingError {
/// Convert this error into its approximate `zx::Status` equivalent.
pub fn as_zx_status(&self) -> zx::Status {
match self {
RightsRoutingError::Invalid { .. } => zx::Status::ACCESS_DENIED,
RightsRoutingError::MissingRightsSource => zx::Status::NOT_FOUND,
}
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
#[derive(Debug, Error, Clone, PartialEq)]
pub enum AvailabilityRoutingError {
#[error(
"Availability requested by the target has stronger guarantees than what \
is being provided at the source."
)]
TargetHasStrongerAvailability,
#[error("Offer uses void source, but target requires the capability")]
OfferFromVoidToRequiredTarget,
#[error("Expose uses void source, but target requires the capability")]
ExposeFromVoidToRequiredTarget,
}
impl From<availability::TargetHasStrongerAvailability> for AvailabilityRoutingError {
fn from(value: availability::TargetHasStrongerAvailability) -> Self {
let availability::TargetHasStrongerAvailability = value;
AvailabilityRoutingError::TargetHasStrongerAvailability
}
}