xml_editor: VFS API update, 'toggle' action

Add an action at the xml_editor to toggle the presence of an XML node.
Edit clients have no explicit knowledge of what nodes are present in the
file being edited so a 'toggle' feature is best implemented server side.

Ref #86
This commit is contained in:
Emery Hemingway
2017-09-28 13:49:09 -05:00
committed by Norman Feske
parent 6263202681
commit 09964463e9
3 changed files with 143 additions and 34 deletions

View File

@@ -2,11 +2,11 @@ This component edits a single XML file as instructed by clients via
Report sessions. It is designed to allow insertion and removal of XML
nodes without revealing the content the file being edited.
The two edit actions are _add_ and _remove_, both operate over XML
The three edit actions are _add_, _remove_, and toggle, all operate over XML
nodes under the parent node in the file being edited. The content of
edits are verified for corrent syntax and XML node type and _name_
attribute are used to prevent the same node from being inserted
twice or to find nodes to remove.
attribute is used to prevent the same node from being inserted
twice and to find nodes to remove or toggle.
Add action
----------
@@ -19,7 +19,13 @@ Add action
Remove action
-------------
! <remove>
! <... name="..."/>
! </remove>
Toggle action
-------------
! <toggle>
! <... name="...">
! ...
! </...>
! </remove>
! </toggle>

View File

@@ -38,6 +38,8 @@ namespace Xml_editor {
#define REVISION_ATTR_NAME "edit_rev"
Genode::Env *_env;
struct Xml_editor::Xml_file
{
Genode::Allocator &alloc;
@@ -95,8 +97,19 @@ struct Xml_editor::Xml_file
/**
* Flush file changes
*/
void sync() {
vfs_handle.ds().sync(path.base()); }
void sync()
{
while (true) {
if (vfs_handle.fs().queue_sync(&vfs_handle))
break;
_env->ep().wait_and_dispatch_one_io_signal();
}
while (true) {
if (vfs_handle.fs().complete_sync(&vfs_handle) != Vfs::File_io_service::SYNC_QUEUED)
break;
_env->ep().wait_and_dispatch_one_io_signal();
}
}
void read_file()
{
@@ -107,25 +120,34 @@ struct Xml_editor::Xml_file
file_size const total = sb.size;
Buffer &next = next_buffer(total ? total : 4096);
buffer = &next;
if (total == 0) {
strncpy(next.ptr, "<config/>", next.size);
} else {
/*
* Read in one pass, reading in multiple passes is
* too complicated and error prone
*/
file_size out = 0;
while (!vfs_handle.fs().queue_read(&vfs_handle, total))
_env->ep().wait_and_dispatch_one_io_signal();
file_size offset = 0;
while (offset < total) {
vfs_handle.seek(offset);
file_size out = 0;
vfs_handle.fs().read(
&vfs_handle,
next.ptr + offset,
total - offset,
out);
offset += out;
for (;;) {
auto r = vfs_handle.fs().complete_read(
&vfs_handle, next.ptr, total, out);
switch (r) {
case Vfs::File_io_service::Read_result::READ_QUEUED:
_env->ep().wait_and_dispatch_one_io_signal();
break;
case Vfs::File_io_service::Read_result::READ_OK:
return;
default:
Genode::error("failed to read XML file");
throw r;
}
}
}
buffer = &next;
}
void write_file(Vfs::file_size length)
@@ -273,6 +295,24 @@ struct Xml_editor::Xml_file
return gen.used();
}
size_t toggle(Xml_node const &node)
{
Name const toggle_name = node.attribute_value("name", Name());
Xml_node const current = xml();
bool name_present = false;
current.for_each_sub_node([&] (Xml_node const &other) {
if (!name_present &&
other.type() == node.type() &&
toggle_name == other.attribute_value("name", Name()))
{
name_present = true;
}
});
return name_present ? remove(node) : add(node);
}
};
@@ -323,9 +363,18 @@ struct Xml_editor::Report_session_component : Genode::Rpc_object<Report::Session
});
};
auto toggle_fn = [&] (Xml_node const &action) {
action.for_each_sub_node([&] (Xml_node const &subnode) {
if (verbose)
log("'", label, "' toggles '", subnode.attribute_value("name", Name()), "'");
content_size = xml_file.toggle(subnode);
});
};
try {
Xml_node edit_node(ram_ds.local_addr<char const>(), length);
edit_node.for_each_sub_node("toggle", toggle_fn);
edit_node.for_each_sub_node("remove", remove_fn);
edit_node.for_each_sub_node("add", add_fn);
if (content_size) {
@@ -515,6 +564,10 @@ struct Xml_editor::Main
die("OPEN_ERR_NAME_TOO_LONG"); break;
case Open_result::OPEN_ERR_NO_SPACE:
die("OPEN_ERR_NO_SPACE"); break;
case Open_result::OPEN_ERR_OUT_OF_RAM:
die("OPEN_ERR_OUT_OF_RAM"); break;
case Open_result::OPEN_ERR_OUT_OF_CAPS:
die("OPEN_ERR_OUT_OF_CAPS"); break;
}
throw ~0;
}
@@ -532,5 +585,8 @@ struct Xml_editor::Main
};
void Component::construct(Genode::Env &env) {
static Xml_editor::Main inst(env); }
void Component::construct(Genode::Env &env)
{
_env = &env;
static Xml_editor::Main inst(env);
}

