base-linux: hybrid file-system server

The current implementation is missing support for symbolic links.

Fixes #944.
This commit is contained in:
Christian Helmuth 2013-11-08 16:44:44 +01:00 committed by Norman Feske
parent 1bbd54c9b7
commit eda44d6154
11 changed files with 1375 additions and 0 deletions

79
base-linux/run/lx_fs.run Normal file
View File

@ -0,0 +1,79 @@
#
# \brief Test for using the libc_fs plugin with the Linux file system
# \author Norman Feske
# \author Christian Helmuth
# \date 2013-11-07
#
assert_spec linux
#
# Build
#
build { core init server/lx_fs test/libc_fs }
create_boot_directory
#
# Generate config
#
install_config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="lx_fs">
<resource name="RAM" quantum="4M"/>
<provides> <service name="File_system"/> </provides>
<config> <policy label="" root="/libc_fs" writeable="yes" /> </config>
</start>
<start name="test-libc_fs">
<resource name="RAM" quantum="2M"/>
</start>
</config>
}
#
# Create test-directory structure
#
exec mkdir -p bin/libc_fs
#
# Boot modules
#
build_boot_image {
core init
ld.lib.so libc.lib.so libc_log.lib.so libc_fs.lib.so
lx_fs test-libc_fs
libc_fs
}
#
# Execute test case
#
run_genode_until {.*child exited with exit value 0.*} 60
puts "\ntest succeeded\n"
#
# Cleanup test-directory structure
#
exec rm -r bin/libc_fs
# vi: set ft=tcl :

View File

@ -0,0 +1,44 @@
This directory contains an Genode file-system service to Linux host fs
wrapper.
Configuration
~~~~~~~~~~~~~
Access to the file system can be tailored for each session depending on the
session's label. By default, no permissions are granted to any session.
To selectively permit access to (a part of) the file system, at least one
policy must be defined.
The following configuration illustates the way of how to express policy.
! <config>
! <!-- constrain sessions according to their labels -->
! <policy label="noux -> root" root="/" />
! <policy label="noux -> home" root="/home/user" writeable="yes" />
! <policy label="noux -> tmp" root="/tmp" writeable="yes" />
! </config>
Session-specific access-control policy is expressed via one or more '<policy>'
nodes. At session-creation time, each policy node is matched against the label
of the new session. If the label of a policy node matches, the defined policy
is applied. If multiple policies match, the one with the longest 'label'
attribute (the most specific one) is selected.
A policy node may contain the following attributes. The mandatory 'root'
attribute defines the viewport of the session onto the file system. The
optional 'writeable' attribute grants the permission to modify the file system.
Example
~~~~~~~
To illustrate the use of lx_fs, refer to the 'base-linux/run/lx_fs.run'
script.
Notes
~~~~~
If the Linux file system experiences changes from other processes
'inotify' may help to keep the servers cache up-to-date. This is not
implemented yet.

View File

