Introduce notifications to file-system interface

This patch extends the file-system interface with the ability to monitor
changes of files or directories. The new 'File_system::sigh' function
can be used to install a signal handler for an open node.

The 'ram_fs' server has been enhanced to support the new interface. So
any file or directory changes can now be observed by 'ram_fs' clients.

Fixes #607
This commit is contained in:
Norman Feske 2013-01-12 21:17:20 +01:00
parent 7318c5d7d4
commit fca8994584
10 changed files with 194 additions and 7 deletions

View File

@ -846,7 +846,11 @@ namespace File_system {
PERR("f_rename() returned an unexpected error code"); PERR("f_rename() returned an unexpected error code");
throw Lookup_failed(); throw Lookup_failed();
} }
}
void sigh(Node_handle, Genode::Signal_context_capability)
{
PWRN("File_system::Session::sigh not supported");
} }
}; };

View File

@ -100,6 +100,10 @@ namespace File_system {
call<Rpc_move>(from_dir, from_name, to_dir, to_name); call<Rpc_move>(from_dir, from_name, to_dir, to_name);
} }
void sigh(Node_handle node, Signal_context_capability sigh)
{
call<Rpc_sigh>(node, sigh);
}
}; };
} }

View File

@ -35,7 +35,7 @@ namespace File_system {
: :
Genode::Connection<Session>( Genode::Connection<Session>(
session("ram_quota=%zd, tx_buf_size=%zd, label=\"%s\"", session("ram_quota=%zd, tx_buf_size=%zd, label=\"%s\"",
3*4096 + tx_buf_size, tx_buf_size, label)), 4*4096 + tx_buf_size, tx_buf_size, label)),
Session_client(cap(), tx_block_alloc) { } Session_client(cap(), tx_block_alloc) { }
}; };
} }

View File

@ -29,6 +29,8 @@ namespace File_system {
Node_handle() : value(-1) { } Node_handle() : value(-1) { }
Node_handle(int v) : value(v) { } Node_handle(int v) : value(v) { }
bool valid() const { return value != -1; }
}; };
@ -273,6 +275,11 @@ namespace File_system {
virtual void move(Dir_handle, Name const &from, virtual void move(Dir_handle, Name const &from,
Dir_handle, Name const &to) = 0; Dir_handle, Name const &to) = 0;
/**
* Register handler that should be notified on node changes
*/
virtual void sigh(Node_handle, Signal_context_capability sigh) = 0;
/******************* /*******************
** RPC interface ** ** RPC interface **
@ -307,6 +314,9 @@ namespace File_system {
GENODE_RPC_THROW(Rpc_move, void, move, GENODE_RPC_THROW(Rpc_move, void, move,
GENODE_TYPE_LIST(Permission_denied, Invalid_name, Lookup_failed), GENODE_TYPE_LIST(Permission_denied, Invalid_name, Lookup_failed),
Dir_handle, Name const &, Dir_handle, Name const &); Dir_handle, Name const &, Dir_handle, Name const &);
GENODE_RPC_THROW(Rpc_sigh, void, sigh,
GENODE_TYPE_LIST(Invalid_handle),
Node_handle, Signal_context_capability);
/* /*
* Manual type-list definition, needed because the RPC interface * Manual type-list definition, needed because the RPC interface
@ -324,8 +334,9 @@ namespace File_system {
Meta::Type_tuple<Rpc_unlink, Meta::Type_tuple<Rpc_unlink,
Meta::Type_tuple<Rpc_truncate, Meta::Type_tuple<Rpc_truncate,
Meta::Type_tuple<Rpc_move, Meta::Type_tuple<Rpc_move,
Meta::Type_tuple<Rpc_sigh,
Meta::Empty> Meta::Empty>
> > > > > > > > > > Rpc_functions; > > > > > > > > > > > Rpc_functions;
}; };
} }

View File

@ -43,12 +43,16 @@ namespace File_system {
*/ */
_entries.insert(node); _entries.insert(node);
_num_entries++; _num_entries++;
mark_as_updated();
} }
void discard_unsynchronized(Node *node) void discard_unsynchronized(Node *node)
{ {
_entries.remove(node); _entries.remove(node);
_num_entries--; _num_entries--;
mark_as_updated();
} }
Node *lookup_and_lock(char const *path, bool return_parent = false) Node *lookup_and_lock(char const *path, bool return_parent = false)

View File

@ -84,6 +84,8 @@ namespace File_system {
* by zero chunks, which do not contribute to 'used_size()'. * by zero chunks, which do not contribute to 'used_size()'.
*/ */
_length = max(_length, seek_offset + len); _length = max(_length, seek_offset + len);
mark_as_updated();
return 0; return 0;
} }
@ -95,6 +97,8 @@ namespace File_system {
_chunk.truncate(size); _chunk.truncate(size);
_length = size; _length = size;
mark_as_updated();
} }
}; };
} }

View File

