| // 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 <fidl/fuchsia.io/cpp/wire.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/zx/result.h> |
| #include <lib/zxio/zxio.h> |
| #include <sys/socket.h> |
| #include <zircon/types.h> |
| |
| #include <variant> |
| |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| |
| namespace fdio_internal { |
| // Helper from the reference documentation for std::visit<> to allow visit-by-overload of |
| // std::variant<>. |
| template <class... Ts> |
| struct overloaded : Ts... { |
| using Ts::operator()...; |
| }; |
| } // namespace fdio_internal |
| |
| 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(https://fxbug.dev/42119552): 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 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(const fdio_ptr& io, uint32_t events, zx::time deadline, |
| uint32_t* out_pending); |
| |
| // 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); |
| |
| // Wraps an arbitrary handle with an object that works with wait hooks. |
| zx::result<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> { |
| template <typename F> |
| static zx::result<fdio_ptr> create(F fn) { |
| void* context = nullptr; |
| return create(context, fn(zxio_allocator, &context)); |
| } |
| static zx::result<fdio_ptr> create(zx::handle handle); |
| static zx::result<fdio_ptr> create(fidl::ClientEnd<fuchsia_io::Node> node, |
| fuchsia_io::wire::Representation representation); |
| virtual zx::result<fdio_ptr> open(std::string_view path, fuchsia_io::Flags flags); |
| virtual zx_status_t clone(zx_handle_t* out_handle) = 0; |
| |
| // |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 zx_status_t dirent_iterator_rewind(zxio_dirent_iterator_t* iterator); |
| 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::Flags* out_flags); |
| virtual zx_status_t set_flags(fuchsia_io::wire::Flags flags); |
| virtual zx_status_t get_flags_deprecated(fuchsia_io::wire::OpenFlags* out_flags); |
| virtual zx_status_t set_flags_deprecated(fuchsia_io::wire::OpenFlags flags); |
| 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 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(https://fxbug.dev/42155608): 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_; } |
| const zxio_storage_t& zxio_storage() const { 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 { |
| public: |
| explicit last_reference(fdio_t* ptr) : ptr_(ptr, deleter) {} |
| ~last_reference() { |
| if (ptr_) { |
| ptr_->close(/*should_wait=*/false); |
| } |
| } |
| |
| 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(const bool should_wait) { |
| return std::exchange(ptr_, nullptr)->close(should_wait); |
| } |
| |
| private: |
| // Custom deleter to keep the destructor buried. |
| std::unique_ptr<fdio_t, void (*)(fdio_t*)> ptr_; |
| }; |
| |
| protected: |
| 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() { |
| close(/*should_wait=*/false); |
| delete this; |
| } |
| |
| private: |
| static zx_status_t zxio_allocator(zxio_object_type_t type, zxio_storage_t** out_storage, |
| void** out_context); |
| |
| static zx::result<fdio_ptr> create(void*& context, zx_status_t status); |
| |
| virtual zx_status_t close(bool should_wait) = 0; |
| |
| uint32_t ioflag_ = 0; |
| |
| zxio_storage_t storage_ = {}; |
| |
| zx::duration rcvtimeo_ = zx::duration::infinite(); |
| |
| zx::duration sndtimeo_ = zx::duration::infinite(); |
| }; |
| |
| #endif // LIB_FDIO_INTERNAL_H_ |