VFS: nonblocking interface

The VFS library can be used in single-threaded or multi-threaded
environments and depending on that, signals are handled by the same thread
which uses the VFS library or possibly by a different thread. If a VFS
plugin needs to block to wait for a signal, there is currently no way
which works reliably in both environments.

For this reason, this commit makes the interface of the VFS library
nonblocking, similar to the File_system session interface.

The most important changes are:

- Directories are created and opened with the 'opendir()' function and the
  directory entries are read with the recently introduced 'queue_read()'
  and 'complete_read()' functions.

- Symbolic links are created and opened with the 'openlink()' function and
  the link target is read with the 'queue_read()' and 'complete_read()'
  functions and written with the 'write()' function.

- The 'write()' function does not wait for signals anymore. This can have
  the effect that data written by a VFS library user has not been
  processed by a file system server yet when the library user asks for the
  size of the file or closes it (both done with RPC functions at the file
  system server). For this reason, a user of the VFS library should
  request synchronization before calling 'stat()' or 'close()'. To make
  sure that a file system server has processed all write request packets
  which a client submitted before the synchronization request,
  synchronization is now requested at the file system server with a
  synchronization packet instead of an RPC function. Because of this
  change, the synchronization interface of the VFS library is now split
  into 'queue_sync()' and 'complete_sync()' functions.

Fixes #2399
This commit is contained in:
Christian Prochaska
2017-08-15 20:51:53 +02:00
committed by Christian Helmuth
parent 8312950e2f
commit b0935ef9b2
49 changed files with 4361 additions and 2307 deletions

View File

