vfs/
common.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//! Common utilities used by both directory and file traits.
6
7use crate::node::Node;
8use fidl::endpoints::ServerEnd;
9use fidl::prelude::*;
10use fidl_fuchsia_io as fio;
11use futures::StreamExt as _;
12use std::sync::Arc;
13use zx_status::Status;
14
15pub use vfs_macros::attribute_query;
16
17/// Set of known rights.
18const FS_RIGHTS: fio::OpenFlags = fio::OPEN_RIGHTS;
19
20/// Returns true if the rights flags in `flags` do not exceed those in `parent_flags`.
21pub fn stricter_or_same_rights(parent_flags: fio::OpenFlags, flags: fio::OpenFlags) -> bool {
22    let parent_rights = parent_flags & FS_RIGHTS;
23    let rights = flags & FS_RIGHTS;
24    return !rights.intersects(!parent_rights);
25}
26
27/// Common logic for rights processing during cloning a node, shared by both file and directory
28/// implementations.
29pub fn inherit_rights_for_clone(
30    parent_flags: fio::OpenFlags,
31    mut flags: fio::OpenFlags,
32) -> Result<fio::OpenFlags, Status> {
33    if flags.intersects(fio::OpenFlags::CLONE_SAME_RIGHTS) && flags.intersects(FS_RIGHTS) {
34        return Err(Status::INVALID_ARGS);
35    }
36
37    // We preserve OPEN_FLAG_APPEND as this is what is the most convenient for the POSIX emulation.
38    //
39    // OPEN_FLAG_NODE_REFERENCE is enforced, according to our current FS permissions design.
40    flags |= parent_flags & (fio::OpenFlags::APPEND | fio::OpenFlags::NODE_REFERENCE);
41
42    // If CLONE_FLAG_SAME_RIGHTS is requested, cloned connection will inherit the same rights
43    // as those from the originating connection.  We have ensured that no FS_RIGHTS flags are set
44    // above.
45    if flags.intersects(fio::OpenFlags::CLONE_SAME_RIGHTS) {
46        flags &= !fio::OpenFlags::CLONE_SAME_RIGHTS;
47        flags |= parent_flags & FS_RIGHTS;
48    }
49
50    if !stricter_or_same_rights(parent_flags, flags) {
51        return Err(Status::ACCESS_DENIED);
52    }
53
54    // Ignore the POSIX flags for clone.
55    flags &= !(fio::OpenFlags::POSIX_WRITABLE | fio::OpenFlags::POSIX_EXECUTABLE);
56
57    Ok(flags)
58}
59
60/// A helper method to send OnOpen event on the handle owned by the `server_end` in case `flags`
61/// contains `OPEN_FLAG_STATUS`.
62///
63/// If the send operation fails for any reason, the error is ignored.  This helper is used during
64/// an Open() or a Clone() FIDL methods, and these methods have no means to propagate errors to the
65/// caller.  OnOpen event is the only way to do that, so there is nowhere to report errors in
66/// OnOpen dispatch.  `server_end` will be closed, so there will be some kind of indication of the
67/// issue.
68///
69/// # Panics
70/// If `status` is `Status::OK`.  In this case `OnOpen` may need to contain a description of the
71/// object, and server_end should not be dropped.
72pub fn send_on_open_with_error(
73    describe: bool,
74    server_end: ServerEnd<fio::NodeMarker>,
75    status: Status,
76) {
77    if status == Status::OK {
78        panic!("send_on_open_with_error() should not be used to respond with Status::OK");
79    }
80
81    if !describe {
82        // There is no reasonable way to report this error.  Assuming the `server_end` has just
83        // disconnected or failed in some other way why we are trying to send OnOpen.
84        let _ = server_end.close_with_epitaph(status);
85        return;
86    }
87
88    let (_, control_handle) = server_end.into_stream_and_control_handle();
89    // Same as above, ignore the error.
90    let _ = control_handle.send_on_open_(status.into_raw(), None);
91    control_handle.shutdown_with_epitaph(status);
92}
93
94/// Trait to be used as a supertrait when an object should allow dynamic casting to an Any.
95///
96/// Separate trait since [`into_any`] requires Self to be Sized, which cannot be satisfied in a
97/// trait without preventing it from being object safe (thus disallowing dynamic dispatch).
98/// Since we provide a generic implementation, the size of each concrete type is known.
99pub trait IntoAny: std::any::Any {
100    /// Cast the given object into a `dyn std::any::Any`.
101    fn into_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static>;
102}
103
104impl<T: 'static + Send + Sync> IntoAny for T {
105    fn into_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static> {
106        self as Arc<dyn std::any::Any + Send + Sync + 'static>
107    }
108}
109
110/// Returns equivalent POSIX mode/permission bits based on the specified rights.
111/// Note that these only set the user bits.
112// TODO(https://fxbug.dev/324112547): Remove this function or make it only visible to this crate.
113pub fn rights_to_posix_mode_bits(readable: bool, writable: bool, executable: bool) -> u32 {
114    return (if readable { libc::S_IRUSR } else { 0 }
115        | if writable { libc::S_IWUSR } else { 0 }
116        | if executable { libc::S_IXUSR } else { 0 })
117    .into();
118}
119
120pub async fn extended_attributes_sender(
121    iterator: ServerEnd<fio::ExtendedAttributeIteratorMarker>,
122    attributes: Vec<Vec<u8>>,
123) {
124    let mut stream = iterator.into_stream();
125
126    let mut chunks = attributes.chunks(fio::MAX_LIST_ATTRIBUTES_CHUNK as usize).peekable();
127
128    while let Some(Ok(fio::ExtendedAttributeIteratorRequest::GetNext { responder })) =
129        stream.next().await
130    {
131        let (chunk, last) = match chunks.next() {
132            Some(chunk) => (chunk, chunks.peek().is_none()),
133            None => (&[][..], true),
134        };
135        #[allow(clippy::unnecessary_lazy_evaluations)]
136        responder.send(Ok((chunk, last))).unwrap_or_else(|_error| {
137            #[cfg(any(test, feature = "use_log"))]
138            log::error!(_error:?; "list extended attributes failed to send a chunk");
139        });
140        if last {
141            break;
142        }
143    }
144}
145
146pub fn encode_extended_attribute_value(
147    value: Vec<u8>,
148) -> Result<fio::ExtendedAttributeValue, Status> {
149    let size = value.len() as u64;
150    if size > fio::MAX_INLINE_ATTRIBUTE_VALUE {
151        #[cfg(target_os = "fuchsia")]
152        {
153            let vmo = fidl::Vmo::create(size)?;
154            vmo.write(&value, 0)?;
155            Ok(fio::ExtendedAttributeValue::Buffer(vmo))
156        }
157        #[cfg(not(target_os = "fuchsia"))]
158        Err(Status::NOT_SUPPORTED)
159    } else {
160        Ok(fio::ExtendedAttributeValue::Bytes(value))
161    }
162}
163
164pub fn decode_extended_attribute_value(
165    value: fio::ExtendedAttributeValue,
166) -> Result<Vec<u8>, Status> {
167    match value {
168        fio::ExtendedAttributeValue::Bytes(val) => Ok(val),
169        #[cfg(target_os = "fuchsia")]
170        fio::ExtendedAttributeValue::Buffer(vmo) => {
171            let length = vmo.get_content_size()?;
172            vmo.read_to_vec(0, length)
173        }
174        #[cfg(not(target_os = "fuchsia"))]
175        fio::ExtendedAttributeValue::Buffer(_) => Err(Status::NOT_SUPPORTED),
176        fio::ExtendedAttributeValue::__SourceBreaking { .. } => Err(Status::NOT_SUPPORTED),
177    }
178}
179
180/// Helper for building [`fio::NodeAttributes2`]` given `requested` attributes. Code will only run
181/// for `requested` attributes.
182///
183/// Example:
184///
185///   attributes!(
186///       requested,
187///       Mutable { creation_time: 123, modification_time: 456 },
188///       Immutable { content_size: 789 }
189///   );
190///
191#[macro_export]
192macro_rules! attributes {
193    ($requested:expr,
194     Mutable {$($mut_a:ident: $mut_v:expr),* $(,)?},
195     Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
196        {
197            use $crate::common::attribute_query;
198            fio::NodeAttributes2 {
199                mutable_attributes: fio::MutableNodeAttributes {
200                    $($mut_a: if $requested.contains(attribute_query!($mut_a)) {
201                        Option::from($mut_v)
202                    } else {
203                        None
204                    }),*,
205                    ..Default::default()
206                },
207                immutable_attributes: fio::ImmutableNodeAttributes {
208                    $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
209                        Option::from($immut_v)
210                    } else {
211                        None
212                    }),*,
213                    ..Default::default()
214                }
215            }
216        }
217    )
218}
219
220/// Helper for building [`fio::NodeAttributes2`]` given immutable attributes in `requested`
221/// Code will only run for `requested` attributes. Mutable attributes in `requested` are ignored.
222///
223/// Example:
224///
225///   immutable_attributes!(
226///       requested,
227///       Immutable { content_size: 789 }
228///   );
229///
230#[macro_export]
231macro_rules! immutable_attributes {
232    ($requested:expr,
233     Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
234        {
235            use $crate::common::attribute_query;
236            fio::NodeAttributes2 {
237                mutable_attributes: Default::default(),
238                immutable_attributes: fio::ImmutableNodeAttributes {
239                    $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
240                        Option::from($immut_v)
241                    } else {
242                        None
243                    }),*,
244                    ..Default::default()
245                },
246            }
247        }
248    )
249}
250
251/// Represents if and how objects should be created with an open request.
252#[derive(PartialEq, Eq)]
253pub enum CreationMode {
254    // Never create object.
255    Never,
256    // Object will be created if it does not exist.
257    AllowExisting,
258    // Create the object, will fail if it does exist.
259    Always,
260    // Create the object as an unnamed and temporary object.
261    UnnamedTemporary,
262    // Create the object as an unnamed, temporary, and unlinkable object.
263    UnlinkableUnnamedTemporary,
264}
265
266/// Used to translate fuchsia.io/Node.SetAttr calls (io1) to fuchsia.io/Node.UpdateAttributes (io2).
267pub(crate) fn io1_to_io2_attrs(
268    flags: fio::NodeAttributeFlags,
269    attrs: fio::NodeAttributes,
270) -> fio::MutableNodeAttributes {
271    fio::MutableNodeAttributes {
272        creation_time: flags
273            .contains(fio::NodeAttributeFlags::CREATION_TIME)
274            .then_some(attrs.creation_time),
275        modification_time: flags
276            .contains(fio::NodeAttributeFlags::MODIFICATION_TIME)
277            .then_some(attrs.modification_time),
278        ..Default::default()
279    }
280}
281
282/// The set of attributes that must be queried to fulfill an io1 GetAttrs request.
283const ALL_IO1_ATTRIBUTES: fio::NodeAttributesQuery = fio::NodeAttributesQuery::PROTOCOLS
284    .union(fio::NodeAttributesQuery::ABILITIES)
285    .union(fio::NodeAttributesQuery::ID)
286    .union(fio::NodeAttributesQuery::CONTENT_SIZE)
287    .union(fio::NodeAttributesQuery::STORAGE_SIZE)
288    .union(fio::NodeAttributesQuery::LINK_COUNT)
289    .union(fio::NodeAttributesQuery::CREATION_TIME)
290    .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
291
292/// Default set of attributes to send to an io1 GetAttr request upon failure.
293const DEFAULT_IO1_ATTRIBUTES: fio::NodeAttributes = fio::NodeAttributes {
294    mode: 0,
295    id: fio::INO_UNKNOWN,
296    content_size: 0,
297    storage_size: 0,
298    link_count: 0,
299    creation_time: 0,
300    modification_time: 0,
301};
302
303const DEFAULT_LINK_COUNT: u64 = 1;
304
305/// Approximate a set of POSIX mode bits based on a node's protocols and abilities. This follows the
306/// C++ VFS implementation, and is only used for io1 GetAttrs calls where the filesystem doesn't
307/// support POSIX mode bits. Returns 0 if the mode bits could not be approximated.
308const fn approximate_posix_mode(
309    protocols: Option<fio::NodeProtocolKinds>,
310    abilities: fio::Abilities,
311) -> u32 {
312    let Some(protocols) = protocols else {
313        return 0;
314    };
315    match protocols {
316        fio::NodeProtocolKinds::DIRECTORY => {
317            let mut mode = libc::S_IFDIR;
318            if abilities.contains(fio::Abilities::ENUMERATE) {
319                mode |= libc::S_IRUSR;
320            }
321            if abilities.contains(fio::Abilities::MODIFY_DIRECTORY) {
322                mode |= libc::S_IWUSR;
323            }
324            if abilities.contains(fio::Abilities::TRAVERSE) {
325                mode |= libc::S_IXUSR;
326            }
327            mode
328        }
329        fio::NodeProtocolKinds::FILE => {
330            let mut mode = libc::S_IFREG;
331            if abilities.contains(fio::Abilities::READ_BYTES) {
332                mode |= libc::S_IRUSR;
333            }
334            if abilities.contains(fio::Abilities::WRITE_BYTES) {
335                mode |= libc::S_IWUSR;
336            }
337            if abilities.contains(fio::Abilities::EXECUTE) {
338                mode |= libc::S_IXUSR;
339            }
340            mode
341        }
342        fio::NodeProtocolKinds::CONNECTOR => fio::MODE_TYPE_SERVICE | libc::S_IRUSR | libc::S_IWUSR,
343        #[cfg(fuchsia_api_level_at_least = "HEAD")]
344        fio::NodeProtocolKinds::SYMLINK => libc::S_IFLNK | libc::S_IRUSR,
345        _ => 0,
346    }
347}
348
349/// Used to translate fuchsia.io/Node.GetAttributes calls (io2) to fuchsia.io/Node.GetAttrs (io1).
350/// We don't return a Result since the fuchsia.io/Node.GetAttrs method doesn't use FIDL errors, and
351/// thus requires we return a status code and set of default attributes for the failure case.
352pub async fn io2_to_io1_attrs<T: Node>(
353    node: &T,
354    rights: fio::Rights,
355) -> (Status, fio::NodeAttributes) {
356    if !rights.contains(fio::Rights::GET_ATTRIBUTES) {
357        return (Status::BAD_HANDLE, DEFAULT_IO1_ATTRIBUTES);
358    }
359
360    let attributes = node.get_attributes(ALL_IO1_ATTRIBUTES).await;
361    let Ok(fio::NodeAttributes2 {
362        mutable_attributes: mut_attrs,
363        immutable_attributes: immut_attrs,
364    }) = attributes
365    else {
366        return (attributes.unwrap_err(), DEFAULT_IO1_ATTRIBUTES);
367    };
368
369    (
370        Status::OK,
371        fio::NodeAttributes {
372            // If the node has POSIX mode bits, use those directly, otherwise synthesize a set based
373            // on the node's protocols/abilities if available.
374            mode: mut_attrs.mode.unwrap_or_else(|| {
375                approximate_posix_mode(
376                    immut_attrs.protocols,
377                    immut_attrs.abilities.unwrap_or_default(),
378                )
379            }),
380            id: immut_attrs.id.unwrap_or(fio::INO_UNKNOWN),
381            content_size: immut_attrs.content_size.unwrap_or_default(),
382            storage_size: immut_attrs.storage_size.unwrap_or_default(),
383            link_count: immut_attrs.link_count.unwrap_or(DEFAULT_LINK_COUNT),
384            creation_time: mut_attrs.creation_time.unwrap_or_default(),
385            modification_time: mut_attrs.modification_time.unwrap_or_default(),
386        },
387    )
388}
389
390pub fn mutable_node_attributes_to_query(
391    attributes: &fio::MutableNodeAttributes,
392) -> fio::NodeAttributesQuery {
393    let mut query = fio::NodeAttributesQuery::empty();
394
395    if attributes.creation_time.is_some() {
396        query |= fio::NodeAttributesQuery::CREATION_TIME;
397    }
398    if attributes.modification_time.is_some() {
399        query |= fio::NodeAttributesQuery::MODIFICATION_TIME;
400    }
401    if attributes.access_time.is_some() {
402        query |= fio::NodeAttributesQuery::ACCESS_TIME;
403    }
404    if attributes.mode.is_some() {
405        query |= fio::NodeAttributesQuery::MODE;
406    }
407    if attributes.uid.is_some() {
408        query |= fio::NodeAttributesQuery::UID;
409    }
410    if attributes.gid.is_some() {
411        query |= fio::NodeAttributesQuery::GID;
412    }
413    if attributes.rdev.is_some() {
414        query |= fio::NodeAttributesQuery::RDEV;
415    }
416    query
417}
418
419#[cfg(test)]
420mod tests {
421    use super::inherit_rights_for_clone;
422
423    use fidl_fuchsia_io as fio;
424
425    // TODO This should be converted into a function as soon as backtrace support is in place.
426    // The only reason this is a macro is to generate error messages that point to the test
427    // function source location in their top stack frame.
428    macro_rules! irfc_ok {
429        ($parent_flags:expr, $flags:expr, $expected_new_flags:expr $(,)*) => {{
430            let res = inherit_rights_for_clone($parent_flags, $flags);
431            match res {
432                Ok(new_flags) => assert_eq!(
433                    $expected_new_flags, new_flags,
434                    "`inherit_rights_for_clone` returned unexpected set of flags.\n\
435                     Expected: {:X}\n\
436                     Actual: {:X}",
437                    $expected_new_flags, new_flags
438                ),
439                Err(status) => panic!("`inherit_rights_for_clone` failed.  Status: {status}"),
440            }
441        }};
442    }
443
444    #[test]
445    fn node_reference_is_inherited() {
446        irfc_ok!(
447            fio::OpenFlags::NODE_REFERENCE,
448            fio::OpenFlags::empty(),
449            fio::OpenFlags::NODE_REFERENCE
450        );
451    }
452}