Skip to main content

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