mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-01 19:46:45 +00:00
4d442bca30
This patch reduces the number of exception types by facilitating globally defined exceptions for common usage patterns shared by most services. In particular, RPC functions that demand a session-resource upgrade not longer reflect this condition via a session-specific exception but via the 'Out_of_ram' or 'Out_of_caps' types. Furthermore, the 'Parent::Service_denied', 'Parent::Unavailable', 'Root::Invalid_args', 'Root::Unavailable', 'Service::Invalid_args', 'Service::Unavailable', and 'Local_service::Factory::Denied' types have been replaced by the single 'Service_denied' exception type defined in 'session/session.h'. This consolidation eases the error handling (there are fewer exceptions to handle), alleviates the need to convert exceptions along the session-creation call chain, and avoids possible aliasing problems (catching the wrong type with the same name but living in a different scope).
253 lines
6.1 KiB
C++
Executable File
253 lines
6.1 KiB
C++
Executable File
/*
|
|
* \brief Service that provides files of a TAR archive as ROM sessions
|
|
* \author Martin stein
|
|
* \author Norman Feske
|
|
* \date 2010-04-21
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2010-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 <base/component.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
#include <base/heap.h>
|
|
#include <base/log.h>
|
|
#include <base/session_label.h>
|
|
#include <root/component.h>
|
|
|
|
namespace Tar_rom {
|
|
|
|
using namespace Genode;
|
|
class Rom_session_component;
|
|
class Rom_root;
|
|
struct Main;
|
|
}
|
|
|
|
|
|
/**
|
|
* A 'Rom_session_component' exports a single file of the tar archive
|
|
*/
|
|
class Tar_rom::Rom_session_component : public Rpc_object<Rom_session>
|
|
{
|
|
private:
|
|
|
|
Ram_session &_ram;
|
|
|
|
char const * const _tar_addr;
|
|
size_t const _tar_size;
|
|
|
|
Ram_dataspace_capability _file_ds;
|
|
|
|
enum {
|
|
/* length of on data block in tar */
|
|
_BLOCK_LEN = 512,
|
|
|
|
/* length of the header field "file-size" in tar */
|
|
_FIELD_SIZE_LEN = 124
|
|
};
|
|
|
|
/**
|
|
* Copy file content into dataspace
|
|
*
|
|
* \param dst destination dataspace
|
|
*/
|
|
void _copy_content_to_dataspace(Region_map &rm, Dataspace_capability dst,
|
|
char const *src, size_t len)
|
|
{
|
|
/* temporarily map dataspace */
|
|
Attached_dataspace ds(rm, dst);
|
|
|
|
/* copy content */
|
|
size_t bytes_to_copy = min(len, ds.size());
|
|
memcpy(ds.local_addr<char>(), src, bytes_to_copy);
|
|
}
|
|
|
|
/**
|
|
* Initialize dataspace containing the content of the archived file
|
|
*/
|
|
Ram_dataspace_capability _init_file_ds(Ram_session &ram, Region_map &rm,
|
|
Session_label const &name)
|
|
{
|
|
/* measure size of archive in blocks */
|
|
unsigned block_id = 0, block_cnt = _tar_size/_BLOCK_LEN;
|
|
|
|
char const *file_content = nullptr;
|
|
unsigned long file_size = 0;
|
|
|
|
/* scan metablocks of archive */
|
|
while (block_id < block_cnt) {
|
|
|
|
ascii_to_unsigned(_tar_addr + block_id*_BLOCK_LEN +
|
|
_FIELD_SIZE_LEN, file_size, 8);
|
|
|
|
/* get name of tar record */
|
|
char const *record_filename = _tar_addr + block_id*_BLOCK_LEN;
|
|
|
|
/* skip leading dot of path if present */
|
|
if (record_filename[0] == '.' && record_filename[1] == '/')
|
|
record_filename++;
|
|
|
|
/* get infos about current file */
|
|
if (name == record_filename) {
|
|
file_content = _tar_addr + (block_id+1) * _BLOCK_LEN;
|
|
break;
|
|
}
|
|
|
|
/* some datablocks */ /* one metablock */
|
|
block_id = block_id + (file_size / _BLOCK_LEN) + 1;
|
|
|
|
/* round up */
|
|
if (file_size % _BLOCK_LEN != 0) block_id++;
|
|
|
|
/* check for end of tar archive */
|
|
if (block_id*_BLOCK_LEN >= _tar_size)
|
|
break;
|
|
|
|
/* lookout for empty eof-blocks */
|
|
if (*(_tar_addr + (block_id*_BLOCK_LEN)) == 0x00)
|
|
if (*(_tar_addr + (block_id*_BLOCK_LEN + 1)) == 0x00)
|
|
break;
|
|
}
|
|
|
|
if (!file_content) {
|
|
error("couldn't find file '", name, "', empty result");
|
|
return Ram_dataspace_capability();
|
|
}
|
|
|
|
/* try to allocate memory for file */
|
|
Ram_dataspace_capability file_ds;
|
|
try {
|
|
file_ds = ram.alloc(file_size);
|
|
|
|
/* get content of file copied into dataspace and return */
|
|
_copy_content_to_dataspace(rm, file_ds, file_content, file_size);
|
|
} catch (...) {
|
|
error("couldn't allocate memory for file, empty result");
|
|
return file_ds;
|
|
}
|
|
|
|
return file_ds;
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor scans and seeks to file
|
|
*
|
|
* \param tar_addr local address to tar archive
|
|
* \param tar_size size of tar archive in bytes
|
|
* \param label name of the requested ROM module
|
|
*
|
|
* \throw Service_denied
|
|
*/
|
|
Rom_session_component(Ram_session &ram, Region_map &rm,
|
|
char const *tar_addr, unsigned tar_size,
|
|
Session_label const &label)
|
|
:
|
|
_ram(ram), _tar_addr(tar_addr), _tar_size(tar_size),
|
|
_file_ds(_init_file_ds(ram, rm, label))
|
|
{
|
|
if (!_file_ds.valid())
|
|
throw Service_denied();
|
|
}
|
|
|
|
/**
|
|
* Destructor
|
|
*/
|
|
~Rom_session_component() { _ram.free(_file_ds); }
|
|
|
|
/**
|
|
* Return dataspace with content of file
|
|
*/
|
|
Rom_dataspace_capability dataspace()
|
|
{
|
|
Dataspace_capability ds = _file_ds;
|
|
return static_cap_cast<Rom_dataspace>(ds);
|
|
}
|
|
|
|
void sigh(Signal_context_capability) { }
|
|
};
|
|
|
|
|
|
class Tar_rom::Rom_root : public Root_component<Rom_session_component>
|
|
{
|
|
private:
|
|
|
|
Env &_env;
|
|
|
|
char const * const _tar_addr;
|
|
unsigned const _tar_size;
|
|
|
|
Rom_session_component *_create_session(const char *args)
|
|
{
|
|
Session_label const label = label_from_args(args);
|
|
Session_label const module_name = label.last_element();
|
|
log("connection for module '", module_name, "' requested");
|
|
|
|
/* create new session for the requested file */
|
|
return new (md_alloc()) Rom_session_component(_env.ram(), _env.rm(),
|
|
_tar_addr, _tar_size,
|
|
module_name.string());
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param tar_base local address of tar archive
|
|
* \param tar_size size of tar archive in bytes
|
|
*/
|
|
Rom_root(Env &env, Allocator &md_alloc,
|
|
char const *tar_addr, size_t tar_size)
|
|
:
|
|
Root_component<Rom_session_component>(env.ep(), md_alloc),
|
|
_env(env), _tar_addr(tar_addr), _tar_size(tar_size)
|
|
{ }
|
|
};
|
|
|
|
|
|
struct Tar_rom::Main
|
|
{
|
|
Env &_env;
|
|
|
|
Attached_rom_dataspace _config { _env, "config" };
|
|
|
|
typedef String<64> Name;
|
|
|
|
/**
|
|
* Read name of tar archive from config
|
|
*/
|
|
Name _tar_name()
|
|
{
|
|
try {
|
|
return _config.xml().sub_node("archive").attribute_value("name", Name());
|
|
} catch (...) {
|
|
error("could not read archive name argument from config");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
Attached_rom_dataspace _tar_ds { _env, _tar_name().string() };
|
|
|
|
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
|
|
|
|
Rom_root _root { _env, _sliced_heap, _tar_ds.local_addr<char>(), _tar_ds.size() };
|
|
|
|
Main(Env &env) : _env(env)
|
|
{
|
|
log("using tar archive '", _tar_name(), "' with size ", _tar_ds.size());
|
|
|
|
env.parent().announce(env.ep().manage(_root));
|
|
}
|
|
};
|
|
|
|
|
|
void Component::construct(Genode::Env &env) { static Tar_rom::Main main(env); }
|
|
|