blob: fd9e568682060134a78e01c39795d50207c45d7f [file] [log] [blame]
// Copyright 2016 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/vnode.h"
#include <fidl/fuchsia.io/cpp/common_types.h>
#include <fidl/fuchsia.io/cpp/natural_types.h>
#include <lib/zx/result.h>
#include <limits.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <string_view>
#include <fbl/ref_ptr.h>
#include "src/storage/lib/vfs/cpp/vfs.h"
#include "src/storage/lib/vfs/cpp/vfs_types.h"
#ifdef __Fuchsia__
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fidl/cpp/wire/channel.h>
#include <lib/fidl/cpp/wire/string_view.h>
#include <lib/file-lock/file-lock.h>
#include <lib/zx/channel.h>
#include <lib/zx/stream.h>
#include <lib/zx/vmo.h>
#include <zircon/availability.h>
#include <map>
#include <memory>
#include <utility>
#include "src/storage/lib/vfs/cpp/fuchsia_vfs.h"
#endif // __Fuchsia__
namespace fio = fuchsia_io;
namespace fs {
#ifdef __Fuchsia__
std::mutex Vnode::gLockAccess;
std::map<const Vnode*, std::shared_ptr<file_lock::FileLock>> Vnode::gLockMap;
#endif
#ifdef __Fuchsia__
Vnode::~Vnode() {
ZX_DEBUG_ASSERT_MSG(!gLockMap.contains(this), "lock entry in gLockMap not cleaned up for Vnode");
}
#else
Vnode::~Vnode() = default;
#endif
#ifdef __Fuchsia__
zx::result<zx::stream> Vnode::CreateStream(uint32_t stream_options) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx_status_t Vnode::ConnectService(zx::channel channel) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t Vnode::WatchDir(FuchsiaVfs* vfs, fio::wire::WatchMask mask, uint32_t options,
fidl::ServerEnd<fuchsia_io::DirectoryWatcher> watcher) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Vnode::GetVmo(fuchsia_io::wire::VmoFlags flags, zx::vmo* out_vmo) {
return ZX_ERR_NOT_SUPPORTED;
}
void Vnode::DeprecatedOpenRemote(fuchsia_io::OpenFlags, fuchsia_io::ModeType, fidl::StringView,
fidl::ServerEnd<fuchsia_io::Node>) const {
ZX_PANIC("OpenRemote should only be called on remote nodes!");
}
#if FUCHSIA_API_LEVEL_AT_LEAST(27)
void Vnode::OpenRemote(fuchsia_io::wire::DirectoryOpenRequest request) const {
ZX_PANIC("OpenRemote should only be called on remote nodes!");
}
#else
void Vnode::OpenRemote(fuchsia_io::wire::DirectoryOpen3Request request) const {
ZX_PANIC("OpenRemote should only be called on remote nodes!");
}
#endif // FUCHSIA_API_LEVEL_AT_LEAST(27)
std::shared_ptr<file_lock::FileLock> Vnode::GetVnodeFileLock() {
std::lock_guard lock_access(gLockAccess);
auto lock = gLockMap.find(this);
if (lock == gLockMap.end()) {
auto inserted = gLockMap.emplace(std::pair(this, std::make_shared<file_lock::FileLock>()));
if (inserted.second) {
lock = inserted.first;
} else {
return nullptr;
}
}
return lock->second;
}
bool Vnode::DeleteFileLock(zx_koid_t owner) {
std::lock_guard lock_access(gLockAccess);
bool deleted = false;
auto lock = gLockMap.find(this);
if (lock != gLockMap.end()) {
deleted = lock->second->Forget(owner);
if (lock->second->NoLocksHeld()) {
gLockMap.erase(this);
}
}
return deleted;
}
// There is no guard here, as the connection is in teardown.
bool Vnode::DeleteFileLockInTeardown(zx_koid_t owner) {
if (!gLockMap.contains(this)) {
return false;
}
return DeleteFileLock(owner);
}
#endif // __Fuchsia__
bool Vnode::ValidateRights([[maybe_unused]] fuchsia_io::Rights rights) const { return true; }
zx::result<> Vnode::DeprecatedValidateOptions(DeprecatedOptions options) const {
// The connection should ensure only one of DIRECTORY and NOT_DIRECTORY is set.
ZX_DEBUG_ASSERT(!((options.flags & fuchsia_io::OpenFlags::kDirectory) &&
options.flags & fuchsia_io::OpenFlags::kNotDirectory));
if (!Supports(options.protocols())) {
if (options.protocols() & fuchsia_io::NodeProtocolKinds::kDirectory) {
return zx::error(ZX_ERR_NOT_DIR);
}
return zx::error(ZX_ERR_NOT_FILE);
}
if (!ValidateRights(options.rights)) {
return zx::error(ZX_ERR_ACCESS_DENIED);
}
return zx::ok();
}
zx_status_t Vnode::Open(fbl::RefPtr<Vnode>* out_redirect) {
{
std::lock_guard lock(mutex_);
open_count_++;
}
if (zx_status_t status = OpenNode(out_redirect); status != ZX_OK) {
// Roll back the open count since we won't get a close for it.
std::lock_guard lock(mutex_);
open_count_--;
return status;
}
return ZX_OK;
}
zx_status_t Vnode::Close() {
{
std::lock_guard lock(mutex_);
open_count_--;
}
return CloseNode();
}
zx_status_t Vnode::Read(void* data, size_t len, size_t off, size_t* out_actual) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Vnode::Write(const void* data, size_t len, size_t offset, size_t* out_actual) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Vnode::Append(const void* data, size_t len, size_t* out_end, size_t* out_actual) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Vnode::Lookup(std::string_view name, fbl::RefPtr<Vnode>* out) {
return ZX_ERR_NOT_SUPPORTED;
}
zx::result<fs::VnodeAttributes> Vnode::GetAttributes() const {
// Return the empty set of attributes by default.
return zx::ok(fs::VnodeAttributes{});
}
zx::result<> Vnode::UpdateAttributes(const VnodeAttributesUpdate&) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx_status_t Vnode::Readdir(VdirCookie* cookie, void* dirents, size_t len, size_t* out_actual) {
return ZX_ERR_NOT_SUPPORTED;
}
zx::result<fbl::RefPtr<Vnode>> Vnode::Create(std::string_view name, CreationType type) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx_status_t Vnode::Unlink(std::string_view name, bool must_be_dir) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t Vnode::Truncate(size_t len) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t Vnode::Rename(fbl::RefPtr<Vnode> newdir, std::string_view oldname,
std::string_view newname, bool src_must_be_dir, bool dst_must_be_dir) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Vnode::Link(std::string_view name, fbl::RefPtr<Vnode> target) {
return ZX_ERR_NOT_SUPPORTED;
}
void Vnode::Sync(SyncCallback closure) { closure(ZX_ERR_NOT_SUPPORTED); }
bool Vnode::IsRemote() const { return false; }
fio::Abilities Vnode::GetAbilities() const {
fio::Abilities abilities = fio::Abilities::kGetAttributes;
if (SupportedMutableAttributes()) {
abilities |= fio::Abilities::kUpdateAttributes;
}
fio::NodeProtocolKinds protocols = GetProtocols();
if (protocols & fio::NodeProtocolKinds::kDirectory) {
abilities |=
fio::Abilities::kModifyDirectory | fio::Abilities::kTraverse | fio::Abilities::kEnumerate;
}
if (protocols & fio::NodeProtocolKinds::kFile) {
abilities |= fio::Abilities::kReadBytes | fio::Abilities::kWriteBytes;
}
return abilities;
}
DirentFiller::DirentFiller(void* ptr, size_t len)
: ptr_(static_cast<char*>(ptr)), pos_(0), len_(len) {}
zx_status_t DirentFiller::Next(std::string_view name, fio::DirentType type, uint64_t ino) {
fs::DirectoryEntry* de = reinterpret_cast<fs::DirectoryEntry*>(ptr_ + pos_);
size_t sz = sizeof(fs::DirectoryEntry) + name.length();
if (sz > len_ - pos_ || name.length() > NAME_MAX) {
return ZX_ERR_INVALID_ARGS;
}
de->ino = ino;
de->name_length = static_cast<uint8_t>(name.length());
de->type = type;
memcpy(de->name, name.data(), name.length());
pos_ += sz;
return ZX_OK;
}
} // namespace fs