libports: component for extracting archives

Issue #2528
This commit is contained in:
Norman Feske 2017-12-20 18:27:32 +01:00 committed by Christian Helmuth
parent 219218dd38
commit bdc8e1f8fb
3 changed files with 284 additions and 0 deletions

View File

@ -0,0 +1,45 @@
create_boot_directory
import_from_depot genodelabs/src/[base_src] \
genodelabs/src/init
install_config {
<config>
<parent-provides>
<service name="CPU"/>
<service name="LOG"/>
<service name="PD"/>
<service name="RAM"/>
<service name="ROM"/>
</parent-provides>
<default-route> <any-service> <parent/> </any-service> </default-route>
<start name="extract" caps="200">
<resource name="RAM" quantum="12M"/>
<config verbose="yes">
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/null"/>
<vfs>
<dir name="archived"> <rom name="test.tar.xz"/> </dir>
<dir name="extracted"> <ram/> </dir>
<dir name="dev"> <log/> <null/> </dir>
</vfs>
<extract archive="/archived/test.tar.xz" to="/extracted"/>
</config>
</start>
</config>
}
exec tar cJf [run_dir]/genode/test.tar.xz -C [genode_dir] tool/depot
build { app/extract }
build_boot_image {
extract
libc.lib.so libm.lib.so posix.lib.so
libarchive.lib.so liblzma.lib.so zlib.lib.so
}
append qemu_args " -nographic "
run_genode_until {child "extract" exited with exit value 0} 30

View File

@ -0,0 +1,236 @@
/*
* \brief Tool for extracting archives
* \author Norman Feske
* \date 2017-12-20
*/
/*
* Copyright (C) 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 <libc/component.h>
#include <base/attached_rom_dataspace.h>
/* libc includes */
#include <fcntl.h>
#include <sys/stat.h>
/* libarchive includes */
#include <archive.h>
#include <archive_entry.h>
namespace Extract {
using namespace Genode;
struct Extracted_archive;
struct Main;
}
/**
* Create compound directories leading to the path
*
* \return true on success
*/
template <Genode::size_t N>
bool create_directories(Genode::String<N> const &path)
{
using namespace Genode;
for (size_t sub_path_len = 0; ; sub_path_len++) {
char const c = path.string()[sub_path_len];
bool const end_of_path = (c == 0);
bool const end_of_elem = (c == '/');
if (!end_of_elem && !end_of_path)
continue;
Genode::String<N> sub_path(Genode::Cstring(path.string(), sub_path_len));
/* create directory for sub path if it does not already exist */
struct stat sb;
sb.st_mode = 0;
stat(sub_path.string(), &sb);
if (!S_ISDIR(sb.st_mode)) {
if (mkdir(sub_path.string(), 0777) < 0)
return false;
}
if (end_of_path)
break;
/* skip '/' */
sub_path_len++;
}
return true;
}
struct Extract::Extracted_archive : Noncopyable
{
struct Source : Noncopyable
{
archive * const ptr = archive_read_new();
~Source()
{
archive_read_close(ptr);
archive_read_free(ptr);
}
} src { };
struct Destination : Noncopyable
{
archive * const ptr = archive_write_disk_new();
~Destination()
{
archive_write_close(ptr);
archive_write_free(ptr);
}
} dst { };
typedef String<256> Path;
struct Exception : Genode::Exception { };
struct Open_failed : Exception { };
struct Read_failed : Exception { };
struct Write_failed : Exception { };
/**
* Constructor
*
* \throw Open_failed
* \throw Read_failed
* \throw Write_failed
*/
explicit Extracted_archive(Path const &path)
{
archive_read_support_format_all(src.ptr);
archive_read_support_filter_all(src.ptr);
size_t const block_size = 10240;
if (archive_read_open_filename(src.ptr, path.string(), block_size))
throw Open_failed();
for (;;) {
struct archive_entry *entry = nullptr;
{
int const ret = archive_read_next_header(src.ptr, &entry);
if (ret == ARCHIVE_EOF)
break;
if (ret != ARCHIVE_OK)
throw Read_failed();
}
if (archive_write_header(dst.ptr, entry) != ARCHIVE_OK)
throw Write_failed();
for (;;) {
void const *buf = nullptr;
size_t size = 0;
::int64_t offset = 0;
{
int const ret = archive_read_data_block(src.ptr, &buf, &size, &offset);
if (ret == ARCHIVE_EOF)
break;
if (ret != ARCHIVE_OK)
throw Read_failed();
}
if (archive_write_data_block(dst.ptr, buf, size, offset) != ARCHIVE_OK)
throw Write_failed();
}
if (archive_write_finish_entry(dst.ptr) != ARCHIVE_OK)
throw Write_failed();
}
}
};
struct Extract::Main
{
Env &_env;
typedef Extracted_archive::Path Path;
Attached_rom_dataspace _config { _env, "config" };
bool _verbose = false;
void _process_config()
{
Xml_node const config = _config.xml();
_verbose = config.attribute_value("verbose", false);
config.for_each_sub_node("extract", [&] (Xml_node node) {
Path const src_path = node.attribute_value("archive", Path());
Path const dst_path = node.attribute_value("to", Path());
bool success = false;
struct Create_directories_failed { };
try {
if (!create_directories(dst_path))
throw Create_directories_failed();
chdir("/");
chdir(dst_path.string());
Extracted_archive extracted_archive(src_path);
success = true;
}
catch (Create_directories_failed) {
warning("failed to created directory '", dst_path, "'"); }
catch (Extracted_archive::Read_failed) {
warning("reading from archive ", src_path, " failed"); }
catch (Extracted_archive::Open_failed) {
warning("could not open archive ", src_path); }
catch (Extracted_archive::Write_failed) {
warning("writing to directory ", dst_path, " failed"); }
/* abort on first error */
if (!success)
throw Exception();
if (_verbose)
log("extracted '", src_path, "' to '", dst_path, "'");
});
}
Main(Env &env) : _env(env)
{
Libc::with_libc([&] () { _process_config(); });
env.parent().exit(0);
}
};
void Libc::Component::construct(Libc::Env &env)
{
static Extract::Main main(env);
}
/* dummy to prevent warning printed by unimplemented libc function */
extern "C" mode_t umask(mode_t value) { return value; }

View File

@ -0,0 +1,3 @@
TARGET = extract
SRC_CC = main.cc
LIBS = libarchive libc