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:
Emery Hemingway
2017-04-11 17:52:27 -05:00
committed by Norman Feske
parent 11956d6e54
commit 7267d88760
4 changed files with 486 additions and 0 deletions

93
run/lz_rom_noux.run Normal file
View 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
View 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
View 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");
});
}

View File

@@ -0,0 +1,3 @@
TARGET = lz_rom
SRC_CC = main.cc
LIBS = lzlib libc