blob: b64e6d0dde5a5470c83f36ceb2285b6c4ca67f38 [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 <fidl/>
#include <fidl/fuchsia.posix.socket.packet/cpp/wire.h>
#include <fidl/fuchsia.posix.socket.raw/cpp/wire.h>
#include <fidl/fuchsia.posix.socket/cpp/wire.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/limits.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/vfs.h>
#include <lib/zx/debuglog.h>
#include <lib/zxio/ops.h>
#include <lib/zxio/zxio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <threads.h>
#include <zircon/types.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/string_buffer.h>
#include "sdk/lib/fdio/cleanpath.h"
using fdio_ptr = fbl::RefPtr<fdio>;
// FDIO provides POSIX I/O functionality over various transports
// via the fdio_t interface abstraction.
// The "pipe" transport is a thin wrapper over Zircon sockets supporting
// vector read/write.
// The "socket_stream"/"socket_dgram" transports implement BSD sockets.
// The "remote" transport uses Zircon channels to implement POSIX files
// and directories.
// The "local" transport resolves and forwards open calls by looking up
// paths in a namespace.
// The "null" transport absorbs writes and is never readable.
// TODO( Eventually, with the exception of the "local" and "null"
// transport, the different transports should become an implementation detail
// in zxio.
struct Errno {
constexpr explicit Errno(int e) : e(e) {}
static constexpr int Ok = 0;
bool is_error() const { return e != 0; }
int e;
// fdio_t ioflag values
#define IOFLAG_CLOEXEC (1 << 0)
#define IOFLAG_EPOLL (1 << 2)
#define IOFLAG_WAITABLE (1 << 3)
#define IOFLAG_NONBLOCK (1 << 4)
// The subset of fdio_t per-fd flags queryable via fcntl.
// Static assertions in ensure we aren't colliding.
// Waits until one or more |events| are signalled, or the |deadline| passes.
// The |events| are of the form |FDIO_EVT_*|, defined in io.h.
// If not NULL, |out_pending| returns a bitmap of all observed events.
zx_status_t fdio_wait(const fdio_ptr& io, uint32_t events, zx::time deadline,
uint32_t* out_pending);
// Possibly return an owned fdio_t corresponding to either the root,
// the cwd, or, for the variants, dirfd. In the absolute path
// case, in_out_path is also adjusted.
fdio_ptr fdio_iodir(int dirfd, std::string_view& in_out_path);
zx::status<fdio_ptr> fdio_synchronous_datagram_socket_create(
zx::eventpair event, fidl::ClientEnd<fuchsia_posix_socket::SynchronousDatagramSocket> client);
zx::status<fdio_ptr> fdio_datagram_socket_create(
zx::socket socket, fidl::ClientEnd<fuchsia_posix_socket::DatagramSocket> client,
size_t tx_meta_buf_size, size_t rx_meta_buf_size);
zx::status<fdio_ptr> fdio_stream_socket_create(
zx::socket socket, fidl::ClientEnd<fuchsia_posix_socket::StreamSocket> client);
zx::status<fdio_ptr> fdio_raw_socket_create(
zx::eventpair event, fidl::ClientEnd<fuchsia_posix_socket_raw::Socket> client);
zx::status<fdio_ptr> fdio_packet_socket_create(
zx::eventpair event, fidl::ClientEnd<fuchsia_posix_socket_packet::Socket> client);
// Creates an |fdio_t| referencing the root of the |ns| namespace.
zx::status<fdio_ptr> fdio_ns_open_root(fdio_ns_t* ns);
// Change the root of the given namespace |ns| to match |io|.
// Does not take ownership of |io|. The caller is responsible for retaining a reference to |io|
// for the duration of this call and for releasing that reference after this function returns.
zx_status_t fdio_ns_set_root(fdio_ns_t* ns, fdio_t* io);
// Validates a |path| argument.
// Returns ZX_OK if |path| is non-null and less than |PATH_MAX| in length
// (excluding the null terminator). Upon success, the length of the path is
// returned via |out_length|.
// Otherwise, returns |ZX_ERR_INVALID_ARGS|.
zx_status_t fdio_validate_path(const char* path, size_t* out_length);
void fdio_chdir(fdio_ptr io, const char* path);
// Wraps an arbitrary handle with an object that works with wait hooks.
zx::status<fdio_ptr> fdio_waitable_create(std::variant<zx::handle, zx::unowned_handle> h,
zx_signals_t signals_in, zx_signals_t signals_out);
// Returns the sum of the capacities of all the entries in |vector|.
size_t fdio_iovec_get_capacity(const zx_iovec_t* vector, size_t vector_count);
// Copies bytes from |buffer| into |vector|.
// Returns the number of bytes copied in |out_actual|.
void fdio_iovec_copy_to(const uint8_t* buffer, size_t buffer_size, const zx_iovec_t* vector,
size_t vector_count, size_t* out_actual);
// Copies bytes from |vector| into |buffer|.
// Returns the number of bytes copied in |out_actual|.
void fdio_iovec_copy_from(const zx_iovec_t* vector, size_t vector_count, uint8_t* buffer,
size_t buffer_size, size_t* out_actual);
using two_path_op = zx_status_t(std::string_view src, zx_handle_t dst_token, std::string_view dst);
// Lifecycle notes:
// Upon creation, objects have a refcount of 1. |acquire| and |release| are used to upref and
// downref, respectively. Upon downref to 0, the object will be freed.
// The close hook must be called before free and should only be called once. In normal use, objects
// are accessed through the fdio_fdtab, and when close is called they are removed from the fdtab and
// the reference that the fdtab itself is holding is released, at which point they will be free()'d
// unless somebody is holding a ref due to an ongoing io transaction, which will certainly fail due
// to underlying handles being closed at which point a downref will happen and destruction will
// follow.
struct fdio : protected fbl::RefCounted<fdio>, protected fbl::Recyclable<fdio> {
static zx::status<fdio_ptr> create(zx::handle handle);
static zx::status<fdio_ptr> create(fidl::ClientEnd<fuchsia_io::Node> node,
fuchsia_io::wire::NodeInfo info);
// Uses || to obtain a ||.
static zx::status<fdio_ptr> create_with_describe(fidl::ClientEnd<fuchsia_io::Node> node);
// Waits for a || event on channel.
static zx::status<fdio_ptr> create_with_on_open(fidl::ClientEnd<fuchsia_io::Node> node);
virtual zx::status<fdio_ptr> open(std::string_view path, fuchsia_io::wire::OpenFlags flags,
uint32_t mode);
virtual zx_status_t clone(zx_handle_t* out_handle) = 0;
virtual zx_status_t add_inotify_filter(std::string_view path, uint32_t mask,
uint32_t watch_descriptor, zx::socket socket);
// |unwrap| releases the underlying handle if applicable. The caller must ensure there are no
// concurrent operations on |io|.
// For example, |fdio_fd_transfer| will call |fdio_unbind_from_fd| which will only succeed when
// the caller has the last unique reference to the |fdio_t|, thus ensuring that the fd is only
// transferred when there are no concurrent operations.
virtual zx_status_t unwrap(zx_handle_t* out_handle);
// |borrow_channel| borrows the underlying handle if applicable.
virtual zx_status_t borrow_channel(zx_handle_t* out_handle);
virtual void wait_begin(uint32_t events, zx_handle_t* out_handle, zx_signals_t* out_signals);
virtual void wait_end(zx_signals_t signals, uint32_t* out_events);
// |posix_ioctl| returns an |Errno|, which wraps an errno to be set on failure, or |Errno::Ok| (0)
// on success.
virtual Errno posix_ioctl(int req, va_list va);
virtual zx_status_t get_token(zx_handle_t* out);
virtual zx_status_t get_attr(zxio_node_attributes_t* out);
virtual zx_status_t set_attr(const zxio_node_attributes_t* attr);
virtual zx_status_t dirent_iterator_init(zxio_dirent_iterator_t* iterator, zxio_t* directory);
virtual zx_status_t dirent_iterator_next(zxio_dirent_iterator_t* iterator,
zxio_dirent_t* inout_entry);
virtual void dirent_iterator_destroy(zxio_dirent_iterator_t* iterator);
virtual zx_status_t watch_directory(zxio_watch_directory_cb cb, zx_time_t deadline,
void* context);
virtual zx_status_t unlink(std::string_view name, int flags);
virtual zx_status_t truncate(uint64_t off);
virtual two_path_op rename;
virtual two_path_op link;
virtual zx_status_t get_flags(fuchsia_io::wire::OpenFlags* out_flags);
virtual zx_status_t set_flags(fuchsia_io::wire::OpenFlags flags);
virtual zx_status_t bind(const struct sockaddr* addr, socklen_t addrlen, int16_t* out_code);
virtual zx_status_t connect(const struct sockaddr* addr, socklen_t addrlen, int16_t* out_code);
virtual zx_status_t listen(int backlog, int16_t* out_code);
virtual zx_status_t accept(int flags, struct sockaddr* addr, socklen_t* addrlen,
zx_handle_t* out_handle, int16_t* out_code);
virtual zx_status_t getsockname(struct sockaddr* addr, socklen_t* addrlen, int16_t* out_code);
virtual zx_status_t getpeername(struct sockaddr* addr, socklen_t* addrlen, int16_t* out_code);
virtual zx_status_t getsockopt(int level, int optname, void* optval, socklen_t* optlen,
int16_t* out_code);
virtual zx_status_t setsockopt(int level, int optname, const void* optval, socklen_t optlen,
int16_t* out_code);
virtual zx_status_t recvmsg(struct msghdr* msg, int flags, size_t* out_actual, int16_t* out_code);
virtual zx_status_t sendmsg(const struct msghdr* msg, int flags, size_t* out_actual,
int16_t* out_code);
virtual zx_status_t shutdown(int how, int16_t* out_code);
virtual bool is_local_dir() { return false; }
// |ioflag| contains mutable properties of this object, shared by
// different transports. Possible values are |IOFLAG_*| in private.h.
// TODO( The value of this field is not preserved when fdio_fd_transfer
// is used.
uint32_t& ioflag() { return ioflag_; }
// The zxio object, if the zxio transport is selected in |ops|.
zxio_storage_t& zxio_storage() { return storage_; }
// Used to implement SO_RCVTIMEO. See `man 7 socket` for details.
zx::duration& rcvtimeo() { return rcvtimeo_; }
// Used to implement SO_SNDTIMEO. See `man 7 socket` for details.
zx::duration& sndtimeo() { return sndtimeo_; }
// Automatically calls |fdio_t::Close| on drop.
struct last_reference {
explicit last_reference(fdio_t* ptr) : ptr_(ptr, deleter) {}
~last_reference() {
if (ptr_) {
last_reference(last_reference&& other) = default;
last_reference& operator=(last_reference&& other) = default;
fdio_t* ExportToRawPtr() { return ptr_.release(); }
zx_status_t unwrap(zx_handle_t* out_handle) { return ptr_->unwrap(out_handle); }
// Close and destroy the underlying object.
zx_status_t Close() { return std::exchange(ptr_, nullptr)->close(); }
// Custom deleter to keep the destructor buried.
std::unique_ptr<fdio_t, void (*)(fdio_t*)> ptr_;
// Helpers from the reference documentation for std::visit<>, to allow
// visit-by-overload of the std::variant<> returned by GetLastReference():
template <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
// explicit deduction guide (not needed as of C++20)
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
friend class fbl::internal::MakeRefCountedHelper<fdio>;
// TODO(tamird/johngro): can we bury ExportToRawPtr? The only user outside of |fdio_slot| and
// |last_reference| is |fdio_unsafe_fd_to_io|.
// TODO(tamird/johngro): can we bury ImportFromRawPtr? The only users outside of |fdio_slot| are
// |fdio_bind_to_fd| and |fdio_unsafe_release|.
friend class fbl::RefPtr<fdio>;
friend class fbl::Recyclable<fdio>;
// Returns the supplied |io| if it is not the last reference to the underlying
// resource.
friend std::variant<last_reference, fdio_ptr> GetLastReference(fdio_ptr io);
static void deleter(fdio_t* ptr) { delete ptr; }
fdio() = default;
virtual ~fdio();
void fbl_recycle() {
delete this;
virtual zx_status_t close() = 0;
uint32_t ioflag_ = 0;
zxio_storage_t storage_ = {};
zx::duration rcvtimeo_ = zx::duration::infinite();
zx::duration sndtimeo_ = zx::duration::infinite();
namespace fdio_internal {
using base = fdio_t;
zx_status_t fdio_open_at(fidl::UnownedClientEnd<fuchsia_io::Directory> directory,
std::string_view path, fuchsia_io::wire::OpenFlags flags,
fidl::ServerEnd<fuchsia_io::Node> request);
struct OpenAtOptions {
// Do not allow this open to resolve to a directory. If the operation would
// open a directory, instead generates a ZX_ERR_NOT_FILE error.
bool disallow_directory;
// Permit absolute path as input. If the provided path is absolute, instead
// generates a ZX_ERR_INVALID_ARGS error.
bool allow_absolute_path;
zx::status<fdio_ptr> open_at_impl(int dirfd, const char* path, fuchsia_io::wire::OpenFlags flags,
uint32_t mode, OpenAtOptions options);
} // namespace fdio_internal
// TODO(tamird): every operation on this type should require the global lock.
struct fdio_slot {
fdio_slot() = default;
fdio_ptr get() {
fdio_t** ptr = std::get_if<fdio_t*>(&inner_);
if (ptr != nullptr) {
return fbl::RefPtr(*ptr);
return nullptr;
fdio_ptr release() {
fdio_t** ptr = std::get_if<fdio_t*>(&inner_);
if (ptr != nullptr) {
fdio_ptr io = fbl::ImportFromRawPtr(*ptr);
inner_ = available{};
return io;
return nullptr;
bool try_set(fdio_ptr io) {
if (std::holds_alternative<available>(inner_)) {
inner_ = fbl::ExportToRawPtr(&io);
return true;
return false;
fdio_ptr replace(fdio_ptr io) {
auto previous = std::exchange(inner_, fbl::ExportToRawPtr(&io));
fdio_t** ptr = std::get_if<fdio_t*>(&previous);
if (ptr != nullptr) {
return fbl::ImportFromRawPtr(*ptr);
return nullptr;
std::optional<void (fdio_slot::*)()> try_reserve() {
if (std::holds_alternative<available>(inner_)) {
inner_ = reserved{};
return &fdio_slot::release_reservation;
return std::nullopt;
bool try_fill(fdio_ptr io) {
if (std::holds_alternative<reserved>(inner_)) {
inner_ = fbl::ExportToRawPtr(&io);
return true;
return false;
struct available {};
struct reserved {};
void release_reservation() {
if (std::holds_alternative<reserved>(inner_)) {
inner_ = available{};
// TODO(https::/ clang incorrectly rejects std::variant<.., fdio_ptr> as a
// non-literal type. When that is fixed, change this |fdio_t*| to |fdio_ptr|.
std::variant<available, reserved, fdio_t*> inner_;
using fdio_state_t = struct {
mtx_t lock;
mtx_t cwd_lock __TA_ACQUIRED_BEFORE(lock);
mode_t umask __TA_GUARDED(lock);
fdio_slot root __TA_GUARDED(lock);
fdio_slot cwd __TA_GUARDED(lock);
std::array<fdio_slot, FDIO_MAX_FD> fdtab __TA_GUARDED(lock);
fdio_ns_t* ns __TA_GUARDED(lock);
fdio_internal::PathBuffer cwd_path __TA_GUARDED(cwd_lock);
extern fdio_state_t __fdio_global_state;
#define fdio_lock (__fdio_global_state.lock)
#define fdio_root_handle (__fdio_global_state.root)
#define fdio_cwd_handle (__fdio_global_state.cwd)
#define fdio_cwd_lock (__fdio_global_state.cwd_lock)
#define fdio_cwd_path (__fdio_global_state.cwd_path)
#define fdio_fdtab (__fdio_global_state.fdtab)
#define fdio_root_ns (__fdio_global_state.ns)
template <class T>
zx::status<typename fidl::WireSyncClient<T>>& get_client() {
static zx::status<typename fidl::WireSyncClient<T>> client;
static std::once_flag once;
std::call_once(once, [&]() {
client = [&]() -> zx::status<typename fidl::WireSyncClient<T>> {
auto endpoints = fidl::CreateEndpoints<T>();
if (endpoints.is_error()) {
return endpoints.take_error();
zx_status_t status = fdio_service_connect_by_name(fidl::DiscoverableProtocolName<T>,
if (status != ZX_OK) {
return zx::error(status);
return zx::ok(fidl::BindSyncClient(std::move(endpoints->client)));
return client;