blob: 6ae29ea2401e271b4dbc27d370430089dc72d15b [file] [log] [blame]
// Copyright 2017 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.
#pragma once
#ifndef __Fuchsia__
#error "Fuchsia-only header"
#endif
#include <stdint.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/unique_ptr.h>
#include <fs/vfs.h>
#include <fs/vnode.h>
#include <fuchsia/io/c/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/zx/event.h>
#include <zircon/fidl.h>
namespace fs {
constexpr zx_signals_t kLocalTeardownSignal = ZX_USER_SIGNAL_1;
// A one-way message which may be emitted by the server without an
// accompanying request. Optionally used as a part of the Open handshake.
struct OnOpenMsg {
fuchsia_io_NodeOnOpenEvent primary;
fuchsia_io_NodeInfo extra;
};
// Connection represents an open connection to a Vnode (the server-side component
// of a file descriptor). Connections will be managed in a |fbl::DoublyLinkedList|.
// The Vnode's methods will be invoked in response to FIDL protocol messages
// received over the channel.
//
// This class is thread-safe.
class Connection : public fbl::DoublyLinkedListable<fbl::unique_ptr<Connection>> {
public:
// Create a connection bound to a particular vnode.
//
// The VFS will be notified when remote side closes the connection.
//
// |vfs| is the VFS which is responsible for dispatching operations to the vnode.
// |vnode| is the vnode which will handle I/O requests.
// |channel| is the channel on which the FIDL protocol will be served.
// |flags| are the file flags passed to |open()|, such as |ZX_FS_FLAG_VNODE_REF_ONLY|;
// they include rights granted to this connection, such as |ZX_FS_RIGHT_READABLE|.
Connection(fs::Vfs* vfs, fbl::RefPtr<fs::Vnode> vnode, zx::channel channel, uint32_t flags);
// Closes the connection.
//
// The connection must not be destroyed if its wait handler is running
// concurrently on another thread.
//
// In practice, this means the connection must have already been remotely
// closed, or it must be destroyed on the wait handler's dispatch thread
// to prevent a race.
virtual ~Connection();
// Set a signal on the channel which causes it to be torn down and
// closed asynchronously.
void AsyncTeardown();
// Explicitly tear down and close the connection synchronously.
void SyncTeardown();
// Begins waiting for messages on the channel.
//
// Must be called at most once in the lifetime of the connection.
zx_status_t Serve();
// Node Operations.
zx_status_t NodeClone(uint32_t flags, zx_handle_t object);
zx_status_t NodeClose(fidl_txn_t* txn);
zx_status_t NodeDescribe(fidl_txn_t* txn);
zx_status_t NodeSync(fidl_txn_t* txn);
zx_status_t NodeGetAttr(fidl_txn_t* txn);
zx_status_t NodeSetAttr(uint32_t flags,
const fuchsia_io_NodeAttributes* attributes,
fidl_txn_t* txn);
zx_status_t NodeIoctl(uint32_t opcode, uint64_t max_out,
const zx_handle_t* handles_data, size_t handles_count,
const uint8_t* in_data, size_t in_count, fidl_txn_t* txn);
// File Operations.
zx_status_t FileRead(uint64_t count, fidl_txn_t* txn);
zx_status_t FileReadAt(uint64_t count, uint64_t offset, fidl_txn_t* txn);
zx_status_t FileWrite(const uint8_t* data_data, size_t data_count, fidl_txn_t* txn);
zx_status_t FileWriteAt(const uint8_t* data_data, size_t data_count,
uint64_t offset, fidl_txn_t* txn);
zx_status_t FileSeek(int64_t offset, fuchsia_io_SeekOrigin start, fidl_txn_t* txn);
zx_status_t FileTruncate(uint64_t length, fidl_txn_t* txn);
zx_status_t FileGetFlags(fidl_txn_t* txn);
zx_status_t FileSetFlags(uint32_t flags, fidl_txn_t* txn);
zx_status_t FileGetBuffer(uint32_t flags, fidl_txn_t* txn);
// Directory Operations.
zx_status_t DirectoryOpen(uint32_t flags, uint32_t mode, const char* path_data,
size_t path_size, zx_handle_t object);
zx_status_t DirectoryUnlink(const char* path_data, size_t path_size, fidl_txn_t* txn);
zx_status_t DirectoryReadDirents(uint64_t max_out, fidl_txn_t* txn);
zx_status_t DirectoryRewind(fidl_txn_t* txn);
zx_status_t DirectoryGetToken(fidl_txn_t* txn);
zx_status_t DirectoryRename(const char* src_data, size_t src_size, zx_handle_t dst_parent_token,
const char* dst_data, size_t dst_size, fidl_txn_t* txn);
zx_status_t DirectoryLink(const char* src_data, size_t src_size, zx_handle_t dst_parent_token,
const char* dst_data, size_t dst_size, fidl_txn_t* txn);
zx_status_t DirectoryWatch(uint32_t mask, uint32_t options, zx_handle_t watcher,
fidl_txn_t* txn);
// DirectoryAdmin Operations.
zx_status_t DirectoryAdminMount(zx_handle_t remote, fidl_txn_t* txn);
zx_status_t DirectoryAdminMountAndCreate(zx_handle_t remote, const char* name, size_t name_size,
uint32_t flags, fidl_txn_t* txn);
zx_status_t DirectoryAdminUnmount(fidl_txn_t* txn);
zx_status_t DirectoryAdminUnmountNode(fidl_txn_t* txn);
zx_status_t DirectoryAdminQueryFilesystem(fidl_txn_t* txn);
zx_status_t DirectoryAdminGetDevicePath(fidl_txn_t* txn);
protected:
// Dispatches incoming FIDL messages which aren't recognized by
// |HandleMessage|.
//
// Takes ownership of the FIDL message's handles.
// The default implementation just closes these handles.
//
// This implementation may be overridden to support additional non-VFS
// FIDL protocols.
virtual zx_status_t HandleFsSpecificMessage(fidl_msg_t* msg, fidl_txn_t* txn);
// Acquires the vnode so subclasses of Connection can cast and dispatch
// the Vnode to more specific subclasses.
fs::Vnode& GetVnode() const { return *vnode_.get(); }
private:
// Callback for when new signals arrive on the channel, which could be:
// readable, peer closed, async teardown request, etc.
void HandleSignals(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal);
// Closes the connection and unregisters it from the VFS object.
void Terminate(bool call_close);
// Sends an explicit close message to the underlying vnode.
// Only necessary if the handler has not returned ERR_DISPATCHER_DONE
// and has been opened.
void CallClose();
// Dispatches incoming FIDL messages.
//
// By default, handles the Node, File, Directory and DirectoryAdmin
// protocols, dispatching to |HandleFsSpecificMessage| if the ordinal is not recognized.
zx_status_t HandleMessage(fidl_msg_t* msg, fidl_txn_t* txn);
bool is_open() const { return wait_.object() != ZX_HANDLE_INVALID; }
void set_closed() { wait_.set_object(ZX_HANDLE_INVALID); }
fs::Vfs* const vfs_;
fbl::RefPtr<fs::Vnode> vnode_;
// Channel on which the connection is being served.
zx::channel channel_;
// Asynchronous wait for incoming messages.
// The object field is |ZX_HANDLE_INVALID| when not actively waiting.
async::WaitMethod<Connection, &Connection::HandleSignals> wait_;
// Open flags such as |ZX_FS_RIGHT_READABLE|, |ZX_FS_FLAG_VNODE_REF_ONLY|, and other bits.
// Permissions on the underlying Vnode are granted on a per-connection basis,
// and expressed as a combination of ZX_FS_RIGHT flags.
// Importantly, ZX_FS_RIGHT flags are hierarchical over Open/Clone. It is never allowed
// to derive a Connection with more rights than the originating connection.
uint32_t flags_;
// Handle to event which allows client to refer to open vnodes in multi-path
// operations (see: link, rename). Defaults to ZX_HANDLE_INVALID.
// Validated on the server-side using cookies.
zx::event token_{};
// Directory cookie for readdir operations.
fs::vdircookie_t dircookie_{};
// Current seek offset.
size_t offset_{};
};
} // namespace fs