blob: 0836b12099167656cc903a61b36d0d20fc1fd836 [file] [log] [blame] [edit]
// 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.
#ifndef LIB_FDIO_INTERNAL_H_
#define LIB_FDIO_INTERNAL_H_
#include <fuchsia/hardware/pty/llcpp/fidl.h>
#include <fuchsia/io/llcpp/fidl.h>
#include <fuchsia/posix/socket/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/limits.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>
using fdio_ns_t = struct fdio_namespace;
// 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(fxbug.dev/43267): 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)
// Socket is connecting to the peer.
#define IOFLAG_SOCKET_CONNECTING (1 << 4)
// Socket is connected to the peer.
#define IOFLAG_SOCKET_CONNECTED (1 << 5)
// Socket is operating in non-blocking mode.
#define IOFLAG_NONBLOCK (1 << 6)
// Socket has an error signal asserted.
#define IOFLAG_SOCKET_HAS_ERROR (1 << 7)
// Socket is listening for new connections.
#define IOFLAG_SOCKET_LISTENING (1 << 8)
// The subset of fdio_t per-fd flags queryable via fcntl.
// Static assertions in unistd.cc ensure we aren't colliding.
#define IOFLAG_FD_FLAGS IOFLAG_CLOEXEC
// 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(fdio_t* io, uint32_t events, zx::time deadline, uint32_t* out_pending);
// Wraps a channel with an fdio_t using remote io.
fdio_t* fdio_remote_create(fidl::ClientEnd<fuchsia_io::Node> node, zx::eventpair event);
// Creates an |fdio_t| from a remote directory connection.
fdio_t* fdio_dir_create(fidl::ClientEnd<fuchsia_io::Directory> dir);
// Creates an |fdio_t| from a remote file connection.
fdio_t* fdio_file_create(fidl::ClientEnd<fuchsia_io::File> file, zx::event event,
zx::stream stream);
// Creates an |fdio_t| from a remote PTY connection.
fdio_t* fdio_pty_create(fidl::ClientEnd<fuchsia_hardware_pty::Device> device, zx::eventpair event);
// Creates a pipe backed by a socket.
fdio_t* fdio_pipe_create(zx::socket socket);
// Creates an |fdio_t| from a VMO and a stream.
//
// The stream should be backed by the given VMO.
fdio_t* fdio_vmo_create(zx::vmo vmo, zx::stream stream);
// Creates an |fdio_t| for a VMO file.
//
// * |vmo| is the VMO that contains the contents of the file.
// * |offset| is the index of the first byte of the file in the VMO.
// * |length| is the number of bytes in the file.
// * |seek| is the initial seek offset within the file (i.e., relative to
// |offset| within the underlying VMO).
fdio_t* fdio_vmofile_create(fidl::ClientEnd<fuchsia_io::File> file, zx::vmo vmo, zx_off_t offset,
zx_off_t length, zx_off_t seek);
fdio_t* fdio_datagram_socket_create(zx::eventpair event,
fidl::ClientEnd<fuchsia_posix_socket::DatagramSocket> client);
fdio_t* fdio_stream_socket_create(zx::socket socket,
fidl::ClientEnd<fuchsia_posix_socket::StreamSocket> client,
zx_info_socket_t info);
// Creates a message port and pair of simple io fdio_t's
zx_status_t fdio_pipe_pair(fdio_t** a, fdio_t** b, uint32_t options);
// Creates an |fdio_t| referencing the root of the |ns| namespace.
fdio_t* 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);
// Create an |fdio_t| from a |node| and an |info|.
//
// Uses |info| to determine what kind of |fdio_t| to create.
//
// Upon success, |out_io| receives ownership of all handles.
//
// Upon failure, consumes all handles.
zx_status_t fdio_from_node_info(fidl::ClientEnd<fuchsia_io::Node> node,
fuchsia_io::wire::NodeInfo info, fdio_t** out_io);
// Creates an |fdio_t| from a |node|.
zx_status_t fdio_from_channel(fidl::ClientEnd<fuchsia_io::Node> node, fdio_t** out_io);
// Creates an |fdio_t| by waiting for a |fuchsia.io/Node.OnOpen| event on |channel|.
//
// Uses the contents of the event to determine what kind of |fdio_t| to create.
//
// Upon success, |out_io| receives ownership of all handles.
//
// Upon failure, consumes all handles.
zx_status_t fdio_from_on_open_event(fidl::ClientEnd<fuchsia_io::Node> client_end, fdio_t** out_io);
// io will be consumed by this and must not be shared
void fdio_chdir(fdio_t* io, const char* path);
// Wraps an arbitrary handle with a fdio_t that works with wait hooks.
// Takes ownership of handle unless shared_handle is true.
fdio_t* fdio_waitable_create(zx_handle_t h, zx_signals_t signals_in, zx_signals_t signals_out,
bool shared_handle);
// 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(const char* src, size_t srclen, zx_handle_t dst_token,
const char* dst, size_t dstlen);
namespace fdio_internal {
template <typename T>
class AllocHelper final {
public:
template <typename... Args>
static fdio_t* alloc(Args&&... args) {
return new T(std::forward<Args>(args)...);
}
};
template <typename T, typename... Args>
fdio_t* alloc(Args&&... args) {
return AllocHelper<T>::alloc(std::forward<Args>(args)...);
}
} // namespace fdio_internal
// 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 {
virtual zx_status_t close() = 0;
virtual zx_status_t open(const char* path, uint32_t flags, uint32_t mode, fdio_t** out);
virtual zx_status_t clone(zx_handle_t* out_handle);
// |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 uint32_t convert_to_posix_mode(zxio_node_protocols_t protocols,
zxio_abilities_t abilities);
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** out_entry);
virtual void dirent_iterator_destroy(zxio_dirent_iterator_t* iterator);
virtual zx_status_t unlink(const char* path, size_t len);
virtual zx_status_t truncate(off_t off);
virtual two_path_op rename;
virtual two_path_op link;
virtual zx_status_t get_flags(uint32_t* out_flags);
virtual zx_status_t set_flags(uint32_t 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.
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_; }
void acquire() { refcount_.fetch_add(1); }
zx_status_t release() {
if (refcount_.fetch_sub(1) == 1) {
zx_status_t status = close();
delete this;
return status;
}
return ZX_OK;
}
bool is_last_reference() { return refcount_.load() == 1; }
protected:
friend class fdio_internal::AllocHelper<fdio>;
fdio() = default;
virtual ~fdio();
private:
// The number of references on this object. Note that each appearance
// in the fd table counts as one reference on the corresponding object.
// Ongoing operations will also contribute to the refcount.
std::atomic_int_fast32_t refcount_ = 1;
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;
} // namespace fdio_internal
using fdio_available = struct {};
using fdio_reserved = struct {};
using fdio_state_t = struct {
mtx_t lock;
mtx_t cwd_lock __TA_ACQUIRED_BEFORE(lock);
mode_t umask __TA_GUARDED(lock);
fdio_t* root __TA_GUARDED(lock);
fdio_t* cwd __TA_GUARDED(lock);
std::array<std::variant<fdio_available, fdio_reserved, fdio_t*>, FDIO_MAX_FD> fdtab
__TA_GUARDED(lock);
fdio_ns_t* ns __TA_GUARDED(lock);
char cwd_path[PATH_MAX] __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)
// Returns an fd number greater than or equal to |starting_fd|, following the
// same rules as fdio_bind_fd. If there are no free file descriptors, -1 is
// returned and |errno| is set to EMFILE. The returned |fd| is not valid for
// use outside of |fdio_assign_reserved| and |fdio_release_reserved|.
int fdio_reserve_fd(int starting_fd);
// Assign the given |io| to the reserved |fd|. If |fd| is not reserved, then -1
// is returned and errno is set to EINVAL.
int fdio_assign_reserved(int fd, fdio_t* io);
// Unassign the reservation at |fd|. If |fd| does not resolve to a reservation
// then -1 is returned and errno is set to EINVAL, otherwise |fd| is returned.
int fdio_release_reserved(int fd);
template <class T>
zx::status<typename T::SyncClient>& get_client() {
static zx::status<typename T::SyncClient> client;
static std::once_flag once;
std::call_once(once, [&]() {
client = [&]() -> zx::status<typename T::SyncClient> {
auto endpoints = fidl::CreateEndpoints<T>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
zx_status_t status =
fdio_service_connect_by_name(T::Name, endpoints->server.channel().release());
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(fidl::BindSyncClient(std::move(endpoints->client)));
}();
});
return client;
}
zx::status<fuchsia_posix_socket::Provider::SyncClient>& fdio_get_socket_provider();
#endif // LIB_FDIO_INTERNAL_H_