blob: 43c9c58b39b18d29927b53bfd24e05027461d893 [file] [log] [blame]
// 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