| // 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 |