@ -0,0 +1,188 @@
/*
* \brief File-system directory node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
/* Genode include */
#include <os/path.h>
/* local includes */
#include <node.h>
#include <util.h>
#include <file.h>
#include <lx_util.h>
namespace File_system {
class Directory;
}
class File_system::Directory : public Node
{
private:
typedef Genode::Path<MAX_PATH_LEN> Path;
DIR *_fd;
Path _path;
Allocator &_alloc;
unsigned long _inode(char const *path, bool create)
{
int ret;
if (create) {
mode_t ugo = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
ret = mkdir(path, ugo);
if (ret == -1)
throw No_space();
}
struct stat s;
ret = lstat(path, &s);
if (ret == -1)
throw Lookup_failed();
return s.st_ino;
}
DIR *_open(char const *path)
{
DIR *fd = opendir(path);
if (!fd)
throw Lookup_failed();
return fd;
}
public:
Directory(Allocator &alloc, char const *path, bool create)
:
Node(_inode(path, create)),
_fd(_open(path)),
_path(path, "./"),
_alloc(alloc)
{
Node::name(basename(path));
}
virtual ~Directory()
{
closedir(_fd);
}
/* FIXME returned file node must be locked */
File * file(char const *name, Mode mode, bool create)
{
File *file = new (&_alloc) File(dirfd(_fd), name, mode, create);
file->lock();
return file;
}
/* FIXME returned directory node must be locked */
Directory * subdir(char const *path, bool create)
{
Path dir_path(path, _path.base());
Directory *dir = new (&_alloc) Directory(_alloc, dir_path.base(), create);
dir->lock();
return dir;
}
Node * node(char const *path)
{
Path node_path(path, _path.base());
/*
* XXX Currently, symlinks are transparently dereferenced by the
* use of stat(). For symlink detection we would need lstat()
* and implement special handling of the root, which may be a
* link!
*/
struct stat s;
int ret = stat(node_path.base(), &s);
if (ret == -1)
throw Lookup_failed();
Node *node = 0;
if (S_ISDIR(s.st_mode))
node = new (&_alloc) Directory(_alloc, node_path.base(), false);
else if (S_ISREG(s.st_mode))
node = new (&_alloc) File(node_path.base(), STAT_ONLY);
else
throw Lookup_failed();
node->lock();
return node;
}
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len < sizeof(Directory_entry)) {
PERR("read buffer too small for directory entry");
return 0;
}
if (seek_offset % sizeof(Directory_entry)) {
PERR("seek offset not aligned to sizeof(Directory_entry)");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
/* seek to index and read entry */
struct dirent *dent;
rewinddir(_fd);
for (unsigned i = 0; i <= index; ++i) {
dent = readdir(_fd);
}
if (!dent)
return 0;
Directory_entry *e = (Directory_entry *)(dst);
switch (dent->d_type) {
case DT_REG: e->type = Directory_entry::TYPE_FILE; break;
case DT_DIR: e->type = Directory_entry::TYPE_DIRECTORY; break;
case DT_LNK: e->type = Directory_entry::TYPE_SYMLINK; break;
default:
return 0;
}
strncpy(e->name, dent->d_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;
}
size_t num_entries() const
{
unsigned num = 0;
rewinddir(_fd);
while (readdir(_fd)) ++num;
return num;
}
};
#endif /* _DIRECTORY_H_ */

138
os/src/server/lx_fs/file.h Normal file
View File

@ -0,0 +1,138 @@
/*
* \brief File node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _FILE_H_
#define _FILE_H_
/* local includes */
#include <node.h>
#include <lx_util.h>
namespace File_system {
class File;
}
class File_system::File : public Node
{
private:
int _fd;
unsigned long _inode(int dir, char const *name, bool create)
{
int ret;
if (create) {
mode_t ugo = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
ret = mknodat(dir, name, S_IFREG | ugo, 0);
if (ret == -1 && errno != EEXIST)
throw No_space();
}
struct stat s;
ret = fstatat(dir, name, &s, 0);
if (ret == -1)
throw Lookup_failed();
return s.st_ino;
}
unsigned long _inode_path(char const *path)
{
int ret;
struct stat s;
ret = stat(path, &s);
if (ret == -1)
throw Lookup_failed();
return s.st_ino;
}
int _open(int dir, char const *name, Mode mode)
{
int fd = openat(dir, name, access_mode(mode));
if (fd == -1)
throw Lookup_failed();
return fd;
}
int _open_path(char const *path, Mode mode)
{
int fd = open(path, access_mode(mode));
if (fd == -1)
throw Lookup_failed();
return fd;
}
public:
File(int dir,
char const *name,
Mode mode,
bool create)
:
Node(_inode(dir, name, create)),
_fd(_open(dir, name, mode))
{
Node::name(name);
}
File(char const *path, Mode mode)
:
Node(_inode_path(path)),
_fd(_open_path(path, mode))
{
Node::name(basename(path));
}
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
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)
{
/* should we append? */
if (seek_offset == ~0ULL) {
off_t off = lseek(_fd, 0, SEEK_END);
if (off == -1)
return 0;
seek_offset = off;
}
int ret = pwrite(_fd, src, len, seek_offset);
return ret == -1 ? 0 : ret;
}
file_size_t length() const
{
struct stat s;
if (fstat(_fd, &s) < 0)
return 0;
return s.st_size;
}
void truncate(file_size_t size)
{
ftruncate(_fd, size);
mark_as_updated();
}
};
#endif /* _FILE_H_ */

