Skip to main content

vfs/directory/
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 several directory implementations.
6
7#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
8use crate::common::stricter_or_same_rights;
9use crate::directory::entry::EntryInfo;
10
11use byteorder::{LittleEndian, WriteBytesExt as _};
12use fidl_fuchsia_io as fio;
13use static_assertions::assert_eq_size;
14use std::io::Write as _;
15use std::mem::size_of;
16#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
17use zx_status::Status;
18
19/// Directories need to make sure that connections to child entries do not receive more rights than
20/// the connection to the directory itself.  Plus there is special handling of the OPEN_FLAG_POSIX_*
21/// flags. This function should be called before calling [`new_connection_validate_flags`] if both
22/// are needed.
23#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
24pub(crate) fn check_child_connection_flags(
25    parent_flags: fio::OpenFlags,
26    mut flags: fio::OpenFlags,
27) -> Result<fio::OpenFlags, Status> {
28    if flags & (fio::OpenFlags::NOT_DIRECTORY | fio::OpenFlags::DIRECTORY)
29        == fio::OpenFlags::NOT_DIRECTORY | fio::OpenFlags::DIRECTORY
30    {
31        return Err(Status::INVALID_ARGS);
32    }
33
34    // Can only specify OPEN_FLAG_CREATE_IF_ABSENT if OPEN_FLAG_CREATE is also specified.
35    if flags.intersects(fio::OpenFlags::CREATE_IF_ABSENT)
36        && !flags.intersects(fio::OpenFlags::CREATE)
37    {
38        return Err(Status::INVALID_ARGS);
39    }
40
41    // Can only use CLONE_FLAG_SAME_RIGHTS when calling Clone.
42    if flags.intersects(fio::OpenFlags::CLONE_SAME_RIGHTS) {
43        return Err(Status::INVALID_ARGS);
44    }
45
46    // Remove POSIX flags when the respective rights are not available ("soft fail").
47    if !parent_flags.intersects(fio::OpenFlags::RIGHT_EXECUTABLE) {
48        flags &= !fio::OpenFlags::POSIX_EXECUTABLE;
49    }
50    if !parent_flags.intersects(fio::OpenFlags::RIGHT_WRITABLE) {
51        flags &= !fio::OpenFlags::POSIX_WRITABLE;
52    }
53
54    // Can only use CREATE flags if the parent connection is writable.
55    if flags.intersects(fio::OpenFlags::CREATE)
56        && !parent_flags.intersects(fio::OpenFlags::RIGHT_WRITABLE)
57    {
58        return Err(Status::ACCESS_DENIED);
59    }
60
61    if stricter_or_same_rights(parent_flags, flags) {
62        Ok(flags)
63    } else {
64        Err(Status::ACCESS_DENIED)
65    }
66}
67
68/// A helper to generate binary encodings for the ReadDirents response.  This function will append
69/// an entry description as specified by `entry` and `name` to the `buf`, and would return `true`.
70/// In case this would cause the buffer size to exceed `max_bytes`, the buffer is then left
71/// untouched and a `false` value is returned.
72pub(crate) fn encode_dirent(
73    buf: &mut Vec<u8>,
74    max_bytes: u64,
75    entry: &EntryInfo,
76    name: &str,
77) -> bool {
78    let header_size = size_of::<u64>() + size_of::<u8>() + size_of::<u8>();
79
80    assert_eq_size!(u64, usize);
81
82    if buf.len() + header_size + name.len() > max_bytes as usize {
83        return false;
84    }
85
86    assert!(
87        name.len() <= fio::MAX_NAME_LENGTH as usize,
88        "Entry names are expected to be no longer than MAX_FILENAME ({}) bytes.\n\
89         Got entry: '{}'\n\
90         Length: {} bytes",
91        fio::MAX_NAME_LENGTH,
92        name,
93        name.len()
94    );
95
96    assert!(
97        fio::MAX_NAME_LENGTH <= u8::max_value() as u64,
98        "Expecting to be able to store MAX_FILENAME ({}) in one byte.",
99        fio::MAX_NAME_LENGTH
100    );
101
102    buf.write_u64::<LittleEndian>(entry.inode())
103        .expect("out should be an in memory buffer that grows as needed");
104    buf.write_u8(name.len() as u8).expect("out should be an in memory buffer that grows as needed");
105    buf.write_u8(entry.type_().into_primitive())
106        .expect("out should be an in memory buffer that grows as needed");
107    buf.write_all(name.as_ref()).expect("out should be an in memory buffer that grows as needed");
108
109    true
110}