mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
Add new 'fs_rom' service
The 'fs_rom' service provides files stored on a file system as ROM modules via the ROM-session interface. Fixes #606
This commit is contained in:
parent
7217ea14d8
commit
267817c2c5
@ -292,6 +292,10 @@ Separate components:
|
||||
Provides each file contained in a tar file obtained via Genode's ROM session
|
||||
as separate ROM session.
|
||||
|
||||
:'os/src/server/fs_rom':
|
||||
Provides files stored in a file system as individual ROM sessions. Please
|
||||
refer to 'os/src/server/fs_rom' for more information.
|
||||
|
||||
:'os/src/server/iso9660':
|
||||
Provides each file of an ISO9660 file system accessed via a block session as
|
||||
separate ROM session.
|
||||
|
16
os/src/server/fs_rom/README
Normal file
16
os/src/server/fs_rom/README
Normal file
@ -0,0 +1,16 @@
|
||||
The 'fs_rom' server translates the 'File_system' session interface to the
|
||||
'ROM' session' interface. Each request for a ROM file is handled by looking
|
||||
up an equally named file on the file system. If no such file could be found,
|
||||
the server watches the file system for the creation of the corresponding file.
|
||||
Furthermore, the server reflects file changes as signals to the ROM session.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
* Symbolic links are not handled
|
||||
* The server needs to allocate RAM for each requested file. The RAM is always
|
||||
allocated from the RAM session of the server. The RAM quota consumed by the
|
||||
server depends on the client requests and the size of the requested files.
|
||||
Therefore, one instance of the server should not be used by untrusted clients
|
||||
and critical clients at the same time. In such situations, multiple instances
|
||||
of the server could be used.
|
470
os/src/server/fs_rom/main.cc
Executable file
470
os/src/server/fs_rom/main.cc
Executable file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* \brief Service that provides files of a file system as ROM sessions
|
||||
* \author Norman Feske
|
||||
* \date 2013-01-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 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 <rom_session/rom_session.h>
|
||||
#include <root/component.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <file_system_session/connection.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/env.h>
|
||||
#include <base/printf.h>
|
||||
#include <os/path.h>
|
||||
|
||||
|
||||
/*********************************************
|
||||
** Utilities for accessing the file system **
|
||||
*********************************************/
|
||||
|
||||
/*
|
||||
* XXX The following generic utilities should be moved to a public place.
|
||||
* They are based on those found in the 'libc_fs' plugin. We should
|
||||
* unify them.
|
||||
*/
|
||||
|
||||
namespace File_system {
|
||||
|
||||
/**
|
||||
* Collect pending packet acknowledgements, freeing the space occupied
|
||||
* by the packet in the bulk buffer
|
||||
*
|
||||
* This function should be called prior enqueing new packets into the
|
||||
* packet stream to free up space in the bulk buffer.
|
||||
*/
|
||||
static void collect_acknowledgements(Session::Tx::Source &source)
|
||||
{
|
||||
while (source.ack_avail())
|
||||
source.release_packet(source.get_acked_packet());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read file content
|
||||
*/
|
||||
static inline size_t read(Session &fs, File_handle const &file_handle,
|
||||
void *dst, size_t count, off_t seek_offset = 0)
|
||||
{
|
||||
Session::Tx::Source &source = *fs.tx();
|
||||
|
||||
size_t const max_packet_size = source.bulk_buffer_size() / 2;
|
||||
|
||||
size_t remaining_count = count;
|
||||
|
||||
while (remaining_count) {
|
||||
|
||||
collect_acknowledgements(source);
|
||||
|
||||
size_t const curr_packet_size = min(remaining_count, max_packet_size);
|
||||
|
||||
Packet_descriptor
|
||||
packet(source.alloc_packet(curr_packet_size),
|
||||
0,
|
||||
file_handle,
|
||||
File_system::Packet_descriptor::READ,
|
||||
curr_packet_size,
|
||||
seek_offset);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
|
||||
packet = source.get_acked_packet();
|
||||
|
||||
size_t const read_num_bytes = min(packet.length(), curr_packet_size);
|
||||
|
||||
/* copy-out payload into destination buffer */
|
||||
memcpy(dst, source.packet_content(packet), read_num_bytes);
|
||||
|
||||
source.release_packet(packet);
|
||||
|
||||
/* prepare next iteration */
|
||||
seek_offset += read_num_bytes;
|
||||
dst = (void *)((Genode::addr_t)dst + read_num_bytes);
|
||||
remaining_count -= read_num_bytes;
|
||||
|
||||
/*
|
||||
* If we received less bytes than requested, we reached the end
|
||||
* of the file.
|
||||
*/
|
||||
if (read_num_bytes < curr_packet_size)
|
||||
break;
|
||||
}
|
||||
|
||||
return count - remaining_count;
|
||||
}
|
||||
|
||||
|
||||
struct Handle_guard
|
||||
{
|
||||
private:
|
||||
|
||||
Session &_session;
|
||||
Node_handle _handle;
|
||||
|
||||
public:
|
||||
|
||||
Handle_guard(Session &session, Node_handle handle)
|
||||
: _session(session), _handle(handle) { }
|
||||
|
||||
~Handle_guard() { _session.close(_handle); }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** ROM service **
|
||||
*****************/
|
||||
|
||||
/**
|
||||
* A 'Rom_session_component' exports a single file of the file system
|
||||
*/
|
||||
class Rom_session_component : public Genode::Rpc_object<Genode::Rom_session>
|
||||
{
|
||||
private:
|
||||
|
||||
File_system::Session &_fs;
|
||||
|
||||
enum { PATH_MAX_LEN = 512 };
|
||||
typedef Genode::Path<PATH_MAX_LEN> Path;
|
||||
|
||||
/**
|
||||
* Name of requested file, interpreted at path into the file system
|
||||
*/
|
||||
Path const _file_path;
|
||||
|
||||
/**
|
||||
* Handle of associated file
|
||||
*/
|
||||
File_system::File_handle _file_handle;
|
||||
|
||||
/**
|
||||
* Handle of currently watched compound directory
|
||||
*
|
||||
* The compund directory is watched only if the requested file could
|
||||
* not be looked up.
|
||||
*/
|
||||
File_system::Node_handle _compound_dir_handle;
|
||||
|
||||
/**
|
||||
* Dataspace exposed as ROM module to the client
|
||||
*/
|
||||
Genode::Ram_dataspace_capability _file_ds;
|
||||
|
||||
/**
|
||||
* Handler for ROM file changes
|
||||
*/
|
||||
Genode::Lock _sigh_lock;
|
||||
Genode::Signal_context_capability _sigh;
|
||||
|
||||
/**
|
||||
* Dispatcher that is called each time when the requested file is not
|
||||
* yet available and the compound directory changes
|
||||
*
|
||||
* The change of the compound directory bears the chance that the
|
||||
* requested file re-appears. So we inform the client about a ROM
|
||||
* module change and thereby give it a chance to call 'dataspace()' in
|
||||
* response.
|
||||
*/
|
||||
Genode::Signal_dispatcher<Rom_session_component> _dir_change_dispatcher;
|
||||
|
||||
/**
|
||||
* Signal-handling function called by the main thread the compound
|
||||
* directory changed.
|
||||
*
|
||||
* Note that this function is not executed in the context of the RPC
|
||||
* entrypoint. Therefore, the access to '_sigh' is synchronized with
|
||||
* the 'sigh()' function using '_sigh_lock'.
|
||||
*/
|
||||
void _dir_changed(unsigned)
|
||||
{
|
||||
Genode::Lock::Guard guard(_sigh_lock);
|
||||
|
||||
PINF("detected directory change");
|
||||
if (_sigh.valid())
|
||||
Genode::Signal_transmitter(_sigh).submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open compound directory of specified file
|
||||
*
|
||||
* \param walk_up If set to true, the function tries to walk up the
|
||||
* hierarchy towards the root and returns the first
|
||||
* existing directory on the way. If set to false, the
|
||||
* function returns the immediate compound directory.
|
||||
*/
|
||||
static File_system::Dir_handle _open_compound_dir(File_system::Session &fs,
|
||||
Path const &path,
|
||||
bool walk_up)
|
||||
{
|
||||
using namespace File_system;
|
||||
|
||||
Genode::Path<PATH_MAX_LEN> dir_path(path.base());
|
||||
|
||||
while (!path.equals("/")) {
|
||||
|
||||
dir_path.strip_last_element();
|
||||
|
||||
try { return fs.dir(dir_path.base(), false); }
|
||||
|
||||
catch (Invalid_handle) { PERR("Invalid_handle"); }
|
||||
catch (Invalid_name) { PERR("Invalid_name"); }
|
||||
catch (Lookup_failed) { PERR("Lookup_failed"); }
|
||||
catch (Permission_denied) { PERR("Permission_denied"); }
|
||||
catch (Name_too_long) { PERR("Name_too_long"); }
|
||||
catch (No_space) { PERR("No_space"); }
|
||||
|
||||
/*
|
||||
* If the directory could not be opened, walk up the hierarchy
|
||||
* towards the root and try again.
|
||||
*/
|
||||
if (!walk_up) break;
|
||||
}
|
||||
return Dir_handle(); /* invalid */
|
||||
}
|
||||
|
||||
/**
|
||||
* Open file with specified name at the file system
|
||||
*/
|
||||
static File_system::File_handle _open_file(File_system::Session &fs,
|
||||
Path const &path)
|
||||
{
|
||||
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);
|
||||
}
|
||||
catch (Invalid_handle) { PERR("Invalid_handle"); }
|
||||
catch (Invalid_name) { PERR("Invalid_name"); }
|
||||
catch (Lookup_failed) { PERR("Lookup_failed"); }
|
||||
|
||||
return file_handle;
|
||||
}
|
||||
|
||||
void _register_for_compound_dir_changes()
|
||||
{
|
||||
/* forget about the previously watched compound directory */
|
||||
if (_compound_dir_handle.valid())
|
||||
_fs.close(_compound_dir_handle);
|
||||
|
||||
_compound_dir_handle = _open_compound_dir(_fs, _file_path, true);
|
||||
|
||||
/* register for changes in compound directory */
|
||||
if (_compound_dir_handle.valid())
|
||||
_fs.sigh(_compound_dir_handle, _dir_change_dispatcher);
|
||||
else
|
||||
PWRN("could not track compound dir, giving up");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize '_file_ds' dataspace with file content
|
||||
*/
|
||||
void _update_dataspace()
|
||||
{
|
||||
using namespace File_system;
|
||||
|
||||
/*
|
||||
* On each repeated call of this function, the dataspace is
|
||||
* replaced with a new one that contains the most current file
|
||||
* content.
|
||||
*/
|
||||
if (_file_ds.valid()) {
|
||||
env()->ram_session()->free(_file_ds);
|
||||
_file_ds = Ram_dataspace_capability();
|
||||
}
|
||||
|
||||
/* close and then re-open the file */
|
||||
if (_file_handle.valid())
|
||||
_fs.close(_file_handle);
|
||||
|
||||
_file_handle = _open_file(_fs, _file_path);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/* register for file changes */
|
||||
if (_sigh.valid() && _file_handle.valid())
|
||||
_fs.sigh(_file_handle, _sigh);
|
||||
|
||||
size_t const file_size = _file_handle.valid()
|
||||
? _fs.status(_file_handle).size : 0;
|
||||
|
||||
/* allocate new RAM dataspace according to file size */
|
||||
if (file_size > 0) {
|
||||
try {
|
||||
_file_ds = env()->ram_session()->alloc(file_size); }
|
||||
catch (...) {
|
||||
PERR("couldn't allocate memory for file, empty result\n");
|
||||
_file_ds = Ram_dataspace_capability();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_file_ds.valid()) {
|
||||
_register_for_compound_dir_changes();
|
||||
return;
|
||||
}
|
||||
|
||||
/* map dataspace locally */
|
||||
void * const dst_addr = env()->rm_session()->attach(_file_ds);
|
||||
|
||||
/* read content from file */
|
||||
read(_fs, _file_handle, dst_addr, file_size);
|
||||
|
||||
/* unmap dataspace */
|
||||
env()->rm_session()->detach(dst_addr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param fs file-system session to read the file from
|
||||
* \param filename requested file name
|
||||
* \param sig_rec signal receiver used to get notified about changes
|
||||
* within the compound directory (in the case when
|
||||
* the requested file could not be found at session-
|
||||
* creation time)
|
||||
*/
|
||||
Rom_session_component(File_system::Session &fs, const char *file_path,
|
||||
Genode::Signal_receiver &sig_reg)
|
||||
:
|
||||
_fs(fs), _file_path(file_path), _file_handle(_open_file(_fs, _file_path)),
|
||||
_dir_change_dispatcher(sig_reg, *this, &Rom_session_component::_dir_changed)
|
||||
{
|
||||
if (!_file_handle.valid())
|
||||
_register_for_compound_dir_changes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Rom_session_component()
|
||||
{
|
||||
/* close re-open the file */
|
||||
if (_file_handle.valid())
|
||||
_fs.close(_file_handle);
|
||||
|
||||
if (_compound_dir_handle.valid())
|
||||
_fs.close(_compound_dir_handle);
|
||||
|
||||
/* close file */
|
||||
Genode::env()->ram_session()->free(_file_ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return dataspace with up-to-date content of file
|
||||
*/
|
||||
Genode::Rom_dataspace_capability dataspace()
|
||||
{
|
||||
_update_dataspace();
|
||||
Genode::Dataspace_capability ds = _file_ds;
|
||||
return Genode::static_cap_cast<Genode::Rom_dataspace>(ds);
|
||||
}
|
||||
|
||||
void sigh(Genode::Signal_context_capability sigh)
|
||||
{
|
||||
Genode::Lock::Guard guard(_sigh_lock);
|
||||
_sigh = sigh;
|
||||
if (_file_handle.valid())
|
||||
_fs.sigh(_file_handle, _sigh);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Rom_root : public Genode::Root_component<Rom_session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
File_system::Session &_fs;
|
||||
Genode::Signal_receiver &_sig_rec;
|
||||
|
||||
Rom_session_component *_create_session(const char *args)
|
||||
{
|
||||
enum { FILENAME_MAX_LEN = 128 };
|
||||
char filename[FILENAME_MAX_LEN];
|
||||
Genode::Arg_string::find_arg(args, "filename")
|
||||
.string(filename, sizeof(filename), "");
|
||||
|
||||
PINF("connection for file '%s' requested\n", filename);
|
||||
|
||||
/* create new session for the requested file */
|
||||
return new (md_alloc()) Rom_session_component(_fs, filename, _sig_rec);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param entrypoint entrypoint to be used for ROM sessions
|
||||
* \param md_alloc meta-data allocator used for ROM sessions
|
||||
* \param fs file-system session
|
||||
*/
|
||||
Rom_root(Genode::Rpc_entrypoint &entrypoint,
|
||||
Genode::Allocator &md_alloc,
|
||||
File_system::Session &fs,
|
||||
Genode::Signal_receiver &sig_rec)
|
||||
:
|
||||
Genode::Root_component<Rom_session_component>(&entrypoint, &md_alloc),
|
||||
_fs(fs), _sig_rec(sig_rec)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* open file-system session */
|
||||
static Genode::Allocator_avl fs_tx_block_alloc(env()->heap());
|
||||
static File_system::Connection fs(fs_tx_block_alloc);
|
||||
|
||||
/* connection to capability service needed to create capabilities */
|
||||
static Cap_connection cap;
|
||||
|
||||
/* creation of the entrypoint and the root interface */
|
||||
static Sliced_heap sliced_heap(env()->ram_session(),
|
||||
env()->rm_session());
|
||||
|
||||
/* receiver of directory-change signals */
|
||||
static Signal_receiver sig_rec;
|
||||
|
||||
enum { STACK_SIZE = 8*1024 };
|
||||
static Rpc_entrypoint ep(&cap, STACK_SIZE, "fs_rom_ep");
|
||||
static Rom_root rom_root(ep, sliced_heap, fs, sig_rec);
|
||||
|
||||
/* announce server*/
|
||||
env()->parent()->announce(ep.manage(&rom_root));
|
||||
|
||||
/* process incoming signals */
|
||||
for (;;) {
|
||||
Signal s = sig_rec.wait_for_signal();
|
||||
static_cast<Signal_dispatcher_base *>(s.context())->dispatch(s.num());
|
||||
}
|
||||
return 0;
|
||||
}
|
3
os/src/server/fs_rom/target.mk
Executable file
3
os/src/server/fs_rom/target.mk
Executable file
@ -0,0 +1,3 @@
|
||||
TARGET = fs_rom
|
||||
SRC_CC = main.cc
|
||||
LIBS = cxx env server signal
|
Loading…
x
Reference in New Issue
Block a user