View File

@ -0,0 +1,38 @@
/*
* \brief Linux utilities
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _LX_UTIL_H_
#define _LX_UTIL_H_
/* Genode includes */
#include <file_system_session/file_system_session.h>
/* Linux includes */
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
namespace File_system {
int access_mode(File_system::Mode const &mode);
}
int File_system::access_mode(File_system::Mode const &mode)
{
switch (mode) {
case STAT_ONLY:
case READ_ONLY: return O_RDONLY;
case WRITE_ONLY: return O_WRONLY;
case READ_WRITE: return O_RDWR;
}
return O_RDONLY;
}
#endif

437
os/src/server/lx_fs/main.cc Normal file
View File

@ -0,0 +1,437 @@
/*
* \brief RAM file system
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <file_system_session/rpc_object.h>
#include <root/component.h>
#include <os/attached_rom_dataspace.h>
#include <os/config.h>
#include <os/server.h>
#include <os/session_policy.h>
#include <util/xml_node.h>
/* local includes */
#include <directory.h>
#include <node_handle_registry.h>
namespace File_system {
struct Main;
struct Session_component;
struct Root;
}
class File_system::Session_component : public Session_rpc_object
{
private:
Server::Entrypoint &_ep;
Allocator &_md_alloc;
Directory &_root;
Node_handle_registry _handle_registry;
bool _writable;
Signal_rpc_member<Session_component> _process_packet_dispatcher;
/******************************
** Packet-stream processing **
******************************/
/**
* Perform packet operation
*
* \return true on success, false on failure
*/
void _process_packet_op(Packet_descriptor &packet, Node &node)
{
void * const content = tx_sink()->packet_content(packet);
size_t const length = packet.length();
seek_off_t const offset = packet.position();
if (!content || (packet.length() > packet.size())) {
packet.succeeded(false);
return;
}
/* resulting length */
size_t res_length = 0;
switch (packet.operation()) {
case Packet_descriptor::READ:
res_length = node.read((char *)content, length, offset);
break;
case Packet_descriptor::WRITE:
res_length = node.write((char const *)content, length, offset);
break;
}
packet.length(res_length);
packet.succeeded(res_length > 0);
}
void _process_packet()
{
Packet_descriptor packet = tx_sink()->get_packet();
/* assume failure by default */
packet.succeeded(false);
try {
Node *node = _handle_registry.lookup_and_lock(packet.handle());
Node_lock_guard guard(*node);
_process_packet_op(packet, *node);
}
catch (Invalid_handle) { PERR("Invalid_handle"); }
catch (Size_limit_reached) { PERR("Size_limit_reached"); }
/*
* The 'acknowledge_packet' function cannot block because we
* checked for 'ready_to_ack' in '_process_packets'.
*/
tx_sink()->acknowledge_packet(packet);
}
/**
* Called by signal dispatcher, executed in the context of the main
* thread (not serialized with the RPC functions)
*/
void _process_packets(unsigned)
{
while (tx_sink()->packet_avail()) {
/*
* Make sure that the '_process_packet' function does not
* block.
*
* If the acknowledgement queue is full, we defer packet
* processing until the client processed pending
* acknowledgements and thereby emitted a ready-to-ack
* signal. Otherwise, the call of 'acknowledge_packet()'
* in '_process_packet' would infinitely block the context
* of the main thread. The main thread is however needed
* for receiving any subsequent 'ready-to-ack' signals.
*/
if (!tx_sink()->ready_to_ack())
return;
_process_packet();
}
}
/**
* Check if string represents a valid path (must start with '/')
*/
static void _assert_valid_path(char const *path)
{
if (!path || path[0] != '/') {
PWRN("malformed path '%s'", path);
throw Lookup_failed();
}
}
public:
/**
* Constructor
*/
Session_component(size_t tx_buf_size,
Server::Entrypoint &ep,
char const *root_dir,
bool writable,
Allocator &md_alloc)
:
Session_rpc_object(env()->ram_session()->alloc(tx_buf_size), ep.rpc_ep()),
_ep(ep),
_md_alloc(md_alloc),
_root(*new (&_md_alloc) Directory(_md_alloc, root_dir, false)),
_writable(writable),
_process_packet_dispatcher(*this, &Session_component::_process_packets)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
*/
Signal_context_capability sigh(_ep.manage(_process_packet_dispatcher));
_tx.sigh_packet_avail(sigh);
_tx.sigh_ready_to_ack(sigh);
}
/**
* Destructor
*/
~Session_component()
{
_ep.dissolve(_process_packet_dispatcher);
Dataspace_capability ds = tx_sink()->dataspace();
env()->ram_session()->free(static_cap_cast<Ram_dataspace>(ds));
destroy(&_md_alloc, &_root);
}
/***************************
** File_system interface **
***************************/
File_handle file(Dir_handle dir_handle, Name const &name, Mode mode, bool create)
{
if (!valid_name(name.string()))
throw Invalid_name();
Directory *dir = _handle_registry.lookup_and_lock(dir_handle);
Node_lock_guard dir_guard(*dir);
if (!_writable)
if (create || (mode != STAT_ONLY && mode != READ_ONLY))
throw Permission_denied();
File *file = dir->file(name.string(), mode, create);
Node_lock_guard file_guard(*file);
return _handle_registry.alloc(file);
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create)
{
PERR("%s not implemented", __func__);
return Symlink_handle();
}
Dir_handle dir(Path const &path, bool create)
{
char const *path_str = path.string();
_assert_valid_path(path_str);
/* skip leading '/' */
path_str++;
if (!_writable && create)
throw Permission_denied();
if (!path.is_valid_string())
throw Name_too_long();
Directory *dir = _root.subdir(path_str, create);
Node_lock_guard guard(*dir);
return _handle_registry.alloc(dir);
}
Node_handle node(Path const &path)
{
char const *path_str = path.string();
_assert_valid_path(path_str);
Node *node = _root.node(path_str + 1);
Node_lock_guard guard(*node);
return _handle_registry.alloc(node);
}
void close(Node_handle handle)
{
/* FIXME when to destruct node? */
_handle_registry.free(handle);
}
Status status(Node_handle node_handle)
{
Node *node = _handle_registry.lookup_and_lock(node_handle);
Node_lock_guard guard(*node);
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;
}
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;
}
PERR("%s for symlinks not implemented", __func__);
return Status();
}
void control(Node_handle, Control)
{
PERR("%s not implemented", __func__);
}
void unlink(Dir_handle, Name const &)
{
PERR("%s not implemented", __func__);
}
void truncate(File_handle file_handle, file_size_t size)
{
if (!_writable)
throw Permission_denied();
File *file = _handle_registry.lookup_and_lock(file_handle);
Node_lock_guard file_guard(*file);
file->truncate(size);
}
void move(Dir_handle, Name const &, Dir_handle, Name const &)
{
PERR("%s not implemented", __func__);
}
void sigh(Node_handle node_handle, Signal_context_capability sigh)
{
_handle_registry.sigh(node_handle, sigh);
}
};
class File_system::Root : public Root_component<Session_component>
{
private:
Server::Entrypoint &_ep;
protected:
Session_component *_create_session(const char *args)
{
/*
* Determine client-specific policy defined implicitly by
* the client's label.
*/
char const *root_dir = ".";
bool writeable = false;
enum { ROOT_MAX_LEN = 256 };
char root[ROOT_MAX_LEN];
root[0] = 0;
try {
Session_label label(args);
Session_policy policy(label);
/*
* Determine directory that is used as root directory of
* the session.
*/
try {
policy.attribute("root").value(root, sizeof(root));
/*
* Make sure the root path is specified with a
* leading path delimiter. For performing the
* lookup, we skip the first character.
*/
if (root[0] != '/')
throw Lookup_failed();
root_dir = root + 1;
} catch (Xml_node::Nonexistent_attribute) {
PERR("Missing \"root\" attribute in policy definition");
throw Root::Unavailable();
} catch (Lookup_failed) {
PERR("Session root directory \"%s\" does not exist", root);
throw Root::Unavailable();
}
/*
* Determine if write access is permitted for the session.
*/
try {
writeable = policy.attribute("writeable").has_value("yes");
} catch (Xml_node::Nonexistent_attribute) { }
} catch (Session_policy::No_policy_defined) {
PERR("Invalid session request, no matching policy");
throw Root::Unavailable();
}
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
/*
* Check if donated ram quota suffices for session data,
* and communication buffer.
*/
size_t session_size = sizeof(Session_component) + tx_buf_size;
if (max((size_t)4096, session_size) > ram_quota) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, session_size);
throw Root::Quota_exceeded();
}
return new (md_alloc())
Session_component(tx_buf_size, _ep, root_dir, writeable, *md_alloc());
}
public:
/**
* Constructor
*
* \param ep entrypoint
* \param sig_rec signal receiver used for handling the
* data-flow signals of packet streams
* \param md_alloc meta-data allocator
*/
Root(Server::Entrypoint &ep, Allocator &md_alloc)
:
Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_ep(ep)
{ }
};
struct File_system::Main
{
Server::Entrypoint &ep;
/*
* Initialize root interface
*/
Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() };
Root fs_root = { ep, sliced_heap };
Main(Server::Entrypoint &ep) : ep(ep)
{
env()->parent()->announce(ep.manage(fs_root));
}
};
/**********************
** Server framework **
**********************/
char const * Server::name() { return "lx_fs_ep"; }
Genode::size_t Server::stack_size() { return 2048 * sizeof(long); }
void Server::construct(Server::Entrypoint &ep) { static File_system::Main inst(ep); }

