| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_STORAGE_LIB_VFS_CPP_VFS_TYPES_H_ |
| #define SRC_STORAGE_LIB_VFS_CPP_VFS_TYPES_H_ |
| |
| #include <fidl/fuchsia.io/cpp/natural_types.h> |
| #include <fidl/fuchsia.io/cpp/wire_types.h> |
| #include <lib/fdio/vfs.h> |
| #include <lib/zx/result.h> |
| #include <zircon/availability.h> |
| #include <zircon/compiler.h> |
| #include <zircon/types.h> |
| |
| #ifdef __Fuchsia__ |
| #include <lib/zx/channel.h> |
| #include <lib/zx/event.h> |
| #include <lib/zx/eventpair.h> |
| #include <lib/zx/handle.h> |
| #include <lib/zx/socket.h> |
| #include <lib/zx/stream.h> |
| #include <lib/zx/vmo.h> |
| #endif |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <optional> |
| |
| #include <fbl/bits.h> |
| |
| // The filesystem server exposes various FIDL protocols on top of the Vnode abstractions. This |
| // header defines some helper types composed with the fuchsia.io protocol types to better express |
| // API requirements. These type names should start with "Vnode" to reduce confusion with their FIDL |
| // counterparts. |
| namespace fs { |
| |
| namespace Rights { |
| |
| // Alias for commonly used read-only directory rights. |
| constexpr fuchsia_io::Rights ReadOnly() { return fuchsia_io::kRStarDir; } |
| |
| // Alias for commonly used write-only directory rights. |
| constexpr fuchsia_io::Rights WriteOnly() { |
| // TODO(https://fxbug.dev/42157659): Restrict GET_ATTRIBUTES. |
| return fuchsia_io::Rights::kGetAttributes | fuchsia_io::kWStarDir; |
| } |
| |
| // Alias for commonly used read-write directory rights. |
| constexpr fuchsia_io::Rights ReadWrite() { return fuchsia_io::kRwStarDir; } |
| |
| // Alias for commonly used read-execute directory rights. |
| constexpr fuchsia_io::Rights ReadExec() { return fuchsia_io::kRxStarDir; } |
| |
| // Alias for all possible rights. |
| constexpr fuchsia_io::Rights All() { return fuchsia_io::Rights::kMask; } |
| |
| } // namespace Rights |
| |
| constexpr fuchsia_io::OpenFlags kAllIo1Rights = fuchsia_io::OpenFlags::kRightReadable | |
| fuchsia_io::OpenFlags::kRightWritable | |
| fuchsia_io::OpenFlags::kRightExecutable; |
| |
| // Specifies the type of object when creating new nodes. |
| enum class CreationType : uint8_t { |
| kFile = 0, |
| kDirectory = 1, |
| // Max value used for fuzzing. |
| kMaxValue = kDirectory, |
| }; |
| |
| // Identifies a single type of node protocol where required for protocol negotiation/resolution. |
| enum class VnodeProtocol : uint8_t { |
| kNode = 0, // All Vnodes support fuchsia.io/Node, so it does not have an explicit representation. |
| kService = uint64_t{fuchsia_io::NodeProtocolKinds::kConnector}, |
| kDirectory = uint64_t{fuchsia_io::NodeProtocolKinds::kDirectory}, |
| kFile = uint64_t{fuchsia_io::NodeProtocolKinds::kFile}, |
| #if FUCHSIA_API_LEVEL_AT_LEAST(HEAD) |
| kSymlink = uint64_t{fuchsia_io::NodeProtocolKinds::kSymlink}, |
| #endif |
| }; |
| |
| // Options specified during opening and cloning. |
| struct VnodeConnectionOptions { |
| fuchsia_io::OpenFlags flags; |
| fuchsia_io::Rights rights; |
| |
| // TODO(https://fxbug.dev/324112857): Remove the following setters, as some aren't compatible with |
| // io2 directly (e.g. not_directory has no equivalent, and set_truncate only applies if the file |
| // protocol was selected). These setters are only used in tests anyways - same with the factory |
| // functions below. |
| |
| constexpr VnodeConnectionOptions set_directory() { |
| flags |= fuchsia_io::OpenFlags::kDirectory; |
| return *this; |
| } |
| |
| constexpr VnodeConnectionOptions set_not_directory() { |
| flags |= fuchsia_io::OpenFlags::kNotDirectory; |
| return *this; |
| } |
| |
| constexpr VnodeConnectionOptions set_node_reference() { |
| flags |= fuchsia_io::OpenFlags::kNodeReference; |
| return *this; |
| } |
| |
| constexpr VnodeConnectionOptions set_truncate() { |
| flags |= fuchsia_io::OpenFlags::kTruncate; |
| return *this; |
| } |
| |
| // Convenience factory functions for commonly used option combinations. |
| |
| constexpr static VnodeConnectionOptions ReadOnly() { |
| VnodeConnectionOptions options; |
| options.rights = Rights::ReadOnly(); |
| return options; |
| } |
| |
| constexpr static VnodeConnectionOptions WriteOnly() { |
| VnodeConnectionOptions options; |
| options.rights = Rights::WriteOnly(); |
| return options; |
| } |
| |
| constexpr static VnodeConnectionOptions ReadWrite() { |
| VnodeConnectionOptions options; |
| options.rights = Rights::ReadWrite(); |
| return options; |
| } |
| |
| constexpr static VnodeConnectionOptions ReadExec() { |
| VnodeConnectionOptions options; |
| options.rights = Rights::ReadExec(); |
| return options; |
| } |
| |
| // Translates the io1 flags passed by the client into an equivalent set of io2 protocols. |
| constexpr fuchsia_io::NodeProtocolKinds protocols() const { |
| constexpr fuchsia_io::NodeProtocolKinds kSupportedIo1Protocols = |
| #if FUCHSIA_API_LEVEL_AT_LEAST(HEAD) |
| // Symlinks are not supported via io1. |
| fuchsia_io::NodeProtocolKinds::kMask ^ fuchsia_io::NodeProtocolKinds::kSymlink; |
| #else |
| fuchsia_io::NodeProtocolKinds::kMask; |
| #endif |
| if (flags & fuchsia_io::OpenFlags::kDirectory) { |
| return fuchsia_io::NodeProtocolKinds::kDirectory; |
| } |
| if (flags & fuchsia_io::OpenFlags::kNotDirectory) { |
| return kSupportedIo1Protocols ^ fuchsia_io::NodeProtocolKinds::kDirectory; |
| } |
| return kSupportedIo1Protocols; |
| } |
| |
| // Converts from fuchsia.io/Directory.Open1 flags to |VnodeConnectionOptions|. Note that in io1, |
| // certain operations were unprivileged so they may be implicitly added to the resulting `rights`. |
| static zx::result<VnodeConnectionOptions> FromOpen1Flags(fuchsia_io::OpenFlags flags); |
| |
| // Converts from fuchsia.io/Directory.Clone flags to |VnodeConnectionOptions|. |
| static zx::result<VnodeConnectionOptions> FromCloneFlags(fuchsia_io::OpenFlags flags, |
| VnodeProtocol protocol); |
| |
| // Converts from |VnodeConnectionOptions| to fuchsia.io flags. |
| fuchsia_io::OpenFlags ToIoV1Flags() const; |
| }; |
| |
| fuchsia_io::OpenFlags RightsToOpenFlags(fuchsia_io::Rights rights); |
| |
| using VnodeAttributesQuery = fuchsia_io::NodeAttributesQuery; |
| |
| // Objective information about a filesystem node, used to implement |Vnode::GetAttributes|. Not |
| // all attributes are supported by all filesystems, so certain attributes may be unreported. |
| struct VnodeAttributes { |
| std::optional<uint64_t> id; |
| std::optional<uint64_t> content_size; |
| std::optional<uint64_t> storage_size; |
| std::optional<uint64_t> link_count; |
| |
| std::optional<uint64_t> creation_time; |
| std::optional<uint64_t> modification_time; |
| |
| // POSIX Compatibility Attributes |
| // TODO(https://fxbug.dev/340626555): Add support for uid/gid. |
| std::optional<uint32_t> mode; |
| |
| // Compare two |VnodeAttributes| instances for equality. |
| bool operator==(const VnodeAttributes& other) const { |
| return id == other.id && content_size == other.content_size && |
| storage_size == other.storage_size && link_count == other.link_count && |
| creation_time == other.creation_time && modification_time == other.modification_time && |
| mode == other.mode; |
| } |
| |
| // Converts from |VnodeAttributes| to fuchsia.io v1 |NodeAttributes|. |
| fuchsia_io::wire::NodeAttributes ToIoV1NodeAttributes() const; |
| }; |
| |
| // A request to update pieces of the |VnodeAttributes| via |Vnode::SetAttributes|. Not all |
| // attributes are supported by all filesystems, which may ignore unsupported attributes. |
| // TODO(https://fxbug.dev/340626555): Add support for setting POSIX mode/uid/gid. |
| struct VnodeAttributesUpdate { |
| std::optional<uint64_t> creation_time; |
| std::optional<uint64_t> modification_time; |
| |
| // Return a set of flags representing those attributes which we want to update. |
| constexpr VnodeAttributesQuery AttributesQuery() const { |
| VnodeAttributesQuery query; |
| if (creation_time) { |
| query |= VnodeAttributesQuery::kCreationTime; |
| } |
| if (modification_time) { |
| query |= VnodeAttributesQuery::kModificationTime; |
| } |
| return query; |
| } |
| }; |
| |
| // Indicates if and when a new object should be created when opening a node. |
| enum class CreationMode : uint8_t { |
| // Never create an object. Will return `ZX_ERR_NOT_FOUND` if there is no existing object. |
| kNever, |
| // Create a new object if one doesn't already exist, otherwise open the existing object. |
| kAllowExisting, |
| // Always create an object. Will return `ZX_ERR_ALREADY_EXISTS` if one already exists. |
| kAlways, |
| }; |
| |
| namespace internal { |
| |
| // Downscope |rights| to only include those operations which |protocol| supports, or those which are |
| // applicable to child nodes. This follows the principle of least privilege. |
| fuchsia_io::Rights DownscopeRights(fuchsia_io::Rights rights, VnodeProtocol protocol); |
| |
| // Determines the protocol to use for serving a connection, based on the |supported| protocols for |
| // a node, and those which were |requested|. |
| // |
| // Note that this function is not part of the |Vnode| interface. The fuchsia.io protocol does not |
| // define a specific order of protocol resolution when |requested| is ambiguous, but we define a |
| // strict mapping here to enforce consistency across the Fuchsia VFS libraries. |
| zx::result<VnodeProtocol> NegotiateProtocol(fuchsia_io::NodeProtocolKinds supported, |
| fuchsia_io::NodeProtocolKinds requested); |
| |
| #if !defined(__Fuchsia__) || __Fuchsia_API_level__ >= 19 |
| constexpr CreationMode CreationModeFromFidl(fuchsia_io::CreationMode mode) { |
| switch (mode) { |
| case fuchsia_io::CreationMode::kNever: |
| case fuchsia_io::CreationMode::kNeverDeprecated: |
| return CreationMode::kNever; |
| case fuchsia_io::CreationMode::kAllowExisting: |
| return CreationMode::kAllowExisting; |
| case fuchsia_io::CreationMode::kAlways: |
| return CreationMode::kAlways; |
| } |
| } |
| |
| constexpr fuchsia_io::CreationMode CreationModeToFidl(CreationMode mode) { |
| switch (mode) { |
| case CreationMode::kNever: |
| return fuchsia_io::CreationMode::kNever; |
| case CreationMode::kAllowExisting: |
| return fuchsia_io::CreationMode::kAllowExisting; |
| case CreationMode::kAlways: |
| return fuchsia_io::CreationMode::kAlways; |
| } |
| } |
| #else |
| constexpr CreationMode CreationModeFromFidl(fuchsia_io::OpenMode mode) { |
| switch (mode) { |
| case fuchsia_io::OpenMode::kOpenExisting: |
| return CreationMode::kNever; |
| case fuchsia_io::OpenMode::kMaybeCreate: |
| return CreationMode::kAllowExisting; |
| case fuchsia_io::OpenMode::kAlwaysCreate: |
| return CreationMode::kAlways; |
| } |
| } |
| |
| constexpr fuchsia_io::OpenMode CreationModeToFidl(CreationMode mode) { |
| switch (mode) { |
| case CreationMode::kNever: |
| return fuchsia_io::OpenMode::kOpenExisting; |
| case CreationMode::kAllowExisting: |
| return fuchsia_io::OpenMode::kMaybeCreate; |
| case CreationMode::kAlways: |
| return fuchsia_io::OpenMode::kAlwaysCreate; |
| } |
| } |
| #endif |
| |
| constexpr CreationMode CreationModeFromFidl(fuchsia_io::OpenFlags flags) { |
| if (flags & fuchsia_io::OpenFlags::kCreateIfAbsent) { |
| return CreationMode::kAlways; |
| } |
| if (flags & fuchsia_io::OpenFlags::kCreate) { |
| return CreationMode::kAllowExisting; |
| } |
| return CreationMode::kNever; |
| } |
| |
| } // namespace internal |
| |
| } // namespace fs |
| |
| #endif // SRC_STORAGE_LIB_VFS_CPP_VFS_TYPES_H_ |