767 lines
19 KiB
C++
767 lines
19 KiB
C++
/*
|
|
* \brief GPT partitioning tool
|
|
* \author Josef Soentgen
|
|
* \date 2018-05-01
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2018 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/allocator_avl.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
#include <base/component.h>
|
|
#include <base/heap.h>
|
|
|
|
/* local includes */
|
|
#include <gpt.h>
|
|
#include <pmbr.h>
|
|
|
|
|
|
namespace Gpt {
|
|
|
|
struct Writer;
|
|
}
|
|
|
|
struct Gpt::Writer
|
|
{
|
|
struct Io_error : Genode::Exception { };
|
|
struct Gpt_invalid : Genode::Exception { };
|
|
|
|
using sector_t = Block::sector_t;
|
|
|
|
Block::Connection<> &_block;
|
|
|
|
Block::Session::Info const _info { _block.info() };
|
|
size_t const _block_size { _info.block_size };
|
|
sector_t const _block_count { _info.block_count };
|
|
|
|
/*
|
|
* Blocks available is a crude approximation that _does not_ take
|
|
* alignment or unusable blocks because of the layout into account!
|
|
*/
|
|
uint64_t _blocks_avail { 0 };
|
|
|
|
/* track actions */
|
|
bool _new_gpt { false };
|
|
bool _new_pmbr { false };
|
|
bool _new_geometry { false };
|
|
|
|
/* flags */
|
|
bool _verbose { false };
|
|
bool _update_geometry { false };
|
|
bool _preserve_hybrid { false };
|
|
bool _initialize { false };
|
|
bool _wipe { false };
|
|
bool _force_alignment { false };
|
|
|
|
Util::Number_of_bytes _entry_alignment { 4096u };
|
|
|
|
Genode::Xml_node *_config { nullptr };
|
|
|
|
void _handle_config(Genode::Xml_node config)
|
|
{
|
|
_verbose = config.attribute_value("verbose", false);
|
|
_initialize = config.attribute_value("initialize", false);
|
|
_wipe = config.attribute_value("wipe", false);
|
|
_force_alignment = config.attribute_value("force_align", false);
|
|
_update_geometry = config.attribute_value("update_geometry", false);
|
|
_preserve_hybrid = config.attribute_value("preserve_hybrid", false);
|
|
|
|
{
|
|
Util::Size_string align =
|
|
config.attribute_value("align", Util::Size_string(4096u));
|
|
ascii_to(align.string(), _entry_alignment);
|
|
}
|
|
|
|
bool const commands = config.has_sub_node("commands");
|
|
|
|
if (_wipe && (_initialize || commands)) {
|
|
Genode::warning("will exit after wiping");
|
|
}
|
|
|
|
_config = &config;
|
|
}
|
|
|
|
/************
|
|
** Tables **
|
|
************/
|
|
|
|
Protective_mbr::Header _pmbr { };
|
|
|
|
Gpt::Header _pgpt { };
|
|
Gpt::Entry _pgpt_entries[MAX_ENTRIES] { };
|
|
|
|
Gpt::Header _bgpt { };
|
|
Gpt::Entry _bgpt_entries[MAX_ENTRIES] { };
|
|
|
|
Block::sector_t _old_backup_hdr_lba { 0 };
|
|
|
|
/**
|
|
* Fill in-memory GPT header/entries and valid
|
|
*
|
|
* \param primary if set to true the primary GPT is checked, if false
|
|
* the backup header is checked
|
|
*
|
|
* \throw Io_error
|
|
*/
|
|
void _fill_and_check_header(bool primary)
|
|
{
|
|
using namespace Genode;
|
|
|
|
Gpt::Header *hdr = primary ? &_pgpt : &_bgpt;
|
|
Gpt::Entry *entries = primary ? _pgpt_entries : _bgpt_entries;
|
|
|
|
memset(hdr, 0, sizeof(Gpt::Header));
|
|
memset(entries, 0, ENTRIES_SIZE);
|
|
|
|
try {
|
|
/* header */
|
|
{
|
|
Block::sector_t const lba = primary
|
|
? 1 : _pgpt.backup_lba;
|
|
Util::Block_io io(_block, _block_size, lba, 1);
|
|
memcpy(hdr, io.addr<Gpt::Header*>(), sizeof(Gpt::Header));
|
|
|
|
if (!hdr->valid(primary)) {
|
|
error(primary ? "primary" : "backup",
|
|
" GPT header not valid");
|
|
throw Gpt_invalid();
|
|
}
|
|
}
|
|
|
|
/* entries */
|
|
{
|
|
uint32_t const max_entries = hdr->gpe_num > (uint32_t)MAX_ENTRIES
|
|
? (uint32_t)MAX_ENTRIES : hdr->gpe_num;
|
|
Block::sector_t const lba = hdr->gpe_lba;
|
|
size_t const count = max_entries * hdr->gpe_size / _block_size;
|
|
|
|
Util::Block_io io(_block, _block_size, lba, count);
|
|
size_t const len = count * _block_size;
|
|
memcpy(entries, io.addr<void const*>(), len);
|
|
}
|
|
} catch (Util::Block_io::Io_error) {
|
|
error("could not read GPT header/entries");
|
|
throw Io_error();
|
|
}
|
|
|
|
if (!Gpt::valid(*hdr, entries, primary)) {
|
|
error("GPT header and entries not valid");
|
|
throw Gpt_invalid();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wipe old backup GPT header from Block device
|
|
*/
|
|
bool _wipe_old_backup_header()
|
|
{
|
|
enum { BLOCK_SIZE = 4096u, };
|
|
if (_block_size > BLOCK_SIZE) {
|
|
Genode::error("block size of ", _block_size, "not supported");
|
|
return false;
|
|
}
|
|
|
|
char zeros[BLOCK_SIZE] { };
|
|
|
|
size_t const blocks = 1 + (ENTRIES_SIZE / _block_size);
|
|
Block::sector_t const lba = _old_backup_hdr_lba - blocks;
|
|
|
|
using namespace Util;
|
|
|
|
try {
|
|
Block_io clear(_block, _block_size, lba, blocks, true, zeros, _block_size);
|
|
} catch (Block_io::Io_error) { return false; }
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Wipe all tables from Block device
|
|
*
|
|
* Note: calling this method actually destroys old data!
|
|
*/
|
|
bool _wipe_tables()
|
|
{
|
|
enum { BLOCK_SIZE = 4096u, };
|
|
if (_block_size > BLOCK_SIZE) {
|
|
Genode::error("block size of ", _block_size, "not supported");
|
|
return false;
|
|
}
|
|
|
|
char zeros[BLOCK_SIZE] { };
|
|
|
|
using namespace Util;
|
|
|
|
try {
|
|
/* PMBR */
|
|
Block_io clear_mbr(_block, _block_size, 0, 1, true, zeros, _block_size);
|
|
size_t const blocks = 1 + (Gpt::ENTRIES_SIZE / _block_size);
|
|
|
|
/* PGPT */
|
|
for (size_t i = 0; i < blocks; i++) {
|
|
Block_io clear_lba(_block, _block_size, 1 + i, 1,
|
|
true, zeros, _block_size);
|
|
}
|
|
|
|
/* BGPT */
|
|
for (size_t i = 0; i < blocks; i++) {
|
|
Block_io clear_lba(_block, _block_size, (_block_count - 1) - i, 1,
|
|
true, zeros, _block_size);
|
|
}
|
|
|
|
return true;
|
|
} catch (Block_io::Io_error) { return false; }
|
|
}
|
|
|
|
/**
|
|
* Setup protective MBR
|
|
*
|
|
* The first protective partition covers the whole Block device from the
|
|
* second block up to the 32bit boundary.
|
|
*/
|
|
void _setup_pmbr()
|
|
{
|
|
_pmbr.partitions[0].type = Protective_mbr::TYPE_PROTECTIVE;
|
|
_pmbr.partitions[0].lba = 1;
|
|
_pmbr.partitions[0].sectors = (uint32_t)(_block_count - 1);
|
|
|
|
_new_pmbr = true;
|
|
}
|
|
|
|
/**
|
|
* Initialize tables
|
|
*
|
|
* All tables, primary GPT and backup GPT as well as the protective MBR
|
|
* will be cleared in memory, a new disk GUID will be generated and the
|
|
* default values will be set.
|
|
*/
|
|
void _initialize_tables()
|
|
{
|
|
_setup_pmbr();
|
|
|
|
/* wipe PGPT and BGPT */
|
|
Genode::memset(&_pgpt, 0, sizeof(_pgpt));
|
|
Genode::memset(_pgpt_entries, 0, sizeof(_pgpt_entries));
|
|
Genode::memset(&_bgpt, 0, sizeof(_bgpt));
|
|
Genode::memset(_bgpt_entries, 0, sizeof(_bgpt_entries));
|
|
|
|
size_t const blocks = (size_t)ENTRIES_SIZE / _block_size;
|
|
|
|
/* setup PGPT, BGPT will be synced later */
|
|
Genode::memcpy(_pgpt.signature, "EFI PART", 8);
|
|
_pgpt.revision = Gpt::REVISION;
|
|
_pgpt.size = sizeof(Gpt::Header);
|
|
_pgpt.lba = Gpt::PGPT_LBA;
|
|
_pgpt.backup_lba = _block_count - 1;
|
|
_pgpt.part_lba_start = 2 + blocks;
|
|
_pgpt.part_lba_end = _block_count - (blocks + 2);
|
|
_pgpt.guid = Gpt::generate_uuid();
|
|
_pgpt.gpe_lba = 2;
|
|
_pgpt.gpe_num = Gpt::MAX_ENTRIES;
|
|
_pgpt.gpe_size = sizeof(Gpt::Entry);
|
|
|
|
_blocks_avail = _pgpt.part_lba_end - _pgpt.part_lba_start;
|
|
|
|
_new_gpt = true;
|
|
}
|
|
|
|
/**
|
|
* Synchronize backup header with changes in the primary header
|
|
*/
|
|
void _sync_backup_header()
|
|
{
|
|
size_t const len = _pgpt.gpe_num * _pgpt.gpe_size;
|
|
Genode::memcpy(_bgpt_entries, _pgpt_entries, len);
|
|
Genode::memcpy(&_bgpt, &_pgpt, sizeof(Gpt::Header));
|
|
|
|
_bgpt.lba = _pgpt.backup_lba;
|
|
_bgpt.backup_lba = _pgpt.lba;
|
|
_bgpt.gpe_lba = _pgpt.part_lba_end + 1;
|
|
}
|
|
|
|
/**
|
|
* Write given header to Block device
|
|
*
|
|
* \param hdr reference to header
|
|
* \param entries pointer to GPT entry array
|
|
* \param primary flag to indicate which header to write, primary if true
|
|
* and backup when false
|
|
*
|
|
* \return true when successful, otherwise false
|
|
*/
|
|
bool _write_header(Gpt::Header const &hdr, Gpt::Entry const *entries, bool primary)
|
|
{
|
|
using namespace Util;
|
|
|
|
try {
|
|
Block::sector_t const hdr_lba = primary
|
|
? 1 : _pgpt.backup_lba;
|
|
Block_io hdr_io(_block, _block_size, hdr_lba, 1, true,
|
|
&hdr, sizeof(Gpt::Header));
|
|
|
|
size_t const len = hdr.gpe_num * hdr.gpe_size;
|
|
size_t const blocks = len / _block_size;
|
|
Block::sector_t const entries_lba = primary
|
|
? hdr_lba + 1
|
|
: _block_count - (blocks + 1);
|
|
Block_io entries_io(_block, _block_size, entries_lba, blocks, true,
|
|
entries, len);
|
|
} catch (Block_io::Io_error) { return false; }
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Write protective MBR to Block device
|
|
*
|
|
* \return true when successful, otherwise false
|
|
*/
|
|
bool _write_pmbr()
|
|
{
|
|
using namespace Util;
|
|
try {
|
|
Block_io pmbr(_block, _block_size, 0, 1, true, &_pmbr, sizeof(_pmbr));
|
|
} catch (Block_io::Io_error) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Commit in-memory changes to Block device
|
|
*
|
|
* \return true if successful, otherwise false
|
|
*/
|
|
bool _commit_changes()
|
|
{
|
|
/* only if in-memory structures changed we want to write */
|
|
if (!_new_gpt && !_new_geometry) { return true; }
|
|
|
|
/* remove stale backup header */
|
|
if (_new_geometry) { _wipe_old_backup_header(); }
|
|
|
|
_sync_backup_header();
|
|
|
|
Gpt::update_crc32(_pgpt, _pgpt_entries);
|
|
Gpt::update_crc32(_bgpt, _bgpt_entries);
|
|
|
|
return _write_header(_pgpt, _pgpt_entries, true)
|
|
&& _write_header(_bgpt, _bgpt_entries, false)
|
|
&& (_new_pmbr ? _write_pmbr() : true) ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Update geometry information, i.e., fill whole Block device
|
|
*/
|
|
void _update_geometry_information()
|
|
{
|
|
if (_pgpt.backup_lba == _block_count - 1) { return; }
|
|
|
|
if (!_preserve_hybrid) { _setup_pmbr(); }
|
|
|
|
_old_backup_hdr_lba = _pgpt.backup_lba;
|
|
|
|
size_t const blocks = (size_t)ENTRIES_SIZE / _block_size;
|
|
|
|
_pgpt.backup_lba = _block_count - 1;
|
|
_pgpt.part_lba_end = _block_count - (blocks + 2);
|
|
|
|
_new_geometry = true;
|
|
}
|
|
|
|
/*************
|
|
** Actions **
|
|
*************/
|
|
|
|
enum {
|
|
INVALID_ENTRY = ~0u,
|
|
INVALID_START = 0,
|
|
INVALID_SIZE = ~0ull,
|
|
};
|
|
|
|
/**
|
|
* Check if given entry number is in range
|
|
*/
|
|
uint32_t _check_range(Gpt::Header const &hdr, uint32_t const entry)
|
|
{
|
|
return (entry != (uint32_t)INVALID_ENTRY
|
|
&& (entry == 0 || entry > hdr.gpe_num))
|
|
? (uint32_t)INVALID_ENTRY : entry;
|
|
}
|
|
|
|
/**
|
|
* Lookup entry by number or label
|
|
*
|
|
* \return pointer to entry if found, otherwise a nullptr is returned
|
|
*/
|
|
Gpt::Entry *_lookup_entry(Genode::Xml_node node)
|
|
{
|
|
Gpt::Label const label = node.attribute_value("label", Gpt::Label());
|
|
uint32_t const entry =
|
|
_check_range(_pgpt,
|
|
node.attribute_value("entry", (uint32_t)INVALID_ENTRY));
|
|
|
|
if (entry == INVALID_ENTRY && !label.valid()) {
|
|
Genode::error("cannot lookup entry, invalid arguments");
|
|
return nullptr;
|
|
}
|
|
|
|
if (entry != INVALID_ENTRY && label.valid()) {
|
|
Genode::warning("entry and label given, entry number will be used");
|
|
}
|
|
|
|
Gpt::Entry *e = nullptr;
|
|
|
|
if (entry != INVALID_ENTRY) {
|
|
/* we start at 0 */
|
|
e = &_pgpt_entries[entry - 1];
|
|
}
|
|
|
|
if (e == nullptr && label.valid()) {
|
|
e = Gpt::lookup_entry(_pgpt_entries, _pgpt.gpe_num, label);
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Add new GPT entry
|
|
*
|
|
* \param node action node that contains the arguments
|
|
*
|
|
* \return true if entry was successfully added, otherwise false
|
|
*/
|
|
bool _do_add(Genode::Xml_node node)
|
|
{
|
|
bool const add = node.has_attribute("entry");
|
|
Gpt::Label const label = node.attribute_value("label", Gpt::Label());
|
|
Gpt::Type const type = node.attribute_value("type", Gpt::Type());
|
|
uint64_t const size = Util::convert(node.attribute_value("size",
|
|
Util::Size_string()));
|
|
|
|
if (_verbose) {
|
|
Genode::log(add ? "Add" : "Append", " entry '",
|
|
label.valid() ? label : "", "' size: ", size);
|
|
}
|
|
|
|
if (!size) {
|
|
Genode::error("invalid size");
|
|
return false;
|
|
}
|
|
|
|
/* check if partition will fit */
|
|
Block::sector_t length = Util::size_to_lba(_block_size, size);
|
|
|
|
Block::sector_t lba = node.attribute_value("start", (Block::sector_t)INVALID_START);
|
|
|
|
Gpt::Entry *e = nullptr;
|
|
if (add) {
|
|
uint32_t const entry = _check_range(_pgpt,
|
|
node.attribute_value("entry", (uint32_t)INVALID_ENTRY));
|
|
|
|
if ( entry == INVALID_ENTRY
|
|
|| lba == INVALID_START
|
|
|| size == INVALID_SIZE) {
|
|
Genode::error("cannot add entry, invalid arguments");
|
|
return false;
|
|
}
|
|
|
|
if (length > _blocks_avail) {
|
|
Genode::error("not enough sectors left (", _blocks_avail,
|
|
") to satisfy request");
|
|
return false;
|
|
}
|
|
|
|
if (_pgpt_entries[entry].valid()) {
|
|
Genode::error("cannot add already existing entry ", entry);
|
|
return false;
|
|
}
|
|
|
|
e = &_pgpt_entries[entry-1];
|
|
|
|
if (e->valid()) {
|
|
Genode::error("cannot add existing entry ", entry);
|
|
return false;
|
|
}
|
|
|
|
} else {
|
|
/* assume append operation */
|
|
Entry const *last = Gpt::find_last_valid(_pgpt, _pgpt_entries);
|
|
|
|
e = last ? Gpt::find_next_free(_pgpt, _pgpt_entries, last)
|
|
: Gpt::find_free(_pgpt, _pgpt_entries);
|
|
|
|
if (!e) {
|
|
Genode::error("cannot append partition, no free entry found");
|
|
return false;
|
|
}
|
|
|
|
if (lba != INVALID_START) {
|
|
Genode::warning("will ignore start LBA in append mode");
|
|
}
|
|
|
|
lba = last ? last->lba_end + 1 : _pgpt.part_lba_start;
|
|
if (lba == INVALID_START) {
|
|
Genode::error("cannot find start LBA");
|
|
return false;
|
|
}
|
|
|
|
/* limit length to available blocks */
|
|
if (length == (~0ull / _block_size)) {
|
|
length = Gpt::gap_length(_pgpt, _pgpt_entries, last ? last : nullptr);
|
|
}
|
|
|
|
/* account for alignment */
|
|
uint64_t const align = _entry_alignment / _block_size;
|
|
if (length < align) {
|
|
Genode::error("cannot satisfy alignment constraints");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Gpt::Uuid const type_uuid = Gpt::type_to_uuid(type);
|
|
|
|
e->type = type_uuid;
|
|
e->guid = Gpt::generate_uuid();
|
|
e->lba_start = Util::align_start(_block_size,
|
|
_entry_alignment, lba);
|
|
uint64_t const lba_start = e->lba_start;
|
|
if (lba_start != lba) {
|
|
Genode::warning("start LBA ", lba, " set to ", lba_start,
|
|
" due to alignment constraints");
|
|
}
|
|
e->lba_end = e->lba_start + (length - 1);
|
|
|
|
if (label.valid()) { e->write_name(label); }
|
|
|
|
_blocks_avail -= length;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Delete existing GPT entry
|
|
*
|
|
* \param node action node that contains the arguments
|
|
*
|
|
* \return true if entry was successfully added, otherwise false
|
|
*/
|
|
bool _do_delete(Genode::Xml_node node)
|
|
{
|
|
Gpt::Entry *e = _lookup_entry(node);
|
|
if (!e) { return false; }
|
|
|
|
if (_verbose) {
|
|
char tmp[48];
|
|
e->read_name(tmp, sizeof(tmp));
|
|
uint32_t const num = Gpt::entry_num(_pgpt_entries, e);
|
|
Genode::log("Delete entry ", num, " '", (char const*)tmp, "'");
|
|
}
|
|
|
|
_blocks_avail += e->length();
|
|
|
|
Genode::memset(e, 0, sizeof(Gpt::Entry));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Update existing GPT entry
|
|
*
|
|
* \param node action node that contains the arguments
|
|
*
|
|
* \return true if entry was successfully added, otherwise false
|
|
*/
|
|
bool _do_modify(Genode::Xml_node node)
|
|
{
|
|
using namespace Genode;
|
|
|
|
Gpt::Entry *e = _lookup_entry(node);
|
|
|
|
if (!e) {
|
|
Genode::error("could not lookup entry");
|
|
return false;
|
|
}
|
|
|
|
if (_verbose) {
|
|
char tmp[48];
|
|
e->read_name(tmp, sizeof(tmp));
|
|
uint32_t const num = Gpt::entry_num(_pgpt_entries, e);
|
|
Genode::log("Modify entry ", num, " '", (char const*)tmp, "'");
|
|
}
|
|
|
|
uint64_t const new_size = Util::convert(node.attribute_value("new_size",
|
|
Util::Size_string()));
|
|
if (new_size) {
|
|
bool const fill = new_size == ~0ull;
|
|
|
|
uint64_t length = fill ? Gpt::gap_length(_pgpt, _pgpt_entries, e)
|
|
: Util::size_to_lba(_block_size, new_size);
|
|
|
|
if (length == 0) {
|
|
error("cannot modify: ", fill ? "no space left"
|
|
: "invalid length");
|
|
return false;
|
|
}
|
|
|
|
uint64_t const old_length = e->length();
|
|
uint64_t const new_length = length + (fill ? old_length : 0);
|
|
uint64_t const expand = new_length > old_length ? new_length - old_length : 0;
|
|
|
|
if (expand && expand > _blocks_avail) {
|
|
Genode::error("cannot modify: new length ", expand, " too large");
|
|
return false;
|
|
}
|
|
|
|
if (!expand) { _blocks_avail += (old_length - new_length); }
|
|
else { _blocks_avail -= (new_length - old_length); }
|
|
|
|
/* XXX overlapping check anyone? */
|
|
e->lba_end = e->lba_start + new_length - 1;
|
|
}
|
|
|
|
Gpt::Label const new_label = node.attribute_value("new_label", Gpt::Label());
|
|
if (new_label.valid()) { e->write_name(new_label); }
|
|
|
|
Gpt::Type const new_type = node.attribute_value("new_type", Gpt::Type());
|
|
if (new_type.valid()) {
|
|
try {
|
|
Gpt::Uuid type_uuid = Gpt::type_to_uuid(new_type);
|
|
memcpy(&e->type, &type_uuid, sizeof(Gpt::Uuid));
|
|
} catch (...) {
|
|
warning("could not update invalid type");
|
|
}
|
|
}
|
|
|
|
/* XXX should we generate a new GUID? */
|
|
// e->guid = Gpt::generate_uuid();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param block reference to underlying Block::Connection
|
|
* \param config copy of config Xml_node
|
|
*
|
|
* \throw Io_error
|
|
*/
|
|
Writer(Block::Connection<> &block, Genode::Xml_node config) : _block(block)
|
|
{
|
|
if (!_info.writeable) {
|
|
Genode::error("cannot write to Block session");
|
|
throw Io_error();
|
|
}
|
|
|
|
/* initial config read in */
|
|
_handle_config(config);
|
|
|
|
/*
|
|
* In case of wiping, end here.
|
|
*/
|
|
if (_wipe) { return; }
|
|
|
|
/*
|
|
* Read and validate the primary GPT header and its entries first
|
|
* and check the backup GPT header afterwards.
|
|
*/
|
|
if (!_initialize) {
|
|
_fill_and_check_header(true);
|
|
_fill_and_check_header(false);
|
|
|
|
if (_update_geometry) {
|
|
Genode::log("Update geometry information");
|
|
_update_geometry_information();
|
|
}
|
|
|
|
/* set available blocks */
|
|
uint64_t const total = _pgpt.part_lba_end - _pgpt.part_lba_start;
|
|
_blocks_avail = total - Gpt::get_used_blocks(_pgpt, _pgpt_entries);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute actions specified in config
|
|
*
|
|
* \return true if actions were executed successfully, otherwise
|
|
* false
|
|
*/
|
|
bool execute_actions()
|
|
{
|
|
if (_wipe) { return _wipe_tables(); }
|
|
|
|
if (_initialize) { _initialize_tables(); }
|
|
|
|
try {
|
|
Genode::Xml_node actions = _config->sub_node("actions");
|
|
|
|
actions.for_each_sub_node([&] (Genode::Xml_node node) {
|
|
bool result = false;
|
|
|
|
if (node.has_type("add")) {
|
|
result = _do_add(node);
|
|
} else if (node.has_type("delete")) {
|
|
result = _do_delete(node);
|
|
} else if (node.has_type("modify")) {
|
|
result = _do_modify(node);
|
|
} else {
|
|
Genode::warning("skipping invalid action");
|
|
return;
|
|
}
|
|
|
|
if (!result) { throw -1; }
|
|
|
|
_new_gpt |= result;
|
|
});
|
|
} catch (...) { return false; }
|
|
|
|
/* finally write changes to disk */
|
|
return _commit_changes();
|
|
}
|
|
};
|
|
|
|
|
|
struct Main
|
|
{
|
|
Genode::Env &_env;
|
|
Genode::Heap _heap { _env.ram(), _env.rm() };
|
|
|
|
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
|
|
|
|
enum { TX_BUF_SIZE = 128u << 10, };
|
|
Genode::Allocator_avl _block_alloc { &_heap };
|
|
Block::Connection<> _block { _env, &_block_alloc, TX_BUF_SIZE };
|
|
|
|
Genode::Constructible<Gpt::Writer> _writer { };
|
|
|
|
Main(Genode::Env &env) : _env(env)
|
|
{
|
|
if (!_config_rom.valid()) {
|
|
Genode::error("invalid config");
|
|
_env.parent().exit(1);
|
|
return;
|
|
}
|
|
|
|
Util::init_random(_heap);
|
|
|
|
try {
|
|
_writer.construct(_block, _config_rom.xml());
|
|
} catch (...) {
|
|
_env.parent().exit(1);
|
|
return;
|
|
}
|
|
|
|
bool const success = _writer->execute_actions();
|
|
_env.parent().exit(success ? 0 : 1);
|
|
}
|
|
};
|
|
|
|
|
|
void Component::construct(Genode::Env &env) { static Main main(env); }
|