file system: use Id_space instead of Node_handle_registry

Fixes #2436
This commit is contained in:
Christian Prochaska
2017-06-20 14:23:22 +02:00
committed by Christian Helmuth
parent 0d1be4abe2
commit 6a43f3c11a
44 changed files with 3623 additions and 3100 deletions

View File

@ -133,22 +133,22 @@ class Fs_log::Root_component :
Dir_handle dir_handle = ensure_dir(_fs, dir_path.base());
Handle_guard dir_guard(_fs, dir_handle);
File_handle handle;
Genode::Constructible<File_handle> handle;
try {
handle = _fs.file(dir_handle, file_name,
File_system::WRITE_ONLY, false);
handle.construct(_fs.file(dir_handle, file_name,
File_system::WRITE_ONLY, false));
/* don't truncate at every new child session */
if (truncate && (strcmp(label_prefix, "") == 0))
_fs.truncate(handle, 0);
_fs.truncate(*handle, 0);
}
catch (File_system::Lookup_failed) {
handle = _fs.file(dir_handle, file_name,
File_system::WRITE_ONLY, true);
handle.construct(_fs.file(dir_handle, file_name,
File_system::WRITE_ONLY, true));
}
return new (md_alloc()) Session_component(_fs, handle, label_prefix);
return new (md_alloc()) Session_component(_fs, *handle, label_prefix);
}
catch (Permission_denied) {
errstr = "permission denied"; }

View File

@ -65,7 +65,7 @@ class Fs_rom::Rom_session_component :
/**
* Handle of associated file
*/
File_system::File_handle _file_handle;
Genode::Constructible<File_system::File_handle> _file_handle;
/**
* Size of current version of the file
@ -83,7 +83,7 @@ class Fs_rom::Rom_session_component :
* The compund directory is watched only if the requested file could
* not be looked up.
*/
File_system::Node_handle _compound_dir_handle;
Genode::Constructible<File_system::Dir_handle> _compound_dir_handle;
/**
* Dataspace exposed as ROM module to the client
@ -95,6 +95,11 @@ class Fs_rom::Rom_session_component :
*/
Genode::Signal_context_capability _sigh;
/*
* Exception
*/
struct Open_compound_dir_failed { };
/**
* Open compound directory of specified file
*
@ -130,9 +135,14 @@ class Fs_rom::Rom_session_component :
*/
if (!walk_up) break;
}
return Dir_handle(); /* invalid */
throw Open_compound_dir_failed();
}
/*
* Exception
*/
struct Open_file_failed { };
/**
* Open file with specified name at the file system
*/
@ -141,25 +151,30 @@ class Fs_rom::Rom_session_component :
{
using namespace File_system;
File_system::File_handle file_handle;
try {
Dir_handle dir = _open_compound_dir(fs, path, false);
Handle_guard guard(fs, dir);
/* open file */
Genode::Path<PATH_MAX_LEN> file_name(path.base());
file_name.keep_only_last_element();
file_handle = fs.file(dir, file_name.base() + 1,
File_system::READ_ONLY, false);
try {
Handle_guard guard(fs, dir);
/* open file */
Genode::Path<PATH_MAX_LEN> file_name(path.base());
file_name.keep_only_last_element();
return fs.file(dir, file_name.base() + 1,
File_system::READ_ONLY, false);
}
catch (Invalid_handle) { Genode::error(_file_path, ": Invalid_handle"); }
catch (Invalid_name) { Genode::error(_file_path, ": invalid_name"); }
catch (Lookup_failed) { Genode::error(_file_path, ": lookup_failed"); }
catch (...) { Genode::error(_file_path, ": unhandled error"); };
throw Open_file_failed();
} catch (Open_compound_dir_failed) {
throw Open_file_failed();
}
catch (Invalid_handle) { Genode::error(_file_path, ": Invalid_handle"); }
catch (Invalid_name) { Genode::error(_file_path, ": invalid_name"); }
catch (Lookup_failed) { Genode::error(_file_path, ": lookup_failed"); }
catch (...) { Genode::error(_file_path, ": unhandled error"); };
return file_handle;
}
void _register_for_compound_dir_changes()
@ -167,18 +182,22 @@ class Fs_rom::Rom_session_component :
using namespace File_system;
/* forget about the previously watched compound directory */
if (_compound_dir_handle.valid())
_fs.close(_compound_dir_handle);
if (_compound_dir_handle.constructed()) {
_fs.close(*_compound_dir_handle);
_compound_dir_handle.destruct();
}
_compound_dir_handle = _open_compound_dir(_fs, _file_path, true);
try {
_compound_dir_handle.construct(_open_compound_dir(_fs, _file_path, true));
/* register for changes in compound directory */
if (_compound_dir_handle.valid())
/* register for changes in compound directory */
_fs.tx()->submit_packet(File_system::Packet_descriptor(
_compound_dir_handle,
*_compound_dir_handle,
File_system::Packet_descriptor::CONTENT_CHANGED));
else
} catch (Open_compound_dir_failed) {
Genode::warning("could not track compound dir, giving up");
}
}
/**
@ -194,42 +213,46 @@ class Fs_rom::Rom_session_component :
* content. The dataspace is re-allocated if the new version
* of the file has become bigger.
*/
{
try {
File_handle const file_handle = _open_file(_fs, _file_path);
if (file_handle.valid()) {
File_system::file_size_t const new_file_size =
_fs.status(file_handle).size;
File_system::file_size_t const new_file_size =
_fs.status(file_handle).size;
if (_file_ds.size() && (new_file_size > _file_size)) {
/* mark as invalid */
_file_ds.realloc(&_env.ram(), 0);
_file_size = 0;
_file_seek = 0;
}
if (_file_ds.size() && (new_file_size > _file_size)) {
/* mark as invalid */
_file_ds.realloc(&_env.ram(), 0);
_file_size = 0;
_file_seek = 0;
}
_fs.close(file_handle);
}
} catch (Open_file_failed) { }
/* close and then re-open the file */
if (_file_handle.valid())
_fs.close(_file_handle);
if (_file_handle.constructed()) {
_fs.close(*_file_handle);
_file_handle.destruct();
}
_file_handle = _open_file(_fs, _file_path);
try {
_file_handle.construct(_open_file(_fs, _file_path));
} catch (Open_file_failed) { }
/*
* If we got the file, we can stop paying attention to the
* compound directory.
*/
if (_file_handle.valid() && _compound_dir_handle.valid())
_fs.close(_compound_dir_handle);
if (_file_handle.constructed() && _compound_dir_handle.constructed()) {
_fs.close(*_compound_dir_handle);
_compound_dir_handle.destruct();
}
/* register for file changes */
if (_file_handle.valid())
if (_file_handle.constructed())
_fs.tx()->submit_packet(File_system::Packet_descriptor(
_file_handle, File_system::Packet_descriptor::CONTENT_CHANGED));
*_file_handle, File_system::Packet_descriptor::CONTENT_CHANGED));
size_t const file_size = _file_handle.valid()
? _fs.status(_file_handle).size : 0;
size_t const file_size = _file_handle.constructed()
? _fs.status(*_file_handle).size : 0;
/* allocate new RAM dataspace according to file size */
if (file_size > 0) {
@ -255,7 +278,7 @@ class Fs_rom::Rom_session_component :
source.bulk_buffer_size() / 2);
File_system::Packet_descriptor
packet(source.alloc_packet(chunk_size),
_file_handle,
*_file_handle,
File_system::Packet_descriptor::READ,
chunk_size,
_file_seek);
@ -284,11 +307,13 @@ class Fs_rom::Rom_session_component :
:
_env(env), _fs(fs),
_file_path(file_path),
_file_handle(_open_file(_fs, _file_path)),
_file_ds(env.ram(), env.rm(), 0) /* realloc later */
{
if (!_file_handle.valid())
_register_for_compound_dir_changes();
try {
_file_handle.construct(_open_file(_fs, _file_path));
} catch (Open_file_failed) { }
_register_for_compound_dir_changes();
}
/**
@ -297,11 +322,11 @@ class Fs_rom::Rom_session_component :
~Rom_session_component()
{
/* close re-open the file */
if (_file_handle.valid())
_fs.close(_file_handle);
if (_file_handle.constructed())
_fs.close(*_file_handle);
if (_compound_dir_handle.valid())
_fs.close(_compound_dir_handle);
if (_compound_dir_handle.constructed())
_fs.close(*_compound_dir_handle);
}
/**
@ -327,8 +352,8 @@ class Fs_rom::Rom_session_component :
switch (packet.operation()) {
case File_system::Packet_descriptor::CONTENT_CHANGED:
if (_file_handle == packet.handle() ||
_compound_dir_handle == packet.handle())
if ((_file_handle.constructed() && (*_file_handle == packet.handle())) ||
(_compound_dir_handle.constructed() && (*_compound_dir_handle == packet.handle())))
{
if (_sigh.valid())
Genode::Signal_transmitter(_sigh).submit();
@ -337,7 +362,7 @@ class Fs_rom::Rom_session_component :
return false;
case File_system::Packet_descriptor::READ: {
if (_file_handle != packet.handle())
if (!(_file_handle.constructed() && (*_file_handle == packet.handle())))
return false;
if (packet.position() > _file_seek || _file_seek >= _file_size) {

View File

@ -5,6 +5,13 @@
* \date 2013-11-11
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
@ -19,13 +26,14 @@
#include <lx_util.h>
namespace File_system {
namespace Lx_fs {
using namespace Genode;
using namespace File_system;
class Directory;
}
class File_system::Directory : public Node
class Lx_fs::Directory : public Node
{
private:
@ -72,6 +80,16 @@ class File_system::Directory : public Node
return fd;
}
size_t _num_entries() const
{
unsigned num = 0;
rewinddir(_fd);
while (readdir(_fd)) ++num;
return num;
}
public:
Directory(Allocator &alloc, char const *path, bool create)
@ -90,11 +108,10 @@ class File_system::Directory : public Node
}
/* FIXME returned file node must be locked */
File * file(char const *name, Mode mode, bool create)
File * file(char const *name, Mode mode, bool create) override
{
File *file = new (&_alloc) File(dirfd(_fd), name, mode, create);
file->lock();
return file;
}
@ -105,7 +122,6 @@ class File_system::Directory : public Node
Directory *dir = new (&_alloc) Directory(_alloc, dir_path.base(), create);
dir->lock();
return dir;
}
@ -134,11 +150,10 @@ class File_system::Directory : public Node
else
throw Lookup_failed();
node->lock();
return node;
}
size_t read(char *dst, size_t len, seek_off_t seek_offset)
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
if (len < sizeof(Directory_entry)) {
Genode::error("read buffer too small for directory entry");
@ -177,20 +192,19 @@ class File_system::Directory : public Node
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
/* writing to directory nodes is not supported */
return 0;
}
size_t num_entries() const
Status status() override
{
unsigned num = 0;
rewinddir(_fd);
while (readdir(_fd)) ++num;
return num;
Status s;
s.inode = inode();
s.size = _num_entries() * sizeof(File_system::Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
return s;
}
};

View File

