Remove ram_fs server

Fixes #3734
This commit is contained in:
Norman Feske 2020-04-21 16:27:07 +02:00
parent f14cc2edab
commit 01bf32b998
11 changed files with 0 additions and 1464 deletions

View File

@ -367,9 +367,6 @@ Separate components:
Provides each file of an ISO9660 file system accessed via a block session as
separate ROM session.
:'os/src/server/ram_fs':
A file-system implementation that keeps all data in memory.
:'dde_rump/src/server/rump_fs':
A file-system server that contains various file-systems ported from the
NetBSD kernel.

View File

@ -1,2 +0,0 @@
SRC_DIR = include/file_system include/ram_fs src/server/ram_fs
include $(GENODE_DIR)/repos/base/recipes/src/content.inc

View File

@ -1 +0,0 @@
2020-04-16 2f7198daac115b393af5a3d71475a7014500c60b

View File

@ -1,3 +0,0 @@
base
os
file_system_session

View File

@ -1,57 +0,0 @@
This directory contains an in-memory file-system implementation.
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
ram_fs policy must be defined.
The following configuration illustates the way of how to express policy.
! <config>
! <!-- preload RAM file system with some ROM images -->
! <content>
! <dir name="tmp">
! <rom name="init" as="blubb" />
! </dir>
! <dir name="home">
! <dir name="user">
! <inline name=".vimrc">
! set hidden
! </inline>
! </dir>
! </dir>
! </content>
! <!-- 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>
The '<content>' sub node of the '<config>' node provides a way to pre-populate
the file system with directories and files. Note that '<dir>' nodes can be
arbitrarily nested. Files can be loaded from the ROM service. By adding the
optional 'at' attribute to a rom node, the file name can be defined
independently from the ROM module name. In addition to creating files from
ROM modules, files can be created from data specified directly as part of the
configuration using '<inline>' nodes. The content of such nodes is used as
file content as is.
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 ram_fs, refer to the 'libports/run/libc_fs.run'
script.

View File

@ -1,250 +0,0 @@
/*
* \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 = 0;
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) { 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();
notify_listeners();
}
void discard(Node *node) override
{
_entries.remove(node);
_num_entries--;
mark_as_updated();
notify_listeners();
}
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) override
{
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,
Session_writeable writeable) 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;
auto type = [&] ()
{
using Node_type = File_system::Node_type;
if (dynamic_cast<Directory *>(node)) return Node_type::DIRECTORY;
if (dynamic_cast<Symlink *>(node)) return Node_type::SYMLINK;
return Node_type::CONTINUOUS_FILE;
};
Directory_entry &e = *(Directory_entry *)(dst);
e = {
.inode = node->inode(),
.type = type(),
.rwx = { .readable = true,
.writeable = writeable == Session_writeable::WRITEABLE,
.executable = true },
.name = { node->name() }
};
return sizeof(Directory_entry);
}
size_t write(char const *, size_t, seek_off_t) override
{
/* writing to directory nodes is not supported */
return 0;
}
Status status(Session_writeable writeable) override
{
return {
.size = _num_entries * sizeof(File_system::Directory_entry),
.type = File_system::Node_type::DIRECTORY,
.rwx = { .readable = true,
.writeable = (writeable == Session_writeable::WRITEABLE),
.executable = true },
.inode = inode(),
.modification_time = modification_time()
};
}
};
#endif /* _INCLUDE__RAM_FS__DIRECTORY_H_ */

View File