View File

@@ -36,12 +36,14 @@ namespace Xml_term_edit {
struct Main;
}
Genode::Env *_env;
struct Xml_term_edit::Command : Cli_monitor::Command
{
Vfs::Dir_file_system &vfs;
Genode::Allocator &alloc;
Reporter &report;
Vfs::Vfs_handle *root_handle = nullptr;
Command(char const *name,
char const *desc,
@@ -53,23 +55,65 @@ struct Xml_term_edit::Command : Cli_monitor::Command
Cli_monitor::Command(name, desc),
vfs(vfs), alloc(alloc), report(report)
{
auto r = vfs.open(
"/", Vfs::Directory_service::OPEN_MODE_RDONLY, &root_handle, alloc);
if (r != Vfs::Directory_service::Open_result::OPEN_OK) {
Genode::error("failed to open VFS root directory");
throw r;
}
cmds.insert(this);
}
void _for_each_argument(Argument_fn const &fn) const override
{
Vfs::Directory_service::Dirent de;
for (Vfs::file_offset i = 0; ; ++i) {
if (vfs.dirent("/", i, de) != Vfs::Directory_service::DIRENT_OK)
break;
switch (de.type) {
case Vfs::Directory_service::DIRENT_TYPE_FILE:
fn(Argument(de.name, ""));
break;
case Vfs::Directory_service::DIRENT_TYPE_END:
typedef Vfs::File_io_service::Read_result Result;
enum { DIRENT_COUNT = 4096 / sizeof(Vfs::Directory_service::Dirent) };
Vfs::Directory_service::Dirent dirents[DIRENT_COUNT];
memset(dirents, 0x00, sizeof(dirents));
root_handle->seek(0);
for (;;) {
while (!vfs.queue_read(root_handle, sizeof(dirents))) {
_env->ep().wait_and_dispatch_one_io_signal();
}
Vfs::file_size read_count = 0;
Result r;
for (;;) {
r = vfs.complete_read(
root_handle, (char*)&dirents, sizeof(dirents), read_count);
if (r == Result::READ_QUEUED) {
_env->ep().wait_and_dispatch_one_io_signal();
}
else
break;
}
if (r != Result::READ_OK) {
Genode::error("failed to read subsystems");
return;
default:
break;
}
if (read_count == 0) return;
root_handle->advance_seek(read_count);
read_count = read_count / DIRENT_COUNT;
for (unsigned i = 0; i < read_count; i++) {
Vfs::Directory_service::Dirent const &e = dirents[i];
switch (e.type) {
case Vfs::Directory_service::DIRENT_TYPE_FILE:
/* check if the VFS returned junk */
if (e.name[0] != '\0')
fn(Argument(e.name, ""));
break;
case Vfs::Directory_service::DIRENT_TYPE_END:
return;
default:
break;
}
}
}
}
@@ -117,7 +161,7 @@ struct Xml_term_edit::Command : Cli_monitor::Command
while (offset < sb.size) {
file_size n = 0;
file_size count = min(sizeof(buf), sb.size-offset);
handle->fs().read(handle, buf, count, n);
handle->fs().complete_read(handle, buf, count, n);
if (!n)
return;
gen.append(buf, n);
@@ -262,5 +306,8 @@ void Xml_term_edit::Main::handle_term()
}
}
void Component::construct(Genode::Env &env) {
static Xml_term_edit::Main inst(env); }
void Component::construct(Genode::Env &env)
{
_env = &env;
static Xml_term_edit::Main inst(env);
}