@ -387,7 +387,27 @@ namespace File_system {
from_dir->discard_unsynchronized(node); from_dir->discard_unsynchronized(node);
to_dir->adopt_unsynchronized(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();
}
void sigh(Node_handle node_handle, Signal_context_capability sigh)
{
_handle_registry.sigh(node_handle, sigh);
} }
}; };

View File

@ -10,9 +10,46 @@
/* Genode includes */ /* Genode includes */
#include <util/list.h> #include <util/list.h>
#include <base/lock.h> #include <base/lock.h>
#include <base/signal.h>
namespace File_system { namespace File_system {
class Listener : public List<Listener>::Element
{
private:
Lock _lock;
Signal_context_capability _sigh;
bool _marked_as_updated;
public:
Listener() : _marked_as_updated(false) { }
Listener(Signal_context_capability sigh)
: _sigh(sigh), _marked_as_updated(false) { }
void notify()
{
Lock::Guard guard(_lock);
if (_marked_as_updated && _sigh.valid())
Signal_transmitter(_sigh).submit();
_marked_as_updated = false;
}
void mark_as_updated()
{
Lock::Guard guard(_lock);
_marked_as_updated = true;
}
bool valid() const { return _sigh.valid(); }
};
class Node : public List<Node>::Element class Node : public List<Node>::Element
{ {
public: public:
@ -25,6 +62,8 @@ namespace File_system {
int _ref_count; int _ref_count;
Name _name; Name _name;
unsigned long const _inode; unsigned long const _inode;
List<Listener> _listeners;
bool _modified;
/** /**
* Generate unique inode number * Generate unique inode number
@ -37,9 +76,19 @@ namespace File_system {
public: public:
Node() : _ref_count(0), _inode(_unique_inode()) { _name[0] = 0; } Node()
: _ref_count(0), _inode(_unique_inode()), _modified(false)
{ _name[0] = 0; }
virtual ~Node() { } virtual ~Node()
{
/* propagate event to listeners */
mark_as_updated();
notify_listeners();
while (_listeners.first())
_listeners.remove(_listeners.first());
}
unsigned long inode() const { return _inode; } unsigned long inode() const { return _inode; }
char const *name() const { return _name; } char const *name() const { return _name; }
@ -55,6 +104,27 @@ namespace File_system {
virtual size_t read(char *dst, size_t len, seek_off_t) = 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; virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
void add_listener(Listener *listener)
{
_listeners.insert(listener);
}
void remove_listener(Listener *listener)
{
_listeners.remove(listener);
}
void notify_listeners()
{
for (Listener *curr = _listeners.first(); curr; curr = curr->next())
curr->notify();
}
void mark_as_updated()
{
for (Listener *curr = _listeners.first(); curr; curr = curr->next())
curr->mark_as_updated();
}
}; };

View File

@ -45,6 +45,12 @@ namespace File_system {
Node *_nodes[MAX_NODE_HANDLES]; Node *_nodes[MAX_NODE_HANDLES];
/**
* Each open node handle can act as a listener to be informed about
* node changes.
*/
Listener _listeners[MAX_NODE_HANDLES];
/** /**
* Allocate node handle * Allocate node handle
* *
@ -90,8 +96,30 @@ namespace File_system {
{ {
Lock::Guard guard(_lock); Lock::Guard guard(_lock);
if (_in_range(handle.value)) if (!_in_range(handle.value))
_nodes[handle.value] = 0; return;
/*
* Notify listeners about the changed file.
*/
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node) { return; }
node->lock();
node->notify_listeners();
/*
* De-allocate handle
*/
Listener &listener = _listeners[handle.value];
if (listener.valid())
node->remove_listener(&listener);
_nodes[handle.value] = 0;
listener = Listener();
node->unlock();
} }
/** /**
@ -122,11 +150,48 @@ namespace File_system {
{ {
Lock::Guard guard(_lock); Lock::Guard guard(_lock);
if (!_in_range(h1.value) || !_in_range(h2.value)) if (!_in_range(h1.value) || !_in_range(h2.value)) {
PDBG("refer_to_same_node -> Invalid_handle");
throw Invalid_handle(); throw Invalid_handle();
}
return _nodes[h1.value] == _nodes[h2.value]; return _nodes[h1.value] == _nodes[h2.value];
} }
/**
* Register signal handler to be notified of node changes
*/
void sigh(Node_handle handle, Signal_context_capability sigh)
{
Lock::Guard guard(_lock);
if (!_in_range(handle.value))
throw Invalid_handle();
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node) {
PDBG("Invalid_handle");
throw Invalid_handle();
}
node->lock();
Node_lock_guard node_lock_guard(*node);
Listener &listener = _listeners[handle.value];
/*
* If there was already a handler registered for the node,
* remove the old handler.
*/
if (listener.valid())
node->remove_listener(&listener);
/*
* Register new handler
*/
listener = Listener(sigh);
node->add_listener(&listener);
}
}; };
} }

View File

@ -419,6 +419,11 @@ namespace File_system {
throw Permission_denied(); throw Permission_denied();
} }
void sigh(Node_handle, Genode::Signal_context_capability)
{
PWRN("File_system::Session::sigh not supported");
}
}; };