| // 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. |
| |
| #include "local-connection.h" |
| |
| #include <lib/zxio/null.h> |
| #include <lib/zxio/zxio.h> |
| #include <zircon/types.h> |
| |
| #include <atomic> |
| #include <new> |
| |
| #include <fbl/ref_ptr.h> |
| |
| #include "sdk/lib/fdio/internal.h" |
| #include "sdk/lib/fdio/namespace/local-filesystem.h" |
| #include "sdk/lib/fdio/namespace/local-vnode.h" |
| |
| namespace fdio_internal { |
| namespace { |
| |
| namespace fio = fuchsia_io; |
| |
| // The directory represents a local directory (either "/" or |
| // some directory between "/" and a mount point), so it has |
| // to emulate directory behavior. |
| struct LocalConnection { |
| LocalConnection(fbl::RefPtr<const fdio_namespace> fs, fbl::RefPtr<LocalVnode> vn); |
| ~LocalConnection() = default; |
| |
| // Hack to embed LocalConnection in the |storage| field of the |fdio_t| struct. |
| // See definition of |zxio_storage_t|, where |zxio_t io| is also the first element. |
| // This allows us to track extra state related to the local connection, as |
| // witnessed by the fields below. |
| zxio_t io; |
| |
| // The namespace instance, containing a directory tree terminating |
| // with various remote channels. |
| fbl::RefPtr<const fdio_namespace> fs; |
| |
| // The vnode corresponding to this directory. |vn| references some |
| // directory in |fs|. |
| fbl::RefPtr<LocalVnode> vn; |
| }; |
| |
| static_assert(offsetof(LocalConnection, io) == 0, "LocalConnection must be castable to zxio_t"); |
| static_assert(offsetof(zxio_storage_t, io) == 0, "LocalConnection must be castable to zxio_t"); |
| |
| static_assert(sizeof(LocalConnection) <= sizeof(zxio_storage_t), |
| "LocalConnection must fit inside zxio_storage_t."); |
| |
| LocalConnection::LocalConnection(fbl::RefPtr<const fdio_namespace> fs, fbl::RefPtr<LocalVnode> vn) |
| : fs(std::move(fs)), vn(std::move(vn)) {} |
| |
| struct local_connection : public fdio_t { |
| LocalConnection& local_dir() { return *reinterpret_cast<LocalConnection*>(&zxio_storage().io); } |
| |
| zx_status_t close(const bool should_wait) override { |
| auto& dir = local_dir(); |
| dir.~LocalConnection(); |
| return ZX_OK; |
| } |
| |
| zx_status_t clone(zx_handle_t* out_handle) override { return ZX_ERR_NOT_SUPPORTED; } |
| |
| // Expects a canonical path (no ..) with no leading |
| // slash and no trailing slash |
| zx::result<fdio_ptr> open(std::string_view path, fio::wire::OpenFlags flags) override { |
| auto& dir = local_dir(); |
| return dir.fs->Open(fbl::RefPtr(dir.vn), path, flags); |
| } |
| |
| zx_status_t get_attr(zxio_node_attributes_t* out) override { |
| zxio_node_attributes_t attr = {}; |
| ZXIO_NODE_ATTR_SET(attr, protocols, ZXIO_NODE_PROTOCOL_DIRECTORY); |
| ZXIO_NODE_ATTR_SET( |
| attr, abilities, |
| ZXIO_OPERATION_ENUMERATE | ZXIO_OPERATION_TRAVERSE | ZXIO_OPERATION_GET_ATTRIBUTES); |
| ZXIO_NODE_ATTR_SET(attr, link_count, 1); |
| *out = attr; |
| return ZX_OK; |
| } |
| |
| zx_status_t dirent_iterator_init(zxio_dirent_iterator_t* iterator, zxio_t* directory) override { |
| auto* dir_iterator = new (iterator) local_dir_dirent_iterator; |
| size_t capacity_of_one = sizeof(zxio_dirent_t) + fio::wire::kMaxFilename + 1; |
| dir_iterator->buffer = malloc(capacity_of_one); |
| dir_iterator->capacity = capacity_of_one; |
| return ZX_OK; |
| } |
| |
| zx_status_t dirent_iterator_next(zxio_dirent_iterator_t* iterator, |
| zxio_dirent_t* inout_entry) override { |
| auto& dir = local_dir(); |
| auto* dir_iterator = reinterpret_cast<local_dir_dirent_iterator*>(iterator); |
| return dir.fs->Readdir(*dir.vn, &dir_iterator->iterator_state, inout_entry); |
| } |
| |
| zx_status_t dirent_iterator_rewind(zxio_dirent_iterator_t* iterator) override { |
| auto* dir_iterator = reinterpret_cast<local_dir_dirent_iterator*>(iterator); |
| dir_iterator->iterator_state = DirentIteratorState(); |
| return ZX_OK; |
| } |
| |
| void dirent_iterator_destroy(zxio_dirent_iterator_t* iterator) override { |
| auto* dir_iterator = reinterpret_cast<local_dir_dirent_iterator*>(iterator); |
| free(dir_iterator->buffer); |
| } |
| |
| zx_status_t unlink(std::string_view name, int flags) override { return ZX_ERR_UNAVAILABLE; } |
| |
| bool is_local_dir() override { return true; } |
| |
| protected: |
| friend class fbl::internal::MakeRefCountedHelper<local_connection>; |
| friend class fbl::RefPtr<local_connection>; |
| |
| local_connection() = default; |
| ~local_connection() override = default; |
| |
| private: |
| struct local_dir_dirent_iterator { |
| // Buffer for storing dirents. |
| void* buffer; |
| |
| // Size of |buffer|. |
| size_t capacity; |
| |
| // Used by |Readdir| to resume from the middle of a directory. |
| DirentIteratorState iterator_state; |
| }; |
| }; |
| |
| } // namespace |
| |
| zx::result<fdio_ptr> CreateLocalConnection(fbl::RefPtr<const fdio_namespace> fs, |
| fbl::RefPtr<LocalVnode> vn) { |
| fdio_ptr io = fbl::MakeRefCounted<local_connection>(); |
| if (io == nullptr) { |
| return zx::error(ZX_ERR_NO_MEMORY); |
| } |
| // Invoke placement new on the new LocalConnection. It will be destroyed in close. |
| zxio_storage_t& storage = io->zxio_storage(); |
| new (&storage) LocalConnection(std::move(fs), std::move(vn)); |
| zxio_default_init(&storage.io); |
| |
| return zx::ok(io); |
| } |
| |
| fbl::RefPtr<LocalVnode> GetLocalNodeFromConnectionIfAny(fdio_t* io) { |
| if (!io->is_local_dir()) { |
| return nullptr; |
| } |
| return fbl::RefPtr<LocalVnode>(reinterpret_cast<local_connection*>(io)->local_dir().vn); |
| } |
| |
| } // namespace fdio_internal |