errors/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use ::routing::error::{ComponentInstanceError, RoutingError};
6use ::routing::policy::PolicyError;
7use ::routing::resolving::ResolverError;
8use anyhow::Error;
9use clonable_error::ClonableError;
10use cm_config::CompatibilityCheckError;
11use cm_rust::UseDecl;
12use cm_types::{Name, Url};
13use component_id_index::InstanceId;
14use fuchsia_fs::directory::WatcherCreateError;
15use moniker::{ChildName, ExtendedMoniker, Moniker, MonikerError};
16use router_error::{Explain, RouterError};
17use sandbox::ConversionError;
18use serve_processargs::BuildNamespaceError;
19use std::sync::Arc;
20use thiserror::Error;
21use {fidl_fuchsia_component as fcomponent, fidl_fuchsia_sys2 as fsys};
22
23/// Errors produced by `Model`.
24#[derive(Debug, Error, Clone)]
25pub enum ModelError {
26    #[error("bad path")]
27    BadPath,
28    #[error(transparent)]
29    MonikerError {
30        #[from]
31        err: MonikerError,
32    },
33    #[error("expected a component instance moniker")]
34    UnexpectedComponentManagerMoniker,
35    #[error(transparent)]
36    RoutingError {
37        #[from]
38        err: RoutingError,
39    },
40    #[error(
41        "opening path `{path}`, in storage directory for `{moniker}` backed by `{source_moniker}`: {err}"
42    )]
43    OpenStorageFailed {
44        source_moniker: ExtendedMoniker,
45        moniker: Moniker,
46        path: String,
47        #[source]
48        err: zx::Status,
49    },
50    #[error(transparent)]
51    StorageError {
52        #[from]
53        err: StorageError,
54    },
55    #[error(transparent)]
56    ComponentInstanceError {
57        #[from]
58        err: ComponentInstanceError,
59    },
60    #[error("service dir VFS for component {moniker}:\n\t{err}")]
61    ServiceDirError {
62        moniker: Moniker,
63
64        #[source]
65        err: VfsError,
66    },
67    #[error("opening directory `{relative_path}` for component `{moniker}` failed")]
68    OpenDirectoryError { moniker: Moniker, relative_path: String },
69    #[error("events: {err}")]
70    EventsError {
71        #[from]
72        err: EventsError,
73    },
74    #[error(transparent)]
75    PolicyError {
76        #[from]
77        err: PolicyError,
78    },
79    #[error("component id index: {err}")]
80    ComponentIdIndexError {
81        #[from]
82        err: component_id_index::IndexError,
83    },
84    #[error(transparent)]
85    ActionError {
86        #[from]
87        err: ActionError,
88    },
89    #[error("resolve: {err}")]
90    ResolveActionError {
91        #[from]
92        err: ResolveActionError,
93    },
94    #[error("start: {err}")]
95    StartActionError {
96        #[from]
97        err: StartActionError,
98    },
99    #[error("open outgoing dir: {err}")]
100    OpenOutgoingDirError {
101        #[from]
102        err: OpenOutgoingDirError,
103    },
104    #[error("router: {err}")]
105    RouterError {
106        #[from]
107        err: RouterError,
108    },
109    #[error("capability provider: {err}")]
110    CapabilityProviderError {
111        #[from]
112        err: CapabilityProviderError,
113    },
114    #[error("open: {err}")]
115    OpenError {
116        #[from]
117        err: OpenError,
118    },
119}
120
121impl ModelError {
122    pub fn instance_not_found(moniker: Moniker) -> ModelError {
123        ModelError::from(ComponentInstanceError::instance_not_found(moniker))
124    }
125
126    pub fn open_directory_error(moniker: Moniker, relative_path: impl Into<String>) -> ModelError {
127        ModelError::OpenDirectoryError { moniker, relative_path: relative_path.into() }
128    }
129}
130
131impl Explain for ModelError {
132    fn as_zx_status(&self) -> zx::Status {
133        match self {
134            ModelError::RoutingError { err } => err.as_zx_status(),
135            ModelError::PolicyError { err } => err.as_zx_status(),
136            ModelError::StartActionError { err } => err.as_zx_status(),
137            ModelError::ComponentInstanceError { err } => err.as_zx_status(),
138            ModelError::OpenOutgoingDirError { err } => err.as_zx_status(),
139            ModelError::RouterError { err } => err.as_zx_status(),
140            ModelError::CapabilityProviderError { err } => err.as_zx_status(),
141            // Any other type of error is not expected.
142            _ => zx::Status::INTERNAL,
143        }
144    }
145}
146
147#[derive(Debug, Error, Clone)]
148pub enum StructuredConfigError {
149    #[error("component has a config schema but resolver did not provide values")]
150    ConfigValuesMissing,
151    #[error("failed to resolve component's config:\n\t{_0}")]
152    ConfigResolutionFailed(#[source] config_encoder::ResolutionError),
153    #[error("couldn't create vmo: {_0}")]
154    VmoCreateFailed(#[source] zx::Status),
155    #[error("failed to match values for key `{key}`")]
156    ValueMismatch { key: String },
157    #[error("failed to find values for key `{key}`")]
158    KeyNotFound { key: String },
159    #[error("failed to route structured config values:\n\t{_0}")]
160    RoutingError(#[from] router_error::RouterError),
161}
162
163#[derive(Clone, Debug, Error)]
164pub enum VfsError {
165    #[error("failed to add node `{name}`: {status}")]
166    AddNodeError { name: String, status: zx::Status },
167    #[error("failed to remove node `{name}`: {status}")]
168    RemoveNodeError { name: String, status: zx::Status },
169}
170
171#[derive(Debug, Error)]
172pub enum RebootError {
173    #[error("failed to connect to admin protocol in root component's exposed dir:\n\t{0}")]
174    ConnectToAdminFailed(#[source] anyhow::Error),
175    #[error("StateControl Admin FIDL:\n\t{0}")]
176    FidlError(#[from] fidl::Error),
177    #[error("StateControl Admin: {0}")]
178    AdminError(zx::Status),
179    #[error("opening root component's exposed dir: {0}")]
180    OpenRootExposedDirFailed(#[from] OpenExposedDirError),
181}
182
183#[derive(Debug, Error)]
184pub enum OpenExposedDirError {
185    #[error("instance is not resolved")]
186    InstanceNotResolved,
187    #[error("instance was destroyed")]
188    InstanceDestroyed,
189    #[error("open error: {0}")]
190    Open(#[from] zx::Status),
191}
192
193impl Explain for OpenExposedDirError {
194    fn as_zx_status(&self) -> zx::Status {
195        match self {
196            Self::InstanceNotResolved => zx::Status::NOT_FOUND,
197            Self::InstanceDestroyed => zx::Status::NOT_FOUND,
198            Self::Open(status) => *status,
199        }
200    }
201}
202
203impl From<OpenExposedDirError> for fsys::OpenError {
204    fn from(value: OpenExposedDirError) -> Self {
205        match value {
206            OpenExposedDirError::InstanceNotResolved => fsys::OpenError::InstanceNotResolved,
207            OpenExposedDirError::InstanceDestroyed => fsys::OpenError::InstanceDestroyed,
208            OpenExposedDirError::Open(_) => fsys::OpenError::FidlError,
209        }
210    }
211}
212
213#[derive(Clone, Debug, Error)]
214pub enum OpenOutgoingDirError {
215    #[error("instance is not resolved")]
216    InstanceNotResolved,
217    #[error("instance is non-executable")]
218    InstanceNonExecutable,
219    #[error("failed to open: {0}")]
220    Open(#[from] zx::Status),
221    #[error("fidl IPC to protocol in outgoing directory:\n\t{0}")]
222    Fidl(fidl::Error),
223}
224
225impl Explain for OpenOutgoingDirError {
226    fn as_zx_status(&self) -> zx::Status {
227        match self {
228            Self::InstanceNotResolved => zx::Status::NOT_FOUND,
229            Self::InstanceNonExecutable => zx::Status::NOT_FOUND,
230            Self::Open(err) => *err,
231            Self::Fidl(_) => zx::Status::NOT_FOUND,
232        }
233    }
234}
235
236impl From<OpenOutgoingDirError> for fsys::OpenError {
237    fn from(value: OpenOutgoingDirError) -> Self {
238        match value {
239            OpenOutgoingDirError::InstanceNotResolved => fsys::OpenError::InstanceNotResolved,
240            OpenOutgoingDirError::InstanceNonExecutable => fsys::OpenError::NoSuchDir,
241            OpenOutgoingDirError::Open(_) => fsys::OpenError::FidlError,
242            OpenOutgoingDirError::Fidl(_) => fsys::OpenError::FidlError,
243        }
244    }
245}
246
247impl From<OpenOutgoingDirError> for RouterError {
248    fn from(value: OpenOutgoingDirError) -> Self {
249        Self::NotFound(Arc::new(value))
250    }
251}
252
253#[derive(Debug, Error, Clone)]
254pub enum AddDynamicChildError {
255    #[error("component collection not found with name `{name}`")]
256    CollectionNotFound { name: String },
257    #[error(
258        "numbered handles can only be provided when adding components to a single-run collection"
259    )]
260    NumberedHandleNotInSingleRunCollection,
261    #[error("name length is longer than the allowed max of {max_len}")]
262    NameTooLong { max_len: usize },
263    #[error("collection `{collection_name}` does not allow dynamic offers")]
264    DynamicOffersNotAllowed { collection_name: String },
265    #[error(transparent)]
266    ActionError {
267        #[from]
268        err: ActionError,
269    },
270    #[error("invalid dictionary")]
271    InvalidDictionary,
272    #[error(
273        "dictionary entry for capability `{capability_name}` conflicts with existing static route"
274    )]
275    StaticRouteConflict { capability_name: Name },
276    #[error(transparent)]
277    AddChildError {
278        #[from]
279        err: AddChildError,
280    },
281}
282
283// This is implemented for fuchsia.component.Realm protocol
284impl Into<fcomponent::Error> for AddDynamicChildError {
285    fn into(self) -> fcomponent::Error {
286        match self {
287            AddDynamicChildError::CollectionNotFound { .. } => {
288                fcomponent::Error::CollectionNotFound
289            }
290            AddDynamicChildError::NumberedHandleNotInSingleRunCollection => {
291                fcomponent::Error::Unsupported
292            }
293            AddDynamicChildError::AddChildError {
294                err: AddChildError::InstanceAlreadyExists { .. },
295            } => fcomponent::Error::InstanceAlreadyExists,
296            AddDynamicChildError::DynamicOffersNotAllowed { .. } => {
297                fcomponent::Error::InvalidArguments
298            }
299            AddDynamicChildError::ActionError { err } => err.into(),
300            AddDynamicChildError::InvalidDictionary { .. } => fcomponent::Error::InvalidArguments,
301            AddDynamicChildError::StaticRouteConflict { .. } => fcomponent::Error::InvalidArguments,
302            AddDynamicChildError::NameTooLong { .. } => fcomponent::Error::InvalidArguments,
303            // TODO(https://fxbug.dev/297403341): This should become its own error in fidl once
304            // we can introduce it without breaking compatibility.
305            AddDynamicChildError::AddChildError {
306                err:
307                    AddChildError::DynamicCapabilityError { err: DynamicCapabilityError::Cycle { .. } },
308            } => fcomponent::Error::DependencyCycle,
309            AddDynamicChildError::AddChildError {
310                err: AddChildError::DynamicCapabilityError { .. },
311            } => fcomponent::Error::InvalidArguments,
312            AddDynamicChildError::AddChildError { err: AddChildError::ChildNameInvalid { .. } } => {
313                fcomponent::Error::InvalidArguments
314            }
315        }
316    }
317}
318
319// This is implemented for fuchsia.sys2.LifecycleController protocol
320impl Into<fsys::CreateError> for AddDynamicChildError {
321    fn into(self) -> fsys::CreateError {
322        match self {
323            AddDynamicChildError::CollectionNotFound { .. } => {
324                fsys::CreateError::CollectionNotFound
325            }
326            AddDynamicChildError::AddChildError {
327                err: AddChildError::InstanceAlreadyExists { .. },
328            } => fsys::CreateError::InstanceAlreadyExists,
329
330            AddDynamicChildError::DynamicOffersNotAllowed { .. } => {
331                fsys::CreateError::DynamicOffersForbidden
332            }
333            AddDynamicChildError::ActionError { .. } => fsys::CreateError::Internal,
334            AddDynamicChildError::InvalidDictionary { .. } => fsys::CreateError::Internal,
335            AddDynamicChildError::StaticRouteConflict { .. } => fsys::CreateError::Internal,
336            AddDynamicChildError::NameTooLong { .. } => fsys::CreateError::BadChildDecl,
337            AddDynamicChildError::AddChildError {
338                err: AddChildError::DynamicCapabilityError { .. },
339            } => fsys::CreateError::BadDynamicOffer,
340            AddDynamicChildError::AddChildError { err: AddChildError::ChildNameInvalid { .. } } => {
341                fsys::CreateError::BadMoniker
342            }
343            AddDynamicChildError::NumberedHandleNotInSingleRunCollection => {
344                fsys::CreateError::NumberedHandlesForbidden
345            }
346        }
347    }
348}
349
350#[derive(Debug, Error, Clone)]
351pub enum AddChildError {
352    #[error("component instance `{child}` in realm `{moniker}` already exists")]
353    InstanceAlreadyExists { moniker: Moniker, child: ChildName },
354    #[error(transparent)]
355    DynamicCapabilityError {
356        #[from]
357        err: DynamicCapabilityError,
358    },
359    #[error("invalid child name: {err}")]
360    ChildNameInvalid {
361        #[from]
362        err: MonikerError,
363    },
364}
365
366#[derive(Debug, Error, Clone, PartialEq)]
367pub enum DynamicCapabilityError {
368    #[error("a dynamic capability was not valid:\n\t{err}")]
369    Invalid {
370        #[source]
371        err: cm_fidl_validator::error::ErrorList,
372    },
373    #[error("dynamic offers are not allowed for {typename}")]
374    UnsupportedType { typename: &'static str },
375    #[error("dynamic offer would create a cycle:\n\t{err}")]
376    Cycle {
377        #[source]
378        err: cm_fidl_validator::error::ErrorList,
379    },
380    #[error("source for dynamic offer not found:\n\t{:?}", offer)]
381    SourceNotFound { offer: cm_rust::OfferDecl },
382    #[error("unknown offer type in dynamic offers")]
383    UnknownOfferType,
384}
385
386#[derive(Debug, Clone, Error)]
387pub enum ActionError {
388    #[error("discover: {err}")]
389    DiscoverError {
390        #[from]
391        err: DiscoverActionError,
392    },
393
394    #[error("resolve: {err}")]
395    ResolveError {
396        #[from]
397        err: ResolveActionError,
398    },
399
400    #[error("unresolve: {err}")]
401    UnresolveError {
402        #[from]
403        err: UnresolveActionError,
404    },
405
406    #[error("start: {err}")]
407    StartError {
408        #[from]
409        err: StartActionError,
410    },
411
412    #[error("stop: {err}")]
413    StopError {
414        #[from]
415        err: StopActionError,
416    },
417
418    #[error("destroy: {err}")]
419    DestroyError {
420        #[from]
421        err: DestroyActionError,
422    },
423
424    #[error("shutdown: {err}")]
425    ShutdownError {
426        #[from]
427        err: ShutdownActionError,
428    },
429}
430
431impl Explain for ActionError {
432    fn as_zx_status(&self) -> zx::Status {
433        match self {
434            ActionError::DiscoverError { .. } => zx::Status::INTERNAL,
435            ActionError::ResolveError { err } => err.as_zx_status(),
436            ActionError::UnresolveError { .. } => zx::Status::INTERNAL,
437            ActionError::StartError { err } => err.as_zx_status(),
438            ActionError::StopError { .. } => zx::Status::INTERNAL,
439            ActionError::DestroyError { .. } => zx::Status::INTERNAL,
440            ActionError::ShutdownError { .. } => zx::Status::INTERNAL,
441        }
442    }
443}
444
445impl From<ActionError> for RouterError {
446    fn from(value: ActionError) -> Self {
447        Self::NotFound(Arc::new(value))
448    }
449}
450
451impl From<ActionError> for fcomponent::Error {
452    fn from(err: ActionError) -> Self {
453        match err {
454            ActionError::DiscoverError { .. } => fcomponent::Error::Internal,
455            ActionError::ResolveError { .. } => fcomponent::Error::Internal,
456            ActionError::UnresolveError { .. } => fcomponent::Error::Internal,
457            ActionError::StartError { err } => err.into(),
458            ActionError::StopError { err } => err.into(),
459            ActionError::DestroyError { err } => err.into(),
460            ActionError::ShutdownError { .. } => fcomponent::Error::Internal,
461        }
462    }
463}
464
465impl From<ActionError> for fsys::ResolveError {
466    fn from(err: ActionError) -> Self {
467        match err {
468            ActionError::ResolveError { err } => err.into(),
469            _ => fsys::ResolveError::Internal,
470        }
471    }
472}
473
474impl From<ActionError> for fsys::UnresolveError {
475    fn from(err: ActionError) -> Self {
476        match err {
477            ActionError::UnresolveError { err } => err.into(),
478            _ => fsys::UnresolveError::Internal,
479        }
480    }
481}
482
483impl From<ActionError> for fsys::StartError {
484    fn from(err: ActionError) -> Self {
485        match err {
486            ActionError::StartError { err } => err.into(),
487            _ => fsys::StartError::Internal,
488        }
489    }
490}
491
492impl From<ActionError> for fsys::StopError {
493    fn from(err: ActionError) -> Self {
494        match err {
495            ActionError::StopError { err } => err.into(),
496            _ => fsys::StopError::Internal,
497        }
498    }
499}
500
501impl From<ActionError> for fsys::DestroyError {
502    fn from(err: ActionError) -> Self {
503        match err {
504            ActionError::DestroyError { err } => err.into(),
505            _ => fsys::DestroyError::Internal,
506        }
507    }
508}
509
510#[derive(Debug, Clone, Error)]
511pub enum DiscoverActionError {
512    #[error("`{moniker}` was destroyed")]
513    InstanceDestroyed { moniker: Moniker },
514}
515
516#[derive(Debug, Clone, Error)]
517pub enum ResolveActionError {
518    #[error("discover during resolve: {err}")]
519    DiscoverActionError {
520        #[from]
521        err: DiscoverActionError,
522    },
523    #[error("`{moniker}` was shut down")]
524    InstanceShutDown { moniker: Moniker },
525    #[error("`{moniker}` was destroyed")]
526    InstanceDestroyed { moniker: Moniker },
527    #[error("could not parse component address for `{url}` at `{moniker}`:\n\t{err}")]
528    ComponentAddressParseError {
529        url: Url,
530        moniker: Moniker,
531        #[source]
532        err: ResolverError,
533    },
534    #[error("resolve failed for `{url}`:\n\t{err}")]
535    ResolverError {
536        url: Url,
537        #[source]
538        err: ResolverError,
539    },
540    #[error("expose dir for `{moniker}`:\n\t{err}")]
541    // TODO(https://fxbug.dev/42071713): Determine whether this is expected to fail.
542    ExposeDirError {
543        moniker: Moniker,
544
545        #[source]
546        err: VfsError,
547    },
548    #[error("adding static child `{child_name}`:\n\t{err}")]
549    AddStaticChildError {
550        child_name: String,
551        #[source]
552        err: AddChildError,
553    },
554    #[error("structured config: {err}")]
555    StructuredConfigError {
556        #[from]
557        err: StructuredConfigError,
558    },
559    #[error("creating package dir proxy: {err}")]
560    PackageDirProxyCreateError {
561        #[source]
562        err: fidl::Error,
563    },
564    #[error("ABI compatibility check for `{url}`: {err}")]
565    AbiCompatibilityError {
566        url: Url,
567        #[source]
568        err: CompatibilityCheckError,
569    },
570    #[error(transparent)]
571    Policy(#[from] PolicyError),
572    #[error("`{moniker}` was interrupted")]
573    Aborted { moniker: Moniker },
574}
575
576impl ResolveActionError {
577    fn as_zx_status(&self) -> zx::Status {
578        match self {
579            ResolveActionError::DiscoverActionError { .. }
580            | ResolveActionError::InstanceShutDown { .. }
581            | ResolveActionError::InstanceDestroyed { .. }
582            | ResolveActionError::ComponentAddressParseError { .. }
583            | ResolveActionError::AbiCompatibilityError { .. } => zx::Status::NOT_FOUND,
584            ResolveActionError::ExposeDirError { .. }
585            | ResolveActionError::AddStaticChildError { .. }
586            | ResolveActionError::StructuredConfigError { .. }
587            | ResolveActionError::Aborted { .. }
588            | ResolveActionError::PackageDirProxyCreateError { .. } => zx::Status::INTERNAL,
589            ResolveActionError::ResolverError { err, .. } => err.as_zx_status(),
590            ResolveActionError::Policy(err) => err.as_zx_status(),
591        }
592    }
593}
594
595// This is implemented for fuchsia.sys2.LifecycleController protocol
596impl Into<fsys::ResolveError> for ResolveActionError {
597    fn into(self) -> fsys::ResolveError {
598        match self {
599            ResolveActionError::ResolverError {
600                err: ResolverError::PackageNotFound(_), ..
601            } => fsys::ResolveError::PackageNotFound,
602            ResolveActionError::ResolverError {
603                err: ResolverError::ManifestNotFound(_), ..
604            } => fsys::ResolveError::ManifestNotFound,
605            ResolveActionError::InstanceShutDown { .. }
606            | ResolveActionError::InstanceDestroyed { .. } => fsys::ResolveError::InstanceNotFound,
607            ResolveActionError::ExposeDirError { .. }
608            | ResolveActionError::ResolverError { .. }
609            | ResolveActionError::StructuredConfigError { .. }
610            | ResolveActionError::ComponentAddressParseError { .. }
611            | ResolveActionError::AddStaticChildError { .. }
612            | ResolveActionError::DiscoverActionError { .. }
613            | ResolveActionError::AbiCompatibilityError { .. }
614            | ResolveActionError::Aborted { .. }
615            | ResolveActionError::PackageDirProxyCreateError { .. } => fsys::ResolveError::Internal,
616            ResolveActionError::Policy(_) => fsys::ResolveError::PolicyError,
617        }
618    }
619}
620
621// This is implemented for fuchsia.sys2.LifecycleController protocol.
622// Starting a component instance also causes a resolve.
623impl Into<fsys::StartError> for ResolveActionError {
624    fn into(self) -> fsys::StartError {
625        match self {
626            ResolveActionError::ResolverError {
627                err: ResolverError::PackageNotFound(_), ..
628            } => fsys::StartError::PackageNotFound,
629            ResolveActionError::ResolverError {
630                err: ResolverError::ManifestNotFound(_), ..
631            } => fsys::StartError::ManifestNotFound,
632            ResolveActionError::InstanceShutDown { .. }
633            | ResolveActionError::InstanceDestroyed { .. } => fsys::StartError::InstanceNotFound,
634            ResolveActionError::ExposeDirError { .. }
635            | ResolveActionError::ResolverError { .. }
636            | ResolveActionError::StructuredConfigError { .. }
637            | ResolveActionError::ComponentAddressParseError { .. }
638            | ResolveActionError::AddStaticChildError { .. }
639            | ResolveActionError::DiscoverActionError { .. }
640            | ResolveActionError::AbiCompatibilityError { .. }
641            | ResolveActionError::Aborted { .. }
642            | ResolveActionError::PackageDirProxyCreateError { .. } => fsys::StartError::Internal,
643            ResolveActionError::Policy(_) => fsys::StartError::PolicyError,
644        }
645    }
646}
647
648#[derive(Debug, Clone, Error)]
649pub enum PkgDirError {
650    #[error("no pkg dir found for component")]
651    NoPkgDir,
652    #[error("opening pkg dir failed: {err}")]
653    OpenFailed {
654        #[from]
655        err: zx::Status,
656    },
657}
658
659impl PkgDirError {
660    fn as_zx_status(&self) -> zx::Status {
661        match self {
662            Self::NoPkgDir => zx::Status::NOT_FOUND,
663            Self::OpenFailed { err } => *err,
664        }
665    }
666}
667
668#[derive(Debug, Clone, Error)]
669pub enum ComponentProviderError {
670    #[error("starting source instance:\n\t{err}")]
671    SourceStartError {
672        #[from]
673        err: ActionError,
674    },
675    #[error("opening source instance's outgoing dir:\n\t{err}")]
676    OpenOutgoingDirError {
677        #[from]
678        err: OpenOutgoingDirError,
679    },
680}
681
682impl ComponentProviderError {
683    pub fn as_zx_status(&self) -> zx::Status {
684        match self {
685            Self::SourceStartError { err } => err.as_zx_status(),
686            Self::OpenOutgoingDirError { err } => err.as_zx_status(),
687        }
688    }
689}
690
691#[derive(Debug, Clone, Error)]
692pub enum CapabilityProviderError {
693    #[error("bad path")]
694    BadPath,
695    #[error(transparent)]
696    ComponentInstanceError {
697        #[from]
698        err: ComponentInstanceError,
699    },
700    #[error(transparent)]
701    PkgDirError {
702        #[from]
703        err: PkgDirError,
704    },
705    #[error("event source: {0}")]
706    EventSourceError(#[from] EventSourceError),
707    #[error(transparent)]
708    ComponentProviderError {
709        #[from]
710        err: ComponentProviderError,
711    },
712    #[error("component_manager namespace: {err}")]
713    CmNamespaceError {
714        #[from]
715        err: ClonableError,
716    },
717    #[error("router: {err}")]
718    RouterError {
719        #[from]
720        err: RouterError,
721    },
722    #[error(transparent)]
723    RoutingError(#[from] RoutingError),
724    #[error("opening vfs failed: {0}")]
725    VfsOpenError(#[source] zx::Status),
726}
727
728impl CapabilityProviderError {
729    pub fn as_zx_status(&self) -> zx::Status {
730        match self {
731            Self::BadPath => zx::Status::INVALID_ARGS,
732            Self::ComponentInstanceError { err } => err.as_zx_status(),
733            Self::CmNamespaceError { .. } => zx::Status::INTERNAL,
734            Self::PkgDirError { err } => err.as_zx_status(),
735            Self::EventSourceError(err) => err.as_zx_status(),
736            Self::ComponentProviderError { err } => err.as_zx_status(),
737            Self::RouterError { err } => err.as_zx_status(),
738            Self::RoutingError(err) => err.as_zx_status(),
739            Self::VfsOpenError(err) => *err,
740        }
741    }
742}
743
744#[derive(Debug, Clone, Error)]
745pub enum OpenError {
746    #[error("failed to get default capability provider: {err}")]
747    GetDefaultProviderError {
748        // TODO(https://fxbug.dev/42068065): This will get fixed when we untangle ModelError
749        #[source]
750        err: Box<ModelError>,
751    },
752    #[error("no capability provider found")]
753    CapabilityProviderNotFound,
754    #[error("capability provider: {err}")]
755    CapabilityProviderError {
756        #[from]
757        err: CapabilityProviderError,
758    },
759    #[error("opening storage capability: {err}")]
760    OpenStorageError {
761        // TODO(https://fxbug.dev/42068065): This will get fixed when we untangle ModelError
762        #[source]
763        err: Box<ModelError>,
764    },
765    #[error("timed out opening capability")]
766    Timeout,
767    #[error("invalid path found")]
768    BadPath,
769    #[error("capability does not support opening: {0}")]
770    DoesNotSupportOpen(ConversionError),
771    #[error("failed to create directory watcher: {err}")]
772    WatcherCreateError {
773        #[from]
774        err: WatcherCreateError,
775    },
776}
777
778impl Explain for OpenError {
779    fn as_zx_status(&self) -> zx::Status {
780        match self {
781            Self::GetDefaultProviderError { err } => err.as_zx_status(),
782            Self::OpenStorageError { err } => err.as_zx_status(),
783            Self::CapabilityProviderError { err } => err.as_zx_status(),
784            Self::CapabilityProviderNotFound => zx::Status::NOT_FOUND,
785            Self::Timeout => zx::Status::TIMED_OUT,
786            Self::BadPath => zx::Status::BAD_PATH,
787            Self::DoesNotSupportOpen(_) => zx::Status::NOT_SUPPORTED,
788            Self::WatcherCreateError { err: WatcherCreateError::SendWatchRequest(_err) } => {
789                zx::Status::PEER_CLOSED
790            }
791            Self::WatcherCreateError { err: WatcherCreateError::WatchError(status) } => *status,
792            Self::WatcherCreateError { err: WatcherCreateError::ChannelConversion(status) } => {
793                *status
794            }
795        }
796    }
797}
798
799impl From<OpenError> for RouterError {
800    fn from(value: OpenError) -> Self {
801        Self::NotFound(Arc::new(value))
802    }
803}
804
805#[derive(Debug, Clone, Error)]
806pub enum StartActionError {
807    #[error("`{moniker}` was shut down")]
808    InstanceShutDown { moniker: Moniker },
809    #[error("`{moniker}` was destroyed")]
810    InstanceDestroyed { moniker: Moniker },
811    #[error("`{moniker}` couldn't resolve during start: {err}")]
812    ResolveActionError {
813        moniker: Moniker,
814        #[source]
815        err: Box<ActionError>,
816    },
817    #[error("runner for `{moniker}` `{runner}` couldn't resolve: {err}")]
818    ResolveRunnerError {
819        moniker: Moniker,
820        runner: Name,
821        #[source]
822        err: Box<RouterError>,
823    },
824    #[error(
825        "`{moniker}` uses `\"on_terminate\": \"reboot\"` but is disallowed by policy:\n\t{err}"
826    )]
827    RebootOnTerminateForbidden {
828        moniker: Moniker,
829        #[source]
830        err: PolicyError,
831    },
832    #[error("creating program input dictionary for `{moniker}`")]
833    InputDictionaryError { moniker: Moniker },
834    #[error("creating namespace: {0}")]
835    CreateNamespaceError(#[from] CreateNamespaceError),
836    #[error("starting program for `{moniker}`: {err}")]
837    StartProgramError {
838        moniker: Moniker,
839        #[source]
840        err: StartError,
841    },
842    #[error("structured configuration for `{moniker}`: {err}")]
843    StructuredConfigError {
844        moniker: Moniker,
845        #[source]
846        err: StructuredConfigError,
847    },
848    #[error("starting eager child of `{moniker}`: {err}")]
849    EagerStartError {
850        moniker: Moniker,
851        #[source]
852        err: Box<ActionError>,
853    },
854    #[error("`{moniker}` was interrupted")]
855    Aborted { moniker: Moniker },
856}
857
858impl StartActionError {
859    fn as_zx_status(&self) -> zx::Status {
860        match self {
861            StartActionError::InstanceDestroyed { .. } | Self::InstanceShutDown { .. } => {
862                zx::Status::NOT_FOUND
863            }
864            StartActionError::StartProgramError { .. }
865            | StartActionError::StructuredConfigError { .. }
866            | StartActionError::EagerStartError { .. } => zx::Status::INTERNAL,
867            StartActionError::RebootOnTerminateForbidden { err, .. } => err.as_zx_status(),
868            StartActionError::ResolveRunnerError { err, .. } => err.as_zx_status(),
869            StartActionError::CreateNamespaceError(err) => err.as_zx_status(),
870            StartActionError::InputDictionaryError { .. } => zx::Status::NOT_FOUND,
871            StartActionError::ResolveActionError { err, .. } => err.as_zx_status(),
872            StartActionError::Aborted { .. } => zx::Status::NOT_FOUND,
873        }
874    }
875}
876
877// This is implemented for fuchsia.sys2.LifecycleController protocol.
878impl Into<fsys::StartError> for StartActionError {
879    fn into(self) -> fsys::StartError {
880        match self {
881            StartActionError::ResolveActionError { err, .. } => (*err).into(),
882            StartActionError::InstanceDestroyed { .. } => fsys::StartError::InstanceNotFound,
883            StartActionError::InstanceShutDown { .. } => fsys::StartError::InstanceNotFound,
884            _ => fsys::StartError::Internal,
885        }
886    }
887}
888
889// This is implemented for fuchsia.component.Realm protocol.
890impl Into<fcomponent::Error> for StartActionError {
891    fn into(self) -> fcomponent::Error {
892        match self {
893            StartActionError::ResolveActionError { .. } => fcomponent::Error::InstanceCannotResolve,
894            StartActionError::RebootOnTerminateForbidden { .. } => fcomponent::Error::AccessDenied,
895            StartActionError::InstanceShutDown { .. } => fcomponent::Error::InstanceDied,
896            StartActionError::InstanceDestroyed { .. } => fcomponent::Error::InstanceDied,
897            _ => fcomponent::Error::InstanceCannotStart,
898        }
899    }
900}
901
902#[derive(Debug, Clone, Error)]
903pub enum StopActionError {
904    #[error("stopping program: {0}")]
905    ProgramStopError(#[source] StopError),
906    #[error("failed to get top instance")]
907    GetTopInstanceFailed,
908    #[error("failed to get parent instance")]
909    GetParentFailed,
910    #[error("failed to destroy dynamic children: {err}")]
911    DestroyDynamicChildrenFailed { err: Box<ActionError> },
912    #[error("resolution during stop: {err}")]
913    ResolveActionError {
914        #[source]
915        err: Box<ActionError>,
916    },
917    #[error("started while shutdown was ongoing")]
918    ComponentStartedDuringShutdown,
919}
920
921// This is implemented for fuchsia.sys2.LifecycleController protocol.
922impl Into<fsys::StopError> for StopActionError {
923    fn into(self) -> fsys::StopError {
924        fsys::StopError::Internal
925    }
926}
927
928impl Into<fcomponent::Error> for StopActionError {
929    fn into(self) -> fcomponent::Error {
930        fcomponent::Error::Internal
931    }
932}
933
934#[cfg(test)]
935impl PartialEq for StopActionError {
936    fn eq(&self, other: &Self) -> bool {
937        match (self, other) {
938            (StopActionError::ProgramStopError(_), StopActionError::ProgramStopError(_)) => true,
939            (StopActionError::GetTopInstanceFailed, StopActionError::GetTopInstanceFailed) => true,
940            (StopActionError::GetParentFailed, StopActionError::GetParentFailed) => true,
941            (
942                StopActionError::DestroyDynamicChildrenFailed { .. },
943                StopActionError::DestroyDynamicChildrenFailed { .. },
944            ) => true,
945            (
946                StopActionError::ResolveActionError { .. },
947                StopActionError::ResolveActionError { .. },
948            ) => true,
949            _ => false,
950        }
951    }
952}
953
954#[derive(Debug, Clone, Error)]
955pub enum ShutdownActionError {
956    #[error("child name invalid: {}", err)]
957    InvalidChildName {
958        #[from]
959        err: MonikerError,
960    },
961    #[error("cycles detected in graph")]
962    CyclesDetected {},
963}
964
965#[derive(Debug, Clone, Error)]
966pub enum DestroyActionError {
967    #[error("discover during destroy: {}", err)]
968    DiscoverActionError {
969        #[from]
970        err: DiscoverActionError,
971    },
972    #[error("shutdown during destroy: {}", err)]
973    ShutdownFailed {
974        #[source]
975        err: Box<ActionError>,
976    },
977    #[error("could not find `{moniker}`")]
978    InstanceNotFound { moniker: Moniker },
979    #[error("`{moniker}` is not resolved")]
980    InstanceNotResolved { moniker: Moniker },
981}
982
983// This is implemented for fuchsia.component.Realm protocol.
984impl Into<fcomponent::Error> for DestroyActionError {
985    fn into(self) -> fcomponent::Error {
986        match self {
987            DestroyActionError::InstanceNotFound { .. } => fcomponent::Error::InstanceNotFound,
988            _ => fcomponent::Error::Internal,
989        }
990    }
991}
992
993// This is implemented for fuchsia.sys2.LifecycleController protocol.
994impl Into<fsys::DestroyError> for DestroyActionError {
995    fn into(self) -> fsys::DestroyError {
996        match self {
997            DestroyActionError::InstanceNotFound { .. } => fsys::DestroyError::InstanceNotFound,
998            DestroyActionError::InstanceNotResolved { .. } => {
999                fsys::DestroyError::InstanceNotResolved
1000            }
1001            _ => fsys::DestroyError::Internal,
1002        }
1003    }
1004}
1005
1006#[derive(Debug, Clone, Error)]
1007pub enum UnresolveActionError {
1008    #[error("shutdown during unresolve: {err}")]
1009    ShutdownFailed {
1010        #[from]
1011        err: StopActionError,
1012    },
1013    #[error("`{moniker}` cannot be unresolved while it is running")]
1014    InstanceRunning { moniker: Moniker },
1015    #[error("`{moniker}` was destroyed")]
1016    InstanceDestroyed { moniker: Moniker },
1017}
1018
1019// This is implemented for fuchsia.sys2.LifecycleController protocol.
1020impl Into<fsys::UnresolveError> for UnresolveActionError {
1021    fn into(self) -> fsys::UnresolveError {
1022        match self {
1023            UnresolveActionError::InstanceDestroyed { .. } => {
1024                fsys::UnresolveError::InstanceNotFound
1025            }
1026            _ => fsys::UnresolveError::Internal,
1027        }
1028    }
1029}
1030
1031#[derive(Debug, Clone, Error)]
1032pub enum CreateNamespaceError {
1033    #[error("failed to clone pkg dir for {moniker}: {err}")]
1034    ClonePkgDirFailed {
1035        moniker: Moniker,
1036        #[source]
1037        err: fuchsia_fs::node::CloneError,
1038    },
1039
1040    #[error("use decl without path cannot be installed into the namespace for {moniker}: {decl:?}")]
1041    UseDeclWithoutPath { moniker: Moniker, decl: UseDecl },
1042
1043    #[error("instance not in index: {0}")]
1044    InstanceNotInInstanceIdIndex(#[from] RoutingError),
1045
1046    #[error("building namespace for {moniker}: {err}")]
1047    BuildNamespaceError {
1048        moniker: Moniker,
1049        #[source]
1050        err: serve_processargs::BuildNamespaceError,
1051    },
1052
1053    #[error("failed to convert namespace into directory for {moniker}: {err}")]
1054    ConvertToDirectory {
1055        moniker: Moniker,
1056        #[source]
1057        err: ClonableError,
1058    },
1059
1060    #[error(transparent)]
1061    ComponentInstanceError(#[from] ComponentInstanceError),
1062}
1063
1064impl CreateNamespaceError {
1065    fn as_zx_status(&self) -> zx::Status {
1066        match self {
1067            Self::ClonePkgDirFailed { .. } => zx::Status::INTERNAL,
1068            Self::UseDeclWithoutPath { .. } => zx::Status::NOT_FOUND,
1069            Self::InstanceNotInInstanceIdIndex(err) => err.as_zx_status(),
1070            Self::BuildNamespaceError { .. } => zx::Status::NOT_FOUND,
1071            Self::ConvertToDirectory { .. } => zx::Status::INTERNAL,
1072            Self::ComponentInstanceError(err) => err.as_zx_status(),
1073        }
1074    }
1075}
1076
1077#[derive(Debug, Clone, Error)]
1078pub enum EventSourceError {
1079    #[error(transparent)]
1080    ComponentInstance(#[from] ComponentInstanceError),
1081    #[error(transparent)]
1082    // TODO(https://fxbug.dev/42068065): This will get fixed when we untangle ModelError
1083    Model(#[from] Box<ModelError>),
1084    #[error("event stream already consumed")]
1085    AlreadyConsumed,
1086}
1087
1088impl EventSourceError {
1089    fn as_zx_status(&self) -> zx::Status {
1090        match self {
1091            Self::ComponentInstance(err) => err.as_zx_status(),
1092            Self::Model(err) => err.as_zx_status(),
1093            Self::AlreadyConsumed => zx::Status::INTERNAL,
1094        }
1095    }
1096}
1097
1098#[derive(Debug, Error, Clone)]
1099pub enum EventsError {
1100    #[error("capability_requested event streams cannot be taken twice")]
1101    CapabilityRequestedStreamTaken,
1102
1103    #[error("model not available")]
1104    ModelNotAvailable,
1105
1106    #[error("instance shut down")]
1107    InstanceShutdown,
1108
1109    #[error("instance destroyed")]
1110    InstanceDestroyed,
1111
1112    #[error("registry not found")]
1113    RegistryNotFound,
1114
1115    #[error("event `{event_name}` appears more than once in a subscription request")]
1116    DuplicateEvent { event_name: Name },
1117
1118    #[error("events not available for subscription: `{names:?}`")]
1119    NotAvailable { names: Vec<Name> },
1120}
1121
1122impl EventsError {
1123    pub fn duplicate_event(event_name: Name) -> Self {
1124        Self::DuplicateEvent { event_name }
1125    }
1126
1127    pub fn not_available(names: Vec<Name>) -> Self {
1128        Self::NotAvailable { names }
1129    }
1130}
1131
1132/// Errors related to isolated storage.
1133#[derive(Debug, Error, Clone)]
1134pub enum StorageError {
1135    #[error("opening directory from `{dir_source_moniker:?}` at `{dir_source_path}`:\n\t{err}")]
1136    OpenRoot {
1137        dir_source_moniker: Option<Moniker>,
1138        dir_source_path: cm_types::Path,
1139        #[source]
1140        err: ClonableError,
1141    },
1142    #[error(
1143        "opening isolated storage from `{dir_source_moniker:?}`'s for `{moniker}` at `{dir_source_path}` (instance_id={instance_id:?}):\n\t{err}"
1144    )]
1145    Open {
1146        dir_source_moniker: Option<Moniker>,
1147        dir_source_path: cm_types::Path,
1148        moniker: Moniker,
1149        instance_id: Option<InstanceId>,
1150        #[source]
1151        err: ClonableError,
1152    },
1153    #[error(
1154        "opening isolated storage from `{dir_source_moniker:?}` at `{dir_source_path}` for {instance_id}:\n\t{err}"
1155    )]
1156    OpenById {
1157        dir_source_moniker: Option<Moniker>,
1158        dir_source_path: cm_types::Path,
1159        instance_id: InstanceId,
1160        #[source]
1161        err: ClonableError,
1162    },
1163    #[error(
1164        "removing isolated storage from {dir_source_moniker:?} at `{dir_source_path}` for `{moniker}` (instance_id={instance_id:?}):n\t{err} "
1165    )]
1166    Remove {
1167        dir_source_moniker: Option<Moniker>,
1168        dir_source_path: cm_types::Path,
1169        moniker: Moniker,
1170        instance_id: Option<InstanceId>,
1171        #[source]
1172        err: ClonableError,
1173    },
1174    #[error("storage path for `{moniker}` (instance_id={instance_id:?}) is invalid")]
1175    InvalidStoragePath { moniker: Moniker, instance_id: Option<InstanceId> },
1176}
1177
1178impl StorageError {
1179    pub fn open_root(
1180        dir_source_moniker: Option<Moniker>,
1181        dir_source_path: cm_types::Path,
1182        err: impl Into<Error>,
1183    ) -> Self {
1184        Self::OpenRoot { dir_source_moniker, dir_source_path, err: err.into().into() }
1185    }
1186
1187    pub fn open(
1188        dir_source_moniker: Option<Moniker>,
1189        dir_source_path: cm_types::Path,
1190        moniker: Moniker,
1191        instance_id: Option<InstanceId>,
1192        err: impl Into<Error>,
1193    ) -> Self {
1194        Self::Open {
1195            dir_source_moniker,
1196            dir_source_path,
1197            moniker,
1198            instance_id,
1199            err: err.into().into(),
1200        }
1201    }
1202
1203    pub fn open_by_id(
1204        dir_source_moniker: Option<Moniker>,
1205        dir_source_path: cm_types::Path,
1206        instance_id: InstanceId,
1207        err: impl Into<Error>,
1208    ) -> Self {
1209        Self::OpenById { dir_source_moniker, dir_source_path, instance_id, err: err.into().into() }
1210    }
1211
1212    pub fn remove(
1213        dir_source_moniker: Option<Moniker>,
1214        dir_source_path: cm_types::Path,
1215        moniker: Moniker,
1216        instance_id: Option<InstanceId>,
1217        err: impl Into<Error>,
1218    ) -> Self {
1219        Self::Remove {
1220            dir_source_moniker,
1221            dir_source_path,
1222            moniker,
1223            instance_id,
1224            err: err.into().into(),
1225        }
1226    }
1227
1228    pub fn invalid_storage_path(moniker: Moniker, instance_id: Option<InstanceId>) -> Self {
1229        Self::InvalidStoragePath { moniker, instance_id }
1230    }
1231}
1232
1233#[derive(Error, Debug, Clone)]
1234pub enum StartError {
1235    #[error("serving namespace: {0}")]
1236    ServeNamespace(BuildNamespaceError),
1237}
1238
1239#[derive(Error, Debug, Clone)]
1240pub enum StopError {
1241    /// Internal errors are not meant to be meaningfully handled by the user.
1242    #[error("internal: {0}")]
1243    Internal(fidl::Error),
1244}