vfs/directory/immutable/
connection.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
5//! Connection to a directory that can not be modified by the client, no matter what permissions
6//! the client has on the FIDL connection.
7
8use crate::directory::connection::{BaseConnection, ConnectionState};
9use crate::directory::entry_container::Directory;
10use crate::execution_scope::ExecutionScope;
11use crate::node::OpenNode;
12use crate::object_request::ConnectionCreator;
13use crate::request_handler::{RequestHandler, RequestListener};
14use crate::{ObjectRequestRef, ProtocolsExt};
15
16use fidl_fuchsia_io as fio;
17use fio::DirectoryRequest;
18use std::ops::ControlFlow;
19use std::pin::Pin;
20use std::sync::Arc;
21use zx_status::Status;
22
23pub struct ImmutableConnection<DirectoryType: Directory> {
24    base: BaseConnection<DirectoryType>,
25}
26
27impl<DirectoryType: Directory> ImmutableConnection<DirectoryType> {
28    /// Creates a new connection to serve the directory. The directory will be served from a new
29    /// async `Task`, not from the current `Task`. Errors in constructing the connection are not
30    /// guaranteed to be returned, they may be sent directly to the client end of the connection.
31    /// This method should be called from within an `ObjectRequest` handler to ensure that errors
32    /// are sent to the client end of the connection.
33    pub async fn create(
34        scope: ExecutionScope,
35        directory: Arc<DirectoryType>,
36        protocols: impl ProtocolsExt,
37        object_request: ObjectRequestRef<'_>,
38    ) -> Result<(), Status> {
39        Self::create_transform_stream(
40            scope,
41            directory,
42            protocols,
43            object_request,
44            std::convert::identity,
45        )
46        .await
47    }
48
49    /// TODO(https://fxbug.dev/326626515): this is an experimental method to run a FIDL
50    /// directory connection until stalled, with the purpose to cleanly stop a component.
51    /// We'll expect to revisit how this works to generalize to all connections later.
52    /// Try not to use this function for other purposes.
53    pub async fn create_transform_stream<Transform, RS>(
54        scope: ExecutionScope,
55        directory: Arc<DirectoryType>,
56        protocols: impl ProtocolsExt,
57        object_request: ObjectRequestRef<'_>,
58        transform: Transform,
59    ) -> Result<(), Status>
60    where
61        Transform: FnOnce(fio::DirectoryRequestStream) -> RS,
62        RS: futures::stream::Stream<Item = Result<DirectoryRequest, fidl::Error>> + Send + 'static,
63    {
64        // Ensure we close the directory if we fail to create the connection.
65        let directory = OpenNode::new(directory);
66
67        let connection = ImmutableConnection {
68            base: BaseConnection::new(scope.clone(), directory, protocols.to_directory_options()?),
69        };
70
71        // If we fail to send the task to the executor, it is probably shut down or is in the
72        // process of shutting down (this is the only error state currently).  So there is nothing
73        // for us to do - the connection will be closed automatically when the connection object is
74        // dropped.
75        if let Ok(requests) = object_request.take().into_request_stream(&connection.base).await {
76            scope.spawn(RequestListener::new(transform(requests), connection));
77        }
78        Ok(())
79    }
80}
81
82impl<DirectoryType: Directory> RequestHandler for ImmutableConnection<DirectoryType> {
83    type Request = Result<DirectoryRequest, fidl::Error>;
84
85    async fn handle_request(self: Pin<&mut Self>, request: Self::Request) -> ControlFlow<()> {
86        let this = self.get_mut();
87        if let Some(_guard) = this.base.scope.try_active_guard() {
88            match request {
89                Ok(request) => match this.base.handle_request(request).await {
90                    Ok(ConnectionState::Alive) => ControlFlow::Continue(()),
91                    Ok(ConnectionState::Closed) | Err(_) => ControlFlow::Break(()),
92                },
93                Err(_) => ControlFlow::Break(()),
94            }
95        } else {
96            ControlFlow::Break(())
97        }
98    }
99}
100
101impl<DirectoryType: Directory> ConnectionCreator<DirectoryType>
102    for ImmutableConnection<DirectoryType>
103{
104    async fn create<'a>(
105        scope: ExecutionScope,
106        node: Arc<DirectoryType>,
107        protocols: impl ProtocolsExt,
108        object_request: ObjectRequestRef<'a>,
109    ) -> Result<(), Status> {
110        Self::create(scope, node, protocols, object_request).await
111    }
112}