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            #[cfg(any(
210                fuchsia_api_level_at_least = "PLATFORM",
211                not(fuchsia_api_level_at_least = "29")
212            ))]
213            fio::NodeRequest::DeprecatedClone { flags, object, control_handle: _ } => {
214                trace::duration!("storage", "Node::Clone");
215                crate::common::send_on_open_with_error(
216                    flags.contains(fio::OpenFlags::DESCRIBE),
217                    object,
218                    Status::NOT_SUPPORTED,
219                );
220            }
221            fio::NodeRequest::Clone { request, control_handle: _ } => {
222                trace::duration!("storage", "Node::Clone");
223                // Suppress any errors in the event a bad `request` channel was provided.
224                self.handle_clone(ServerEnd::new(request.into_channel()));
225            }
226            fio::NodeRequest::Close { responder } => {
227                trace::duration!("storage", "Node::Close");
228                responder.send(Ok(()))?;
229                return Ok(ConnectionState::Closed);
230            }
231            fio::NodeRequest::Sync { responder } => {
232                trace::duration!("storage", "Node::Sync");
233                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
234            }
235            #[cfg(fuchsia_api_level_at_least = "28")]
236            fio::NodeRequest::DeprecatedGetAttr { responder } => {
237                async move {
238                    let (status, attrs) =
239                        crate::common::io2_to_io1_attrs(self.node.as_ref(), self.options.rights)
240                            .await;
241                    responder.send(status.into_raw(), &attrs)
242                }
243                .trace(trace::trace_future_args!("storage", "Node::GetAttr"))
244                .await?;
245            }
246            #[cfg(not(fuchsia_api_level_at_least = "28"))]
247            fio::NodeRequest::GetAttr { responder } => {
248                async move {
249                    let (status, attrs) =
250                        crate::common::io2_to_io1_attrs(self.node.as_ref(), self.options.rights)
251                            .await;
252                    responder.send(status.into_raw(), &attrs)
253                }
254                .trace(trace::trace_future_args!("storage", "Node::GetAttr"))
255                .await?;
256            }
257            #[cfg(fuchsia_api_level_at_least = "28")]
258            fio::NodeRequest::DeprecatedSetAttr { flags: _, attributes: _, responder } => {
259                trace::duration!("storage", "Node::SetAttr");
260                responder.send(Status::BAD_HANDLE.into_raw())?;
261            }
262            #[cfg(not(fuchsia_api_level_at_least = "28"))]
263            fio::NodeRequest::SetAttr { flags: _, attributes: _, responder } => {
264                trace::duration!("storage", "Node::SetAttr");
265                responder.send(Status::BAD_HANDLE.into_raw())?;
266            }
267            fio::NodeRequest::GetAttributes { query, responder } => {
268                async move {
269                    if !self.options.rights.intersects(fio::Operations::GET_ATTRIBUTES) {
270                        responder.send(Err(Status::ACCESS_DENIED.into_raw()))
271                    } else {
272                        let attrs = self.node.get_attributes(query).await;
273                        responder.send(
274                            attrs
275                                .as_ref()
276                                .map(|attrs| {
277                                    (&attrs.mutable_attributes, &attrs.immutable_attributes)
278                                })
279                                .map_err(|status| status.into_raw()),
280                        )
281                    }
282                }
283                .trace(trace::trace_future_args!("storage", "Node::GetAttributes"))
284                .await?;
285            }
286            fio::NodeRequest::UpdateAttributes { payload: _, responder } => {
287                trace::duration!("storage", "Node::UpdateAttributes");
288                responder.send(Err(Status::BAD_HANDLE.into_raw()))?;
289            }
290            fio::NodeRequest::ListExtendedAttributes { iterator, .. } => {
291                trace::duration!("storage", "Node::ListExtendedAttributes");
292                iterator.close_with_epitaph(Status::NOT_SUPPORTED)?;
293            }
294            fio::NodeRequest::GetExtendedAttribute { responder, .. } => {
295                trace::duration!("storage", "Node::GetExtendedAttribute");
296                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
297            }
298            fio::NodeRequest::SetExtendedAttribute { responder, .. } => {
299                trace::duration!("storage", "Node::SetExtendedAttribute");
300                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
301            }
302            fio::NodeRequest::RemoveExtendedAttribute { responder, .. } => {
303                trace::duration!("storage", "Node::RemoveExtendedAttribute");
304                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
305            }
306            fio::NodeRequest::GetFlags { responder } => {
307                trace::duration!("storage", "Node::GetFlags");
308                responder.send(Ok(fio::Flags::from(&self.options)))?;
309            }
310            fio::NodeRequest::SetFlags { flags: _, responder } => {
311                trace::duration!("storage", "Node::SetFlags");
312                responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
313            }
314            fio::NodeRequest::DeprecatedGetFlags { responder } => {
315                trace::duration!("storage", "Node::GetFlags");
316                responder.send(Status::OK.into_raw(), fio::OpenFlags::NODE_REFERENCE)?;
317            }
318            fio::NodeRequest::DeprecatedSetFlags { flags: _, responder } => {
319                trace::duration!("storage", "Node::SetFlags");
320                responder.send(Status::BAD_HANDLE.into_raw())?;
321            }
322            fio::NodeRequest::Query { responder } => {
323                trace::duration!("storage", "Node::Query");
324                responder.send(fio::NodeMarker::PROTOCOL_NAME.as_bytes())?;
325            }
326            fio::NodeRequest::QueryFilesystem { responder } => {
327                trace::duration!("storage", "Node::QueryFilesystem");
328                responder.send(Status::NOT_SUPPORTED.into_raw(), None)?;
329            }
330            fio::NodeRequest::_UnknownMethod { .. } => (),
331        }
332        Ok(ConnectionState::Alive)
333    }
334
335    fn handle_clone(&mut self, server_end: ServerEnd<fio::NodeMarker>) {
336        self.node.will_clone();
337        let connection = Self {
338            scope: self.scope.clone(),
339            node: OpenNode::new(self.node.clone()),
340            options: self.options,
341        };
342        self.scope.spawn(RequestListener::new(server_end.into_stream(), connection));
343    }
344}
345
346impl<N: Node> RequestHandler for Connection<N> {
347    type Request = Result<fio::NodeRequest, fidl::Error>;
348
349    async fn handle_request(self: Pin<&mut Self>, request: Self::Request) -> ControlFlow<()> {
350        let this = self.get_mut();
351        if let Some(_guard) = this.scope.try_active_guard() {
352            match request {
353                Ok(request) => match this.handle_request(request).await {
354                    Ok(ConnectionState::Alive) => ControlFlow::Continue(()),
355                    Ok(ConnectionState::Closed) | Err(_) => ControlFlow::Break(()),
356                },
357                Err(_) => ControlFlow::Break(()),
358            }
359        } else {
360            ControlFlow::Break(())
361        }
362    }
363}
364
365impl<N: Node> Representation for Connection<N> {
366    type Protocol = fio::NodeMarker;
367
368    async fn get_representation(
369        &self,
370        requested_attributes: fio::NodeAttributesQuery,
371    ) -> Result<fio::Representation, Status> {
372        Ok(fio::Representation::Node(fio::NodeInfo {
373            attributes: if requested_attributes.is_empty() {
374                None
375            } else {
376                Some(self.node.get_attributes(requested_attributes).await?)
377            },
378            ..Default::default()
379        }))
380    }
381
382    #[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "32")))]
383    async fn node_info(&self) -> Result<fio::NodeInfoDeprecated, Status> {
384        Ok(fio::NodeInfoDeprecated::Service(fio::Service))
385    }
386}
387
388impl<N: Node> ConnectionCreator<N> for Connection<N> {
389    async fn create<'a>(
390        scope: ExecutionScope,
391        node: Arc<N>,
392        protocols: impl crate::ProtocolsExt,
393        object_request: ObjectRequestRef<'a>,
394    ) -> Result<(), Status> {
395        Self::create(scope, node, protocols, object_request).await
396    }
397}
398
399/// This struct is a RAII wrapper around a node that will call close() on it when dropped.
400pub struct OpenNode<T: Node> {
401    node: Arc<T>,
402}
403
404impl<T: Node> OpenNode<T> {
405    pub fn new(node: Arc<T>) -> Self {
406        Self { node }
407    }
408}
409
410impl<T: Node> Drop for OpenNode<T> {
411    fn drop(&mut self) {
412        self.node.clone().close();
413    }
414}
415
416impl<T: Node> std::ops::Deref for OpenNode<T> {
417    type Target = Arc<T>;
418
419    fn deref(&self) -> &Self::Target {
420        &self.node
421    }
422}