134
os/src/server/lx_fs/node.h Normal file
View File

@ -0,0 +1,134 @@
/*
* \brief File-system node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _NODE_H_
#define _NODE_H_
/* Genode includes */
#include <util/list.h>
#include <base/lock.h>
#include <base/signal.h>
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
{
public:
typedef char Name[128];
private:
Lock _lock;
Name _name;
unsigned long const _inode;
List<Listener> _listeners;
public:
Node(unsigned long inode) : _inode(inode) { _name[0] = 0; }
virtual ~Node()
{
/* propagate event to listeners */
mark_as_updated();
notify_listeners();
while (_listeners.first())
_listeners.remove(_listeners.first());
}
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
void lock() { _lock.lock(); }
void unlock() { _lock.unlock(); }
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;
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();
}
};
/**
* Guard used for properly releasing node locks
*/
struct Node_lock_guard
{
Node &node;
Node_lock_guard(Node &node) : node(node) { }
~Node_lock_guard() { node.unlock(); }
};
}
#endif /* _NODE_H_ */

View File

@ -0,0 +1,198 @@
/*
* \brief Facility for managing the session-local node-handle namespace
* \author Norman Feske
* \date 2012-04-11
*/
#ifndef _NODE_HANDLE_REGISTRY_H_
#define _NODE_HANDLE_REGISTRY_H_
namespace File_system {
class Node;
class Directory;
class File;
class Symlink;
/**
* Type trait for determining the node type for a given handle type
*/
template<typename T> struct Node_type;
template<> struct Node_type<Node_handle> { typedef Node Type; };
template<> struct Node_type<Dir_handle> { typedef Directory Type; };
template<> struct Node_type<File_handle> { typedef File Type; };
template<> struct Node_type<Symlink_handle> { typedef Symlink Type; };
/**
* Type trait for determining the handle type for a given node type
*/
template<typename T> struct Handle_type;
template<> struct Handle_type<Node> { typedef Node_handle Type; };
template<> struct Handle_type<Directory> { typedef Dir_handle Type; };
template<> struct Handle_type<File> { typedef File_handle Type; };
template<> struct Handle_type<Symlink> { typedef Symlink_handle Type; };
class Node_handle_registry
{
private:
/* maximum number of open nodes per session */
enum { MAX_NODE_HANDLES = 128U };
Lock mutable _lock;
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
*
* \throw Out_of_node_handles
*/
int _alloc(Node *node)
{
Lock::Guard guard(_lock);
for (unsigned i = 0; i < MAX_NODE_HANDLES; i++)
if (!_nodes[i]) {
_nodes[i] = node;
return i;
}
throw Out_of_node_handles();
}
bool _in_range(int handle) const
{
return ((handle >= 0) && (handle < MAX_NODE_HANDLES));
}
public:
Node_handle_registry()
{
for (unsigned i = 0; i < MAX_NODE_HANDLES; i++)
_nodes[i] = 0;
}
template <typename NODE_TYPE>
typename Handle_type<NODE_TYPE>::Type alloc(NODE_TYPE *node)
{
typedef typename Handle_type<NODE_TYPE>::Type Handle;
return Handle(_alloc(node));
}
/**
* Release node handle
*/
void free(Node_handle handle)
{
Lock::Guard guard(_lock);
if (!_in_range(handle.value))
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();
}
/**
* Lookup node using its handle as key
*
* The node returned by this function is in a locked state.
*
* \throw Invalid_handle
*/
template <typename HANDLE_TYPE>
typename Node_type<HANDLE_TYPE>::Type *lookup_and_lock(HANDLE_TYPE handle)
{
Lock::Guard guard(_lock);
if (!_in_range(handle.value))
throw Invalid_handle();
typedef typename Node_type<HANDLE_TYPE>::Type Node;
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node)
throw Invalid_handle();
node->lock();
return node;
}
bool refer_to_same_node(Node_handle h1, Node_handle h2) const
{
Lock::Guard guard(_lock);
if (!_in_range(h1.value) || !_in_range(h2.value)) {
PDBG("refer_to_same_node -> Invalid_handle");
throw Invalid_handle();
}
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);
}
};
}
#endif /* _NODE_HANDLE_REGISTRY_H_ */

