blob: 2de843fbb6f74d6733bfc2f226e6f4cea73e8519 [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/wire.h>
#include <lib/fidl/txn_header.h>
#include <lib/zx/handle.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <zircon/assert.h>
#include <memory>
#include <utility>
#include <fbl/string_buffer.h>
#include "src/storage/lib/vfs/cpp/debug.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::kMaxFilename,
"POSIX NAME_MAX inconsistent with Fuchsia MAX_FILENAME");
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 (token_) {
vfs_->TokenDiscard(std::move(token_));
}
}
void Connection::NodeClone(fio::OpenFlags flags, VnodeProtocol protocol,
fidl::ServerEnd<fio::Node> server_end) {
auto clone_result = [=]() -> zx::result<std::tuple<fbl::RefPtr<Vnode>, VnodeConnectionOptions>> {
if (!ValidateCloneFlags(flags)) {
FS_PRETTY_TRACE_DEBUG("[NodeClone] invalid flags: ", flags);
return zx::error(ZX_ERR_INVALID_ARGS);
}
auto clone_options = VnodeConnectionOptions::FromCloneFlags(flags);
FS_PRETTY_TRACE_DEBUG("[NodeClone] our rights: ", rights(), ", clone 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::error(ZX_ERR_ACCESS_DENIED);
}
// Ensure we map the request to the correct flags based on the connection's protocol.
switch (protocol) {
case fs::VnodeProtocol::kNode: {
clone_options.flags |= fio::OpenFlags::kNodeReference;
break;
}
case fs::VnodeProtocol::kDirectory: {
clone_options.flags |= fio::OpenFlags::kDirectory;
break;
}
default: {
clone_options.flags |= fio::OpenFlags::kNotDirectory;
break;
}
}
if (zx::result validated = vnode()->ValidateOptions(clone_options); validated.is_error()) {
return validated.take_error();
}
fbl::RefPtr vn = vnode();
if (protocol != VnodeProtocol::kNode) {
// We only need to open the underlying Vnode again if this isn't a node reference connection.
if (zx_status_t open_status = OpenVnode(&vn); open_status != ZX_OK) {
return zx::error(open_status);
}
}
return zx::ok(std::make_tuple(std::move(vn), clone_options));
}();
if (clone_result.is_ok()) {
auto [vnode, options] = *std::move(clone_result);
vfs_->Serve(vnode, server_end.TakeChannel(), options);
} else {
FS_PRETTY_TRACE_DEBUG("[NodeClone] error: ", clone_result.status_string());
if (flags & fio::OpenFlags::kDescribe) {
// Ignore errors since there is nothing we can do if this fails.
[[maybe_unused]] auto result =
fidl::WireSendEvent(server_end)
->OnOpen(clone_result.error_value(), fio::wire::NodeInfoDeprecated());
}
}
}
zx::result<VnodeAttributes> Connection::NodeGetAttr() const {
FS_PRETTY_TRACE_DEBUG("[NodeGetAttr] rights: ", rights());
// TODO(https://fxbug.dev/324080764): This io1 operation should require the GET_ATTRIBUTES right.
fs::VnodeAttributes attr;
if (zx_status_t status = vnode_->GetAttributes(&attr); status != ZX_OK) {
return zx::error(status);
}
return zx::ok(attr);
}
zx::result<> Connection::NodeSetAttr(fio::NodeAttributeFlags flags,
const fio::wire::NodeAttributes& attributes) {
FS_PRETTY_TRACE_DEBUG("[NodeSetAttr] our rights: ", rights(), ", incoming flags: ", flags);
if (!(rights_ & fio::Rights::kUpdateAttributes)) {
return zx::error(ZX_ERR_BAD_HANDLE);
}
fs::VnodeAttributesUpdate update;
if (flags & fio::NodeAttributeFlags::kCreationTime) {
update.set_creation_time(attributes.creation_time);
}
if (flags & fio::NodeAttributeFlags::kModificationTime) {
update.set_modification_time(attributes.modification_time);
}
return zx::make_result(vnode_->SetAttributes(update));
}
zx::result<fio::wire::FilesystemInfo> Connection::NodeQueryFilesystem() const {
zx::result<FilesystemInfo> info = vfs_->GetFilesystemInfo();
if (info.is_error()) {
return info.take_error();
}
return zx::ok(info.value().ToFidl());
}
} // namespace fs::internal