server/lz_rom: decompress lzipped files to ROM sessions
See 'src/server/lz_rom/README' and 'run/lz_rom_noux.run' for more information. Fix #70
This commit is contained in:
committed by
Norman Feske
parent
11956d6e54
commit
7267d88760
93
run/lz_rom_noux.run
Normal file
93
run/lz_rom_noux.run
Normal file
@@ -0,0 +1,93 @@
|
||||
build {
|
||||
core init
|
||||
drivers/timer
|
||||
lib/libc_noux
|
||||
noux-pkg/coreutils
|
||||
noux/minimal
|
||||
server/log_terminal
|
||||
server/lz_rom
|
||||
}
|
||||
|
||||
# strip coreutils binaries and create tar archive
|
||||
exec sh -c "[cross_dev_prefix]strip bin/coreutils/bin/*"
|
||||
exec tar cfv bin/coreutils.tar -h -C bin/coreutils .
|
||||
|
||||
create_boot_directory
|
||||
|
||||
install_config {
|
||||
<config verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="RAM"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="terminal">
|
||||
<binary name="log_terminal" />
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Terminal"/></provides>
|
||||
</start>
|
||||
<start name="lz_rom">
|
||||
<resource name="RAM" quantum="64M"/>
|
||||
<provides> <service name="ROM"/> </provides>
|
||||
<config>
|
||||
<vfs> <tar name="coreutils.tar.lz.tar"/> </vfs>
|
||||
<libc/>
|
||||
</config>
|
||||
</start>
|
||||
<start name="noux">
|
||||
<resource name="RAM" quantum="32M"/>
|
||||
<config verbose="yes">
|
||||
<fstab> <tar name="coreutils.tar" /> </fstab>
|
||||
<start name="/bin/ls"> <arg value="-Rla"/> </start>
|
||||
</config>
|
||||
<route>
|
||||
<service name="ROM" label="coreutils.tar">
|
||||
<child name="lz_rom"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
# Lzip the coreutils tarball
|
||||
exec lzip --force --keep bin/coreutils.tar
|
||||
|
||||
# Tar the Lziped tarball because a zero padded ROM will not work
|
||||
exec tar cf bin/coreutils.tar.lz.tar -C bin coreutils.tar.lz
|
||||
|
||||
build_boot_image {
|
||||
core init ld.lib.so
|
||||
coreutils.tar.lz.tar
|
||||
libc.lib.so
|
||||
libc_noux.lib.so
|
||||
libm.lib.so
|
||||
log_terminal
|
||||
lz_rom
|
||||
noux
|
||||
timer
|
||||
}
|
||||
|
||||
append qemu_args " -nographic -serial mon:stdio "
|
||||
|
||||
if {[have_spec x86_64]} {
|
||||
# coreutils.tar is really huge when built for x86_64
|
||||
append qemu_args " -m 300 "
|
||||
}
|
||||
|
||||
run_genode_until {child "noux" exited with exit value 0.*\n} 30
|
||||
|
||||
exec rm bin/coreutils.tar.lz.tar bin/coreutils.tar.lz bin/coreutils.tar
|
||||
58
src/server/lz_rom/README
Normal file
58
src/server/lz_rom/README
Normal file
@@ -0,0 +1,58 @@
|
||||
This component services accepts ROM sessions requests and opens a
|
||||
file with the name of the ROM request label appended with '.lz'.
|
||||
The file content is passed through Lzip decompression and returned
|
||||
to the client. All sessions are static, no update signals shall be
|
||||
issued.
|
||||
|
||||
The Lzip format is designed for archiving and sharing data. In the
|
||||
general case the speed of decompression is bound by CPU and RAM
|
||||
resources and is thus assumed to be slower than local storage.
|
||||
|
||||
|
||||
Example configuration
|
||||
---------------------
|
||||
In this init configuration snippet ROM requests are serviced from a
|
||||
hierarchy. Initially the parent is consulted, followed by a query for
|
||||
a raw file on the file-system, and finally an lzipped file.
|
||||
|
||||
! <start name="rom_fallback">
|
||||
! <resource name="RAM" quantum="4M"/>
|
||||
! <provides> <service name="ROM"/> </provides>
|
||||
! <config>
|
||||
! <fallback/> <!-- request from parent first -->
|
||||
! <fallback label="fs"/>
|
||||
! <fallback label="lz"/>
|
||||
! </config>
|
||||
! <route>
|
||||
! <service name="ROM" label_prefix="fs ->" >
|
||||
! <child name="fs_rom"/> </service>
|
||||
! <service name="ROM" label_prefix="lz ->" >
|
||||
! <child name="lz_rom"/> </service>
|
||||
! <!-- fallthrough to parent -->
|
||||
! <any-service> <parent/> </any-service>
|
||||
! </route>
|
||||
! </start>
|
||||
|
||||
! <start name="fs_rom">
|
||||
! <resource name="RAM" quantum="96M"/>
|
||||
! <provides><service name="ROM"/></provides>
|
||||
! <route>
|
||||
! <service name="File_system">
|
||||
! <child name="fs_server"/> </service>
|
||||
! <any-service> <parent/> </any-service>
|
||||
! </route>
|
||||
! </start>
|
||||
|
||||
! <start name="lz_rom">
|
||||
! <resource name="RAM" quantum="96M"/>
|
||||
! <provides><service name="ROM"/></provides>
|
||||
! <config>
|
||||
! <vfs> <fs writeable="no"/> </vfs>
|
||||
! <libc/>
|
||||
! </config>
|
||||
! <route>
|
||||
! <service name="File_system">
|
||||
! <child name="fs_server"/> </service>
|
||||
! <any-service> <parent/> </any-service>
|
||||
! </route>
|
||||
! </start>
|
||||
332
src/server/lz_rom/main.cc
Normal file
332
src/server/lz_rom/main.cc
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* \brief ROM Lzlip decompressor
|
||||
* \author Emery Hemingway
|
||||
* \date 2017-04-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: Multithreaded decompression.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/session_policy.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/service.h>
|
||||
#include <base/session_label.h>
|
||||
#include <libc/component.h>
|
||||
#include <base/log.h>
|
||||
|
||||
namespace {
|
||||
using namespace Genode;
|
||||
|
||||
/* Lzlib includes */
|
||||
#include <lzlib.h>
|
||||
}
|
||||
|
||||
namespace Lz_rom {
|
||||
using namespace Genode;
|
||||
|
||||
struct Session;
|
||||
struct Main;
|
||||
|
||||
typedef Session_state::Args Args;
|
||||
|
||||
struct File_error { };
|
||||
struct Decompression_error { };
|
||||
}
|
||||
|
||||
|
||||
struct Lz_rom::Session :
|
||||
Genode::Rpc_object<Genode::Rom_session>,
|
||||
Genode::Parent::Server
|
||||
{
|
||||
Parent::Client parent_client;
|
||||
|
||||
Id_space<Parent::Server>::Element server_id;
|
||||
|
||||
Session(Id_space<Parent::Server> &server_space,
|
||||
Parent::Server::Id server_id,
|
||||
Libc::Env &env, Genode::Allocator &alloc,
|
||||
Session_label const &label,
|
||||
LZ_Decoder *decoder);
|
||||
|
||||
Attached_ram_dataspace ram_ds;
|
||||
|
||||
/***************************
|
||||
** ROM session interface **
|
||||
***************************/
|
||||
|
||||
Rom_dataspace_capability dataspace() override
|
||||
{
|
||||
Genode::Dataspace_capability ds_cap = ram_ds.cap();
|
||||
return static_cap_cast<Rom_dataspace>(ds_cap);
|
||||
}
|
||||
|
||||
void sigh(Signal_context_capability sigh) override { }
|
||||
};
|
||||
|
||||
|
||||
Lz_rom::Session::Session(Id_space<Parent::Server> &server_space,
|
||||
Parent::Server::Id server_id,
|
||||
Libc::Env &env, Genode::Allocator &alloc,
|
||||
Session_label const &label,
|
||||
LZ_Decoder *decoder)
|
||||
:
|
||||
server_id(*this, server_space, server_id),
|
||||
ram_ds(env.ram(), env.rm(), 0)
|
||||
{
|
||||
using namespace Vfs;
|
||||
typedef Vfs::Directory_service::Stat_result Stat_result;
|
||||
typedef Vfs::Directory_service::Open_result Open_result;
|
||||
typedef Vfs::File_io_service::Read_result Read_result;
|
||||
|
||||
/* Get file size */
|
||||
Vfs::Directory_service::Stat stat;
|
||||
if (env.vfs().stat(label.string(), stat) != Stat_result::STAT_OK)
|
||||
throw File_error();
|
||||
if (!stat.size)
|
||||
throw File_error();
|
||||
|
||||
/* Open file */
|
||||
Vfs_handle *fh;
|
||||
Open_result res = env.vfs().open(
|
||||
label.string(), Vfs::Directory_service::OPEN_MODE_RDONLY, &fh, alloc);
|
||||
if (res != Open_result::OPEN_OK)
|
||||
throw File_error();
|
||||
Vfs_handle::Guard handle_guard(fh);
|
||||
|
||||
size_t compressed_size = stat.size;
|
||||
size_t uncompressed_size;
|
||||
|
||||
/* read the uncompressed file size from the end of the file */
|
||||
fh->seek(stat.size - 16);
|
||||
{
|
||||
uint64_t data_size = 0;
|
||||
|
||||
file_size n = 0;
|
||||
Read_result res = fh->fs().read(fh, (char*)&data_size, sizeof(data_size), n);
|
||||
if (res != Read_result::READ_OK || n != sizeof(data_size))
|
||||
throw File_error();
|
||||
|
||||
/* XXX: little-endian only */
|
||||
uncompressed_size = data_size;
|
||||
}
|
||||
if (uncompressed_size == 0)
|
||||
throw File_error();
|
||||
|
||||
/*
|
||||
* TODO: check the compressed member size
|
||||
* to see if this is a concatenated file
|
||||
*/
|
||||
|
||||
LZ_decompress_reset(decoder);
|
||||
|
||||
/* Allocate the ROM buffer now that the size is known */
|
||||
ram_ds.realloc(&env.ram(), uncompressed_size);
|
||||
uint8_t *rom_buf = ram_ds.local_addr<uint8_t>();
|
||||
|
||||
/* Page aligned size of ROM dataspace */
|
||||
size_t const rom_size = ram_ds.size();
|
||||
/* Offset of encoded data*/
|
||||
size_t enc_off = rom_size - compressed_size;
|
||||
/* Offset of decoded data */
|
||||
size_t dec_off = 0;
|
||||
|
||||
/* Read the compressed data into the back of the ROM dataspace */
|
||||
{
|
||||
file_size read_off = enc_off;
|
||||
file_size read_len = compressed_size;
|
||||
fh->seek(0);
|
||||
while (read_off < rom_size) {
|
||||
file_size n = 0;
|
||||
Read_result res = fh->fs().read(
|
||||
fh, (char*)(rom_buf+read_off), read_len, n);
|
||||
if (res != Read_result::READ_OK)
|
||||
throw File_error();
|
||||
fh->advance_seek(n);
|
||||
read_len -= n;
|
||||
read_off += n;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decode from the back of the dataspace to the front */
|
||||
while (dec_off < uncompressed_size) {
|
||||
if (enc_off < rom_size) {
|
||||
int write_size = min(LZ_decompress_write_size(decoder),
|
||||
int(rom_size - enc_off));
|
||||
|
||||
/* write to the decoder */
|
||||
write_size = LZ_decompress_write(decoder, rom_buf+enc_off, write_size);
|
||||
if (write_size < 0)
|
||||
throw Decompression_error();
|
||||
enc_off += write_size;
|
||||
}
|
||||
|
||||
/* read from the decoder */
|
||||
int read_size = LZ_decompress_read(
|
||||
decoder, rom_buf+dec_off, uncompressed_size-dec_off);
|
||||
if (read_size < 0)
|
||||
throw Decompression_error();
|
||||
|
||||
dec_off += read_size;
|
||||
}
|
||||
|
||||
/* Sweep the crumbs out of the page boundry gap */
|
||||
memset(rom_buf+uncompressed_size, 0x00, rom_size - uncompressed_size);
|
||||
|
||||
LZ_decompress_finish(decoder);
|
||||
}
|
||||
|
||||
|
||||
struct Lz_rom::Main
|
||||
{
|
||||
Id_space<Parent::Server> server_id_space;
|
||||
|
||||
Libc::Env &env;
|
||||
|
||||
Attached_rom_dataspace config_rom { env, "config" };
|
||||
|
||||
Attached_rom_dataspace session_requests { env, "session_requests" };
|
||||
|
||||
Sliced_heap session_alloc { env.ram(), env.rm() };
|
||||
Heap vfs_alloc { env.ram(), env.rm() };
|
||||
|
||||
bool config_stale = false;
|
||||
|
||||
void handle_config() {
|
||||
config_stale = true; }
|
||||
|
||||
void handle_session_request(Xml_node request);
|
||||
|
||||
void handle_session_requests()
|
||||
{
|
||||
if (config_stale) {
|
||||
config_rom.update();
|
||||
config_stale = false;
|
||||
}
|
||||
|
||||
session_requests.update();
|
||||
|
||||
Xml_node const requests = session_requests.xml();
|
||||
|
||||
requests.for_each_sub_node([&] (Xml_node request) {
|
||||
handle_session_request(request);
|
||||
});
|
||||
}
|
||||
|
||||
Signal_handler<Main> config_handler {
|
||||
env.ep(), *this, &Main::handle_config };
|
||||
|
||||
Signal_handler<Main> session_request_handler {
|
||||
env.ep(), *this, &Main::handle_session_requests };
|
||||
|
||||
LZ_Decoder *decoder = LZ_decompress_open();
|
||||
|
||||
Main(Libc::Env &env) : env(env)
|
||||
{
|
||||
config_rom.sigh(config_handler);
|
||||
session_requests.sigh(session_request_handler);
|
||||
|
||||
/* handle requests that have queued before or during construction */
|
||||
handle_session_requests();
|
||||
}
|
||||
|
||||
~Main()
|
||||
{
|
||||
LZ_decompress_close(decoder);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
void Lz_rom::Main::handle_session_request(Xml_node request)
|
||||
{
|
||||
if (!request.has_attribute("id"))
|
||||
return;
|
||||
|
||||
Id_space<Parent::Server>::Id const server_id {
|
||||
request.attribute_value("id", 0UL) };
|
||||
|
||||
if (request.has_type("create")) {
|
||||
if (!request.has_sub_node("args"))
|
||||
return;
|
||||
|
||||
typedef Session_state::Args Args;
|
||||
Args const args = request.sub_node("args").decoded_content<Args>();
|
||||
Session_label const request_label =
|
||||
label_from_args(args.string()).last_element();
|
||||
char new_label[request_label.capacity()];
|
||||
snprintf(new_label, sizeof(new_label), "/%s.lz", request_label.string());
|
||||
Session_label const filename(new_label);
|
||||
|
||||
try {
|
||||
Session *session = new (session_alloc)
|
||||
Session(server_id_space, server_id, env, vfs_alloc, filename, decoder);
|
||||
env.parent().deliver_session_cap(
|
||||
server_id, env.ep().manage(*session));
|
||||
return;
|
||||
} catch (File_error) {
|
||||
log("failed to open or read file '", filename, "'");
|
||||
} catch (Decompression_error) {
|
||||
char const *msg = "";
|
||||
|
||||
switch (LZ_decompress_errno(decoder)) {
|
||||
case LZ_ok:
|
||||
error("no error"); break;
|
||||
|
||||
case LZ_bad_argument:
|
||||
msg = "at least one of the arguments passed to the library function was invalid"; break;
|
||||
|
||||
case LZ_mem_error:
|
||||
msg = "no memory available"; break;
|
||||
|
||||
case LZ_sequence_error:
|
||||
msg = "a library function was called in the wrong order"; break;
|
||||
|
||||
case LZ_header_error:
|
||||
msg = "an invalid member header was read"; break;
|
||||
|
||||
case LZ_unexpected_eof:
|
||||
msg = "the end of the data stream was reached in the middle of a member"; break;
|
||||
|
||||
case LZ_data_error:
|
||||
msg = "the data stream is corrupt"; break;
|
||||
|
||||
case LZ_library_error:
|
||||
msg = "a bug was detected in the library"; break;
|
||||
}
|
||||
error("failed to decompress '", filename, "', ", msg);
|
||||
} catch (...) { }
|
||||
env.parent().session_response(server_id, Parent::INVALID_ARGS);
|
||||
}
|
||||
|
||||
if (request.has_type("close")) {
|
||||
server_id_space.apply<Session>(server_id, [&] (Session &session) {
|
||||
destroy(session_alloc, &session);
|
||||
env.parent().session_response(server_id, Parent::SESSION_CLOSED);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************
|
||||
** Component **
|
||||
***************/
|
||||
|
||||
void Libc::Component::construct(Libc::Env &env)
|
||||
{
|
||||
Libc::with_libc([&] () {
|
||||
static Lz_rom::Main inst(env);
|
||||
env.parent().announce("ROM");
|
||||
});
|
||||
}
|
||||
3
src/server/lz_rom/target.mk
Normal file
3
src/server/lz_rom/target.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
TARGET = lz_rom
|
||||
SRC_CC = main.cc
|
||||
LIBS = lzlib libc
|
||||
Reference in New Issue
Block a user