vfs/
node.rs

1// Copyright 2023 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//! Implementation of a (limited) node connection.
6
7use crate::common::{inherit_rights_for_clone, IntoAny};
8use crate::directory::entry::GetEntryInfo;
9use crate::directory::entry_container::MutableDirectory;
10use crate::execution_scope::ExecutionScope;
11use crate::name::Name;
12use crate::object_request::{run_synchronous_future_or_spawn, ConnectionCreator, Representation};
13use crate::protocols::ToNodeOptions;
14use crate::request_handler::{RequestHandler, RequestListener};
15use crate::{ObjectRequest, ObjectRequestRef, ToObjectRequest};
16use anyhow::Error;
17use fidl::endpoints::ServerEnd;
18use fidl_fuchsia_io as fio;
19use libc::{S_IRUSR, S_IWUSR};
20use std::future::{ready, Future};
21use std::ops::ControlFlow;
22use std::pin::Pin;
23use std::sync::Arc;
24use zx_status::Status;
25
26/// POSIX emulation layer access attributes for all services created with service().
27#[cfg(not(target_os = "macos"))]
28pub const POSIX_READ_WRITE_PROTECTION_ATTRIBUTES: u32 = S_IRUSR | S_IWUSR;
29#[cfg(target_os = "macos")]
30pub const POSIX_READ_WRITE_PROTECTION_ATTRIBUTES: u16 = S_IRUSR | S_IWUSR;
31
32#[derive(Clone, Copy)]
33pub struct NodeOptions {
34    pub rights: fio::Operations,
35}
36
37impl From<&NodeOptions> for fio::Flags {
38    fn from(options: &NodeOptions) -> Self {
39        // There is 1:1 mapping between `fio::Operations` and `fio::Flags`.
40        fio::Flags::PROTOCOL_NODE | fio::Flags::from_bits_truncate(options.rights.bits())
41    }
42}
43
44/// All nodes must implement this trait.
45pub trait Node: GetEntryInfo + IntoAny + Send + Sync + 'static {
46    /// Returns node attributes (io2).
47    fn get_attributes(
48        &self,
49        requested_attributes: fio::NodeAttributesQuery,
50    ) -> impl Future<Output = Result<fio::NodeAttributes2, Status>> + Send
51    where
52        Self: Sized;
53
54    /// Called when the node is about to be opened as the node protocol.  Implementers can use this
55    /// to perform any initialization or reference counting.  Errors here will result in the open
56    /// failing.  By default, this forwards to the infallible will_clone.
57    fn will_open_as_node(&self) -> Result<(), Status> {
58        self.will_clone();
59        Ok(())
60    }
61
62    /// Called when the node is about to be cloned (and also by the default implementation of
63    /// will_open_as_node).  Implementations that perform their own open count can use this.  Each
64    /// call to `will_clone` will be accompanied by an eventual call to `close`.
65    fn will_clone(&self) {}
66
67    /// Called when the node is closed.
68    fn close(self: Arc<Self>) {}
69
70    fn link_into(
71        self: Arc<Self>,
72        _destination_dir: Arc<dyn MutableDirectory>,
73        _name: Name,
74    ) -> impl Future<Output = Result<(), Status>> + Send
75    where
76        Self: Sized,
77    {
78        ready(Err(Status::NOT_SUPPORTED))
79    }
80
81    /// Returns information about the filesystem.
82    fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
83        Err(Status::NOT_SUPPORTED)
84    }
85
86    /// Opens the node using the node protocol.
87    fn open_as_node(
88        self: Arc<Self>,
89        scope: ExecutionScope,
90        options: NodeOptions,
91        object_request: ObjectRequestRef<'_>,
92    ) -> Result<(), Status>
93    where
94        Self: Sized,
95    {
96        self.will_open_as_node()?;
97        Connection::create_sync(scope, self, options, object_request.take());
98        Ok(())
99    }
100}
101
102/// Represents a FIDL (limited) node connection.
103pub struct Connection<N: Node> {
104    // Execution scope this connection and any async operations and connections it creates will
105    // use.
106    scope: ExecutionScope,
107
108    // The underlying node.
109    node: OpenNode<N>,
110
111    // Node options.
112    options: NodeOptions,
113}
114
115/// Return type for [`handle_request()`] functions.
116enum ConnectionState {
117    /// Connection is still alive.
118    Alive,
119    /// Connection have received Node::Close message, it was dropped by the peer, or an error had
120    /// occurred.  As we do not perform any actions, except for closing our end we do not
121    /// distinguish those cases, unlike file and directory connections.
122    Closed,
123}
124
125impl<N: Node> Connection<N> {
126    /// Creates a new connection to serve the node. The node will be served from a new async `Task`,
127    /// not from the current `Task`. Errors in constructing the connection are not guaranteed to be
128    /// returned, they may be sent directly to the client end of the connection. This method should
129    /// be called from within an `ObjectRequest` handler to ensure that errors are sent to the
130    /// client end of the connection.
131    pub async fn create(
132        scope: ExecutionScope,
133        node: Arc<N>,
134        options: impl ToNodeOptions,
135        object_request: ObjectRequestRef<'_>,
136    ) -> Result<(), Status> {
137        let node = OpenNode::new(node);
138        let options = options.to_node_options(node.entry_info().type_())?;
139        let connection = Connection { scope: scope.clone(), node, options };
140        if let Ok(requests) = object_request.take().into_request_stream(&connection).await {
141            scope.spawn(RequestListener::new(requests, connection));
142        }
143        Ok(())
144    }
145
146    /// Similar to `create` but optimized for nodes whose implementation is synchronous and creating
147    /// the connection is being done from a non-async context.
148    pub fn create_sync(
149        scope: ExecutionScope,
150        node: Arc<N>,
151        options: impl ToNodeOptions,
152        object_request: ObjectRequest,
153    ) {
154        run_synchronous_future_or_spawn(
155            scope.clone(),
156            object_request.handle_async(async |object_request| {
157                Self::create(scope, node, options, object_request).await
158            }),
159        )
160    }
161
162    /// Handle a [`NodeRequest`].
163    async fn handle_request(&mut self, req: fio::NodeRequest) -> Result<ConnectionState, Error> {
164        match req {
165            #[cfg(fuchsia_api_level_at_least = "26")]
166            fio::NodeRequest::DeprecatedClone { flags, object, control_handle: _ } => {
167                self.handle_clone_deprecated(flags, object).await;
168            }
169            #[cfg(not(fuchsia_api_level_at_least = "26"))]
170            fio::NodeRequest::Clone { flags, object, control_handle: _ } => {
171                self.handle_clone_deprecated(flags, object).await;
172            }
173            #[cfg(fuchsia_api_level_at_least = "26")]
174            fio::NodeRequest::Clone { request, control_handle: _ } => {
175                // Suppress any errors in the event a bad `request` channel was provided.
176                self.handle_clone(ServerEnd::new(request.into_channel()));
177            }
178            #[cfg(not(fuchsia_api_level_at_least = "26"))]
179            fio::NodeRequest::Clone2 { request, control_handle: _ } => {
180                // Suppress any errors in the event a bad `request` channel was provided.
181                self.handle_clone(ServerEnd::new(request.into_channel()));
182            }
183            fio::NodeRequest::Close { responder } => {
184                responder.send(Ok(()))?;
185                return Ok(ConnectionState::Closed);
186            }
187            fio::NodeRequest::GetConnectionInfo { responder } => {
188                responder.send(fio::ConnectionInfo {
189                    rights: Some(self.options.rights),
190                    ..Default::default()
191                })?;
192            }
193            fio::NodeRequest::Sync { responder } => {
194                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
195            }
196            fio::NodeRequest::GetAttr { responder } => {
197                let (status, attrs) =
198                    crate::common::io2_to_io1_attrs(self.node.as_ref(), self.options.rights).await;
199                responder.send(status.into_raw(), &attrs)?;
200            }
201            fio::NodeRequest::SetAttr { flags: _, attributes: _, responder } => {
202                responder.send(Status::BAD_HANDLE.into_raw())?;
203            }
204            fio::NodeRequest::GetAttributes { query, responder } => {
205                let result = self.node.get_attributes(query).await;
206                responder.send(
207                    result
208                        .as_ref()
209                        .map(|attrs| (&attrs.mutable_attributes, &attrs.immutable_attributes))
210                        .map_err(|status| status.into_raw()),
211                )?;
212            }
213            fio::NodeRequest::UpdateAttributes { payload: _, responder } => {
214                responder.send(Err(Status::BAD_HANDLE.into_raw()))?;
215            }
216            fio::NodeRequest::ListExtendedAttributes { iterator, .. } => {
217                iterator.close_with_epitaph(Status::NOT_SUPPORTED)?;
218            }
219            fio::NodeRequest::GetExtendedAttribute { responder, .. } => {
220                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
221            }
222            fio::NodeRequest::SetExtendedAttribute { responder, .. } => {
223                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
224            }
225            fio::NodeRequest::RemoveExtendedAttribute { responder, .. } => {
226                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
227            }
228            #[cfg(fuchsia_api_level_at_least = "NEXT")]
229            fio::NodeRequest::GetFlags { responder } => {
230                responder.send(Ok(fio::Flags::from(&self.options)))?;
231            }
232            #[cfg(fuchsia_api_level_at_least = "NEXT")]
233            fio::NodeRequest::SetFlags { flags: _, responder } => {
234                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
235            }
236            #[cfg(fuchsia_api_level_at_least = "NEXT")]
237            fio::NodeRequest::DeprecatedGetFlags { responder } => {
238                responder.send(Status::OK.into_raw(), fio::OpenFlags::NODE_REFERENCE)?;
239            }
240            #[cfg(fuchsia_api_level_at_least = "NEXT")]
241            fio::NodeRequest::DeprecatedSetFlags { flags: _, responder } => {
242                responder.send(Status::BAD_HANDLE.into_raw())?;
243            }
244            #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
245            fio::NodeRequest::GetFlags { responder } => {
246                responder.send(Status::OK.into_raw(), fio::OpenFlags::NODE_REFERENCE)?;
247            }
248            #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
249            fio::NodeRequest::SetFlags { flags: _, responder } => {
250                responder.send(Status::BAD_HANDLE.into_raw())?;
251            }
252            fio::NodeRequest::Query { responder } => {
253                responder.send(fio::NODE_PROTOCOL_NAME.as_bytes())?;
254            }
255            fio::NodeRequest::QueryFilesystem { responder } => {
256                responder.send(Status::NOT_SUPPORTED.into_raw(), None)?;
257            }
258            fio::NodeRequest::_UnknownMethod { .. } => (),
259        }
260        Ok(ConnectionState::Alive)
261    }
262
263    async fn handle_clone_deprecated(
264        &mut self,
265        flags: fio::OpenFlags,
266        server_end: ServerEnd<fio::NodeMarker>,
267    ) {
268        flags
269            .to_object_request(server_end)
270            .handle_async(async |object_request| {
271                let options = inherit_rights_for_clone(fio::OpenFlags::NODE_REFERENCE, flags)?
272                    .to_node_options(self.node.entry_info().type_())?;
273
274                self.node.will_clone();
275
276                let connection = Self {
277                    scope: self.scope.clone(),
278                    node: OpenNode::new(self.node.clone()),
279                    options,
280                };
281
282                let requests = object_request.take().into_request_stream(&connection).await?;
283                self.scope.spawn(RequestListener::new(requests, connection));
284
285                Ok(())
286            })
287            .await;
288    }
289
290    fn handle_clone(&mut self, server_end: ServerEnd<fio::NodeMarker>) {
291        self.node.will_clone();
292        let connection = Self {
293            scope: self.scope.clone(),
294            node: OpenNode::new(self.node.clone()),
295            options: self.options,
296        };
297        self.scope.spawn(RequestListener::new(server_end.into_stream(), connection));
298    }
299}
300
301impl<N: Node> RequestHandler for Connection<N> {
302    type Request = Result<fio::NodeRequest, fidl::Error>;
303
304    async fn handle_request(self: Pin<&mut Self>, request: Self::Request) -> ControlFlow<()> {
305        let this = self.get_mut();
306        let _guard = this.scope.active_guard();
307        match request {
308            Ok(request) => match this.handle_request(request).await {
309                Ok(ConnectionState::Alive) => ControlFlow::Continue(()),
310                Ok(ConnectionState::Closed) | Err(_) => ControlFlow::Break(()),
311            },
312            Err(_) => ControlFlow::Break(()),
313        }
314    }
315}
316
317impl<N: Node> Representation for Connection<N> {
318    type Protocol = fio::NodeMarker;
319
320    #[cfg(fuchsia_api_level_at_least = "NEXT")]
321    async fn get_representation(
322        &self,
323        requested_attributes: fio::NodeAttributesQuery,
324    ) -> Result<fio::Representation, Status> {
325        Ok(fio::Representation::Node(fio::NodeInfo {
326            attributes: if requested_attributes.is_empty() {
327                None
328            } else {
329                Some(self.node.get_attributes(requested_attributes).await?)
330            },
331            ..Default::default()
332        }))
333    }
334
335    #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
336    async fn get_representation(
337        &self,
338        requested_attributes: fio::NodeAttributesQuery,
339    ) -> Result<fio::Representation, Status> {
340        Ok(fio::Representation::Connector(fio::ConnectorInfo {
341            attributes: if requested_attributes.is_empty() {
342                None
343            } else {
344                Some(self.node.get_attributes(requested_attributes).await?)
345            },
346            ..Default::default()
347        }))
348    }
349
350    async fn node_info(&self) -> Result<fio::NodeInfoDeprecated, Status> {
351        Ok(fio::NodeInfoDeprecated::Service(fio::Service))
352    }
353}
354
355impl<N: Node> ConnectionCreator<N> for Connection<N> {
356    async fn create<'a>(
357        scope: ExecutionScope,
358        node: Arc<N>,
359        protocols: impl crate::ProtocolsExt,
360        object_request: ObjectRequestRef<'a>,
361    ) -> Result<(), Status> {
362        Self::create(scope, node, protocols, object_request).await
363    }
364}
365
366/// This struct is a RAII wrapper around a node that will call close() on it when dropped.
367pub struct OpenNode<T: Node> {
368    node: Arc<T>,
369}
370
371impl<T: Node> OpenNode<T> {
372    pub fn new(node: Arc<T>) -> Self {
373        Self { node }
374    }
375}
376
377impl<T: Node> Drop for OpenNode<T> {
378    fn drop(&mut self) {
379        self.node.clone().close();
380    }
381}
382
383impl<T: Node> std::ops::Deref for OpenNode<T> {
384    type Target = Arc<T>;
385
386    fn deref(&self) -> &Self::Target {
387        &self.node
388    }
389}