mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 08:25:38 +00:00
server/fs_report: open/close file for each write
The file may also be changed by other components, so a open-close cycle for write() is more robust. For example, Vim removes the original file and recreates with the new content.
This commit is contained in:
parent
ab77f94348
commit
8f102264d3
@ -23,6 +23,7 @@
|
||||
#include <util/arg_string.h>
|
||||
|
||||
namespace Fs_report {
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Report;
|
||||
using namespace Vfs;
|
||||
@ -33,56 +34,94 @@ namespace Fs_report {
|
||||
|
||||
typedef Genode::Path<Session_label::capacity()> Path;
|
||||
|
||||
static bool create_parent_dir(Vfs::Directory_service &vfs, Path const &child,
|
||||
Genode::Allocator &alloc)
|
||||
{
|
||||
typedef Vfs::Directory_service::Opendir_result Opendir_result;
|
||||
static bool create_parent_dir(Vfs::Directory_service &vfs, Path const &child,
|
||||
Genode::Allocator &alloc)
|
||||
{
|
||||
typedef Vfs::Directory_service::Opendir_result Opendir_result;
|
||||
|
||||
Path parent = child;
|
||||
parent.strip_last_element();
|
||||
if (parent == "/")
|
||||
return true;
|
||||
Path parent = child;
|
||||
parent.strip_last_element();
|
||||
if (parent == "/")
|
||||
return true;
|
||||
|
||||
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))
|
||||
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.opendir(parent.base(), true, &dir_handle, alloc);
|
||||
}
|
||||
|
||||
switch (res) {
|
||||
case Opendir_result::OPENDIR_OK:
|
||||
vfs.close(dir_handle);
|
||||
return true;
|
||||
case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
res = vfs.opendir(parent.base(), true, &dir_handle, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
switch (res) {
|
||||
case Opendir_result::OPENDIR_OK:
|
||||
vfs.close(dir_handle);
|
||||
return true;
|
||||
case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Fs_report::Session_component : public Genode::Rpc_object<Report::Session>
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Entrypoint &_ep;
|
||||
Genode::Allocator &_alloc;
|
||||
Vfs::File_system &_vfs;
|
||||
|
||||
Attached_ram_dataspace _ds;
|
||||
Path _path { };
|
||||
|
||||
file_size _file_size = 0;
|
||||
bool _success = true;
|
||||
|
||||
struct Open_failed { };
|
||||
|
||||
template <typename FN> void _file_op(FN const &fn)
|
||||
{
|
||||
typedef Vfs::Directory_service::Open_result Open_result;
|
||||
|
||||
Vfs_handle *handle;
|
||||
Open_result res = _vfs.open(_path.base(),
|
||||
Directory_service::OPEN_MODE_WRONLY,
|
||||
&handle, _alloc);
|
||||
|
||||
/* try to create file if not accessible */
|
||||
if (res == Open_result::OPEN_ERR_UNACCESSIBLE) {
|
||||
res = _vfs.open(_path.base(),
|
||||
Directory_service::OPEN_MODE_WRONLY |
|
||||
Directory_service::OPEN_MODE_CREATE,
|
||||
&handle, _alloc);
|
||||
}
|
||||
|
||||
if (res != Open_result::OPEN_OK) {
|
||||
error("failed to open '", _path, "' res=", (int)res);
|
||||
throw Open_failed();
|
||||
}
|
||||
|
||||
fn(handle);
|
||||
|
||||
/* sync file operations before close */
|
||||
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();
|
||||
|
||||
handle->close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Session_component(Session_component const &);
|
||||
Session_component &operator = (Session_component const &);
|
||||
|
||||
Path _leaf_path { };
|
||||
|
||||
Attached_ram_dataspace _ds;
|
||||
Genode::Entrypoint &_ep;
|
||||
|
||||
Vfs_handle *_handle = nullptr;
|
||||
file_size _file_size = 0;
|
||||
bool _success = true;
|
||||
|
||||
public:
|
||||
|
||||
Session_component(Genode::Env &env,
|
||||
@ -90,87 +129,57 @@ 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), _ep(env.ep())
|
||||
:
|
||||
_ep(env.ep()), _alloc(alloc), _vfs(vfs),
|
||||
_ds(env.ram(), env.rm(), buffer_size),
|
||||
_path(path_from_label<Path>(label.string()))
|
||||
{
|
||||
typedef Vfs::Directory_service::Open_result Open_result;
|
||||
create_parent_dir(_vfs, _path, _alloc);
|
||||
|
||||
Path path = path_from_label<Path>(label.string());
|
||||
|
||||
create_parent_dir(vfs, path, alloc);
|
||||
|
||||
Open_result res = vfs.open(
|
||||
path.base(),
|
||||
Directory_service::OPEN_MODE_WRONLY |
|
||||
Directory_service::OPEN_MODE_CREATE,
|
||||
&_handle, alloc);
|
||||
|
||||
if (res == Open_result::OPEN_ERR_EXISTS) {
|
||||
res = vfs.open(
|
||||
path.base(),
|
||||
Directory_service::OPEN_MODE_WRONLY,
|
||||
&_handle, alloc);
|
||||
if (res == Open_result::OPEN_OK)
|
||||
_handle->fs().ftruncate(_handle, 0);
|
||||
}
|
||||
|
||||
if (res != Open_result::OPEN_OK) {
|
||||
error("failed to open '", path, "'");
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
/* get the leaf path from the leaf file-system */
|
||||
if (char const *leaf_path = _handle->ds().leaf_path(path.base()))
|
||||
_leaf_path.import(leaf_path);
|
||||
try {
|
||||
_file_op([&] (Vfs_handle *handle) { handle->fs().ftruncate(handle, 0); });
|
||||
} catch (...) { throw Service_denied(); }
|
||||
}
|
||||
|
||||
~Session_component()
|
||||
{
|
||||
if (_handle)
|
||||
_handle->ds().close(_handle);
|
||||
}
|
||||
~Session_component() { }
|
||||
|
||||
Dataspace_capability dataspace() override { return _ds.cap(); }
|
||||
|
||||
void submit(size_t const length) override
|
||||
{
|
||||
/* TODO: close and reopen on error */
|
||||
auto fn = [&] (Vfs_handle *handle) {
|
||||
|
||||
typedef Vfs::File_io_service::Write_result Write_result;
|
||||
typedef Vfs::File_io_service::Write_result Write_result;
|
||||
|
||||
if (_file_size != length)
|
||||
_handle->fs().ftruncate(_handle, length);
|
||||
if (_file_size != length)
|
||||
handle->fs().ftruncate(handle, length);
|
||||
|
||||
size_t offset = 0;
|
||||
while (offset < length) {
|
||||
file_size n = 0;
|
||||
size_t offset = 0;
|
||||
while (offset < length) {
|
||||
file_size n = 0;
|
||||
|
||||
_handle->seek(offset);
|
||||
Write_result res = _handle->fs().write(
|
||||
_handle, _ds.local_addr<char const>()+offset,
|
||||
length - offset, n);
|
||||
handle->seek(offset);
|
||||
Write_result res = handle->fs().write(
|
||||
handle, _ds.local_addr<char const>() + offset,
|
||||
length - offset, n);
|
||||
|
||||
if (res != Write_result::WRITE_OK) {
|
||||
/* do not spam the log */
|
||||
if (_success)
|
||||
error("failed to write report to '", _leaf_path, "'");
|
||||
_file_size = 0;
|
||||
_success = false;
|
||||
return;
|
||||
if (res != Write_result::WRITE_OK) {
|
||||
/* do not spam the log */
|
||||
if (_success)
|
||||
error("failed to write report to '", _path, "'");
|
||||
_file_size = 0;
|
||||
_success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
offset += n;
|
||||
}
|
||||
|
||||
offset += n;
|
||||
}
|
||||
_file_size = length;
|
||||
_success = true;
|
||||
};
|
||||
|
||||
_file_size = length;
|
||||
_success = true;
|
||||
|
||||
/* flush to notify watchers */
|
||||
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();
|
||||
try { _file_op(fn); } catch (...) { }
|
||||
}
|
||||
|
||||
void response_sigh(Genode::Signal_context_capability) override { }
|
||||
|
Loading…
x
Reference in New Issue
Block a user