blob: 7941e76e5697fd7d6c05a5a5ef8221c9b9238ad4 [file] [log] [blame]
// Copyright 2017 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.
#include "src/storage/lib/vfs/cpp/connection/connection.h"
#include <fidl/fuchsia.io/cpp/common_types.h>
#include <fidl/fuchsia.io/cpp/natural_types.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fidl/cpp/wire/channel.h>
#include <lib/fidl/cpp/wire/object_view.h>
#include <lib/zx/channel.h>
#include <lib/zx/result.h>
#include <limits.h>
#include <zircon/assert.h>
#include <zircon/availability.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <utility>
#include <fbl/ref_ptr.h>
#include "src/storage/lib/vfs/cpp/debug.h"
#include "src/storage/lib/vfs/cpp/fuchsia_vfs.h"
#include "src/storage/lib/vfs/cpp/vfs_types.h"
#include "src/storage/lib/vfs/cpp/vnode.h"
namespace fio = fuchsia_io;
static_assert(fio::wire::kOpenFlagsAllowedWithNodeReference ==
(fio::wire::OpenFlags::kDirectory | fio::wire::OpenFlags::kNotDirectory |
fio::wire::OpenFlags::kDescribe | fio::wire::OpenFlags::kNodeReference),
"OPEN_FLAGS_ALLOWED_WITH_NODE_REFERENCE value mismatch");
static_assert(PATH_MAX == fio::wire::kMaxPathLength + 1,
"POSIX PATH_MAX inconsistent with Fuchsia MAX_PATH_LENGTH");
static_assert(NAME_MAX == fio::wire::kMaxNameLength,
"POSIX NAME_MAX inconsistent with Fuchsia MAX_NAME_LENGTH");
namespace fs::internal {
Connection::Connection(FuchsiaVfs* vfs, fbl::RefPtr<Vnode> vnode, fuchsia_io::Rights rights)
: vfs_(vfs), vnode_(std::move(vnode)), rights_(rights) {
ZX_DEBUG_ASSERT(vfs);
ZX_DEBUG_ASSERT(vnode_);
}
Connection::~Connection() {
// Release the token associated with this connection's vnode since the connection will be
// releasing the vnode's reference once this function returns.
if (auto vfs = vfs_.Upgrade(); vfs && token_) {
vfs->TokenDiscard(std::move(token_));
}
}
void Connection::NodeCloneDeprecated(fio::OpenFlags flags, VnodeProtocol protocol,
fidl::ServerEnd<fio::Node> server_end) {
zx_status_t status = [&]() -> zx_status_t {
zx::result clone_options = DeprecatedOptions::FromCloneFlags(flags, protocol);
if (clone_options.is_error()) {
FS_PRETTY_TRACE_DEBUG("[NodeCloneDeprecated] invalid clone flags: ", flags);
return clone_options.error_value();
}
FS_PRETTY_TRACE_DEBUG("[NodeCloneDeprecated] our rights: ", rights(),
", options: ", *clone_options);
// If CLONE_SAME_RIGHTS is requested, cloned connection will inherit the same rights as those
// from the originating connection.
if (clone_options->flags & fio::OpenFlags::kCloneSameRights) {
clone_options->rights = rights_;
} else if (clone_options->rights - rights_) {
// Return ACCESS_DENIED if the client asked for a right the parent connection doesn't have.
return ZX_ERR_ACCESS_DENIED;
}
if (zx::result validated = vnode()->DeprecatedValidateOptions(*clone_options);
validated.is_error()) {
return validated.error_value();
}
auto vfs = vfs_.Upgrade();
if (!vfs)
return ZX_ERR_CANCELED;
fbl::RefPtr vn = vnode();
// We only need to open the Vnode for non-node reference connection.
if (protocol != VnodeProtocol::kNode) {
if (zx_status_t open_status = OpenVnode(&vn); open_status != ZX_OK) {
return open_status;
}
}
// On failure, |Vfs::ServeDeprecated()| will close the channel with an epitaph.
vfs->ServeDeprecated(vn, server_end.TakeChannel(), *clone_options);
return ZX_OK;
}();
if (status != ZX_OK) {
FS_PRETTY_TRACE_DEBUG("[NodeCloneDeprecated] error: ", zx_status_get_string(status));
if (flags & fio::wire::OpenFlags::kDescribe) {
// Ignore errors since there is nothing we can do if this fails.
[[maybe_unused]] auto result = fidl::WireSendEvent(server_end)->OnOpen(status, {});
}
server_end.Close(status);
}
}
void Connection::NodeClone(fio::Flags flags, zx::channel object) const {
FS_PRETTY_TRACE_DEBUG("[NodeClone] reopening with flags: ", flags);
auto vfs = this->vfs();
if (!vfs) {
fidl::ServerEnd<fio::Node>{std::move(object)}.Close(ZX_ERR_CANCELED);
return;
}
// On failure, |Vfs::Serve()| will close the channel with an epitaph.
[[maybe_unused]] zx_status_t status = vfs->Serve(vnode(), std::move(object), flags);
#if FS_TRACE_DEBUG_ENABLED
if (status != ZX_OK) {
FS_PRETTY_TRACE_DEBUG("[NodeClone] serve failed: ", zx_status_get_string(status));
}
#endif // FS_TRACE_DEBUG_ENABLED
}
zx::result<> Connection::NodeUpdateAttributes(const VnodeAttributesUpdate& update) {
FS_PRETTY_TRACE_DEBUG("[NodeSetAttr] our rights: ", rights(),
", setting attributes: ", update.Query(),
", supported attributes: ", vnode_->SupportedMutableAttributes());
if (!(rights_ & fio::Rights::kUpdateAttributes)) {
return zx::error(ZX_ERR_BAD_HANDLE);
}
// Check that the Vnode allows setting the attributes we are updating.
if (update.Query() - vnode_->SupportedMutableAttributes()) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
return vnode_->UpdateAttributes(update);
}
zx::result<fio::wire::FilesystemInfo> Connection::NodeQueryFilesystem() const {
auto vfs = vfs_.Upgrade();
if (!vfs)
return zx::error(ZX_ERR_CANCELED);
zx::result<FilesystemInfo> info = vfs->GetFilesystemInfo();
if (info.is_error()) {
return info.take_error();
}
return zx::ok(info.value().ToFidl());
}
namespace {
// Helper function to reduce verbosity in |NodeAttributes:Build| impl below by using type deduction.
// Returns an external (non-owning) |fidl::ObjectView| to |obj|.
template <typename T>
fidl::ObjectView<T> ExternalView(T* obj) {
return fidl::ObjectView<T>::FromExternal(obj);
}
} // namespace
zx::result<fio::wire::NodeAttributes2*> NodeAttributeBuilder::Build(
fuchsia_io::NodeAttributesQuery query) {
if (attributes_.is_error()) {
return zx::error(attributes_.error_value());
}
fs::VnodeAttributes* attributes = &attributes_.value();
// Immutable attributes:
auto immutable_builder = ImmutableAttrs::ExternalBuilder(ExternalView(&immutable_frame_));
if (query & fio::NodeAttributesQuery::kProtocols) {
immutable_builder.protocols(ExternalView(&protocols_));
}
if (query & fio::NodeAttributesQuery::kAbilities) {
immutable_builder.abilities(ExternalView(&abilities_));
}
if (query & fio::NodeAttributesQuery::kContentSize && attributes->content_size) {
immutable_builder.content_size(ExternalView(&*attributes->content_size));
}
if (query & fio::NodeAttributesQuery::kStorageSize && attributes->storage_size) {
immutable_builder.storage_size(ExternalView(&*attributes->storage_size));
}
if (query & fio::NodeAttributesQuery::kLinkCount && attributes->link_count) {
immutable_builder.link_count(ExternalView(&*attributes->link_count));
}
if (query & fio::NodeAttributesQuery::kId && attributes->id) {
immutable_builder.id(ExternalView(&*attributes->id));
}
if (query & fio::NodeAttributesQuery::kChangeTime && attributes->change_time) {
immutable_builder.change_time(ExternalView(&*attributes->change_time));
}
// Mutable attributes:
auto mutable_builder = MutableAttrs::ExternalBuilder(ExternalView(&mutable_frame_));
if (query & fio::NodeAttributesQuery::kCreationTime && attributes->creation_time) {
mutable_builder.creation_time(ExternalView(&*attributes->creation_time));
}
if (query & fio::NodeAttributesQuery::kModificationTime && attributes->modification_time) {
mutable_builder.modification_time(ExternalView(&*attributes->modification_time));
}
if (query & fio::NodeAttributesQuery::kMode && attributes->mode) {
mutable_builder.mode(*attributes->mode);
}
if (query & fio::NodeAttributesQuery::kUid && attributes->uid) {
mutable_builder.uid(*attributes->uid);
}
if (query & fio::NodeAttributesQuery::kGid && attributes->gid) {
mutable_builder.gid(*attributes->gid);
}
if (query & fio::NodeAttributesQuery::kRdev && attributes->rdev) {
mutable_builder.rdev(ExternalView(&*attributes->rdev));
}
if (query & fio::NodeAttributesQuery::kAccessTime && attributes->access_time) {
mutable_builder.access_time(ExternalView(&*attributes->access_time));
}
// Build the wire table, which is now valid as long as this object remains in scope.
wire_table_ = NodeAttributes2{
.mutable_attributes = mutable_builder.Build(),
.immutable_attributes = immutable_builder.Build(),
};
return zx::ok(&wire_table_);
}
} // namespace fs::internal