blob: 88706a5152e6bfd92ce2f23eaa0d0b656aa05f86 [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::{component_id_index::ComponentIdIndexError, policy::PolicyError},
clonable_error::ClonableError,
cm_rust::CapabilityName,
fidl_fuchsia_component as fcomponent, fuchsia_zircon_status as zx,
moniker::{AbsoluteMoniker, ChildMoniker},
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: AbsoluteMoniker },
#[error("component manager instance unavailable")]
ComponentManagerInstanceUnavailable {},
#[error("policy checker not found for component instance {}", moniker)]
PolicyCheckerNotFound { moniker: AbsoluteMoniker },
#[error("component ID index not found for component instance {}", moniker)]
ComponentIdIndexNotFound { moniker: AbsoluteMoniker },
#[error("malformed url {} for component instance {}", url, moniker)]
MalformedUrl { url: String, moniker: AbsoluteMoniker },
#[error("url {} for component {} does not resolve to an absolute url", url, moniker)]
NoAbsoluteUrl { url: String, moniker: AbsoluteMoniker },
// 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: AbsoluteMoniker,
#[source]
err: ClonableError,
},
// 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 unresolve `{}`: {}", moniker, err)]
UnresolveFailed {
moniker: AbsoluteMoniker,
#[source]
err: ClonableError,
},
}
impl ComponentInstanceError {
pub fn as_zx_status(&self) -> zx::Status {
match self {
ComponentInstanceError::ResolveFailed { .. }
| ComponentInstanceError::InstanceNotFound { .. } => zx::Status::NOT_FOUND,
_ => zx::Status::UNAVAILABLE,
}
}
pub fn instance_not_found(moniker: AbsoluteMoniker) -> ComponentInstanceError {
ComponentInstanceError::InstanceNotFound { moniker }
}
pub fn cm_instance_unavailable() -> ComponentInstanceError {
ComponentInstanceError::ComponentManagerInstanceUnavailable {}
}
pub fn resolve_failed(moniker: AbsoluteMoniker, err: impl Into<anyhow::Error>) -> Self {
Self::ResolveFailed { moniker, err: err.into().into() }
}
pub fn unresolve_failed(moniker: AbsoluteMoniker, err: impl Into<anyhow::Error>) -> Self {
Self::UnresolveFailed { 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::PolicyCheckerNotFound { moniker: self_moniker },
Self::PolicyCheckerNotFound { moniker: other_moniker },
) => self_moniker.eq(other_moniker),
(
Self::ComponentIdIndexNotFound { moniker: self_moniker },
Self::ComponentIdIndexNotFound { moniker: other_moniker },
) => self_moniker.eq(other_moniker),
(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("Instance identified as source of capability is not running: `{}`.", moniker)]
SourceInstanceStopped { moniker: AbsoluteMoniker },
#[error(
"Instance identified as source of capability is a non-executable component: `{}`.",
moniker
)]
SourceInstanceNotExecutable { moniker: AbsoluteMoniker },
#[error(
"Source for directory backing storage from `{}` must be a component or component manager's namespace, but was {}.",
storage_moniker,
source_type
)]
StorageDirectorySourceInvalid { source_type: String, storage_moniker: AbsoluteMoniker },
#[error(
"Source for directory storage from `{}` was a child `{}`, but this child was not found.",
storage_moniker,
child_moniker
)]
StorageDirectorySourceChildNotFound {
storage_moniker: AbsoluteMoniker,
child_moniker: ChildMoniker,
},
#[error(
"A `storage` declaration with a backing directory from child `{}` was found at `{}` for \
`{}`, but no matching `expose` declaration was found in the child.",
child_moniker,
moniker,
capability_id
)]
StorageFromChildExposeNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_id: String,
},
#[error(
"`{}` tried to use a storage capability which is restricted to the component ID index. \
To add this component, visit https://fuchsia.dev/go/components/instance-id.",
moniker
)]
ComponentNotInIdIndex { moniker: AbsoluteMoniker },
#[error(
"A `use from parent` declaration was found at `/` for `{}`, or a `register from parent` \
declaration was found in the root environment or one of the root component's declared environments, \
but no built-in capability matches.",
capability_id
)]
UseFromComponentManagerNotFound { capability_id: String },
#[error(
"A `register from parent` declaration was found at `/` or in one of the root component's declared \
environments for `{}` but no built-in capability matches.",
capability_id
)]
RegisterFromComponentManagerNotFound { capability_id: String },
#[error(
"An `offer from parent` declaration was found at `/` for `{}`, \
but no built-in capability matches.",
capability_id
)]
OfferFromComponentManagerNotFound { capability_id: String },
#[error(
"A `storage` declaration with a backing directory was found at `/` for `{}`, \
but no built-in capability matches.",
capability_id
)]
StorageFromComponentManagerNotFound { capability_id: String },
#[error(
"`{}` tried to use `{}` from its parent, but the parent does not offer that capability. \
Note, use clauses in CML default to using from parent.",
moniker,
capability_id
)]
UseFromParentNotFound { moniker: AbsoluteMoniker, capability_id: String },
#[error(
"`{}` tried to use `{}` from its child `#{}`, but no such child was found.",
moniker,
capability_id,
child_moniker
)]
UseFromChildInstanceNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_id: String,
},
#[error(
"`{}` tried to use {} `{}` from its environment, but no matching {} registration was found.",
moniker,
capability_type,
capability_name,
capability_type
)]
UseFromEnvironmentNotFound {
moniker: AbsoluteMoniker,
capability_type: String,
capability_name: CapabilityName,
},
#[error(
"`{}` tried to use {} `{}` from the root environment. This is not allowed.",
moniker,
capability_type,
capability_name
)]
UseFromRootEnvironmentNotAllowed {
moniker: AbsoluteMoniker,
capability_type: String,
capability_name: CapabilityName,
},
#[error(
"`{}` tried to register {} `{}` from its parent in its environment, but the parent \
does not offer `{}`.",
moniker,
capability_type,
capability_name,
capability_name
)]
EnvironmentFromParentNotFound {
moniker: AbsoluteMoniker,
capability_type: String,
capability_name: CapabilityName,
},
#[error(
"`{}` tried to register {} `{}` from its child `#{}` in its environment, but `#{}` \
does not expose `{}`.",
moniker,
capability_type,
capability_name,
child_moniker,
child_moniker,
capability_name
)]
EnvironmentFromChildExposeNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_type: String,
capability_name: CapabilityName,
},
#[error(
"`{}` tried to register {} `{}` from its child `#{}` in its environment, but no such child
was found.",
moniker,
capability_type,
capability_name,
child_moniker
)]
EnvironmentFromChildInstanceNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_name: CapabilityName,
capability_type: String,
},
#[error(
"`{}` tried to offer `{}` from its parent, but the parent does not offer that capability.",
moniker,
capability_id
)]
OfferFromParentNotFound { moniker: AbsoluteMoniker, capability_id: String },
#[error(
"`{}` tried to use a storage capability `{}` from its parent, but the parent does not \
offer that capability. Note, use clauses in CML default to using from parent.",
moniker,
capability_id
)]
StorageFromParentNotFound { moniker: AbsoluteMoniker, capability_id: String },
#[error(
"`{}` tried to offer `{}` from `#{}`, but no such child was found.",
moniker,
capability_id,
child_moniker
)]
OfferFromChildInstanceNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_id: String,
},
#[error(
"`{}` tried to offer `{}` from the collection `#{}`, but no such collection was found.",
moniker,
capability,
collection
)]
OfferFromCollectionNotFound {
collection: String,
moniker: AbsoluteMoniker,
capability: CapabilityName,
},
#[error(
"`{}` tried to offer `{}` from its child `#{}`, but `#{}` does not expose `{}`.",
moniker,
capability_id,
child_moniker,
child_moniker,
capability_id
)]
OfferFromChildExposeNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_id: String,
},
// TODO: Could this be distinguished by use/offer/expose?
#[error(
"A framework capability was sourced to `{}` with id `{}`, but no such \
framework capability was found.",
moniker,
capability_id
)]
CapabilityFromFrameworkNotFound { moniker: AbsoluteMoniker, capability_id: String },
#[error(
"A capability was sourced to a base capability `{}` from `{}`, but this is unsupported.",
capability_id,
moniker
)]
CapabilityFromCapabilityNotFound { moniker: AbsoluteMoniker, capability_id: String },
// TODO: Could this be distinguished by use/offer/expose?
#[error(
"A capability was sourced to component manager with id `{}`, but no matching \
capability was found.",
capability_id
)]
CapabilityFromComponentManagerNotFound { capability_id: String },
#[error(
"A capability was sourced to storage capability `{}` with id `{}`, but no matching \
capability was found.",
storage_capability,
capability_id
)]
CapabilityFromStorageCapabilityNotFound { storage_capability: String, capability_id: String },
#[error(
"`{}` tried to expose `{}` from its child `#{}`, but no such child was found.",
moniker,
capability_id,
child_moniker
)]
ExposeFromChildInstanceNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_id: String,
},
#[error(
"`{}` tried to expose `{}` from the collection `#{}`, but no such collection was found.",
moniker,
capability,
collection
)]
ExposeFromCollectionNotFound {
collection: String,
moniker: AbsoluteMoniker,
capability: CapabilityName,
},
#[error(
"`{}` tried to expose `{}` from its child `#{}`, but `#{}` does not expose `{}`.",
moniker,
capability_id,
child_moniker,
child_moniker,
capability_id
)]
ExposeFromChildExposeNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_id: String,
},
#[error(
"`{}` tried to expose `{}` from the framework, but no such framework capability was found.",
moniker,
capability_id
)]
ExposeFromFrameworkNotFound { moniker: AbsoluteMoniker, capability_id: String },
#[error(
"`{}` tried to use `{}` from its child `#{}`, but `#{}` does not expose `{}`.",
moniker,
capability_id,
child_moniker,
child_moniker,
capability_id
)]
UseFromChildExposeNotFound {
child_moniker: ChildMoniker,
moniker: AbsoluteMoniker,
capability_id: String,
},
#[error("Routing a capability from an unsupported source type: {}.", source_type)]
UnsupportedRouteSource { source_type: String },
#[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)]
ComponentIdIndexError(#[from] ComponentIdIndexError),
}
impl RoutingError {
/// Convert this error into its approximate `fuchsia.component.Error` equivalent.
pub fn as_fidl_error(&self) -> fcomponent::Error {
fcomponent::Error::ResourceUnavailable
}
/// Convert this error into its approximate `zx::Status` equivalent.
pub fn as_zx_status(&self) -> zx::Status {
match self {
RoutingError::PolicyError(_) => zx::Status::ACCESS_DENIED,
RoutingError::ComponentInstanceError(err) => err.as_zx_status(),
_ => zx::Status::UNAVAILABLE,
}
}
pub fn source_instance_stopped(moniker: &AbsoluteMoniker) -> Self {
Self::SourceInstanceStopped { moniker: moniker.clone() }
}
pub fn source_instance_not_executable(moniker: &AbsoluteMoniker) -> Self {
Self::SourceInstanceNotExecutable { moniker: moniker.clone() }
}
pub fn storage_directory_source_invalid(
source_type: impl Into<String>,
storage_moniker: &AbsoluteMoniker,
) -> Self {
Self::StorageDirectorySourceInvalid {
source_type: source_type.into(),
storage_moniker: storage_moniker.clone(),
}
}
pub fn storage_directory_source_child_not_found(
storage_moniker: &AbsoluteMoniker,
child_moniker: &ChildMoniker,
) -> Self {
Self::StorageDirectorySourceChildNotFound {
storage_moniker: storage_moniker.clone(),
child_moniker: child_moniker.clone(),
}
}
pub fn storage_from_child_expose_not_found(
child_moniker: &ChildMoniker,
moniker: &AbsoluteMoniker,
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 storage_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
Self::StorageFromComponentManagerNotFound { capability_id: capability_id.into() }
}
pub fn use_from_parent_not_found(
moniker: &AbsoluteMoniker,
capability_id: impl Into<String>,
) -> Self {
Self::UseFromParentNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn use_from_child_instance_not_found(
child_moniker: &ChildMoniker,
moniker: &AbsoluteMoniker,
capability_id: impl Into<String>,
) -> Self {
Self::UseFromChildInstanceNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn offer_from_parent_not_found(
moniker: &AbsoluteMoniker,
capability_id: impl Into<String>,
) -> Self {
Self::OfferFromParentNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn storage_from_parent_not_found(
moniker: &AbsoluteMoniker,
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: &ChildMoniker,
moniker: &AbsoluteMoniker,
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: &ChildMoniker,
moniker: &AbsoluteMoniker,
capability_id: impl Into<String>,
) -> Self {
Self::OfferFromChildExposeNotFound {
child_moniker: child_moniker.clone(),
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn expose_from_child_instance_not_found(
child_moniker: &ChildMoniker,
moniker: &AbsoluteMoniker,
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: &ChildMoniker,
moniker: &AbsoluteMoniker,
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: &AbsoluteMoniker,
capability_id: impl Into<String>,
) -> Self {
Self::CapabilityFromFrameworkNotFound {
moniker: moniker.clone(),
capability_id: capability_id.into(),
}
}
pub fn capability_from_capability_not_found(
moniker: &AbsoluteMoniker,
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 capability_from_storage_capability_not_found(
storage_capability: impl Into<String>,
capability_id: impl Into<String>,
) -> Self {
Self::CapabilityFromStorageCapabilityNotFound {
storage_capability: storage_capability.into(),
capability_id: capability_id.into(),
}
}
pub fn expose_from_framework_not_found(
moniker: &AbsoluteMoniker,
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() }
}
}
/// 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 greater than provided rights")]
Invalid,
#[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 {
zx::Status::UNAVAILABLE
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
#[derive(Debug, Error, Clone, PartialEq)]
pub enum AvailabilityRoutingError {
#[error("Availability of offer is optional, but target requires the capability")]
OptionalOfferToRequiredTarget,
#[error("Offer uses void source, but target requires the capability")]
OfferFromVoidToRequiredTarget,
#[error("Offer uses void source, so the route cannot be completed")]
OfferFromVoidToOptionalTarget,
}