@ -56,8 +56,19 @@ class Fs_log::Session_component : public Genode::Rpc_object<Genode::Log_session>
~Session_component()
{
_fs.sync(_handle);
_fs.close(_handle);
/* sync */
File_system::Session::Tx::Source &source = *_fs.tx();
File_system::Packet_descriptor packet = source.get_acked_packet();
if (packet.operation() == File_system::Packet_descriptor::SYNC)
_fs.close(packet.handle());
packet = File_system::Packet_descriptor(
packet, _handle, File_system::Packet_descriptor::SYNC, 0, 0);
source.submit_packet(packet);
}
@ -78,9 +89,13 @@ class Fs_log::Session_component : public Genode::Rpc_object<Genode::Log_session>
File_system::Session::Tx::Source &source = *_fs.tx();
File_system::Packet_descriptor packet(
source.get_acked_packet(),
_handle, File_system::Packet_descriptor::WRITE,
File_system::Packet_descriptor packet = source.get_acked_packet();
if (packet.operation() == File_system::Packet_descriptor::SYNC)
_fs.close(packet.handle());
packet = File_system::Packet_descriptor(
packet, _handle, File_system::Packet_descriptor::WRITE,
msg_len, File_system::SEEK_TAIL);
char *buf = source.packet_content(packet);

View File

@ -34,25 +34,29 @@ namespace Fs_report {
typedef Genode::Path<Session_label::capacity()> Path;
static bool create_parent_dir(Vfs::Directory_service &vfs, Path const &child)
static bool create_parent_dir(Vfs::Directory_service &vfs, Path const &child,
Genode::Allocator &alloc)
{
typedef Vfs::Directory_service::Mkdir_result Mkdir_result;
typedef Vfs::Directory_service::Opendir_result Opendir_result;
Path parent = child;
parent.strip_last_element();
if (parent == "/")
return true;
Mkdir_result res = vfs.mkdir(parent.base(), 0);
if (res == Mkdir_result::MKDIR_ERR_NO_ENTRY) {
if (!create_parent_dir(vfs, parent))
Vfs_handle *dir_handle;
Opendir_result res = vfs.opendir(parent.base(), true, &dir_handle, alloc);
if (res == Opendir_result::OPENDIR_ERR_LOOKUP_FAILED) {
if (!create_parent_dir(vfs, parent, alloc))
return false;
res = vfs.mkdir(parent.base(), 0);
res = vfs.opendir(parent.base(), true, &dir_handle, alloc);
}
switch (res) {
case Mkdir_result::MKDIR_OK:
case Mkdir_result::MKDIR_ERR_EXISTS:
case Opendir_result::OPENDIR_OK:
vfs.close(dir_handle);
return true;
case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS:
return true;
default:
return false;
@ -67,7 +71,8 @@ class Fs_report::Session_component : public Genode::Rpc_object<Report::Session>
Path _leaf_path;
Attached_ram_dataspace _ds;
Attached_ram_dataspace _ds;
Genode::Entrypoint &_ep;
Vfs_handle *_handle;
file_size _file_size = 0;
@ -80,13 +85,13 @@ class Fs_report::Session_component : public Genode::Rpc_object<Report::Session>
Vfs::File_system &vfs,
Genode::Session_label const &label,
size_t buffer_size)
: _ds(env.ram(), env.rm(), buffer_size)
: _ds(env.ram(), env.rm(), buffer_size), _ep(env.ep())
{
typedef Vfs::Directory_service::Open_result Open_result;
Path path = path_from_label<Path>(label.string());
create_parent_dir(vfs, path);
create_parent_dir(vfs, path, alloc);
Open_result res = vfs.open(
path.base(),
@ -155,7 +160,12 @@ class Fs_report::Session_component : public Genode::Rpc_object<Report::Session>
_success = true;
/* flush to notify watchers */
_handle->ds().sync(_leaf_path.base());
while (!_handle->fs().queue_sync(_handle))
_ep.wait_and_dispatch_one_io_signal();
while (_handle->fs().complete_sync(_handle) ==
Vfs::File_io_service::SYNC_QUEUED)
_ep.wait_and_dispatch_one_io_signal();
}
void response_sigh(Genode::Signal_context_capability) override { }

View File

@ -91,6 +91,16 @@ class Lx_fs::Session_component : public Session_rpc_object
case Packet_descriptor::READ_READY:
/* not supported */
break;
case Packet_descriptor::SYNC:
/**
* We could call sync(2) here but for now we forward just the
* reminder because besides testing, there is currently no
* use-case.
*/
Genode::warning("SYNC not implemented!");
break;
}
packet.length(res_length);
@ -327,13 +337,6 @@ class Lx_fs::Session_component : public Session_rpc_object
{
Genode::error(__func__, " not implemented");
}
/**
* We could call sync(2) here but for now we forward just the
* reminder because besides testing, there is currently no
* use-case.
*/
void sync(Node_handle) override { Genode::warning("sync() not implemented!"); }
};

View File

@ -95,6 +95,10 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
case Packet_descriptor::READ_READY:
/* not supported */
break;
case Packet_descriptor::SYNC:
open_node.node().notify_listeners();
break;
}
packet.length(res_length);
@ -150,7 +154,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
static void _assert_valid_path(char const *path)
{
if (!path || path[0] != '/') {
Genode::warning("malformed path ''", path, "'");
Genode::warning("malformed path '", Genode::Cstring(path), "'");
throw Lookup_failed();
}
}
@ -471,19 +475,6 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
throw Invalid_handle();
}
}
void sync(Node_handle handle) override
{
auto sync_fn = [&] (Open_node &open_node) {
open_node.node().notify_listeners();
};
try {
_open_node_registry.apply<Open_node>(handle, sync_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
};

View File

@ -677,6 +677,10 @@ class Trace_fs::Session_component : public Session_rpc_object
case Packet_descriptor::READ_READY:
/* not supported */
break;
case Packet_descriptor::SYNC:
/* not supported */
break;
}
packet.length(res_length);

View File

@ -22,20 +22,6 @@ namespace File_system {
using namespace Vfs;
static inline void assert_mkdir(Directory_service::Mkdir_result r)
{
typedef Directory_service::Mkdir_result Result;
switch (r) {
case Result::MKDIR_ERR_NAME_TOO_LONG: throw Name_too_long();
case Result::MKDIR_ERR_NO_ENTRY: throw Lookup_failed();
case Result::MKDIR_ERR_NO_SPACE: throw No_space();
case Result::MKDIR_ERR_NO_PERM: throw Permission_denied();
case Result::MKDIR_ERR_EXISTS: throw Node_already_exists();
case Result::MKDIR_OK: break;
}
}
static inline void assert_open(Directory_service::Open_result r)
{
typedef Directory_service::Open_result Result;
@ -46,32 +32,41 @@ namespace File_system {
case Result::OPEN_ERR_NO_SPACE: throw No_space();
case Result::OPEN_ERR_NO_PERM: throw Permission_denied();
case Result::OPEN_ERR_EXISTS: throw Node_already_exists();
case Result::OPEN_ERR_OUT_OF_RAM: throw Out_of_ram();
case Result::OPEN_ERR_OUT_OF_CAPS: throw Out_of_caps();
case Result::OPEN_OK: break;
}
}
static inline void assert_symlink(Directory_service::Symlink_result r)
static inline void assert_opendir(Directory_service::Opendir_result r)
{
typedef Directory_service::Symlink_result Result;
typedef Directory_service::Opendir_result Result;
switch (r) {
case Result::SYMLINK_ERR_NAME_TOO_LONG: throw Invalid_name();
case Result::SYMLINK_ERR_NO_ENTRY: throw Lookup_failed();
case Result::SYMLINK_ERR_NO_SPACE: throw No_space();
case Result::SYMLINK_ERR_NO_PERM: throw Permission_denied();
case Result::SYMLINK_ERR_EXISTS: throw Node_already_exists();
case Result::SYMLINK_OK: break;
case Result::OPENDIR_ERR_LOOKUP_FAILED: throw Lookup_failed();
case Result::OPENDIR_ERR_NAME_TOO_LONG: throw Invalid_name();
case Result::OPENDIR_ERR_NODE_ALREADY_EXISTS: throw Node_already_exists();
case Result::OPENDIR_ERR_NO_SPACE: throw No_space();
case Result::OPENDIR_ERR_OUT_OF_RAM: throw Out_of_ram();
case Result::OPENDIR_ERR_OUT_OF_CAPS: throw Out_of_caps();
case Result::OPENDIR_ERR_PERMISSION_DENIED: throw Permission_denied();
case Result::OPENDIR_OK: break;
}
}
static inline void assert_readlink(Directory_service::Readlink_result r)
static inline void assert_openlink(Directory_service::Openlink_result r)
{
typedef Directory_service::Readlink_result Result;
typedef Directory_service::Openlink_result Result;
switch (r) {
case Result::READLINK_ERR_NO_ENTRY: throw Lookup_failed();
case Result::READLINK_ERR_NO_PERM: throw Permission_denied();
case Result::READLINK_OK: break;
case Result::OPENLINK_ERR_LOOKUP_FAILED: throw Lookup_failed();
case Result::OPENLINK_ERR_NAME_TOO_LONG: throw Invalid_name();
case Result::OPENLINK_ERR_NODE_ALREADY_EXISTS: throw Node_already_exists();
case Result::OPENLINK_ERR_NO_SPACE: throw No_space();
case Result::OPENLINK_ERR_OUT_OF_RAM: throw Out_of_ram();
case Result::OPENLINK_ERR_OUT_OF_CAPS: throw Out_of_caps();
case Result::OPENLINK_ERR_PERMISSION_DENIED: throw Permission_denied();
case Result::OPENLINK_OK: break;
}
}

View File

@ -43,7 +43,7 @@ namespace Vfs_server {
class Vfs_server::Session_component : public File_system::Session_rpc_object,
public File_io_handler
public Node_io_handler
{
private:
@ -113,13 +113,13 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
** Packet-stream processing **
******************************/
struct Not_read_ready { };
struct Not_ready { };
struct Dont_ack { };
/**
* Perform packet operation
*
* \throw Not_read_ready
* \throw Not_ready
* \throw Dont_ack
*/
void _process_packet_op(Packet_descriptor &packet)
@ -142,46 +142,36 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
case Packet_descriptor::READ:
try {
_apply(static_cast<File_handle>(packet.handle().value), [&] (File &node) {
_apply(packet.handle(), [&] (Node &node) {
if (!node.read_ready()) {
node.notify_read_ready(true);
throw Not_read_ready();
throw Not_ready();
}
if (node.mode&READ_ONLY)
res_length = node.read(_vfs, (char *)content, length, seek);
if (node.mode() & READ_ONLY)
res_length = node.read((char *)content, length, seek);
});
}
catch (Not_read_ready) { throw; }
catch (Operation_incomplete) { throw Not_read_ready(); }
catch (...) {
try {
_apply(packet.handle(), [&] (Node &node) {
if (!node.read_ready())
throw Not_read_ready();
if (node.mode&READ_ONLY)
res_length = node.read(_vfs, (char *)content, length, seek);
});
}
catch (Not_read_ready) { throw; }
catch (Operation_incomplete) { throw Not_read_ready(); }
catch (...) { }
}
catch (Not_ready) { throw; }
catch (Operation_incomplete) { throw Not_ready(); }
catch (...) { }
break;
case Packet_descriptor::WRITE:
try {
_apply(packet.handle(), [&] (Node &node) {
if (node.mode&WRITE_ONLY)
res_length = node.write(_vfs, (char const *)content, length, seek);
if (node.mode() & WRITE_ONLY)
res_length = node.write((char const *)content, length, seek);
});
} catch (Operation_incomplete) {
throw Not_ready();
} catch (...) { }
break;
case Packet_descriptor::READ_READY:
try {
_apply(static_cast<File_handle>(packet.handle().value), [] (File &node) {
if (!node.read_ready()) {
@ -197,6 +187,20 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
case Packet_descriptor::CONTENT_CHANGED:
/* The VFS does not track file changes yet */
throw Dont_ack();
case Packet_descriptor::SYNC:
/**
* Sync the VFS and send any pending signals on the node.
*/
try {
_apply(packet.handle(), [&] (Node &node) {
node.sync();
});
} catch (Operation_incomplete) {
throw Not_ready();
} catch (...) { Genode::error("SYNC: unhandled exception"); }
break;
}
packet.length(res_length);
@ -208,7 +212,7 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
try {
_process_packet_op(packet);
return true;
} catch (Not_read_ready) {
} catch (Not_ready) {
_backlog_packet = packet;
}
@ -218,8 +222,10 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
bool _process_backlog()
{
/* indicate success if there's no backlog */
if (!_backlog_packet.size())
if (!_backlog_packet.size() &&
(_backlog_packet.operation() != Packet_descriptor::SYNC)) {
return true;
}
/* only start processing if acknowledgement is possible */
if (!tx_sink()->ready_to_ack())
@ -358,7 +364,7 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
_tx.sigh_packet_avail(_process_packet_handler);
_tx.sigh_ready_to_ack(_process_packet_handler);
_root.construct(_node_space, vfs, root_path, false);
_root.construct(_node_space, vfs, _alloc, *this, root_path, false);
}
/**
@ -380,17 +386,27 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
_ram.upgrade(new_quota);
}
/* File_io_handler interface */
void handle_file_io(File &file) override
/*
* Called by the IO response handler for events which are not
* node-specific, for example after 'release_packet()' to signal
* that a previously failed 'alloc_packet()' may succeed now.
*/
void handle_general_io()
{
if (file.notify_read_ready() && file.read_ready()
_process_packets();
}
/* Node_io_handler interface */
void handle_node_io(Node &node) override
{
if (node.notify_read_ready() && node.read_ready()
&& tx_sink()->ready_to_ack()) {
Packet_descriptor packet(Packet_descriptor(),
Node_handle { file.id().value },
Node_handle { node.id().value },
Packet_descriptor::READ_READY,
0, 0);
tx_sink()->acknowledge_packet(packet);
file.notify_read_ready(false);
node.notify_read_ready(false);
}
_process_packets();
}
@ -420,7 +436,8 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
throw Lookup_failed();
Directory *dir;
try { dir = new (_alloc) Directory(_node_space, _vfs, path_str, create); }
try { dir = new (_alloc) Directory(_node_space, _vfs, _alloc,
*this, path_str, create); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Dir_handle(dir->id().value);
@ -474,7 +491,8 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
Node *node;
try { node = new (_alloc) Node(_node_space, path_str, STAT_ONLY); }
try { node = new (_alloc) Node(_node_space, path_str, STAT_ONLY,
*this); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Node_handle { node->id().value };
@ -571,16 +589,6 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
});
}
/**
* Sync the VFS and send any pending signals on the node.
*/
void sync(Node_handle handle) override
{
_apply(handle, [&] (Node &node) {
_vfs.sync(node.path());
});
}
void control(Node_handle, Control) override { }
};
@ -589,13 +597,35 @@ struct Vfs_server::Io_response_handler : Vfs::Io_response_handler
{
Session_registry &_session_registry;
bool _in_progress { false };
bool _handle_general_io { false };
Io_response_handler(Session_registry &session_registry)
: _session_registry(session_registry) { }
void handle_io_response(Vfs::Vfs_handle::Context *context) override
{
if (_in_progress) {
/* called recursively, context is nullptr in this case */
_handle_general_io = true;
return;
}
_in_progress = true;
if (Vfs_server::Node *node = static_cast<Vfs_server::Node *>(context))
node->handle_io_response();
else
_handle_general_io = true;
while (_handle_general_io) {
_handle_general_io = false;
_session_registry.for_each([ ] (Registered_session &r) {
r.handle_general_io();
});
}
_in_progress = false;
}
};

View File

@ -36,9 +36,9 @@ namespace Vfs_server {
typedef Genode::Id_space<Node> Node_space;
struct File_io_handler
struct Node_io_handler
{
virtual void handle_file_io(File &file) = 0;
virtual void handle_node_io(Node &node) = 0;
};
/**
@ -83,155 +83,27 @@ namespace Vfs_server {
}
struct Vfs_server::Node : File_system::Node_base, Node_space::Element,
Vfs::Vfs_handle::Context
class Vfs_server::Node : public File_system::Node_base, public Node_space::Element,
public Vfs::Vfs_handle::Context
{
Path const _path;
Mode const mode;
Node(Node_space &space, char const *node_path, Mode node_mode)
:
Node_space::Element(*this, space),
_path(node_path), mode(node_mode)
{ }
virtual ~Node() { }
char const *path() { return _path.base(); }
virtual size_t read(Vfs::File_system&, char*, size_t, seek_off_t) { return 0; }
virtual size_t write(Vfs::File_system&, char const*, size_t, seek_off_t) { return 0; }
virtual bool read_ready() { return false; }
virtual void handle_io_response() { }
};
struct Vfs_server::Symlink : Node
{
Symlink(Node_space &space,
Vfs::File_system &vfs,
char const *link_path,
Mode mode,
bool create)
: Node(space, link_path, mode)
{
if (create)
assert_symlink(vfs.symlink("", link_path));
}
/********************
** Node interface **
********************/
size_t read(Vfs::File_system &vfs, char *dst, size_t len, seek_off_t seek_offset)
{
Vfs::file_size res = 0;
vfs.readlink(path(), dst, len, res);
return res;
}
size_t write(Vfs::File_system &vfs, char const *src, size_t len, seek_off_t seek_offset)
{
/*
* if the symlink target is too long return a short result
* because a competent File_system client will error on a
* length mismatch
*/
if (len > MAX_PATH_LEN) {
return len >> 1;
}
/* ensure symlink gets something null-terminated */
Genode::String<MAX_PATH_LEN+1> target(Genode::Cstring(src, len));
size_t const target_len = target.length()-1;
switch (vfs.symlink(target.string(), path())) {
case Directory_service::SYMLINK_OK: break;
case Directory_service::SYMLINK_ERR_NAME_TOO_LONG:
return target_len >> 1;
default: return 0;
}
mark_as_updated();
notify_listeners();
return target_len;
}
bool read_ready() override { return true; }
};
class Vfs_server::File : public Node
{
private:
File_io_handler &_file_io_handler;
Vfs::Vfs_handle *_handle;
char const *_leaf_path; /* offset pointer to Node::_path */
bool _notify_read_ready = false;
enum class Op_state {
IDLE, READ_QUEUED
} op_state = Op_state::IDLE;
public:
File(Node_space &space,
Vfs::File_system &vfs,
Genode::Allocator &alloc,
File_io_handler &file_io_handler,
char const *file_path,
Mode fs_mode,
bool create)
:
Node(space, file_path, fs_mode),
_file_io_handler(file_io_handler)
enum Op_state { IDLE, READ_QUEUED, SYNC_QUEUED };
private:
Path const _path;
Mode const _mode;
bool _notify_read_ready = false;
protected:
Node_io_handler &_node_io_handler;
Vfs::Vfs_handle *_handle { nullptr };
Op_state op_state { Op_state::IDLE };
size_t _read(char *dst, size_t len, seek_off_t seek_offset)
{
unsigned vfs_mode =
(fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0);
assert_open(vfs.open(file_path, vfs_mode, &_handle, alloc));
_leaf_path = vfs.leaf_path(path());
_handle->context = this;
}
~File() { _handle->ds().close(_handle); }
void truncate(file_size_t size)
{
assert_truncate(_handle->fs().ftruncate(_handle, size));
mark_as_updated();
}
void notify_read_ready(bool requested)
{
if (requested)
_handle->fs().notify_read_ready(_handle);
_notify_read_ready = requested;
}
bool notify_read_ready() const { return _notify_read_ready; }
/********************
** Node interface **
********************/
size_t read(Vfs::File_system&, char *dst, size_t len,
seek_off_t seek_offset) override
{
if (seek_offset == SEEK_TAIL) {
typedef Directory_service::Stat_result Result;
Vfs::Directory_service::Stat st;
/* if stat fails, try and see if the VFS will seek to the end */
seek_offset = (_handle->ds().stat(_leaf_path, st) == Result::STAT_OK) ?
((len < st.size) ? (st.size - len) : 0) : SEEK_TAIL;
}
_handle->seek(seek_offset);
typedef Vfs::File_io_service::Read_result Result;
@ -242,34 +114,14 @@ class Vfs_server::File : public Node
switch (op_state) {
case Op_state::IDLE:
if (!_handle->fs().queue_read(_handle, dst, len, out_result, out_count))
if (!_handle->fs().queue_read(_handle, len))
throw Operation_incomplete();
switch (out_result) {
case Result::READ_OK:
op_state = Op_state::IDLE;
return out_count;
case Result::READ_ERR_WOULD_BLOCK:
case Result::READ_ERR_AGAIN:
case Result::READ_ERR_INTERRUPT:
op_state = Op_state::IDLE;
throw Operation_incomplete();
case Result::READ_ERR_IO:
case Result::READ_ERR_INVALID:
op_state = Op_state::IDLE;
/* FIXME revise error handling */
return 0;
case Result::READ_QUEUED:
op_state = Op_state::READ_QUEUED;
break;
}
/* fall through */
case Op_state::READ_QUEUED:
out_result = _handle->fs().complete_read(_handle, dst, len, out_count);
out_result = _handle->fs().complete_read(_handle, dst, len,
out_count);
switch (out_result) {
case Result::READ_OK:
op_state = Op_state::IDLE;
@ -292,16 +144,206 @@ class Vfs_server::File : public Node
throw Operation_incomplete();
}
break;
case Op_state::SYNC_QUEUED:
throw Operation_incomplete();
}
return 0;
}
size_t write(Vfs::File_system&, char const *src, size_t len,
seek_off_t seek_offset) override
size_t _write(char const *src, size_t len,
seek_off_t seek_offset)
{
Vfs::file_size res = 0;
_handle->seek(seek_offset);
try {
_handle->fs().write(_handle, src, len, res);
} catch (Vfs::File_io_service::Insufficient_buffer) {
throw Operation_incomplete();
}
if (res)
mark_as_updated();
return res;
}
public:
Node(Node_space &space, char const *node_path, Mode node_mode,
Node_io_handler &node_io_handler)
:
Node_space::Element(*this, space),
_path(node_path), _mode(node_mode),
_node_io_handler(node_io_handler)
{ }
virtual ~Node() { }
char const *path() { return _path.base(); }
Mode mode() const { return _mode; }
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset)
{ return 0; }
virtual size_t write(char const *src, size_t len,
seek_off_t seek_offset) { return 0; }
bool read_ready() { return _handle->fs().read_ready(_handle); }
void handle_io_response()
{
_node_io_handler.handle_node_io(*this);
}
void notify_read_ready(bool requested)
{
if (requested)
_handle->fs().notify_read_ready(_handle);
_notify_read_ready = requested;
}
bool notify_read_ready() const { return _notify_read_ready; }
void sync()
{
typedef Vfs::File_io_service::Sync_result Result;
Result out_result = Result::SYNC_OK;
switch (op_state) {
case Op_state::IDLE:
if (!_handle->fs().queue_sync(_handle))
throw Operation_incomplete();
/* fall through */
case Op_state::SYNC_QUEUED:
out_result = _handle->fs().complete_sync(_handle);
switch (out_result) {
case Result::SYNC_OK:
op_state = Op_state::IDLE;
return;
case Result::SYNC_QUEUED:
op_state = Op_state::SYNC_QUEUED;
throw Operation_incomplete();
}
break;
case Op_state::READ_QUEUED:
throw Operation_incomplete();
}
}
};
struct Vfs_server::Symlink : Node
{
Symlink(Node_space &space,
Vfs::File_system &vfs,
Genode::Allocator &alloc,
Node_io_handler &node_io_handler,
char const *link_path,
Mode mode,
bool create)
: Node(space, link_path, mode, node_io_handler)
{
assert_openlink(vfs.openlink(link_path, create, &_handle, alloc));
_handle->context = this;
}
/********************
** Node interface **
********************/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (seek_offset != 0) {
/* partial read is not supported */
return 0;
}
return _read(dst, len, 0);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
/*
* if the symlink target is too long return a short result
* because a competent File_system client will error on a
* length mismatch
*/
if (len > MAX_PATH_LEN) {
return len >> 1;
}
/* ensure symlink gets something null-terminated */
Genode::String<MAX_PATH_LEN+1> target(Genode::Cstring(src, len));
size_t const target_len = target.length()-1;
file_size out_count;
if (_handle->fs().write(_handle, target.string(), target_len, out_count) !=
File_io_service::WRITE_OK)
return 0;
mark_as_updated();
notify_listeners();
return out_count;
}
};
class Vfs_server::File : public Node
{
private:
char const *_leaf_path; /* offset pointer to Node::_path */
public:
File(Node_space &space,
Vfs::File_system &vfs,
Genode::Allocator &alloc,
Node_io_handler &node_io_handler,
char const *file_path,
Mode fs_mode,
bool create)
:
Node(space, file_path, fs_mode, node_io_handler)
{
unsigned vfs_mode =
(fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0);
assert_open(vfs.open(file_path, vfs_mode, &_handle, alloc));
_leaf_path = vfs.leaf_path(path());
_handle->context = this;
}
~File() { _handle->ds().close(_handle); }
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
if (seek_offset == SEEK_TAIL) {
typedef Directory_service::Stat_result Result;
Vfs::Directory_service::Stat st;
/* if stat fails, try and see if the VFS will seek to the end */
seek_offset = (_handle->ds().stat(_leaf_path, st) == Result::STAT_OK) ?
((len < st.size) ? (st.size - len) : 0) : SEEK_TAIL;
}
return _read(dst, len, seek_offset);
}
size_t write(char const *src, size_t len,
seek_off_t seek_offset) override
{
if (seek_offset == SEEK_TAIL) {
typedef Directory_service::Stat_result Result;
Vfs::Directory_service::Stat st;
@ -311,35 +353,35 @@ class Vfs_server::File : public Node
st.size : SEEK_TAIL;
}
_handle->seek(seek_offset);
_handle->fs().write(_handle, src, len, res);
if (res)
mark_as_updated();
return res;
return _write(src, len, seek_offset);
}
bool read_ready() override { return _handle->fs().read_ready(_handle); }
void handle_io_response() override
void truncate(file_size_t size)
{
_file_io_handler.handle_file_io(*this);
assert_truncate(_handle->fs().ftruncate(_handle, size));
mark_as_updated();
}
};
struct Vfs_server::Directory : Node
{
Directory(Node_space &space, Vfs::File_system &vfs, char const *dir_path, bool create)
: Node(space, dir_path, READ_ONLY)
Directory(Node_space &space,
Vfs::File_system &vfs,
Genode::Allocator &alloc,
Node_io_handler &node_io_handler,
char const *dir_path,
bool create)
: Node(space, dir_path, READ_ONLY, node_io_handler)
{
if (create)
assert_mkdir(vfs.mkdir(dir_path, 0));
assert_opendir(vfs.opendir(dir_path, create, &_handle, alloc));
_handle->context = this;
}
Node_space::Id file(Node_space &space,
Vfs::File_system &vfs,
Genode::Allocator &alloc,
File_io_handler &file_io_handler,
Node_io_handler &node_io_handler,
char const *file_path,
Mode mode,
bool create)
@ -350,7 +392,7 @@ struct Vfs_server::Directory : Node
File *file;
try {
file = new (alloc)
File(space, vfs, alloc, file_io_handler, path_str, mode, create);
File(space, vfs, alloc, node_io_handler, path_str, mode, create);
} catch (Out_of_memory) { throw Out_of_ram(); }
if (create)
@ -368,13 +410,9 @@ struct Vfs_server::Directory : Node
Path subpath(link_path, path());
char const *path_str = subpath.base();
if (!create) {
Vfs::file_size out;
assert_readlink(vfs.readlink(path_str, nullptr, 0, out));
}
Symlink *link;
try { link = new (alloc) Symlink(space, vfs, path_str, mode, create); }
try { link = new (alloc) Symlink(space, vfs, alloc, _node_io_handler,
path_str, mode, create); }
catch (Out_of_memory) { throw Out_of_ram(); }
if (create)
mark_as_updated();
@ -386,7 +424,7 @@ struct Vfs_server::Directory : Node
** Node interface **
********************/
size_t read(Vfs::File_system &vfs, char *dst, size_t len, seek_off_t seek_offset)
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
Directory_service::Dirent vfs_dirent;
size_t blocksize = sizeof(File_system::Directory_entry);
@ -396,8 +434,10 @@ struct Vfs_server::Directory : Node
size_t remains = len;
while (remains >= blocksize) {
if (vfs.dirent(path(), index++, vfs_dirent)
!= Vfs::Directory_service::DIRENT_OK)
if ((_read((char*)&vfs_dirent, sizeof(vfs_dirent),
index * sizeof(vfs_dirent)) < sizeof(vfs_dirent)) ||
(vfs_dirent.type == Vfs::Directory_service::DIRENT_TYPE_END))
return len - remains;
File_system::Directory_entry *fs_dirent = (Directory_entry *)dst;
@ -422,7 +462,11 @@ struct Vfs_server::Directory : Node
return len - remains;
}
bool read_ready() override { return true; }
size_t write(char const *src, size_t len,
seek_off_t seek_offset) override
{
return 0;
}
};
#endif /* _VFS__NODE_H_ */