@ -1,137 +0,0 @@
/*
* \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 <ram_fs/param.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 <num_level_3_entries()> Chunk_level_3;
typedef Chunk_index<num_level_2_entries(), Chunk_level_3> Chunk_level_2;
typedef Chunk_index<num_level_1_entries(), Chunk_level_2> Chunk_level_1;
typedef Chunk_index<num_level_0_entries(), 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, Session_writeable) 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(Session_writeable writeable) override
{
return {
.size = _length,
.type = File_system::Node_type::CONTINUOUS_FILE,
.rwx = { .readable = true,
.writeable = (writeable == Session_writeable::WRITEABLE),
.executable = true },
.inode = inode(),
.modification_time = modification_time()
};
}
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

@ -1,777 +0,0 @@
/*
* \brief RAM file system
* \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.
*/
/* Genode includes */
#include <file_system/open_node.h>
#include <file_system_session/rpc_object.h>
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <root/component.h>
#include <os/session_policy.h>
/* local includes */
#include "directory.h"
/*************************
** File-system service **
*************************/
namespace Ram_fs {
using namespace File_system;
using File_system::Packet_descriptor;
using File_system::Path;
class Session_component;
class Root;
class Main;
};
class Ram_fs::Session_component : public File_system::Session_rpc_object
{
private:
typedef File_system::Open_node<Node> Open_node;
Genode::Entrypoint &_ep;
Genode::Ram_allocator &_ram;
Genode::Allocator &_alloc;
Directory &_root;
Id_space<File_system::Node> _open_node_registry { };
Session_writeable const _writeable;
Signal_handler<Session_component> _process_packet_handler;
/******************************
** Packet-stream processing **
******************************/
/**
* Perform packet operation
*
* \return true on success, false on failure
*/
void _process_packet_op(Packet_descriptor &packet, Open_node &open_node)
{
size_t const length = packet.length();
/* resulting length */
size_t res_length = 0;
bool succeeded = false;
switch (packet.operation()) {
case Packet_descriptor::READ:
if (packet.length() <= packet.size()) {
Locked_ptr<Node> node { open_node.node() };
if (!node.valid())
break;
res_length = node->read((char *)tx_sink()->packet_content(packet),
length, packet.position(), _writeable);
/* read data or EOF is a success */
succeeded = res_length || (packet.position() >= node->status(_writeable).size);
}
break;
case Packet_descriptor::WRITE:
if (packet.length() <= packet.size()) {
Locked_ptr<Node> node { open_node.node() };
if (!node.valid())
break;
if (_writeable == Session_writeable::READ_ONLY)
break;
res_length = node->write((char const *)tx_sink()->packet_content(packet),
length, packet.position());
/* File system session can't handle partial writes */
if (res_length != length) {
Genode::error("partial write detected ",
res_length, " vs ", length);
/* don't acknowledge */
return;
}
succeeded = true;
}
open_node.mark_as_written();
break;
case Packet_descriptor::WRITE_TIMESTAMP: {
Locked_ptr<Node> node { open_node.node() };
if (!node.valid())
break;
if (_writeable == Session_writeable::WRITEABLE)
packet.with_timestamp([&] (File_system::Timestamp const time) {
node->update_modification_time(time);
succeeded = true;
});
break;
}
case Packet_descriptor::CONTENT_CHANGED:
Genode::error("CONTENT_CHANGED packets from clients have no effect");
return;
case Packet_descriptor::READ_READY:
/* not supported */
succeeded = true;
break;
case Packet_descriptor::SYNC: {
Locked_ptr<Node> node { open_node.node() };
if (!node.valid())
break;
node->notify_listeners();
open_node.unmark_as_written();
succeeded = true;
break;
}
}
packet.length(res_length);
packet.succeeded(succeeded);
tx_sink()->acknowledge_packet(packet);
}
void _process_packet()
{
Packet_descriptor packet = tx_sink()->get_packet();
/* assume failure by default */
packet.succeeded(false);
auto process_packet_fn = [&] (Open_node &open_node) {
_process_packet_op(packet, open_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 (Genode::Packet_descriptor::Invalid_packet) {
Genode::error("dropping invalid File_system packet");
}
}
void _process_packets()
{
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 (most start with '/')
*/
static void _assert_valid_path(char const *path)
{
if (!path || path[0] != '/') {
Genode::warning("malformed path '", Genode::Cstring(path), "'");
throw Lookup_failed();
}
}
public:
/**
* Constructor
*/
Session_component(size_t tx_buf_size, Genode::Entrypoint &ep,
Genode::Ram_allocator &ram, Genode::Region_map &rm,
Genode::Allocator &alloc,
Directory &root, Session_writeable writeable)
:
Session_rpc_object(ram.alloc(tx_buf_size), rm, ep.rpc_ep()),
_ep(ep),
_ram(ram),
_alloc(alloc),
_root(root),
_writeable(writeable),
_process_packet_handler(_ep, *this, &Session_component::_process_packets)
{
/*
* Register '_process_packets' method as signal handler for
* packet-avail and ready-to-ack signals.
*/
_tx.sigh_packet_avail(_process_packet_handler);
_tx.sigh_ready_to_ack(_process_packet_handler);
}
/**
* Destructor
*/
~Session_component()
{
while (_open_node_registry.apply_any<Open_node>([&] (Open_node &node) {
destroy(_alloc, &node); })) { }
Dataspace_capability ds = tx_sink()->dataspace();
_ram.free(static_cap_cast<Ram_dataspace>(ds));
}
/***************************
** File_system interface **
***************************/
File_handle file(Dir_handle dir_handle, Name const &name,
Mode mode, bool create) override
{
if (!valid_name(name.string()))
throw Invalid_name();
auto file_fn = [&] (Open_node &open_node) {
Locked_ptr<Node> dir { open_node.node() };
if (!dir.valid())
throw Unavailable();
if (_writeable == Session_writeable::READ_ONLY)
if (mode != STAT_ONLY && mode != READ_ONLY)
throw Permission_denied();
if (create) {
if (_writeable == Session_writeable::READ_ONLY)
throw Permission_denied();
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(); }
}
File *file = dir->lookup_file(name.string());
Open_node *open_file =
new (_alloc) Open_node(file->weak_ptr(), _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) override
{
if (!valid_name(name.string()))
throw Invalid_name();
auto symlink_fn = [&] (Open_node &open_node) {
Locked_ptr<Node> dir { open_node.node() };
if (!dir.valid())
throw Unavailable();
if (create) {
if (_writeable == Session_writeable::READ_ONLY)
throw Permission_denied();
if (dir->has_sub_node_unsynchronized(name.string()))
throw Node_already_exists();
try {
Symlink * const symlink = new (_alloc)
Symlink(name.string());
dir->adopt_unsynchronized(symlink);
}
catch (Allocator::Out_of_memory) { throw No_space(); }
}
Symlink *symlink = dir->lookup_symlink(name.string());
Open_node *open_symlink =
new (_alloc) Open_node(symlink->weak_ptr(), _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) override
{
char const *path_str = path.string();
_assert_valid_path(path_str);
/* skip leading '/' */
path_str++;
if (create) {
if (_writeable == Session_writeable::READ_ONLY)
throw Permission_denied();
if (!path.valid_string())
throw Name_too_long();
Directory *parent = _root.lookup_parent(path_str);
char const *name = basename(path_str);
if (parent->has_sub_node_unsynchronized(name))
throw Node_already_exists();
try {
parent->adopt_unsynchronized(new (_alloc) Directory(name));
} catch (Allocator::Out_of_memory) {
throw No_space();
}
}
Directory *dir = _root.lookup_dir(path_str);
Open_node *open_dir =
new (_alloc) Open_node(dir->weak_ptr(), _open_node_registry);
return Dir_handle { open_dir->id().value };
}
Node_handle node(Path const &path) override
{
_assert_valid_path(path.string());
Node *node = _root.lookup(path.string() + 1);
Open_node *open_node =
new (_alloc) Open_node(node->weak_ptr(), _open_node_registry);
return open_node->id();
}
Watch_handle watch(Path const &path) override
{
_assert_valid_path(path.string());
Node *node = _root.lookup(path.string() + 1);
Open_node *watcher = new (_alloc)
Open_node(node->weak_ptr(), _open_node_registry);
/*
* like other open nodes, just the only
* kind registered for notifications
*/
watcher->register_notify(*tx_sink());
return Watch_handle { watcher->id().value };
}
void close(Node_handle handle) override
{
auto close_fn = [&] (Open_node &open_node) {
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) override
{
auto status_fn = [&] (Open_node &open_node) {
Locked_ptr<Node> node { open_node.node() };
if (!node.valid())
throw Unavailable();
return node->status(_writeable);
};
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) override { }
void unlink(Dir_handle dir_handle, Name const &name) override
{
if (!valid_name(name.string()))
throw Invalid_name();
if (_writeable == Session_writeable::READ_ONLY)
throw Permission_denied();
auto unlink_fn = [&] (Open_node &open_node) {
Locked_ptr<Node> dir { open_node.node() };
if (!dir.valid())
throw Unavailable();
Node *node = dir->lookup(name.string());
dir->discard(node);
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) override
{
if (_writeable == Session_writeable::READ_ONLY)
throw Permission_denied();
auto truncate_fn = [&] (Open_node &open_node) {
Locked_ptr<Node> node { open_node.node() };
if (!node.valid())
throw Unavailable();
node->truncate(size);
open_node.mark_as_written();
};
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,
Dir_handle to_dir_handle, Name const &to_name) override
{
if (_writeable == Session_writeable::READ_ONLY)
throw Permission_denied();
if (!valid_name(from_name.string()))
throw Lookup_failed();
if (!valid_name(to_name.string()))
throw Invalid_name();
auto move_fn = [&] (Open_node &open_from_dir_node) {
auto inner_move_fn = [&] (Open_node &open_to_dir_node) {
Locked_ptr<Node> from_dir { open_from_dir_node.node() };
if (!from_dir.valid())
throw Unavailable();
Node *node = from_dir->lookup(from_name.string());
node->name(to_name.string());
if (!(open_to_dir_node.node() == open_from_dir_node.node())) {
Locked_ptr<Node> to_dir { open_to_dir_node.node() };
if (!to_dir.valid())
throw Unavailable();
from_dir->discard(node);
to_dir->adopt_unsynchronized(node);
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();
}
}
};
class Ram_fs::Root : public Root_component<Session_component>
{
private:
Genode::Entrypoint &_ep;
Genode::Allocator &_alloc;
Genode::Ram_allocator &_ram;
Genode::Region_map &_rm;
Genode::Xml_node const _config;
Directory &_root_dir;
protected:
Session_component *_create_session(const char *args) override
{
/*
* Determine client-specific policy defined implicitly by
* the client's label.
*/
Genode::Path<MAX_PATH_LEN> session_root;
Directory *session_root_dir = nullptr;
bool writeable = false;
Session_label const label = label_from_args(args);
try {
Session_policy policy(label, _config);
/*
* Determine directory that is used as root directory of
* the session. Clients without a specified root are denied.
*/
if (!policy.has_attribute("root")) {
Genode::error("missing \"root\" attribute in policy definition");
throw Service_denied();
}
typedef String<MAX_PATH_LEN> Root;
Root const root = policy.attribute_value("root", Root());
session_root.import(root.string(), "/");
/*
* Determine if the session is writeable.
* Policy overrides client argument, both default to false.
*/
if (policy.attribute_value("writeable", false))
writeable = Arg_string::find_arg(args, "writeable").bool_value(false);
} catch (Session_policy::No_policy_defined) {
Genode::error("invalid session request, no matching policy");
throw Service_denied();
}
/* apply client's root offset */
{
char tmp[MAX_PATH_LEN] { };
Arg_string::find_arg(args, "root").string(tmp, sizeof(tmp), "/");
if (Genode::strcmp("/", tmp, sizeof(tmp))) {
session_root.append("/");
session_root.append(tmp);
}
}
session_root.remove_trailing('/');
if (session_root == "/") {
session_root_dir = &_root_dir;
} else {
try {
/*
* The root path is specified with a leading path
* delimiter. For performing the lookup, we skip the first
* character.
*/
session_root_dir = _root_dir.lookup_dir(
session_root.base() + 1);
}
catch (Lookup_failed) { throw Service_denied(); }
}
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).aligned_size();
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").aligned_size();
if (!tx_buf_size) {
Genode::error(label, " requested a session with a zero length transmission buffer");
throw Service_denied();
}
/*
* 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) {
Genode::error("insufficient 'ram_quota', got ", ram_quota, ", "
"need ", session_size);
throw Insufficient_ram_quota();
}
return new (md_alloc())
Session_component(tx_buf_size, _ep, _ram, _rm, _alloc,
*session_root_dir,
writeable ? Session_writeable::WRITEABLE
: Session_writeable::READ_ONLY);
}
public:
/**
* Constructor
*
* \param ep entrypoint
* \param md_alloc meta-data allocator
* \param alloc general-purpose allocator
* \param root_dir root-directory handle (anchor for fs)
*/
Root(Genode::Entrypoint &ep, Genode::Ram_allocator &ram,
Genode::Region_map &rm, Genode::Xml_node config,
Allocator &md_alloc, Allocator &alloc, Directory &root_dir)
:
Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_ep(ep), _alloc(alloc), _ram(ram), _rm(rm), _config(config),
_root_dir(root_dir)
{ }
};
static void preload_content(Genode::Env &env,
Genode::Allocator &alloc,
Genode::Xml_node node,
Ram_fs::Directory &dir)
{
using namespace File_system;
for (unsigned i = 0; i < node.num_sub_nodes(); i++) {
Xml_node sub_node = node.sub_node(i);
/*
* Lookup name attribtue, let 'Nonexistent_attribute' exception fall
* through because this configuration error is considered fatal.
*/
typedef String<MAX_NAME_LEN> Name;
Name const name = sub_node.attribute_value("name", Name());
/*
* Create directory
*/
if (sub_node.has_type("dir")) {
Ram_fs::Directory *sub_dir = new (&alloc) Ram_fs::Directory(name.string());
/* traverse into the new directory */
preload_content(env, alloc, sub_node, *sub_dir);
dir.adopt_unsynchronized(sub_dir);
}
/*
* Create file from ROM module
*/
if (sub_node.has_type("rom")) {
/* read "as" attribute, use "name" as default */
Name const as = sub_node.attribute_value("as", name);
/* read file content from ROM module */
try {
Attached_rom_dataspace rom(env, name.string());
Ram_fs::File &file = *new (&alloc) Ram_fs::File(alloc, as.string());
file.write(rom.local_addr<char>(), rom.size(), 0);
dir.adopt_unsynchronized(&file);
}
catch (Rom_connection::Rom_connection_failed) {
Genode::warning("failed to open ROM module \"", name, "\""); }
catch (Region_map::Region_conflict) {
Genode::warning("Could not locally attach ROM module \"", name, "\""); }
}
/*
* Create file from inline data provided as content of the XML node
*/
if (sub_node.has_type("inline")) {
Ram_fs::File &file = *new (&alloc) Ram_fs::File(alloc, name.string());
sub_node.with_raw_content([&] (char const *start, size_t length) {
file.write(start, length, 0); });
dir.adopt_unsynchronized(&file);
}
}
}
struct Ram_fs::Main
{
Genode::Env &_env;
Directory _root_dir { "" };
Genode::Attached_rom_dataspace _config { _env, "config" };
/*
* Initialize root interface
*/
Genode::Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
Genode::Heap _heap { _env.ram(), _env.rm() };
Root _fs_root { _env.ep(), _env.ram(), _env.rm(), _config.xml(),
_sliced_heap, _heap, _root_dir };
Main(Genode::Env &env) : _env(env)
{
/* preload RAM file system with content as declared in the config */
try {
preload_content(_env, _heap, _config.xml().sub_node("content"), _root_dir); }
catch (Xml_node::Nonexistent_sub_node) { }
_env.parent().announce(_env.ep().manage(_fs_root));
}
};
void Component::construct(Genode::Env &env) { static Ram_fs::Main inst(env); }

View File

@ -1,148 +0,0 @@
/*
* \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;
using File_system::Timestamp;
class Node;
class File;
class Symlink;
enum class Session_writeable { READ_ONLY, WRITEABLE };
}
class Ram_fs::Node : public File_system::Node_base,
private Weak_object<Node>,
private List<Node>::Element
{
public:
typedef char Name[128];
using List<Node>::Element::next;
using Weak_object<Node>::weak_ptr;
private:
friend class List<Node>;
friend class Locked_ptr<Node>;
int _ref_count;
Name _name;
unsigned long const _inode;
Timestamp _modification_time { };
/**
* 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;
_modification_time.value = File_system::Timestamp::INVALID;
}
virtual ~Node() { lock_for_destruction(); }
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 update_modification_time(Timestamp const time)
{
_modification_time = time;
}
Timestamp modification_time() const { return _modification_time; }
/*
* 'Session_writeable' is supplied to the 'read' method to reflect the
* writeability in directory entries read from 'Directory' nodes.
*/
virtual size_t read(char *dst, size_t len, seek_off_t, Session_writeable) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
virtual Status status(Session_writeable) = 0;
/* File functionality */
virtual void truncate(File_system::file_size_t)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-file node");
}
/* Directory functionality */
virtual bool has_sub_node_unsynchronized(char const *) const
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return false;
}
virtual void adopt_unsynchronized(Node *)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
}
virtual File *lookup_file(char const *)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
virtual Symlink *lookup_symlink(char const *)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
virtual Node *lookup(char const *, bool = false)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
virtual void discard(Node *)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
}
};
#endif /* _INCLUDE__RAM_FS__NODE_H_ */

View File

@ -1,82 +0,0 @@
/*
* \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, Session_writeable) 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
{
size_t const consumed_len = len;
/* Ideal symlink operations are atomic. */
if (seek_offset) return 0;
for (size_t i = 0; i < len; ++i) {
if (src[i] == '\0') {
len = i;
break;
}
}
/*
* if the target is too long return a
* short result to indicate the error
*/
if (len > sizeof(_link_to))
return len >> 1;
Genode::memcpy(_link_to, src, len);
_len = len;
return consumed_len;
}
Status status(Session_writeable writeable) override
{
return {
.size = _len,
.type = File_system::Node_type::SYMLINK,
.rwx = { .readable = true,
.writeable = (writeable == Session_writeable::WRITEABLE),
.executable = true },
.inode = inode(),
.modification_time = modification_time()
};
}
};
#endif /* _INCLUDE__RAM_FS__SYMLINK_H_ */

View File

@ -1,4 +0,0 @@
TARGET = ram_fs
SRC_CC = main.cc
LIBS = base
INC_DIR += $(PRG_DIR)