@ -5,6 +5,13 @@
* \date 2013-11-11
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FILE_H_
#define _FILE_H_
@ -13,12 +20,13 @@
#include <lx_util.h>
namespace File_system {
namespace Lx_fs {
using namespace File_system;
class File;
}
class File_system::File : public Node
class Lx_fs::File : public Node
{
private:
@ -74,6 +82,16 @@ class File_system::File : public Node
return fd;
}
file_size_t _length() const
{
struct stat s;
if (fstat(_fd, &s) < 0)
return 0;
return s.st_size;
}
public:
File(int dir,
@ -95,14 +113,14 @@ class File_system::File : public Node
Node::name(basename(path));
}
size_t read(char *dst, size_t len, seek_off_t seek_offset)
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
int ret = pread(_fd, dst, len, seek_offset);
return ret == -1 ? 0 : ret;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
/* should we append? */
if (seek_offset == ~0ULL) {
@ -117,17 +135,16 @@ class File_system::File : public Node
return ret == -1 ? 0 : ret;
}
file_size_t length() const
Status status() override
{
struct stat s;
if (fstat(_fd, &s) < 0)
return 0;
return s.st_size;
Status s;
s.inode = inode();
s.size = _length();
s.mode = File_system::Status::MODE_FILE;
return s;
}
void truncate(file_size_t size)
void truncate(file_size_t size) override
{
if (ftruncate(_fd, size)) /* nothing */;

View File

@ -16,7 +16,7 @@
#include <base/heap.h>
#include <base/attached_rom_dataspace.h>
#include <root/component.h>
#include <file_system/node_handle_registry.h>
#include <file_system/open_node.h>
#include <file_system_session/rpc_object.h>
#include <os/session_policy.h>
#include <util/xml_node.h>
@ -25,22 +25,29 @@
#include <directory.h>
namespace File_system {
namespace Lx_fs {
using namespace File_system;
using File_system::Packet_descriptor;
using File_system::Path;
struct Main;
struct Session_component;
struct Root;
}
class File_system::Session_component : public Session_rpc_object
class Lx_fs::Session_component : public Session_rpc_object
{
private:
Genode::Env &_env;
Allocator &_md_alloc;
Directory &_root;
Node_handle_registry _handle_registry;
bool _writable;
typedef File_system::Open_node<Node> Open_node;
Genode::Env &_env;
Allocator &_md_alloc;
Directory &_root;
Id_space<File_system::Node> _open_node_registry;
bool _writable;
Signal_handler<Session_component> _process_packet_dispatcher;
@ -54,7 +61,7 @@ class File_system::Session_component : public Session_rpc_object
*
* \return true on success, false on failure
*/
void _process_packet_op(Packet_descriptor &packet, Node &node)
void _process_packet_op(Packet_descriptor &packet, Open_node &open_node)
{
void * const content = tx_sink()->packet_content(packet);
size_t const length = packet.length();
@ -66,18 +73,18 @@ class File_system::Session_component : public Session_rpc_object
case Packet_descriptor::READ:
if (content && (packet.length() <= packet.size()))
res_length = node.read((char *)content, length, packet.position());
res_length = open_node.node().read((char *)content, length, packet.position());
break;
case Packet_descriptor::WRITE:
if (content && (packet.length() <= packet.size()))
res_length = node.write((char const *)content, length, packet.position());
res_length = open_node.node().write((char const *)content, length, packet.position());
break;
case Packet_descriptor::CONTENT_CHANGED:
_handle_registry.register_notify(*tx_sink(), packet.handle());
open_node.register_notify(*tx_sink());
/* notify_listeners may bounce the packet back*/
node.notify_listeners();
open_node.node().notify_listeners();
/* otherwise defer acknowledgement of this packet */
return;
@ -98,13 +105,16 @@ class File_system::Session_component : public Session_rpc_object
/* assume failure by default */
packet.succeeded(false);
try {
Node *node = _handle_registry.lookup_and_lock(packet.handle());
Node_lock_guard guard(node);
auto process_packet_fn = [&] (Open_node &open_node) {
_process_packet_op(packet, open_node);
};
_process_packet_op(packet, *node);
try {
_open_node_registry.apply<Open_node>(packet.handle(), process_packet_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
Genode::error("Invalid_handle");
tx_sink()->acknowledge_packet(packet);
}
catch (Invalid_handle) { Genode::error("Invalid_handle"); }
}
/**
@ -191,23 +201,35 @@ class File_system::Session_component : public Session_rpc_object
if (!valid_name(name.string()))
throw Invalid_name();
Directory *dir = _handle_registry.lookup_and_lock(dir_handle);
Node_lock_guard dir_guard(dir);
auto file_fn = [&] (Open_node &open_node) {
if (!_writable)
if (create || (mode != STAT_ONLY && mode != READ_ONLY))
throw Permission_denied();
Node &dir = open_node.node();
File *file = dir->file(name.string(), mode, create);
if (!_writable)
if (create || (mode != STAT_ONLY && mode != READ_ONLY))
throw Permission_denied();
Node_lock_guard file_guard(file);
return _handle_registry.alloc(file);
File *file = dir.file(name.string(), mode, create);
Open_node *open_file =
new (_md_alloc) Open_node(*file, _open_node_registry);
return open_file->id();
};
try {
return File_handle {
_open_node_registry.apply<Open_node>(dir_handle, file_fn).value
};
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create)
{
Genode::error(__func__, " not implemented");
return Symlink_handle();
throw Permission_denied();
}
Dir_handle dir(Path const &path, bool create)
@ -226,8 +248,11 @@ class File_system::Session_component : public Session_rpc_object
throw Name_too_long();
Directory *dir = _root.subdir(path_str, create);
Node_lock_guard guard(dir);
return _handle_registry.alloc(dir);
Open_node *open_dir =
new (_md_alloc) Open_node(*dir, _open_node_registry);
return Dir_handle { open_dir->id().value };
}
Node_handle node(Path const &path)
@ -238,43 +263,38 @@ class File_system::Session_component : public Session_rpc_object
Node *node = _root.node(path_str + 1);
Node_lock_guard guard(node);
return _handle_registry.alloc(node);
Open_node *open_node =
new (_md_alloc) Open_node(*node, _open_node_registry);
return open_node->id();
}
void close(Node_handle handle)
{
/* FIXME when to destruct node? */
_handle_registry.free(handle);
auto close_fn = [&] (Open_node &open_node) {
Node &node = open_node.node();
destroy(_md_alloc, &open_node);
destroy(_md_alloc, &node);
};
try {
_open_node_registry.apply<Open_node>(handle, close_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Status status(Node_handle node_handle)
{
Node *node = _handle_registry.lookup_and_lock(node_handle);
Node_lock_guard guard(node);
auto status_fn = [&] (Open_node &open_node) {
return open_node.node().status();
};
Status s;
s.inode = node->inode();
s.size = 0;
s.mode = 0;
File *file = dynamic_cast<File *>(node);
if (file) {
s.size = file->length();
s.mode = File_system::Status::MODE_FILE;
return s;
try {
return _open_node_registry.apply<Open_node>(node_handle, status_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
Directory *dir = dynamic_cast<Directory *>(node);
if (dir) {
s.size = dir->num_entries()*sizeof(Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
return s;
}
Genode::error(__func__, " for symlinks not implemented");
return Status();
}
void control(Node_handle, Control)
@ -292,9 +312,15 @@ class File_system::Session_component : public Session_rpc_object
if (!_writable)
throw Permission_denied();
File *file = _handle_registry.lookup_and_lock(file_handle);
Node_lock_guard file_guard(file);
file->truncate(size);
auto truncate_fn = [&] (Open_node &open_node) {
open_node.node().truncate(size);
};
try {
_open_node_registry.apply<Open_node>(file_handle, truncate_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
void move(Dir_handle, Name const &, Dir_handle, Name const &)
@ -311,7 +337,7 @@ class File_system::Session_component : public Session_rpc_object
};
class File_system::Root : public Root_component<Session_component>
class Lx_fs::Root : public Root_component<Session_component>
{
private:
@ -419,7 +445,7 @@ class File_system::Root : public Root_component<Session_component>
};
struct File_system::Main
struct Lx_fs::Main
{
Genode::Env &env;
@ -434,4 +460,4 @@ struct File_system::Main
};
void Component::construct(Genode::Env &env) { static File_system::Main inst(env); }
void Component::construct(Genode::Env &env) { static Lx_fs::Main inst(env); }

View File

@ -5,43 +5,70 @@
* \date 2013-11-11
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _NODE_H_
#define _NODE_H_
/* Genode includes */
#include <file_system/listener.h>
#include <file_system/node.h>
namespace File_system {
class Node : public Node_base
{
public:
typedef char Name[128];
private:
Name _name;
unsigned long const _inode;
public:
Node(unsigned long inode) : _inode(inode) { _name[0] = 0; }
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { Genode::strncpy(_name, name, sizeof(_name)); }
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
};
namespace Lx_fs {
using namespace File_system;
class Node;
class File;
}
class Lx_fs::Node : public File_system::Node_base
{
public:
typedef char Name[128];
private:
Name _name;
unsigned long const _inode;
public:
Node(unsigned long inode) : _inode(inode) { _name[0] = 0; }
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { Genode::strncpy(_name, name, sizeof(_name)); }
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
virtual Status status() = 0;
/*
* File functionality
*/
virtual void truncate(file_size_t size)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-file node");
}
/*
* Directory functionality
*/
virtual File *file(char const *name, Mode mode, bool create)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
};
#endif /* _NODE_H_ */

View File

@ -7,6 +7,13 @@
* FIXME unfinished
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SYMLINK_H_
#define _SYMLINK_H_
@ -26,18 +33,20 @@ class File_system::Symlink : public Node
char _link_to[MAX_PATH_LEN];
file_size_t _length() const { return strlen(_link_to) + 1; }
public:
Symlink(char const *name) { Node::name(name); }
size_t read(char *dst, size_t len, seek_off_t seek_offset)
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
size_t count = min(len, sizeof(_link_to) + 1);
Genode::strncpy(dst, _link_to, count);
return count;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
/* Ideal symlink operations are atomic. */
if (seek_offset) return 0;
@ -47,7 +56,14 @@ class File_system::Symlink : public Node
return count;
}
file_size_t length() const { return strlen(_link_to) + 1; }
Status status() override
{
Status s;
s.inode = inode();
s.size = _length();
s.mode = File_system::Status::MODE_SYMLINK;
return s;
}
};
#endif /* _SYMLINK_H_ */

View File

@ -0,0 +1,233 @@
/*
* \brief File-system directory node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__RAM_FS__DIRECTORY_H_
#define _INCLUDE__RAM_FS__DIRECTORY_H_
/* Genode includes */
#include <file_system/util.h>
/* local includes */
#include "node.h"
#include "file.h"
#include "symlink.h"
namespace Ram_fs { class Directory; }
class Ram_fs::Directory : public Node
{
private:
List<Node> _entries;
size_t _num_entries;
Node *_entry_unsynchronized(size_t index)
{
Node *node = _entries.first();
for (unsigned i = 0; i < index && node; node = node->next(), i++);
return node;
}
public:
Directory(char const *name) : _num_entries(0) { Node::name(name); }
bool has_sub_node_unsynchronized(char const *name) const override
{
Node const *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if (strcmp(sub_node->name(), name) == 0)
return true;
return false;
}
void adopt_unsynchronized(Node *node) override
{
/*
* XXX inc ref counter
*/
_entries.insert(node);
_num_entries++;
mark_as_updated();
}
void discard(Node *node) override
{
_entries.remove(node);
_num_entries--;
mark_as_updated();
}
Node *lookup(char const *path, bool return_parent = false) override
{
if (strcmp(path, "") == 0) {
return this;
}
if (!path || path[0] == '/')
throw File_system::Lookup_failed();
/* find first path delimiter */
unsigned i = 0;
for (; path[i] && path[i] != '/'; i++);
/*
* If no path delimiter was found, we are the parent of the
* specified path.
*/
if (path[i] == 0 && return_parent) {
return this;
}
/*
* The offset 'i' corresponds to the end of the first path
* element, which can be either the end of the string or the
* first '/' character.
*/
/* try to find entry that matches the first path element */
Node *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if ((strlen(sub_node->name()) == i) &&
(strcmp(sub_node->name(), path, i) == 0))
break;
if (!sub_node)
throw File_system::Lookup_failed();
if (!File_system::contains_path_delimiter(path)) {
/*
* Because 'path' is a basename that corresponds to an
* existing sub_node, we have found what we were looking
* for.
*/
return sub_node;
}
/*
* As 'path' contains one or more path delimiters, traverse
* into the sub directory names after the first path element.
*/
/*
* We cannot traverse into anything other than a directory.
*
* XXX we might follow symlinks here
*/
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
if (!sub_dir)
throw File_system::Lookup_failed();
return sub_dir->lookup(path + i + 1, return_parent);
}
Directory *lookup_dir(char const *path)
{
Node *node = lookup(path);
Directory *dir = dynamic_cast<Directory *>(node);
if (dir)
return dir;
throw File_system::Lookup_failed();
}
File *lookup_file(char const *path) override
{
Node *node = lookup(path);
File *file = dynamic_cast<File *>(node);
if (file)
return file;
throw File_system::Lookup_failed();
}
Symlink *lookup_symlink(char const *path)
{
Node *node = lookup(path);
Symlink *symlink = dynamic_cast<Symlink *>(node);
if (symlink)
return symlink;
throw File_system::Lookup_failed();
}
/**
* Lookup parent directory of the specified path
*
* \throw File_system::Lookup_failed
*/
Directory *lookup_parent(char const *path)
{
return static_cast<Directory *>(lookup(path, true));
}
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
using File_system::Directory_entry;
if (len < sizeof(Directory_entry)) {
Genode::error("read buffer too small for directory entry");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
if (seek_offset % sizeof(Directory_entry)) {
Genode::error("seek offset not alighed to sizeof(Directory_entry)");
return 0;
}
Node *node = _entry_unsynchronized(index);
/* index out of range */
if (!node)
return 0;
Directory_entry *e = (Directory_entry *)(dst);
e->inode = node->inode();
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
strncpy(e->name, node->name(), sizeof(e->name));
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
/* writing to directory nodes is not supported */
return 0;
}
Status status() override
{
Status s;
s.inode = inode();
s.size = _num_entries * sizeof(File_system::Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
return s;
}
};
#endif /* _INCLUDE__RAM_FS__DIRECTORY_H_ */

View File

@ -0,0 +1,132 @@
/*
* \brief File node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__RAM_FS__FILE_H_
#define _INCLUDE__RAM_FS__FILE_H_
/* Genode includes */
#include <file_system_session/file_system_session.h>
#include <base/allocator.h>
/* local includes */
#include <ram_fs/chunk.h>
#include "node.h"
namespace Ram_fs
{
using File_system::Chunk;
using File_system::Chunk_index;
using File_system::file_size_t;
using File_system::SEEK_TAIL;
class File;
}
class Ram_fs::File : public Node
{
private:
typedef Chunk<4096> Chunk_level_3;
typedef Chunk_index<128, Chunk_level_3> Chunk_level_2;
typedef Chunk_index<64, Chunk_level_2> Chunk_level_1;
typedef Chunk_index<64, Chunk_level_1> Chunk_level_0;
Chunk_level_0 _chunk;
file_size_t _length;
public:
File(Allocator &alloc, char const *name)
: _chunk(alloc, 0), _length(0) { Node::name(name); }
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
file_size_t const chunk_used_size = _chunk.used_size();
if (seek_offset == SEEK_TAIL)
seek_offset = (len < _length) ? (_length - len) : 0;
else if (seek_offset >= _length)
return 0;
/*
* Constrain read transaction to available chunk data
*
* Note that 'chunk_used_size' may be lower than '_length'
* because 'Chunk' may have truncated tailing zeros.
*/
if (seek_offset + len >= _length)
len = _length - seek_offset;
file_size_t read_len = len;
if (seek_offset + read_len > chunk_used_size) {
if (chunk_used_size >= seek_offset)
read_len = chunk_used_size - seek_offset;
else
read_len = 0;
}
_chunk.read(dst, read_len, seek_offset);
/* add zero padding if needed */
if (read_len < len)
memset(dst + read_len, 0, len - read_len);
return len;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
if (seek_offset == SEEK_TAIL)
seek_offset = _length;
if (seek_offset + len >= Chunk_level_0::SIZE) {
len = (Chunk_level_0::SIZE-1) - seek_offset;
Genode::error(name(), ": size limit ", (long)Chunk_level_0::SIZE, " reached");
}
_chunk.write(src, len, (size_t)seek_offset);
/*
* Keep track of file length. We cannot use 'chunk.used_size()'
* as file length because trailing zeros may by represented
* by zero chunks, which do not contribute to 'used_size()'.
*/
_length = max(_length, seek_offset + len);
mark_as_updated();
return len;
}
Status status() override
{
Status s;
s.inode = inode();
s.size = _length;
s.mode = File_system::Status::MODE_FILE;
return s;
}
void truncate(file_size_t size) override
{
if (size < _chunk.used_size())
_chunk.truncate(size);
_length = size;
mark_as_updated();
}
};
#endif /* _INCLUDE__RAM_FS__FILE_H_ */

View File

@ -12,7 +12,7 @@
*/
/* Genode includes */
#include <file_system/node_handle_registry.h>
#include <file_system/open_node.h>
#include <file_system_session/rpc_object.h>
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
@ -21,14 +21,18 @@
#include <os/session_policy.h>
/* local includes */
#include <ram_fs/directory.h>
#include "directory.h"
/*************************
** File-system service **
*************************/
namespace File_system {
namespace Ram_fs {
using namespace File_system;
using File_system::Packet_descriptor;
using File_system::Path;
class Session_component;
class Root;
@ -36,16 +40,18 @@ namespace File_system {
};
class File_system::Session_component : public Session_rpc_object
class Ram_fs::Session_component : public File_system::Session_rpc_object
{
private:
Genode::Entrypoint &_ep;
Genode::Ram_session &_ram;
Genode::Allocator &_alloc;
Directory &_root;
Node_handle_registry _handle_registry;
bool _writable;
typedef File_system::Open_node<Node> Open_node;
Genode::Entrypoint &_ep;
Genode::Ram_session &_ram;
Genode::Allocator &_alloc;
Directory &_root;
Id_space<File_system::Node> _open_node_registry;
bool _writable;
Signal_handler<Session_component> _process_packet_handler;
@ -59,7 +65,7 @@ class File_system::Session_component : public Session_rpc_object
*
* \return true on success, false on failure
*/
void _process_packet_op(Packet_descriptor &packet, Node &node)
void _process_packet_op(Packet_descriptor &packet, Open_node &open_node)
{
void * const content = tx_sink()->packet_content(packet);
size_t const length = packet.length();
@ -71,17 +77,17 @@ class File_system::Session_component : public Session_rpc_object
case Packet_descriptor::READ:
if (content && (packet.length() <= packet.size()))
res_length = node.read((char *)content, length, packet.position());
res_length = open_node.node().read((char *)content, length, packet.position());
break;
case Packet_descriptor::WRITE:
if (content && (packet.length() <= packet.size()))
res_length = node.write((char const *)content, length, packet.position());
res_length = open_node.node().write((char const *)content, length, packet.position());
break;
case Packet_descriptor::CONTENT_CHANGED:
_handle_registry.register_notify(*tx_sink(), packet.handle());
node.notify_listeners();
open_node.register_notify(*tx_sink());
open_node.node().notify_listeners();
return;
case Packet_descriptor::READ_READY:
@ -101,13 +107,13 @@ class File_system::Session_component : public Session_rpc_object
/* assume failure by default */
packet.succeeded(false);
try {
Node *node = _handle_registry.lookup_and_lock(packet.handle());
Node_lock_guard guard(node);
auto process_packet_fn = [&] (Open_node &open_node) {
_process_packet_op(packet, open_node);
};
_process_packet_op(packet, *node);
}
catch (Invalid_handle) {
try {
_open_node_registry.apply<Open_node>(packet.handle(), process_packet_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
Genode::error("Invalid_handle");
tx_sink()->acknowledge_packet(packet);
}
@ -193,33 +199,46 @@ class File_system::Session_component : public Session_rpc_object
if (!valid_name(name.string()))
throw Invalid_name();
Directory *dir = _handle_registry.lookup_and_lock(dir_handle);
Node_lock_guard dir_guard(dir);
auto file_fn = [&] (Open_node &open_node) {
if (!_writable)
if (mode != STAT_ONLY && mode != READ_ONLY)
throw Permission_denied();
if (create) {
Node &dir = open_node.node();
if (!_writable)
throw Permission_denied();
if (mode != STAT_ONLY && mode != READ_ONLY)
throw Permission_denied();
if (dir->has_sub_node_unsynchronized(name.string()))
throw Node_already_exists();
if (create) {
try {
File * const file = new (_alloc)
File(_alloc, name.string());
if (!_writable)
throw Permission_denied();
dir->adopt_unsynchronized(file);
if (dir.has_sub_node_unsynchronized(name.string()))
throw Node_already_exists();
try {
File * const file = new (_alloc)
File(_alloc, name.string());
dir.adopt_unsynchronized(file);
}
catch (Allocator::Out_of_memory) { throw No_space(); }
}
catch (Allocator::Out_of_memory) { throw No_space(); }
}
File *file = dir->lookup_and_lock_file(name.string());
Node_lock_guard file_guard(file);
return _handle_registry.alloc(file);
File *file = dir.lookup_file(name.string());
Open_node *open_file =
new (_alloc) Open_node(*file, _open_node_registry);
return open_file->id();
};
try {
return File_handle {
_open_node_registry.apply<Open_node>(dir_handle, file_fn).value
};
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create)
@ -227,29 +246,42 @@ class File_system::Session_component : public Session_rpc_object
if (!valid_name(name.string()))
throw Invalid_name();
Directory *dir = _handle_registry.lookup_and_lock(dir_handle);
Node_lock_guard dir_guard(dir);
auto symlink_fn = [&] (Open_node &open_node) {
if (create) {
Node &dir = open_node.node();
if (!_writable)
throw Permission_denied();
if (create) {
if (dir->has_sub_node_unsynchronized(name.string()))
throw Node_already_exists();
if (!_writable)
throw Permission_denied();
try {
Symlink * const symlink = new (_alloc)
Symlink(name.string());
if (dir.has_sub_node_unsynchronized(name.string()))
throw Node_already_exists();
dir->adopt_unsynchronized(symlink);
try {
Symlink * const symlink = new (_alloc)
Symlink(name.string());
dir.adopt_unsynchronized(symlink);
}
catch (Allocator::Out_of_memory) { throw No_space(); }
}
catch (Allocator::Out_of_memory) { throw No_space(); }
}
Symlink *symlink = dir->lookup_and_lock_symlink(name.string());
Node_lock_guard file_guard(symlink);
return _handle_registry.alloc(symlink);
Symlink *symlink = dir.lookup_symlink(name.string());
Open_node *open_symlink =
new (_alloc) Open_node(*symlink, _open_node_registry);
return open_symlink->id();
};
try {
return Symlink_handle {
_open_node_registry.apply<Open_node>(dir_handle, symlink_fn).value
};
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Dir_handle dir(Path const &path, bool create)
@ -269,9 +301,7 @@ class File_system::Session_component : public Session_rpc_object
if (!path.valid_string())
throw Name_too_long();
Directory *parent = _root.lookup_and_lock_parent(path_str);
Node_lock_guard guard(parent);
Directory *parent = _root.lookup_parent(path_str);
char const *name = basename(path_str);
@ -285,55 +315,61 @@ class File_system::Session_component : public Session_rpc_object
}
}
Directory *dir = _root.lookup_and_lock_dir(path_str);
Node_lock_guard guard(dir);
return _handle_registry.alloc(dir);
Directory *dir = _root.lookup_dir(path_str);
Open_node *open_dir =
new (_alloc) Open_node(*dir, _open_node_registry);
return Dir_handle { open_dir->id().value };
}
Node_handle node(Path const &path)
{
_assert_valid_path(path.string());
Node *node = _root.lookup_and_lock(path.string() + 1);
Node *node = _root.lookup(path.string() + 1);
Node_lock_guard guard(node);
return _handle_registry.alloc(node);
Open_node *open_node =
new (_alloc) Open_node(*node, _open_node_registry);
return open_node->id();
}
void close(Node_handle handle)
{
_handle_registry.free(handle);
auto close_fn = [&] (Open_node &open_node) {
Node &node = open_node.node();
/*
* Notify listeners about the changed file.
*/
node.notify_listeners();
/*
* De-allocate handle
*/
destroy(_alloc, &open_node);
};
try {
_open_node_registry.apply<Open_node>(handle, close_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Status status(Node_handle node_handle)
{
Node *node = _handle_registry.lookup_and_lock(node_handle);
Node_lock_guard guard(node);
auto status_fn = [&] (Open_node &open_node) {
return open_node.node().status();
};
Status s;
s.inode = node->inode();
s.size = 0;
s.mode = 0;
File *file = dynamic_cast<File *>(node);
if (file) {
s.size = file->length();
s.mode = File_system::Status::MODE_FILE;
return s;
try {
return _open_node_registry.apply<Open_node>(node_handle, status_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
Directory *dir = dynamic_cast<Directory *>(node);
if (dir) {
s.size = dir->num_entries()*sizeof(Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
return s;
}
Symlink *symlink = dynamic_cast<Symlink *>(node);
if (symlink) {
s.size = symlink->length();
s.mode = File_system::Status::MODE_SYMLINK;
return s;
}
return Status();
}
void control(Node_handle, Control) { }
@ -346,18 +382,25 @@ class File_system::Session_component : public Session_rpc_object
if (!_writable)
throw Permission_denied();
Directory *dir = _handle_registry.lookup_and_lock(dir_handle);
Node_lock_guard dir_guard(dir);
auto unlink_fn = [&] (Open_node &open_node) {
Node *node = dir->lookup_and_lock(name.string());
Node &dir = open_node.node();
dir->discard_unsynchronized(node);
Node *node = dir.lookup(name.string());
// XXX implement ref counting, do not destroy node that is
// is still referenced by a node handle
dir.discard(node);
node->unlock();
destroy(_alloc, node);
// XXX implement ref counting, do not destroy node that is
// is still referenced by a node handle
destroy(_alloc, node);
};
try {
_open_node_registry.apply<Open_node>(dir_handle, unlink_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
void truncate(File_handle file_handle, file_size_t size)
@ -365,9 +408,15 @@ class File_system::Session_component : public Session_rpc_object
if (!_writable)
throw Permission_denied();
File *file = _handle_registry.lookup_and_lock(file_handle);
Node_lock_guard file_guard(file);
file->truncate(size);
auto truncate_fn = [&] (Open_node &open_node) {
open_node.node().truncate(size);
};
try {
_open_node_registry.apply<Open_node>(file_handle, truncate_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
void move(Dir_handle from_dir_handle, Name const &from_name,
@ -382,47 +431,69 @@ class File_system::Session_component : public Session_rpc_object
if (!valid_name(to_name.string()))
throw Invalid_name();
Directory *from_dir = _handle_registry.lookup_and_lock(from_dir_handle);
Node_lock_guard from_dir_guard(from_dir);
auto move_fn = [&] (Open_node &open_from_dir_node) {
Node *node = from_dir->lookup_and_lock(from_name.string());
Node_lock_guard node_guard(node);
node->name(to_name.string());
auto inner_move_fn = [&] (Open_node &open_to_dir_node) {
if (!_handle_registry.refer_to_same_node(from_dir_handle, to_dir_handle)) {
Directory *to_dir = _handle_registry.lookup_and_lock(to_dir_handle);
Node_lock_guard to_dir_guard(to_dir);
Node &from_dir = open_from_dir_node.node();
from_dir->discard_unsynchronized(node);
to_dir->adopt_unsynchronized(node);
Node *node = from_dir.lookup(from_name.string());
node->name(to_name.string());
/*
* If the file was moved from one directory to another we
* need to inform the new directory 'to_dir'. The original
* directory 'from_dir' will always get notified (i.e.,
* when just the file name was changed) below.
*/
to_dir->mark_as_updated();
to_dir->notify_listeners();
Node &to_dir = open_to_dir_node.node();
if (&to_dir != &from_dir) {
from_dir.discard(node);
to_dir.adopt_unsynchronized(node);
/*
* If the file was moved from one directory to another we
* need to inform the new directory 'to_dir'. The original
* directory 'from_dir' will always get notified (i.e.,
* when just the file name was changed) below.
*/
to_dir.mark_as_updated();
to_dir.notify_listeners();
from_dir.mark_as_updated();
from_dir.notify_listeners();
node->mark_as_updated();
node->notify_listeners();
}
};
try {
_open_node_registry.apply<Open_node>(to_dir_handle, inner_move_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
};
try {
_open_node_registry.apply<Open_node>(from_dir_handle, move_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
from_dir->mark_as_updated();
from_dir->notify_listeners();
node->mark_as_updated();
node->notify_listeners();
}
void sync(Node_handle handle) override
{
Node *node = _handle_registry.lookup_and_lock(handle);
Node_lock_guard guard(node);
node->notify_listeners();
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();
}
}
};
class File_system::Root : public Root_component<Session_component>
class Ram_fs::Root : public Root_component<Session_component>
{
private:
@ -493,9 +564,8 @@ class File_system::Root : public Root_component<Session_component>
* delimiter. For performing the lookup, we skip the first
* character.
*/
session_root_dir = _root_dir.lookup_and_lock_dir(
session_root_dir = _root_dir.lookup_dir(
session_root.base() + 1);
session_root_dir->unlock();
}
catch (Lookup_failed) { throw Service_denied(); }
}
@ -588,7 +658,7 @@ struct Attribute_string
static void preload_content(Genode::Env &env,
Genode::Allocator &alloc,
Genode::Xml_node node,
File_system::Directory &dir)
Ram_fs::Directory &dir)
{
using namespace File_system;
@ -606,7 +676,7 @@ static void preload_content(Genode::Env &env,
*/
if (sub_node.has_type("dir")) {
Directory *sub_dir = new (&alloc) Directory(name);
Ram_fs::Directory *sub_dir = new (&alloc) Ram_fs::Directory(name);
/* traverse into the new directory */
preload_content(env, alloc, sub_node, *sub_dir);
@ -626,7 +696,7 @@ static void preload_content(Genode::Env &env,
try {
Attached_rom_dataspace rom(env, name);
File *file = new (&alloc) File(alloc, as);
Ram_fs::File *file = new (&alloc) Ram_fs::File(alloc, as);
file->write(rom.local_addr<char>(), rom.size(), 0);
dir.adopt_unsynchronized(file);
}
@ -641,7 +711,7 @@ static void preload_content(Genode::Env &env,
*/
if (sub_node.has_type("inline")) {
File *file = new (&alloc) File(alloc, name);
Ram_fs::File *file = new (&alloc) Ram_fs::File(alloc, name);
file->write(sub_node.content_addr(), sub_node.content_size(), 0);
dir.adopt_unsynchronized(file);
}
@ -649,7 +719,7 @@ static void preload_content(Genode::Env &env,
}
struct File_system::Main
struct Ram_fs::Main
{
Genode::Env &_env;
@ -679,4 +749,4 @@ struct File_system::Main
};
void Component::construct(Genode::Env &env) { static File_system::Main inst(env); }
void Component::construct(Genode::Env &env) { static Ram_fs::Main inst(env); }

View File

@ -0,0 +1,120 @@
/*
* \brief File-system node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__RAM_FS__NODE_H_
#define _INCLUDE__RAM_FS__NODE_H_
/* Genode includes */
#include <file_system/listener.h>
#include <file_system/node.h>
#include <util/list.h>
namespace Ram_fs {
using namespace Genode;
using File_system::seek_off_t;
using File_system::Status;
class Node;
class File;
class Symlink;
}
class Ram_fs::Node : public File_system::Node_base, public List<Node>::Element
{
public:
typedef char Name[128];
private:
int _ref_count;
Name _name;
unsigned long const _inode;
/**
* Generate unique inode number
*/
static unsigned long _unique_inode()
{
static unsigned long inode_count;
return ++inode_count;
}
public:
Node()
: _ref_count(0), _inode(_unique_inode())
{ _name[0] = 0; }
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
virtual Status status() = 0;
/* File functionality */
virtual void truncate(File_system::file_size_t size)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-file node");
}
/* Directory functionality */
virtual bool has_sub_node_unsynchronized(char const *name) const
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return false;
}
virtual void adopt_unsynchronized(Node *node)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
}
virtual File *lookup_file(char const *path)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
virtual Symlink *lookup_symlink(char const *path)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
virtual Node *lookup(char const *path, bool return_parent = false)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
virtual void discard(Node *node)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
}
};
#endif /* _INCLUDE__RAM_FS__NODE_H_ */

View File

@ -0,0 +1,61 @@
/*
* \brief Symlink file-system node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__RAM_FS__SYMLINK_H_
#define _INCLUDE__RAM_FS__SYMLINK_H_
/* local includes */
#include "node.h"
namespace Ram_fs { class Symlink; }
class Ram_fs::Symlink : public Node
{
private:
char _link_to[File_system::MAX_PATH_LEN];
size_t _len;
public:
Symlink(char const *name): _len(0) { Node::name(name); }
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
size_t count = min(len, _len-seek_offset);
Genode::memcpy(dst, _link_to+seek_offset, count);
return count;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
/* Ideal symlink operations are atomic. */
if (seek_offset) return 0;
_len = min(len, sizeof(_link_to));
Genode::memcpy(_link_to, src, _len);
return _len;
}
Status status() override
{
Status s;
s.inode = inode();
s.size = _len;
s.mode = File_system::Status::MODE_SYMLINK;
return s;
}
};
#endif /* _INCLUDE__RAM_FS__SYMLINK_H_ */

View File

@ -4,6 +4,13 @@
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
@ -15,200 +22,201 @@
#include <file.h>
#include <symlink.h>
namespace File_system {
namespace Trace_fs {
class Directory;
}
class Directory : public Node
{
private:
class Trace_fs::Directory : public Node
{
private:
List<Node> _entries;
size_t _num_entries;
List<Node> _entries;
size_t _num_entries;
public:
public:
Directory(char const *name) : _num_entries(0) { Node::name(name); }
Directory(char const *name) : _num_entries(0) { Node::name(name); }
/**
* Check if the directory has the specified subnode
*
* \param name name of the searched subnode
*
* \return true if the subnode was found, either false
/**
* Check if the directory has the specified subnode
*
* \param name name of the searched subnode
*
* \return true if the subnode was found, either false
*/
bool has_sub_node_unsynchronized(char const *name) const
{
Node const *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if (strcmp(sub_node->name(), name) == 0)
return true;
return false;
}
/**
* Add node to the list of subnodes
*
* \param pointer to node
*/
void adopt_unsynchronized(Node *node)
{
/*
* XXX inc ref counter
*/
bool has_sub_node_unsynchronized(char const *name) const
{
Node const *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if (strcmp(sub_node->name(), name) == 0)
return true;
_entries.insert(node);
_num_entries++;
return false;
mark_as_updated();
}
/**
* Remove the node from the list of subnodes
*
* \param node pointer to node
*/
void discard_unsynchronized(Node *node)
{
_entries.remove(node);
_num_entries--;
mark_as_updated();
}
/**
* Lookup node which belongs to the specified path
*
* \param path path to lookup
* \param return_parent if true return parent node, otherwise
* actual path node
*
* \return node node founc
* \throws Lookup_failed
*/
Node *lookup(char const *path, bool return_parent = false) override
{
if (strcmp(path, "") == 0) {
return this;
}
if (!path || path[0] == '/')
throw Lookup_failed();
/**
* Add node to the list of subnodes
*
* \param pointer to node
/* find first path delimiter */
unsigned i = 0;
for (; path[i] && path[i] != '/'; i++);
/*
* If no path delimiter was found, we are the parent of the
* specified path.
*/
void adopt_unsynchronized(Node *node)
{
/*
* XXX inc ref counter
*/
_entries.insert(node);
_num_entries++;
mark_as_updated();
if (path[i] == 0 && return_parent) {
return this;
}
/**
* Remove the node from the list of subnodes
*
* \param node pointer to node
/*
* The offset 'i' corresponds to the end of the first path
* element, which can be either the end of the string or the
* first '/' character.
*/
void discard_unsynchronized(Node *node)
{
_entries.remove(node);
_num_entries--;
mark_as_updated();
/* try to find entry that matches the first path element */
Node *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if ((strlen(sub_node->name()) == i) &&
(strcmp(sub_node->name(), path, i) == 0))
break;
if (!sub_node)
throw Lookup_failed();
if (!contains_path_delimiter(path)) {
/*
* Because 'path' is a basename that corresponds to an
* existing sub_node, we have found what we were looking
* for.
*/
return sub_node;
}
/**
* Lookup node which belongs to the specified path
*
* \param path path to lookup
* \param return_parent if true return parent node, otherwise
* actual path node
*
* \return node node founc
* \throws Lookup_failed
/*
* As 'path' contains one or more path delimiters, traverse
* into the sub directory names after the first path element.
*/
Node *lookup(char const *path, bool return_parent = false)
{
if (strcmp(path, "") == 0) {
return this;
}
if (!path || path[0] == '/')
throw Lookup_failed();
/* find first path delimiter */
unsigned i = 0;
for (; path[i] && path[i] != '/'; i++);
/*
* If no path delimiter was found, we are the parent of the
* specified path.
*/
if (path[i] == 0 && return_parent) {
return this;
}
/*
* The offset 'i' corresponds to the end of the first path
* element, which can be either the end of the string or the
* first '/' character.
*/
/* try to find entry that matches the first path element */
Node *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if ((strlen(sub_node->name()) == i) &&
(strcmp(sub_node->name(), path, i) == 0))
break;
if (!sub_node)
throw Lookup_failed();
if (!contains_path_delimiter(path)) {
/*
* Because 'path' is a basename that corresponds to an
* existing sub_node, we have found what we were looking
* for.
*/
return sub_node;
}
/*
* As 'path' contains one or more path delimiters, traverse
* into the sub directory names after the first path element.
*/
/*
* We cannot traverse into anything other than a directory.
*
* XXX we might follow symlinks here
*/
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
if (!sub_dir)
throw Lookup_failed();
return sub_dir->lookup(path + i + 1, return_parent);
}
/**
* Return number of subnodes
/*
* We cannot traverse into anything other than a directory.
*
* XXX we might follow symlinks here
*/
size_t num_entries() const { return _num_entries; }
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
if (!sub_dir)
throw Lookup_failed();
return sub_dir->lookup(path + i + 1, return_parent);
}
/**
* Return number of subnodes
*/
size_t num_entries() const { return _num_entries; }
/********************
** Node interface **
********************/
/********************
** Node interface **
********************/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len < sizeof(Directory_entry)) {
Genode::error("read buffer too small for directory entry");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
if (seek_offset % sizeof(Directory_entry)) {
Genode::error("seek offset not alighed to sizeof(Directory_entry)");
return 0;
}
/* find list element */
Node *node = _entries.first();
for (unsigned i = 0; i < index && node; node = node->next(), i++);
/* index out of range */
if (!node)
return 0;
Directory_entry *e = (Directory_entry *)(dst);
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
strncpy(e->name, node->name(), sizeof(e->name));
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
/* writing to directory nodes is not supported */
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len < sizeof(Directory_entry)) {
Genode::error("read buffer too small for directory entry");
return 0;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = _num_entries * sizeof (Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
seek_off_t index = seek_offset / sizeof(Directory_entry);
return s;
if (seek_offset % sizeof(Directory_entry)) {
Genode::error("seek offset not alighed to sizeof(Directory_entry)");
return 0;
}
};
}
/* find list element */
Node *node = _entries.first();
for (unsigned i = 0; i < index && node; node = node->next(), i++);
/* index out of range */
if (!node)
return 0;
Directory_entry *e = (Directory_entry *)(dst);
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
strncpy(e->name, node->name(), sizeof(e->name));
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
/* writing to directory nodes is not supported */
return 0;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = _num_entries * sizeof (Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
return s;
}
};
#endif /* _DIRECTORY_H_ */

View File

@ -23,195 +23,198 @@
#include <node.h>
#include <chunk.h>
namespace File_system {
/**
*
*
*/
class Changeable_content
{
protected:
/**
* This member is used to communicate the state and
* must be set true by classes using this class in case
* the content has changed.
*/
bool _changed;
/**
* This method is called when the content change is
* acknowledged. It may be overriden by any class using
* this particular class.
*/
virtual void _refresh_content() { }
public:
Changeable_content() : _changed(false) { }
/**
* Check if the content was changed
*
* This evaluation has to be made by classes using this
* particular class.
*
* \return true if changed, otherwise false
*/
bool changed() const { return _changed; }
/**
* Acknowledge the content has changed
*/
void acknowledge_change()
{
_changed = false;
_refresh_content();
}
};
/**
* File interface
*/
class File : public Node
{
public:
File(char const *name)
{
Node::name(name);
}
virtual ~File() { }
/********************
** Node interface **
********************/
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset) = 0;
virtual Status status() const = 0;
/********************
** File interface **
********************/
virtual file_size_t length() const = 0;
virtual void truncate(file_size_t size) = 0;
};
/**
* Memory buffered file
*
* This file merely exists in memory and grows automatically.
*/
class Buffered_file : public File
{
private:
typedef Chunk<4096> Chunk_level_3;
typedef Chunk_index<128, Chunk_level_3> Chunk_level_2;
typedef Chunk_index<64, Chunk_level_2> Chunk_level_1;
typedef Chunk_index<64, Chunk_level_1> Chunk_level_0;
Chunk_level_0 _chunk;
file_size_t _length;
public:
Buffered_file(Allocator &alloc, char const *name)
: File(name), _chunk(alloc, 0), _length(0) { }
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
file_size_t const chunk_used_size = _chunk.used_size();
if (seek_offset >= _length)
return 0;
/*
* Constrain read transaction to available chunk data
*
* Note that 'chunk_used_size' may be lower than '_length'
* because 'Chunk' may have truncated tailing zeros.
*/
if (seek_offset + len >= _length)
len = _length - seek_offset;
file_size_t read_len = len;
if (seek_offset + read_len > chunk_used_size) {
if (chunk_used_size >= seek_offset)
read_len = chunk_used_size - seek_offset;
else
read_len = 0;
}
_chunk.read(dst, read_len, seek_offset);
/* add zero padding if needed */
if (read_len < len)
memset(dst + read_len, 0, len - read_len);
return len;
}
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
if (seek_offset == (seek_off_t)(~0))
seek_offset = _chunk.used_size();
if (seek_offset + len >= Chunk_level_0::SIZE) {
len = (Chunk_level_0::SIZE-1) - seek_offset;
Genode::error(name(), ": size limit ", (long)Chunk_level_0::SIZE, " reached");
}
_chunk.write(src, len, (size_t)seek_offset);
/*
* Keep track of file length. We cannot use 'chunk.used_size()'
* as file length because trailing zeros may by represented
* by zero chunks, which do not contribute to 'used_size()'.
*/
_length = max(_length, seek_offset + len);
mark_as_updated();
return len;
}
virtual Status status() const
{
Status s;
s.inode = inode();
s.size = _length;
s.mode = File_system::Status::MODE_FILE;
return s;
}
virtual file_size_t length() const { return _length; }
void truncate(file_size_t size)
{
if (size < _chunk.used_size())
_chunk.truncate(size);
_length = size;
mark_as_updated();
}
};
namespace Trace_fs {
class Changeable_content;
class File;
class Buffered_file;
}
/**
*
*
*/
class Trace_fs::Changeable_content
{
protected:
/**
* This member is used to communicate the state and
* must be set true by classes using this class in case
* the content has changed.
*/
bool _changed;
/**
* This method is called when the content change is
* acknowledged. It may be overriden by any class using
* this particular class.
*/
virtual void _refresh_content() { }
public:
Changeable_content() : _changed(false) { }
/**
* Check if the content was changed
*
* This evaluation has to be made by classes using this
* particular class.
*
* \return true if changed, otherwise false
*/
bool changed() const { return _changed; }
/**
* Acknowledge the content has changed
*/
void acknowledge_change()
{
_changed = false;
_refresh_content();
}
};
/**
* File interface
*/
class Trace_fs::File : public Node
{
public:
File(char const *name)
{
Node::name(name);
}
virtual ~File() { }
/********************
** Node interface **
********************/
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset) = 0;
virtual Status status() const = 0;
/********************
** File interface **
********************/
virtual file_size_t length() const = 0;
virtual void truncate(file_size_t size) = 0;
};
/**
* Memory buffered file
*
* This file merely exists in memory and grows automatically.
*/
class Trace_fs::Buffered_file : public File
{
private:
typedef Chunk<4096> Chunk_level_3;
typedef Chunk_index<128, Chunk_level_3> Chunk_level_2;
typedef Chunk_index<64, Chunk_level_2> Chunk_level_1;
typedef Chunk_index<64, Chunk_level_1> Chunk_level_0;
Chunk_level_0 _chunk;
file_size_t _length;
public:
Buffered_file(Allocator &alloc, char const *name)
: File(name), _chunk(alloc, 0), _length(0) { }
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
file_size_t const chunk_used_size = _chunk.used_size();
if (seek_offset >= _length)
return 0;
/*
* Constrain read transaction to available chunk data
*
* Note that 'chunk_used_size' may be lower than '_length'
* because 'Chunk' may have truncated tailing zeros.
*/
if (seek_offset + len >= _length)
len = _length - seek_offset;
file_size_t read_len = len;
if (seek_offset + read_len > chunk_used_size) {
if (chunk_used_size >= seek_offset)
read_len = chunk_used_size - seek_offset;
else
read_len = 0;
}
_chunk.read(dst, read_len, seek_offset);
/* add zero padding if needed */
if (read_len < len)
memset(dst + read_len, 0, len - read_len);
return len;
}
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
if (seek_offset == (seek_off_t)(~0))
seek_offset = _chunk.used_size();
if (seek_offset + len >= Chunk_level_0::SIZE) {
len = (Chunk_level_0::SIZE-1) - seek_offset;
Genode::error(name(), ": size limit ", (long)Chunk_level_0::SIZE, " reached");
}
_chunk.write(src, len, (size_t)seek_offset);
/*
* Keep track of file length. We cannot use 'chunk.used_size()'
* as file length because trailing zeros may by represented
* by zero chunks, which do not contribute to 'used_size()'.
*/
_length = max(_length, seek_offset + len);
mark_as_updated();
return len;
}
virtual Status status() const
{
Status s;
s.inode = inode();
s.size = _length;
s.mode = File_system::Status::MODE_FILE;
return s;
}
virtual file_size_t length() const { return _length; }
void truncate(file_size_t size) override
{
if (size < _chunk.used_size())
_chunk.truncate(size);
_length = size;
mark_as_updated();
}
};
#endif /* _FILE_H_ */

View File

@ -22,261 +22,262 @@
#include <trace_files.h>
namespace Trace_fs {
typedef Genode::size_t size_t;
struct Followed_subject : public File_system::Directory
{
public:
/**
* This class manages the access to the trace subject's trace buffer
*/
class Trace_buffer_manager
{
public:
class Already_managed { };
class Not_managed { };
struct Process_entry
{
virtual size_t operator()(Genode::Trace::Buffer::Entry&) = 0;
};
private:
Genode::Trace::Buffer *buffer;
Genode::Trace::Buffer::Entry current_entry;
public:
Trace_buffer_manager(Genode::Region_map &rm,
Genode::Dataspace_capability ds_cap)
:
buffer(rm.attach(ds_cap)),
current_entry(buffer->first())
{ }
size_t dump_entry(Process_entry &process)
{
size_t len = process(current_entry);
current_entry = buffer->next(current_entry);
return len;
}
bool last_entry() const
{
return current_entry.last();
}
void rewind() { current_entry = buffer->first(); }
};
private:
Genode::Allocator &_md_alloc;
Genode::Region_map &_rm;
int _handle;
Genode::Trace::Subject_id _id;
Genode::Trace::Policy_id _policy_id;
bool _was_traced;
Trace_buffer_manager *_buffer_manager;
public:
File_system::Active_file active_file;
File_system::Buffer_size_file buffer_size_file;
File_system::Cleanup_file cleanup_file;
File_system::Enable_file enable_file;
File_system::Events_file events_file;
File_system::Policy_file policy_file;
Followed_subject(Genode::Allocator &md_alloc, char const *name,
Genode::Region_map &rm,
Genode::Trace::Subject_id &id, int handle)
:
Directory(name),
_md_alloc(md_alloc),
_rm(rm),
_handle(handle),
_id(id),
_was_traced(false),
_buffer_manager(0),
active_file(_id),
buffer_size_file(),
cleanup_file(_id),
enable_file(_id),
events_file(_id, _md_alloc),
policy_file(_id, _md_alloc)
{
adopt_unsynchronized(&active_file);
adopt_unsynchronized(&cleanup_file);
adopt_unsynchronized(&enable_file);
adopt_unsynchronized(&events_file);
adopt_unsynchronized(&buffer_size_file);
adopt_unsynchronized(&policy_file);
}
~Followed_subject()
{
discard_unsynchronized(&active_file);
discard_unsynchronized(&cleanup_file);
discard_unsynchronized(&enable_file);
discard_unsynchronized(&events_file);
discard_unsynchronized(&buffer_size_file);
discard_unsynchronized(&policy_file);
}
bool marked_for_cleanup() const { return cleanup_file.cleanup(); }
bool was_traced() const { return _was_traced; }
Trace_buffer_manager* trace_buffer_manager() { return _buffer_manager; }
void manage_trace_buffer(Genode::Dataspace_capability ds_cap)
{
if (_buffer_manager != 0)
throw Trace_buffer_manager::Already_managed();
_buffer_manager = new (&_md_alloc) Trace_buffer_manager(_rm, ds_cap);
}
void unmanage_trace_buffer()
{
if (_buffer_manager == 0)
throw Trace_buffer_manager::Not_managed();
destroy(&_md_alloc, _buffer_manager);
_buffer_manager = 0;
}
const Genode::Trace::Subject_id id() const { return _id; }
const Genode::Trace::Policy_id policy_id() const { return _policy_id; }
void policy_id(Genode::Trace::Policy_id &id) { _policy_id.id = id.id; }
bool policy_valid() const { return (_policy_id.id != 0); }
void invalidate_policy() { _policy_id = Genode::Trace::Policy_id(); }
int handle() const { return _handle; }
};
/**
* This registry contains all current followed trace subjects
*/
class Followed_subject_registry
{
public:
class Invalid_subject { };
class Out_of_subject_handles { };
private:
/* XXX abitrary limit - needs to be revisited when highly
* dynamic scenarios are executed */
enum { MAX_SUBJECTS = 1024U };
Followed_subject *_subjects[MAX_SUBJECTS];
Genode::Allocator &_md_alloc;
/**
* Find free subject handle
*
* \throw Out_of_subject_handles
*/
int _find_free_handle()
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
if (!_subjects[i]) {
return i;
}
throw Out_of_subject_handles();
}
bool _in_range(int handle) const
{
return ((handle >= 0) && (handle < MAX_SUBJECTS));
}
public:
Followed_subject_registry(Genode::Allocator &md_alloc)
:
_md_alloc(md_alloc)
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
_subjects[i] = 0;
}
/**
* Return maximal number of subject that can be stored in the registry
*
* \return maximal number of subjects
*/
unsigned max_subjects() const { return MAX_SUBJECTS; }
/**
* Allocate new subject
*
* \param name name of subject
* \param id subject id of tracre subject
*/
Followed_subject *alloc(char const *name, Genode::Trace::Subject_id &id,
Genode::Region_map &rm)
{
int handle = _find_free_handle();
_subjects[handle] = new (&_md_alloc) Followed_subject(_md_alloc, name, rm, id, handle);
return _subjects[handle];
}
/**
* Free subject
*
* \param subject pointer to subject
*/
void free(Followed_subject *subject)
{
int handle = subject->handle();
if (!_in_range(handle))
return;
if(!_subjects[handle])
return;
_subjects[handle] = 0;
destroy(&_md_alloc, subject);
}
/**
* Lookup subject by using the id
*
* \throw Invalid_subject();
*/
Followed_subject *lookup(Genode::Trace::Subject_id const &sid)
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
if (_subjects[i]) {
if (_subjects[i]->id().id == sid.id)
return _subjects[i];
}
throw Invalid_subject();
}
};
class Followed_subject;
class Followed_subject_registry;
}
class Trace_fs::Followed_subject : public Directory
{
public:
/**
* This class manages the access to the trace subject's trace buffer
*/
class Trace_buffer_manager
{
public:
class Already_managed { };
class Not_managed { };
struct Process_entry
{
virtual size_t operator()(Genode::Trace::Buffer::Entry&) = 0;
};
private:
Genode::Trace::Buffer *buffer;
Genode::Trace::Buffer::Entry current_entry;
public:
Trace_buffer_manager(Genode::Region_map &rm,
Genode::Dataspace_capability ds_cap)
:
buffer(rm.attach(ds_cap)),
current_entry(buffer->first())
{ }
size_t dump_entry(Process_entry &process)
{
size_t len = process(current_entry);
current_entry = buffer->next(current_entry);
return len;
}
bool last_entry() const
{
return current_entry.last();
}
void rewind() { current_entry = buffer->first(); }
};
private:
Genode::Allocator &_md_alloc;
Genode::Region_map &_rm;
int _handle;
Genode::Trace::Subject_id _id;
Genode::Trace::Policy_id _policy_id;
bool _was_traced;
Trace_buffer_manager *_buffer_manager;
public:
Active_file active_file;
Buffer_size_file buffer_size_file;
Cleanup_file cleanup_file;
Enable_file enable_file;
Events_file events_file;
Policy_file policy_file;
Followed_subject(Genode::Allocator &md_alloc, char const *name,
Genode::Region_map &rm,
Genode::Trace::Subject_id &id, int handle)
:
Directory(name),
_md_alloc(md_alloc),
_rm(rm),
_handle(handle),
_id(id),
_was_traced(false),
_buffer_manager(0),
active_file(_id),
buffer_size_file(),
cleanup_file(_id),
enable_file(_id),
events_file(_id, _md_alloc),
policy_file(_id, _md_alloc)
{
adopt_unsynchronized(&active_file);
adopt_unsynchronized(&cleanup_file);
adopt_unsynchronized(&enable_file);
adopt_unsynchronized(&events_file);
adopt_unsynchronized(&buffer_size_file);
adopt_unsynchronized(&policy_file);
}
~Followed_subject()
{
discard_unsynchronized(&active_file);
discard_unsynchronized(&cleanup_file);
discard_unsynchronized(&enable_file);
discard_unsynchronized(&events_file);
discard_unsynchronized(&buffer_size_file);
discard_unsynchronized(&policy_file);
}
bool marked_for_cleanup() const { return cleanup_file.cleanup(); }
bool was_traced() const { return _was_traced; }
Trace_buffer_manager* trace_buffer_manager() { return _buffer_manager; }
void manage_trace_buffer(Genode::Dataspace_capability ds_cap)
{
if (_buffer_manager != 0)
throw Trace_buffer_manager::Already_managed();
_buffer_manager = new (&_md_alloc) Trace_buffer_manager(_rm, ds_cap);
}
void unmanage_trace_buffer()
{
if (_buffer_manager == 0)
throw Trace_buffer_manager::Not_managed();
destroy(&_md_alloc, _buffer_manager);
_buffer_manager = 0;
}
const Genode::Trace::Subject_id id() const { return _id; }
const Genode::Trace::Policy_id policy_id() const { return _policy_id; }
void policy_id(Genode::Trace::Policy_id &id) { _policy_id.id = id.id; }
bool policy_valid() const { return (_policy_id.id != 0); }
void invalidate_policy() { _policy_id = Genode::Trace::Policy_id(); }
int handle() const { return _handle; }
};
/**
* This registry contains all current followed trace subjects
*/
class Trace_fs::Followed_subject_registry
{
public:
class Invalid_subject { };
class Out_of_subject_handles { };
private:
/* XXX abitrary limit - needs to be revisited when highly
* dynamic scenarios are executed */
enum { MAX_SUBJECTS = 1024U };
Followed_subject *_subjects[MAX_SUBJECTS];
Genode::Allocator &_md_alloc;
/**
* Find free subject handle
*
* \throw Out_of_subject_handles
*/
int _find_free_handle()
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
if (!_subjects[i]) {
return i;
}
throw Out_of_subject_handles();
}
bool _in_range(int handle) const
{
return ((handle >= 0) && (handle < MAX_SUBJECTS));
}
public:
Followed_subject_registry(Genode::Allocator &md_alloc)
:
_md_alloc(md_alloc)
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
_subjects[i] = 0;
}
/**
* Return maximal number of subject that can be stored in the registry
*
* \return maximal number of subjects
*/
unsigned max_subjects() const { return MAX_SUBJECTS; }
/**
* Allocate new subject
*
* \param name name of subject
* \param id subject id of tracre subject
*/
Followed_subject *alloc(char const *name, Genode::Trace::Subject_id &id,
Genode::Region_map &rm)
{
int handle = _find_free_handle();
_subjects[handle] = new (&_md_alloc) Followed_subject(_md_alloc, name, rm, id, handle);
return _subjects[handle];
}
/**
* Free subject
*
* \param subject pointer to subject
*/
void free(Followed_subject *subject)
{
int handle = subject->handle();
if (!_in_range(handle))
return;
if(!_subjects[handle])
return;
_subjects[handle] = 0;
destroy(&_md_alloc, subject);
}
/**
* Lookup subject by using the id
*
* \throw Invalid_subject();
*/
Followed_subject *lookup(Genode::Trace::Subject_id const &sid)
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
if (_subjects[i]) {
if (_subjects[i]->id().id == sid.id)
return _subjects[i];
}
throw Invalid_subject();
}
};
#endif /* _SUBJECT_REGISTRY_H_ */

View File

@ -13,7 +13,7 @@
/* Genode includes */
#include <base/component.h>
#include <file_system/node_handle_registry.h>
#include <file_system/open_node.h>
#include <file_system_session/rpc_object.h>
#include <base/attached_rom_dataspace.h>
#include <os/session_policy.h>
@ -33,6 +33,18 @@
#include <trace_files.h>
namespace Trace_fs {
using File_system::Packet_descriptor;
using File_system::Path;
class Trace_file_system;
struct Main;
struct Session_component;
struct Root;
}
/**
* Return true if 'str' is a valid file name
*/
@ -59,7 +71,7 @@ static inline bool valid_filename(char const *str)
* needed, refreshing their content or deleting them if they are no
* longer of any use.
*/
class Trace_file_system
class Trace_fs::Trace_file_system
{
private:
@ -70,13 +82,6 @@ class Trace_file_system
typedef Genode::Trace::Subject_info Subject_info;
typedef Genode::Trace::Connection Trace;
typedef Trace_fs::Followed_subject_registry Followed_subject_registry;
typedef Trace_fs::Followed_subject Followed_subject;
typedef File_system::Directory Directory;
typedef File_system::Node Node;
/**
* Simple node list
*
@ -95,9 +100,9 @@ class Trace_file_system
*/
struct Node_list_entry : public Genode::List<Node_list_entry>::Element
{
File_system::Node *node;
Node *node;
Node_list_entry(File_system::Node *n) : node(n) { }
Node_list_entry(Node *n) : node(n) { }
};
Genode::Allocator &_md_alloc;
@ -598,23 +603,18 @@ class Trace_file_system
};
namespace File_system {
struct Main;
struct Session_component;
struct Root;
}
class File_system::Session_component : public Session_rpc_object
class Trace_fs::Session_component : public Session_rpc_object
{
private:
Genode::Entrypoint &_ep;
Ram_session &_ram;
Allocator &_md_alloc;
Directory &_root_dir;
Node_handle_registry _handle_registry;
bool _writeable;
typedef File_system::Open_node<Node> Open_node;
Genode::Entrypoint &_ep;
Ram_session &_ram;
Allocator &_md_alloc;
Directory &_root_dir;
Id_space<File_system::Node> _open_node_registry;
bool _writeable;
unsigned _subject_limit;
unsigned _poll_interval;
@ -647,7 +647,7 @@ class File_system::Session_component : public Session_rpc_object
/**
* Perform packet operation
*/
void _process_packet_op(Packet_descriptor &packet, Node &node)
void _process_packet_op(Packet_descriptor &packet, Open_node &open_node)
{
void * const content = tx_sink()->packet_content(packet);
size_t const length = packet.length();
@ -659,18 +659,18 @@ class File_system::Session_component : public Session_rpc_object
case Packet_descriptor::READ:
if (content && (packet.length() <= packet.size()))
res_length = node.read((char *)content, length, packet.position());
res_length = open_node.node().read((char *)content, length, packet.position());
break;
case Packet_descriptor::WRITE:
if (content && (packet.length() <= packet.size()))
res_length = node.write((char const *)content, length, packet.position());
res_length = open_node.node().write((char const *)content, length, packet.position());
break;
case Packet_descriptor::CONTENT_CHANGED:
_handle_registry.register_notify(*tx_sink(), packet.handle());
open_node.register_notify(*tx_sink());
/* notify_listeners may bounce the packet back*/
node.notify_listeners();
open_node.node().notify_listeners();
/* otherwise defer acknowledgement of this packet */
return;
@ -691,11 +691,15 @@ class File_system::Session_component : public Session_rpc_object
/* assume failure by default */
packet.succeeded(false);
auto process_packet_fn = [&] (Open_node &open_node) {
_process_packet_op(packet, open_node);
};
try {
Node *node = _handle_registry.lookup(packet.handle());
_process_packet_op(packet, *node);
_open_node_registry.apply<Open_node>(packet.handle(), process_packet_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
Genode::error("Invalid_handle");
}
catch (Invalid_handle) { Genode::error("Invalid_handle"); }
/*
* The 'acknowledge_packet' function cannot block because we
@ -748,20 +752,20 @@ class File_system::Session_component : public Session_rpc_object
/**
* Constructor
*/
Session_component(size_t tx_buf_size,
Genode::Entrypoint &ep,
Genode::Ram_session &ram,
Genode::Region_map &rm,
Genode::Env &env,
File_system::Directory &root_dir,
Allocator &md_alloc,
unsigned subject_limit,
unsigned poll_interval,
size_t trace_quota,
size_t trace_meta_quota,
size_t trace_parent_levels,
size_t buffer_size,
size_t buffer_size_max)
Session_component(size_t tx_buf_size,
Genode::Entrypoint &ep,
Genode::Ram_session &ram,
Genode::Region_map &rm,
Genode::Env &env,
Directory &root_dir,
Allocator &md_alloc,
unsigned subject_limit,
unsigned poll_interval,
size_t trace_quota,
size_t trace_meta_quota,
size_t trace_parent_levels,
size_t buffer_size,
size_t buffer_size_max)
:
Session_rpc_object(ram.alloc(tx_buf_size), rm, ep.rpc_ep()),
_ep(ep),
@ -814,22 +818,36 @@ class File_system::Session_component : public Session_rpc_object
if (!valid_filename(name.string()))
throw Invalid_name();
Directory *dir = _handle_registry.lookup(dir_handle);
auto file_fn = [&] (Open_node &open_node) {
if (create)
throw Permission_denied();
Node &dir = open_node.node();
File *file = dynamic_cast<File*>(dir->lookup(name.string()));
if (!file)
throw Invalid_name();
if (create)
throw Permission_denied();
return _handle_registry.alloc(file);
File *file = dynamic_cast<File*>(dir.lookup(name.string()));
if (!file)
throw Invalid_name();
Open_node *open_file =
new (_md_alloc) Open_node(*file, _open_node_registry);
return open_file->id();
};
try {
return File_handle {
_open_node_registry.apply<Open_node>(dir_handle, file_fn).value
};
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create)
{
Genode::warning("symlinks not supported");
return Symlink_handle();
throw Permission_denied();
}
Dir_handle dir(Path const &path, bool create)
@ -848,7 +866,10 @@ class File_system::Session_component : public Session_rpc_object
if (!dir)
throw Invalid_name();
return _handle_registry.alloc(dir);
Open_node *open_dir =
new (_md_alloc) Open_node(*dir, _open_node_registry);
return Dir_handle { open_dir->id().value };
}
Node_handle node(Path const &path)
@ -859,40 +880,61 @@ class File_system::Session_component : public Session_rpc_object
Node *node = _root_dir.lookup(path_str + 1);
return _handle_registry.alloc(node);
Open_node *open_node =
new (_md_alloc) Open_node(*node, _open_node_registry);
return open_node->id();
}
void close(Node_handle handle)
{
Node *node;
auto close_fn = [&] (Open_node &open_node) {
try { node = _handle_registry.lookup(handle); }
catch (Invalid_handle) {
Genode::error("close() called with invalid handle");
return;
}
Node &node = open_node.node();
/**
* Acknowledge the change of the content of files which may be
* modified by the user of the file system.
*/
Changeable_content *changeable = dynamic_cast<Changeable_content*>(node);
if (changeable) {
if (changeable->changed()) {
changeable->acknowledge_change();
/**
* Acknowledge the change of the content of files which may be
* modified by the user of the file system.
*/
Changeable_content *changeable = dynamic_cast<Changeable_content*>(&node);
if (changeable) {
if (changeable->changed()) {
changeable->acknowledge_change();
/* let the trace fs perform the provoked actions */
_trace_fs->handle_changed_node(node);
/* let the trace fs perform the provoked actions */
_trace_fs->handle_changed_node(&node);
}
}
}
_handle_registry.free(handle);
/*
* Notify listeners about the changed file.
*/
node.notify_listeners();
/*
* De-allocate handle
*/
destroy(_md_alloc, &open_node);
};
try {
_open_node_registry.apply<Open_node>(handle, close_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Status status(Node_handle node_handle)
{
Node *node = _handle_registry.lookup(node_handle);
return node->status();
auto status_fn = [&] (Open_node &open_node) {
return open_node.node().status();
};
try {
return _open_node_registry.apply<Open_node>(node_handle, status_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
void control(Node_handle, Control) { }
@ -900,22 +942,22 @@ class File_system::Session_component : public Session_rpc_object
void truncate(File_handle handle, file_size_t size)
{
Node *node;
auto truncate_fn = [&] (Open_node &open_node) {
open_node.node().truncate(size);
};
try {
node = _handle_registry.lookup(handle);
File *file = dynamic_cast<File*>(node);
if (file) { file->truncate(size); }
_open_node_registry.apply<Open_node>(handle, truncate_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
catch (Invalid_handle) { }
}
void move(Dir_handle, Name const &, Dir_handle, Name const &) { }
};
class File_system::Root : public Root_component<Session_component>
class Trace_fs::Root : public Root_component<Session_component>
{
private:
@ -1055,7 +1097,7 @@ class File_system::Root : public Root_component<Session_component>
};
struct File_system::Main
struct Trace_fs::Main
{
Env &_env;
@ -1074,4 +1116,4 @@ struct File_system::Main
}
};
void Component::construct(Genode::Env &env) { static File_system::Main main(env); }
void Component::construct(Genode::Env &env) { static Trace_fs::Main main(env); }

View File

@ -4,6 +4,13 @@
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _NODE_H_
#define _NODE_H_
@ -13,49 +20,67 @@
#include <base/lock.h>
#include <base/signal.h>
namespace File_system {
namespace Trace_fs {
using namespace File_system;
using namespace Genode;
class Node : public Node_base, public List<Node>::Element
{
public:
typedef char Name[128];
private:
Name _name;
unsigned long const _inode;
/**
* Generate unique inode number
*/
static unsigned long _unique_inode()
{
static unsigned long inode_count;
return ++inode_count;
}
public:
Node()
: _inode(_unique_inode())
{ _name[0] = 0; }
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
virtual Status status() const = 0;
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
};
class Node;
}
class Trace_fs::Node : public Node_base, public List<Node>::Element
{
public:
typedef char Name[128];
private:
Name _name;
unsigned long const _inode;
/**
* Generate unique inode number
*/
static unsigned long _unique_inode()
{
static unsigned long inode_count;
return ++inode_count;
}
public:
Node()
: _inode(_unique_inode())
{ _name[0] = 0; }
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
virtual Status status() const = 0;
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
/*
* Directory functionality
*/
virtual Node *lookup(char const *path, bool return_parent = false)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
/*
* File functionality
*/
virtual void truncate(file_size_t size)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-file node");
}
};
#endif /* _NODE_H_ */

View File

@ -4,30 +4,38 @@
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SYMLINK_H_
#define _SYMLINK_H_
/* local includes */
#include <file.h>
namespace File_system {
class Symlink : public File
{
public:
Symlink(char const *name) : File(name) { }
size_t read(char *dst, size_t len, seek_off_t seek_offset) {
return 0; }
size_t write(char const *src, size_t len, seek_off_t seek_offset) {
return 0; }
file_size_t length() const { return 0; }
void truncate(file_size_t) { }
};
namespace Trace_fs {
class Symlink;
}
class Trace_fs::Symlink : public File
{
public:
Symlink(char const *name) : File(name) { }
size_t read(char *dst, size_t len, seek_off_t seek_offset) {
return 0; }
size_t write(char const *src, size_t len, seek_off_t seek_offset) {
return 0; }
file_size_t length() const { return 0; }
void truncate(file_size_t) { }
};
#endif /* _SYMLINK_H_ */

View File

@ -21,412 +21,418 @@
#include <file.h>
namespace File_system {
/**
* The State_file is a stateful file that is used to implement
* files in the file system, which may trigger a action in the
* file system backend.
*/
class State_file : public File,
public Changeable_content
{
protected:
bool _state;
public:
State_file(char const *name)
: File(name), _state(false) { }
bool state() const { return _state; }
/********************
** Node interface **
********************/
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
/* limit len */
if (len > 2)
len = 2;
switch (len) {
case 2:
dst[1] = '\n';
case 1:
dst[0] = 0x30 + (char)_state;
break;
default:
/* zero length is useless */
break;
}
return len;
}
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
char buf[32];
if (len >= sizeof buf)
return 0;
using namespace Genode;
strncpy(buf, src, min(len + 1, sizeof (buf)));
/**
* For now, we only check the leading digit and do not care
* about the rest.
*/
if (!strcmp(buf, "1", 1)) {
_state = true;
}
else if (!strcmp(buf, "0", 1)) {
_state = false;
} else {
/* silently ignore bogus writes */
return 0;
}
Changeable_content::_changed = true;
return len;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = 2;
s.mode = File_system::Status::MODE_FILE;
return s;
}
/********************
** File interface **
********************/
file_size_t length() const { return 2; }
void truncate(file_size_t size) { }
};
/**
* The Active_file node shows the state of the tracing
*/
class Active_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
bool _active;
public:
Active_file(Genode::Trace::Subject_id &id)
: State_file("active"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool active() const { return State_file::state(); }
void set_active() { _state = true; }
void set_inactive() { _state = false; }
};
/**
* The Cleanup_file is used to trigger the removal of files used by
* the traced subject and to free utilized memory.
*/
class Cleanup_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Cleanup_file(Genode::Trace::Subject_id &id)
: State_file("cleanup"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool cleanup() const { return State_file::state(); }
};
/**
* The Enable_file is used to initiate the tracing process
*/
class Enable_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Enable_file(Genode::Trace::Subject_id &id)
: State_file("enable"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool enabled() const { return State_file::state(); }
};
/**
* The Events_file encapsulates the trace buffer of traced thread
*/
class Events_file : public Buffered_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Events_file(Genode::Trace::Subject_id &id,
Allocator &md_alloc)
: Buffered_file(md_alloc, "events"), _id(id) { }
Genode::Trace::Subject_id id() const { return _id; }
size_t append(char const *src, size_t len)
{
Buffered_file::write(src, len, length());
return len;
}
/********************
** File interface **
********************/
/* override to prevent the user from overriding the file */
size_t write(char const *src, size_t len, seek_off_t seek_offset) { return 0; }
void truncate(file_size_t size) { }
};
/**
* This file contains the size of the trace buffer
*/
class Buffer_size_file : public File,
public Changeable_content
{
private:
file_size_t _length;
unsigned long _size_limit;
unsigned long _size;
char _content[32];
Genode::size_t _content_filled;
/**
* Check if new size honors the size limit
*
* \param size new size of the buffer
* \return size limit if new size is greater, otherwise new size
*/
size_t _check_size_limit(size_t size)
{
if (size > _size_limit)
return _size_limit;
else
return size;
}
/**
* Evalute the current content of the buffer
*/
void _refresh_content()
{
unsigned long tmp = 0;
_content[_content_filled - 1] = '\0';
_content_filled = 0;
_length = Genode::strlen(_content);
/* account for \n when reading from the file */
_length += 1;
ascii_to(_content, tmp);
_size = _check_size_limit(tmp);
}
public:
/**
* Constructor
*/
Buffer_size_file() : File("buffer_size"), _size_limit(0), _size(0) { }
/**
* Return current size of the trace buffer
*/
unsigned long size() const { return _size; }
/**
* Set current size of the trace buffer
*/
void size(unsigned long size)
{
_size = _check_size_limit(size);
/* update file content */
_length = Genode::snprintf(_content, sizeof (_content), "%lu", _size);
/* account for \n when reading from the file */
_length += 1;
}
/**
* Set max size of a trace buffer
*/
void size_limit(unsigned long limit) { _size_limit = limit; }
/**
* Return maximal size of the trace buffer
*/
unsigned long size_limit() const { return _size_limit; }
/********************
** Node interface **
********************/
/**
* Read current maximal size of the trace buffer
*/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len > 32) {
Genode::error("len:'", len, "' to small");
return 0;
}
char buf[32];
Genode::snprintf(buf, sizeof (buf), "%lu\n", _size);
memcpy(dst, buf, len);
return len;
}
/**
* Write new current maximal size of the trace buffer
*/
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
if ((_content_filled + len) > sizeof (_content))
return 0;
Genode::memcpy(_content + _content_filled, src, len);
_content_filled += len;
Changeable_content::_changed = true;
return len;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = _length;
s.mode = File_system::Status::MODE_FILE;
return s;
}
/********************
** File interface **
********************/
file_size_t length() const { return _length; }
void truncate(file_size_t size) { }
};
/**
* Policy file
*/
class Policy_file : public Buffered_file,
public Changeable_content
{
private:
Genode::Trace::Subject_id &_id;
file_size_t _length;
public:
Policy_file(Genode::Trace::Subject_id &id,
Genode::Allocator &md_alloc)
: Buffered_file(md_alloc, "policy"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
/********************
** Node interface **
********************/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
return Buffered_file::read(dst, len, seek_offset);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
size_t written = Buffered_file::write(src, len, seek_offset);
if (written > 0)
_changed = true;
return written;
}
/********************
** File interface **
********************/
void truncate(file_size_t size)
{
Buffered_file::truncate(size);
_changed = true;
}
};
namespace Trace_fs {
class State_file;
class Active_file;
class Cleanup_file;
class Enable_file;
class Events_file;
class Buffer_size_file;
class Policy_file;
}
/**
* The State_file is a stateful file that is used to implement
* files in the file system, which may trigger a action in the
* file system backend.
*/
class Trace_fs::State_file : public File,
public Changeable_content
{
protected:
bool _state;
public:
State_file(char const *name)
: File(name), _state(false) { }
bool state() const { return _state; }
/********************
** Node interface **
********************/
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
/* limit len */
if (len > 2)
len = 2;
switch (len) {
case 2:
dst[1] = '\n';
case 1:
dst[0] = 0x30 + (char)_state;
break;
default:
/* zero length is useless */
break;
}
return len;
}
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
char buf[32];
if (len >= sizeof buf)
return 0;
using namespace Genode;
strncpy(buf, src, min(len + 1, sizeof (buf)));
/**
* For now, we only check the leading digit and do not care
* about the rest.
*/
if (!strcmp(buf, "1", 1)) {
_state = true;
}
else if (!strcmp(buf, "0", 1)) {
_state = false;
} else {
/* silently ignore bogus writes */
return 0;
}
Changeable_content::_changed = true;
return len;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = 2;
s.mode = File_system::Status::MODE_FILE;
return s;
}
/********************
** File interface **
********************/
file_size_t length() const { return 2; }
void truncate(file_size_t size) { }
};
/**
* The Active_file node shows the state of the tracing
*/
class Trace_fs::Active_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
bool _active;
public:
Active_file(Genode::Trace::Subject_id &id)
: State_file("active"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool active() const { return State_file::state(); }
void set_active() { _state = true; }
void set_inactive() { _state = false; }
};
/**
* The Cleanup_file is used to trigger the removal of files used by
* the traced subject and to free utilized memory.
*/
class Trace_fs::Cleanup_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Cleanup_file(Genode::Trace::Subject_id &id)
: State_file("cleanup"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool cleanup() const { return State_file::state(); }
};
/**
* The Enable_file is used to initiate the tracing process
*/
class Trace_fs::Enable_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Enable_file(Genode::Trace::Subject_id &id)
: State_file("enable"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool enabled() const { return State_file::state(); }
};
/**
* The Events_file encapsulates the trace buffer of traced thread
*/
class Trace_fs::Events_file : public Buffered_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Events_file(Genode::Trace::Subject_id &id,
Allocator &md_alloc)
: Buffered_file(md_alloc, "events"), _id(id) { }
Genode::Trace::Subject_id id() const { return _id; }
size_t append(char const *src, size_t len)
{
Buffered_file::write(src, len, length());
return len;
}
/********************
** File interface **
********************/
/* override to prevent the user from overriding the file */
size_t write(char const *src, size_t len, seek_off_t seek_offset) { return 0; }
void truncate(file_size_t size) { }
};
/**
* This file contains the size of the trace buffer
*/
class Trace_fs::Buffer_size_file : public File,
public Changeable_content
{
private:
file_size_t _length;
unsigned long _size_limit;
unsigned long _size;
char _content[32];
Genode::size_t _content_filled;
/**
* Check if new size honors the size limit
*
* \param size new size of the buffer
* \return size limit if new size is greater, otherwise new size
*/
size_t _check_size_limit(size_t size)
{
if (size > _size_limit)
return _size_limit;
else
return size;
}
/**
* Evalute the current content of the buffer
*/
void _refresh_content()
{
unsigned long tmp = 0;
_content[_content_filled - 1] = '\0';
_content_filled = 0;
_length = Genode::strlen(_content);
/* account for \n when reading from the file */
_length += 1;
ascii_to(_content, tmp);
_size = _check_size_limit(tmp);
}
public:
/**
* Constructor
*/
Buffer_size_file() : File("buffer_size"), _size_limit(0), _size(0) { }
/**
* Return current size of the trace buffer
*/
unsigned long size() const { return _size; }
/**
* Set current size of the trace buffer
*/
void size(unsigned long size)
{
_size = _check_size_limit(size);
/* update file content */
_length = Genode::snprintf(_content, sizeof (_content), "%lu", _size);
/* account for \n when reading from the file */
_length += 1;
}
/**
* Set max size of a trace buffer
*/
void size_limit(unsigned long limit) { _size_limit = limit; }
/**
* Return maximal size of the trace buffer
*/
unsigned long size_limit() const { return _size_limit; }
/********************
** Node interface **
********************/
/**
* Read current maximal size of the trace buffer
*/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len > 32) {
Genode::error("len:'", len, "' to small");
return 0;
}
char buf[32];
Genode::snprintf(buf, sizeof (buf), "%lu\n", _size);
memcpy(dst, buf, len);
return len;
}
/**
* Write new current maximal size of the trace buffer
*/
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
if ((_content_filled + len) > sizeof (_content))
return 0;
Genode::memcpy(_content + _content_filled, src, len);
_content_filled += len;
Changeable_content::_changed = true;
return len;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = _length;
s.mode = File_system::Status::MODE_FILE;
return s;
}
/********************
** File interface **
********************/
file_size_t length() const { return _length; }
void truncate(file_size_t size) { }
};
/**
* Policy file
*/
class Trace_fs::Policy_file : public Buffered_file,
public Changeable_content
{
private:
Genode::Trace::Subject_id &_id;
file_size_t _length;
public:
Policy_file(Genode::Trace::Subject_id &id,
Genode::Allocator &md_alloc)
: Buffered_file(md_alloc, "policy"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
/********************
** Node interface **
********************/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
return Buffered_file::read(dst, len, seek_offset);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
size_t written = Buffered_file::write(src, len, seek_offset);
if (written > 0)
_changed = true;
return written;
}
/********************
** File interface **
********************/
void truncate(file_size_t size)
{
Buffered_file::truncate(size);
_changed = true;
}
};
#endif /* _TRACE_FILES_H_ */

View File

@ -94,16 +94,17 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
* \throw Invalid_handle
*/
template <typename HANDLE_TYPE, typename FUNC>
void _apply(HANDLE_TYPE handle, FUNC const &fn)
auto _apply(HANDLE_TYPE handle, FUNC const &fn)
-> typename Genode::Trait::Functor<decltype(&FUNC::operator())>::Return_type
{
Node_space::Id id { handle.value };
try { _node_space.apply<Node>(id, [&] (Node &node) {
try { return _node_space.apply<Node>(id, [&] (Node &node) {
typedef typename Node_type<HANDLE_TYPE>::Type Typed_node;
Typed_node *n = dynamic_cast<Typed_node *>(&node);
if (!n)
throw Invalid_handle();
fn(*n);
return fn(*n);
}); } catch (Node_space::Unknown_id) { throw Invalid_handle(); }
}
@ -385,7 +386,7 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
if (file.notify_read_ready() && file.read_ready()
&& tx_sink()->ready_to_ack()) {
Packet_descriptor packet(Packet_descriptor(),
Node_handle(file.id().value),
Node_handle { file.id().value },
Packet_descriptor::READ_READY,
0, 0);
tx_sink()->acknowledge_packet(packet);
@ -431,33 +432,29 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
if ((create || (fs_mode & WRITE_ONLY)) && (!_writable))
throw Permission_denied();
File_handle new_handle;
_apply(dir_handle, [&] (Directory &dir) {
return _apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
new_handle = dir.file(
_node_space, _vfs, _alloc, *this, name_str, fs_mode, create).value;
return File_handle {
dir.file(_node_space, _vfs, _alloc, *this, name_str, fs_mode, create).value
};
});
return new_handle;
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create) override
{
if (create && !_writable) throw Permission_denied();
Symlink_handle new_handle;
_apply(dir_handle, [&] (Directory &dir) {
return _apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
new_handle = dir.symlink(
return Symlink_handle {dir.symlink(
_node_space, _vfs, _alloc, name_str,
_writable ? READ_WRITE : READ_ONLY, create).value;
_writable ? READ_WRITE : READ_ONLY, create).value
};
});
return new_handle;
}
Node_handle node(File_system::Path const &path) override
@ -465,7 +462,7 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
char const *path_str = path.string();
/* '/' is bound to '0' */
if (!strcmp(path_str, "/"))
return Node_handle(0);
return Node_handle { 0 };
_assert_valid_path(path_str);
@ -480,7 +477,7 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object,
try { node = new (_alloc) Node(_node_space, path_str, STAT_ONLY); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Node_handle(node->id().value);
return Node_handle { node->id().value };
}
void close(Node_handle handle) override