View File

@ -0,0 +1,50 @@
/*
* \brief Symlink file-system node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*
* FIXME unfinished
*/
#ifndef _SYMLINK_H_
#define _SYMLINK_H_
/* local includes */
#include <node.h>
#include <lx_util.h>
namespace File_system {
class Symlink;
}
class File_system::Symlink : public Node
{
private:
char _link_to[MAX_PATH_LEN];
public:
Symlink(char const *name) { Node::name(name); }
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
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 count = min(len, sizeof(_link_to) + 1);
Genode::strncpy(_link_to, src, count);
return count;
}
file_size_t length() const { return strlen(_link_to) + 1; }
};
#endif /* _SYMLINK_H_ */

View File

@ -0,0 +1,6 @@
TARGET = lx_fs
REQUIRES = linux
SRC_CC = main.cc
LIBS = base config server lx_hybrid
INC_DIR += $(PRG_DIR) /usr/include

View File

@ -0,0 +1,63 @@
/*
* \brief Utilities
* \author Norman Feske
* \date 2012-04-11
*/
#ifndef _UTIL_H_
#define _UTIL_H_
/**
* Return base-name portion of null-terminated path string
*/
static inline char const *basename(char const *path)
{
char const *start = path;
for (; *path; path++)
if (*path == '/')
start = path + 1;
return start;
}
/**
* Return true if specified path is a base name (contains no path delimiters)
*/
static inline bool is_basename(char const *path)
{
for (; *path; path++)
if (*path == '/')
return false;
return true;
}
/**
* Return true if character 'c' occurs in null-terminated string 'str'
*/
static inline bool string_contains(char const *str, char c)
{
for (; *str; str++)
if (*str == c)
return true;
return false;
}
/**
* Return true if 'str' is a valid node name
*/
static inline bool valid_name(char const *str)
{
if (string_contains(str, '/')) return false;
/* must have at least one character */
if (str[0] == 0) return false;
return true;
}
#endif /* _UTIL_H_ */