Consistently name block components

This patch replaces abbreviations like "blk", "cli", and "srv" by their
full forms "block", "client", and "server".

Fixes #1258
This commit is contained in:
Norman Feske
2019-01-06 21:17:07 +01:00
parent bf62d6b896
commit 81fb10daaa
133 changed files with 289 additions and 289 deletions

View File

@ -0,0 +1,96 @@
This directory contains an implementation of a block-device-partition server.
Behavior
--------
The server uses Genode's block-session interfaces as both front and back end,
leading to the most common use case where this server will reside "between" a
block-driver server and a higher level component like a file-system server.
At startup, the partition server will try to parse the master boot record (MBR)
of its back-end block session. If no partition table is found, the whole block
device is exported as partition '0'. In the other case, the MBR and possible
extended boot records (EBRs) are parsed and offered as separate block sessions
to the front-end clients. The four primary partitions will receive partition
numbers '1' to '4' whereas the first logical partition will be assigned to '5'.
The partition server also understands the GUID partition table (GPT). It will
always try to read the MBR as well as the GPT and will bail out if both are
considered valid. It is up to the user to choose the right table. To do so,
the 'ignore_mbr' or 'ignore_gpt' config attribute may be specified. Using both
at the same time is a configuration error. Apart from that, the server will
show a warning in case a protective MBR is found but GPT is ignored and abort.
If valid GPT was encountered without a proper protective MBR it will use the
GPT but show a diagnostic warning.
In order to route a client to the right partition, the server parses its
configuration section looking for 'policy' tags.
XML Syntax:
! <policy label="<program name>" partition="<partition number>" writeable="<boolean>"/>
part_block supports partition reporting, which can be enabled via the
<report> configuration node. See below for an example. The report
looks like follows (for MBR resp. GPT).
! <partitions type="mbr">
! <partition number="1" type="12" start="2048" length="2048"/>
! <partition number="2" type="15" start="4096" length="16384"/>
! <partition number="5" type="12" start="6144" length="4096"/>
! <partition number="6" type="12" start="12288" length="8192"/>
! </partitions>
! <partitions type="gpt">
! <partition number="1" name="one" type="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"
! guid="5f4061cc-8d4a-4e6f-ad15-10b881b79aee" start="2048" length="2048"/>
! <partition number="2" name="two" type="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"
! guid="87199a83-d0f4-4a01-b9e3-6516a8579d61" start="4096" length="16351"/>
! </partitions>
Clients have read-only access to partitions unless overriden by a 'writeable'
policy attribute.
Usage
-----
Configuration snippet with two clients and an (hypothetical) IDE driver:
!<start name="ata_driver">
! <resource name="RAM" quantum="1M" />
! <provides><service name="Block"/></provides>
! <config ata="yes" />
!</start>
!
!<start name="part_block">
! <resource name="RAM" quantum="10M" />
! <provides><service name="Block" /></provides>
!
! <!-- route part_block to the ata_driver -->
! <route>
! <any-service><child name="ata_driver"/> <parent/><any-child/></any-service>
! </route>
!
! <!-- allow program 'test-part1' to access logical partition '6', while program
! 'test-part2' receives access to primary partition 1 -->
! <config>
! <report partitions="yes"/>
! <policy label_prefix="test-part1" partition="6" writeable="yes"/>
! <policy label_prefix="test-part2" partition="1" writeable="yes"/>
! </config>
!</start>
!
!<!-- part_block clients -->
!<start name="test-part1">
! <binary name="test-part"/>
! <resource name="RAM" quantum="10M" />
! <route>
! <any-service> <child name="part_block" /> <parent/> <any-child/> </any-service>
! </route>
!</start>
!
!<start name="test-part2">
! <binary name="test-part"/>
! <resource name="RAM" quantum="10M" />
! <route>
! <any-service> <child name="part_block" /> <parent/> <any-child/> </any-service>
! </route>
!</start>

View File

@ -0,0 +1,344 @@
/*
* \brief Block-session component for partition server
* \author Stefan Kalkowski
* \date 2013-12-04
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _PART_BLOCK__COMPONENT_H_
#define _PART_BLOCK__COMPONENT_H_
#include <base/exception.h>
#include <base/component.h>
#include <os/session_policy.h>
#include <root/component.h>
#include <block_session/rpc_object.h>
#include "gpt.h"
namespace Block {
using namespace Genode;
class Session_component;
class Root;
};
class Block::Session_component : public Block::Session_rpc_object,
private List<Block::Session_component>::Element,
public Block_dispatcher
{
private:
friend class List<Block::Session_component>;
/*
* Noncopyable
*/
Session_component(Session_component const &);
Session_component &operator = (Session_component const &);
Ram_dataspace_capability _rq_ds;
addr_t _rq_phys;
Partition *_partition;
Signal_handler<Session_component> _sink_ack;
Signal_handler<Session_component> _sink_submit;
bool _req_queue_full;
bool _ack_queue_full;
Packet_descriptor _p_to_handle { };
unsigned _p_in_fly;
Block::Driver &_driver;
bool _writeable;
/**
* Acknowledge a packet already handled
*/
inline void _ack_packet(Packet_descriptor &packet)
{
if (!tx_sink()->ready_to_ack())
error("Not ready to ack!");
tx_sink()->acknowledge_packet(packet);
_p_in_fly--;
}
/**
* Range check packet request
*/
inline bool _range_check(Packet_descriptor &p) {
return p.block_number() + p.block_count() <= _partition->sectors; }
/**
* Handle a single request
*/
void _handle_packet(Packet_descriptor packet)
{
_p_to_handle = packet;
_p_to_handle.succeeded(false);
/* ignore invalid packets */
if (!packet.size() || !_range_check(_p_to_handle)) {
_ack_packet(_p_to_handle);
return;
}
bool write = _p_to_handle.operation() == Packet_descriptor::WRITE;
sector_t off = _p_to_handle.block_number() + _partition->lba;
size_t cnt = _p_to_handle.block_count();
if (write && !_writeable) {
_ack_packet(_p_to_handle);
return;
}
try {
_driver.io(write, off, cnt,
tx_sink()->packet_content(_p_to_handle),
*this, _p_to_handle);
} catch (Block::Session::Tx::Source::Packet_alloc_failed) {
if (!_req_queue_full) {
_req_queue_full = true;
Session_component::wait_queue().insert(this);
}
} catch (Genode::Packet_descriptor::Invalid_packet) {
Genode::error("dropping invalid Block packet");
_p_to_handle = Packet_descriptor();
}
}
/**
* Triggered when a packet was placed into the empty submit queue
*/
void _packet_avail()
{
_ack_queue_full = _p_in_fly >= tx_sink()->ack_slots_free();
/*
* as long as more packets are available, and we're able to ack
* them, and the driver's request queue isn't full,
* direct the packet request to the driver backend
*/
for (; !_req_queue_full && tx_sink()->packet_avail() &&
!_ack_queue_full; _p_in_fly++,
_ack_queue_full = _p_in_fly >= tx_sink()->ack_slots_free())
_handle_packet(tx_sink()->get_packet());
}
/**
* Triggered when an ack got removed from the full ack queue
*/
void _ready_to_ack() { _packet_avail(); }
public:
/**
* Constructor
*/
Session_component(Ram_dataspace_capability rq_ds,
Partition *partition,
Genode::Entrypoint &ep,
Genode::Region_map &rm,
Block::Driver &driver,
bool writeable)
: Session_rpc_object(rm, rq_ds, ep.rpc_ep()),
_rq_ds(rq_ds),
_rq_phys(Dataspace_client(_rq_ds).phys_addr()),
_partition(partition),
_sink_ack(ep, *this, &Session_component::_ready_to_ack),
_sink_submit(ep, *this, &Session_component::_packet_avail),
_req_queue_full(false),
_ack_queue_full(false),
_p_in_fly(0),
_driver(driver),
_writeable(writeable)
{
_tx.sigh_ready_to_ack(_sink_ack);
_tx.sigh_packet_avail(_sink_submit);
}
~Session_component()
{
_driver.remove_dispatcher(*this);
if (_req_queue_full)
wait_queue().remove(this);
}
Ram_dataspace_capability const rq_ds() const { return _rq_ds; }
Partition *partition() { return _partition; }
void dispatch(Packet_descriptor &request, Packet_descriptor &reply)
{
request.succeeded(reply.succeeded());
if (request.operation() == Block::Packet_descriptor::READ) {
void *src =
_driver.session().tx()->packet_content(reply);
Genode::size_t sz =
request.block_count() * _driver.blk_size();
try { Genode::memcpy(tx_sink()->packet_content(request), src, sz); }
catch (Genode::Packet_descriptor::Invalid_packet) {
request.succeeded(false);
}
}
_ack_packet(request);
if (_ack_queue_full)
_packet_avail();
}
static List<Session_component>& wait_queue()
{
static List<Session_component> l;
return l;
}
static void wake_up()
{
for (; Session_component *c = wait_queue().first();)
{
wait_queue().remove(c);
c->_req_queue_full = false;
c->_handle_packet(c->_p_to_handle);
c->_packet_avail();
}
}
/*******************************
** Block session interface **
*******************************/
void info(sector_t *blk_count, size_t *blk_size,
Operations *ops)
{
Operations driver_ops = _driver.ops();
*blk_count = _partition->sectors;
*blk_size = _driver.blk_size();
*ops = Operations();
typedef Block::Packet_descriptor::Opcode Opcode;
if (driver_ops.supported(Opcode::READ))
ops->set_operation(Opcode::READ);
if (_writeable && driver_ops.supported(Opcode::WRITE))
ops->set_operation(Opcode::WRITE);
}
void sync() { _driver.session().sync(); }
};
/**
* Root component, handling new session requests
*/
class Block::Root :
public Genode::Root_component<Block::Session_component>
{
private:
Genode::Env &_env;
Genode::Xml_node _config;
Block::Driver &_driver;
Block::Partition_table &_table;
protected:
void _destroy_session(Session_component *session) override
{
Ram_dataspace_capability rq_ds = session->rq_ds();
Genode::Root_component<Session_component>::_destroy_session(session);
_env.ram().free(rq_ds);
}
/**
* Always returns the singleton block-session component
*/
Session_component *_create_session(const char *args) override
{
long num = -1;
bool writeable = false;
Session_label const label = label_from_args(args);
char const *label_str = label.string();
try {
Session_policy policy(label, _config);
/* read partition attribute */
policy.attribute("partition").value(&num);
/* sessions are not writeable by default */
writeable = policy.attribute_value("writeable", false);
} catch (Xml_node::Nonexistent_attribute) {
error("policy does not define partition number for for '",
label_str, "'");
throw Service_denied();
} catch (Session_policy::No_policy_defined) {
error("rejecting session request, no matching policy for '",
label_str, "'");
throw Service_denied();
}
if (!_table.partition(num)) {
error("Partition ", num, " unavailable for '", label_str, "'");
throw Service_denied();
}
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
if (!tx_buf_size)
throw Service_denied();
/* delete ram quota by the memory needed for the session */
size_t session_size = max((size_t)4096,
sizeof(Session_component)
+ sizeof(Allocator_avl));
if (ram_quota < session_size)
throw Insufficient_ram_quota();
/*
* Check if donated ram quota suffices for both
* communication buffers. Also check both sizes separately
* to handle a possible overflow of the sum of both sizes.
*/
if (tx_buf_size > ram_quota - session_size) {
error("insufficient 'ram_quota', got ", ram_quota, ", need ",
tx_buf_size + session_size);
throw Insufficient_ram_quota();
}
if (writeable)
writeable = Arg_string::find_arg(args, "writeable").bool_value(true);
Ram_dataspace_capability ds_cap;
ds_cap = _env.ram().alloc(tx_buf_size);
Session_component *session = new (md_alloc())
Session_component(ds_cap, _table.partition(num),
_env.ep(), _env.rm(), _driver,
writeable);
log("session opened at partition ", num, " for '", label_str, "'");
return session;
}
public:
Root(Genode::Env &env, Genode::Xml_node config, Genode::Heap &heap,
Block::Driver &driver, Block::Partition_table &table)
: Root_component(env.ep(), heap), _env(env), _config(config),
_driver(driver), _table(table) { }
};
#endif /* _PART_BLOCK__COMPONENT_H_ */

View File

@ -0,0 +1,174 @@
/*
* \brief Block-session driver for partition server
* \author Stefan Kalkowski
* \date 2013-12-04
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _PART_BLOCK__DRIVER_H_
#define _PART_BLOCK__DRIVER_H_
#include <base/env.h>
#include <base/allocator_avl.h>
#include <base/signal.h>
#include <base/tslab.h>
#include <base/heap.h>
#include <util/list.h>
#include <block_session/connection.h>
namespace Block {
class Block_dispatcher;
class Driver;
};
struct Block::Block_dispatcher : Genode::Interface
{
virtual void dispatch(Packet_descriptor&, Packet_descriptor&) = 0;
};
bool operator== (const Block::Packet_descriptor& p1,
const Block::Packet_descriptor& p2)
{
return p1.operation() == p2.operation() &&
p1.block_number() == p2.block_number() &&
p1.block_count() == p2.block_count();
}
class Block::Driver
{
public:
class Request : public Genode::List<Request>::Element
{
private:
Block_dispatcher &_dispatcher;
Packet_descriptor _cli;
Packet_descriptor _srv;
public:
Request(Block_dispatcher &d,
Packet_descriptor &cli,
Packet_descriptor &srv)
: _dispatcher(d), _cli(cli), _srv(srv) {}
bool handle(Packet_descriptor& reply)
{
bool ret = reply == _srv;
if (ret) _dispatcher.dispatch(_cli, reply);
return ret;
}
bool same_dispatcher(Block_dispatcher &same) {
return &same == &_dispatcher; }
};
private:
enum { BLK_SZ = Session::TX_QUEUE_SIZE*sizeof(Request) };
Genode::Tslab<Request, BLK_SZ> _r_slab;
Genode::List<Request> _r_list { };
Genode::Allocator_avl _block_alloc;
Block::Connection _session;
Block::sector_t _blk_cnt = 0;
Genode::size_t _blk_size = 0;
Genode::Signal_handler<Driver> _source_ack;
Genode::Signal_handler<Driver> _source_submit;
Block::Session::Operations _ops { };
void _ready_to_submit();
void _ack_avail()
{
/* check for acknowledgements */
while (_session.tx()->ack_avail()) {
Packet_descriptor p = _session.tx()->get_acked_packet();
for (Request *r = _r_list.first(); r; r = r->next()) {
if (r->handle(p)) {
_r_list.remove(r);
Genode::destroy(&_r_slab, r);
break;
}
}
_session.tx()->release_packet(p);
}
_ready_to_submit();
}
public:
Driver(Genode::Env &env, Genode::Heap &heap)
: _r_slab(&heap),
_block_alloc(&heap),
_session(env, &_block_alloc, 4 * 1024 * 1024),
_source_ack(env.ep(), *this, &Driver::_ack_avail),
_source_submit(env.ep(), *this, &Driver::_ready_to_submit)
{
_session.info(&_blk_cnt, &_blk_size, &_ops);
}
Genode::size_t blk_size() { return _blk_size; }
Genode::size_t blk_cnt() { return _blk_cnt; }
Session::Operations ops() { return _ops; }
Session_client& session() { return _session; }
void work_asynchronously()
{
_session.tx_channel()->sigh_ack_avail(_source_ack);
_session.tx_channel()->sigh_ready_to_submit(_source_submit);
}
static Driver& driver();
void io(bool write, sector_t nr, Genode::size_t cnt, void* addr,
Block_dispatcher &dispatcher, Packet_descriptor& cli)
{
if (!_session.tx()->ready_to_submit())
throw Block::Session::Tx::Source::Packet_alloc_failed();
Block::Packet_descriptor::Opcode op = write
? Block::Packet_descriptor::WRITE
: Block::Packet_descriptor::READ;
Genode::size_t size = _blk_size * cnt;
Packet_descriptor p(_session.dma_alloc_packet(size),
op, nr, cnt);
Request *r = new (&_r_slab) Request(dispatcher, cli, p);
_r_list.insert(r);
if (write)
Genode::memcpy(_session.tx()->packet_content(p),
addr, size);
_session.tx()->submit_packet(p);
}
void remove_dispatcher(Block_dispatcher &dispatcher)
{
for (Request *r = _r_list.first(); r;) {
if (!r->same_dispatcher(dispatcher)) {
r = r->next();
continue;
}
Request *remove = r;
r = r->next();
_r_list.remove(remove);
Genode::destroy(&_r_slab, remove);
}
}
};
#endif /* _PART_BLOCK__DRIVER_H_ */

View File

@ -0,0 +1,104 @@
/*
* \brief Poor man's partition probe for known file system
* \author Josef Soentgen
* \date 2018-05-03
*/
/*
* 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.
*/
#ifndef _PART_BLOCK__FSPROBE_H_
#define _PART_BLOCK__FSPROBE_H_
/* Genode includes */
#include <base/env.h>
#include <base/log.h>
namespace Fs {
using namespace Genode;
using Type = Genode::String<32>;
Type probe(uint8_t *buffer, size_t len);
/**
* Probe for Ext2/3/4
*/
Type _probe_extfs(uint8_t *p, size_t len)
{
if (len < 4096) { return Type(); }
/* super block starts a byte offset 1024 */
p += 0x400;
bool const found_ext_sig = p[0x38] == 0x53 && p[0x39] == 0xEF;
enum {
COMPAT_HAS_JOURNAL = 0x004u,
INCOMPAT_EXTENTS = 0x040u,
RO_COMPAT_METADATA_CSUM = 0x400u,
};
uint32_t const compat = *(uint32_t*)(p + 0x5C);
uint32_t const incompat = *(uint32_t*)(p + 0x60);
uint32_t const ro_compat = *(uint32_t*)(p + 0x64);
/* the feature flags should denote a given Ext version */
bool const ext3 = compat & COMPAT_HAS_JOURNAL;
bool const ext4 = ext3 && ((incompat & INCOMPAT_EXTENTS)
&& (ro_compat & RO_COMPAT_METADATA_CSUM));
if (found_ext_sig && ext4) { return Type("Ext4"); }
else if (found_ext_sig && ext3) { return Type("Ext3"); }
else if (found_ext_sig) { return Type("Ext2"); }
return Type();
}
/**
* Probe for FAT16/32
*/
Type _probe_fatfs(uint8_t *p, size_t len)
{
if (len < 512) { return Type(); }
/* at least the checks ring true when mkfs.vfat is used... */
bool const found_boot_sig = p[510] == 0x55 && p[511] == 0xAA;
bool const fat16 = p[38] == 0x28 || p[38] == 0x29;
bool const fat32 = (p[66] == 0x28 || p[66] == 0x29)
&& (p[82] == 'F' && p[83] == 'A');
if (found_boot_sig && fat32) { return Type("FAT32"); }
if (found_boot_sig && fat16) { return Type("FAT16"); }
return Type();
}
}
Fs::Type Fs::probe(uint8_t *p, size_t len)
{
/* Ext2/3/4 */
{
Type t = _probe_extfs(p, len);
if (t.valid()) { return t; }
}
/* FAT16/32 */
{
Type t = _probe_fatfs(p, len);
if (t.valid()) { return t; }
}
return Type();
}
#endif /* _PART_BLOCK__FSPROBE_H_ */

View File

@ -0,0 +1,411 @@
/*
* \brief GUID Partition table definitions
* \author Josef Soentgen
* \date 2014-09-19
*/
/*
* Copyright (C) 2014-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.
*/
#ifndef _PART_BLOCK__GPT_H_
#define _PART_BLOCK__GPT_H_
#include <base/env.h>
#include <base/log.h>
#include <block_session/client.h>
#include <util/misc_math.h>
#include "driver.h"
#include "partition_table.h"
#include "fsprobe.h"
namespace {
static bool const verbose = false;
/* simple bitwise CRC32 checking */
static inline Genode::uint32_t crc32(void const * const buf, Genode::size_t size)
{
Genode::uint8_t const *p = static_cast<Genode::uint8_t const*>(buf);
Genode::uint32_t crc = ~0U;
while (size--) {
crc ^= *p++;
for (Genode::uint32_t j = 0; j < 8; j++)
crc = (-Genode::int32_t(crc & 1) & 0xedb88320) ^ (crc >> 1);
}
return crc ^ ~0U;
}
} /* anonymous namespace */
class Gpt : public Block::Partition_table
{
private:
enum { MAX_PARTITIONS = 128 };
/* contains pointers to valid partitions or 0 */
Block::Partition *_part_list[MAX_PARTITIONS] { 0 };
typedef Block::Partition_table::Sector Sector;
/**
* DCE uuid struct
*/
struct Uuid
{
enum { UUID_NODE_LEN = 6 };
Genode::uint32_t time_low;
Genode::uint16_t time_mid;
Genode::uint16_t time_hi_and_version;
Genode::uint8_t clock_seq_hi_and_reserved;
Genode::uint8_t clock_seq_low;
Genode::uint8_t node[UUID_NODE_LEN];
char const *to_string()
{
static char buffer[37 + 1];
Genode::snprintf(buffer, sizeof(buffer),
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
time_low, time_mid, time_hi_and_version,
clock_seq_hi_and_reserved, clock_seq_low,
node[0], node[1], node[2], node[3], node[4], node[5]);
return buffer;
}
} __attribute__((packed));
/**
* GUID parition table header
*/
struct Gpt_hdr
{
enum { HEADER_LBA = 1 };
char _sig[8]; /* identifies GUID Partition Table */
Genode::uint32_t _revision; /* GPT specification revision */
Genode::uint32_t _hdr_size; /* size of GPT header */
Genode::uint32_t _hdr_crc; /* CRC32 of GPT header */
Genode::uint32_t _reserved; /* must be zero */
Genode::uint64_t _hdr_lba; /* LBA that contains this header */
Genode::uint64_t _backup_hdr_lba; /* LBA of backup GPT header */
Genode::uint64_t _part_lba_start; /* first LBA usable for partitions */
Genode::uint64_t _part_lba_end; /* last LBA usable for partitions */
Uuid _guid; /* GUID to identify the disk */
Genode::uint64_t _gpe_lba; /* first LBA of GPE array */
Genode::uint32_t _entries; /* number of entries in GPE array */
Genode::uint32_t _entry_size; /* size of each GPE */
Genode::uint32_t _gpe_crc; /* CRC32 of GPE array */
void dump_hdr(bool check_primary)
{
if (!verbose) return;
using namespace Genode;
log("GPT ", check_primary ? "primary" : "backup", " header:");
log(" rev: ", (unsigned) _revision);
log(" size: ", (unsigned) _hdr_size);
log(" crc: ", Hex(_hdr_crc, Hex::OMIT_PREFIX));
log(" reserved: ", (unsigned) _reserved);
log(" hdr lba: ", (unsigned long long) _hdr_lba);
log(" bak lba: ", (unsigned long long) _backup_hdr_lba);
log(" part start lba: ", (unsigned long long) _part_lba_start);
log(" part end lba: ", (unsigned long long) _part_lba_end);
log(" guid: ", _guid.to_string());
log(" gpe lba: ", (unsigned long long) _gpe_lba);
log(" entries: ", (unsigned) _entries);
log(" entry size: ", (unsigned) _entry_size);
log(" gpe crc: ", Hex(_gpe_crc, Hex::OMIT_PREFIX));
}
bool valid(Block::Driver &driver, bool check_primary = true)
{
dump_hdr(check_primary);
/* check sig */
if (Genode::strcmp(_sig, "EFI PART", 8) != 0)
return false;
/* check header crc */
Genode::uint32_t crc = _hdr_crc;
_hdr_crc = 0;
if (crc32(this, _hdr_size) != crc) {
Genode::error("Wrong GPT header checksum");
return false;
}
/* check header lba */
if (check_primary)
if (_hdr_lba != HEADER_LBA)
return false;
/* check GPT entry array */
Genode::size_t length = _entries * _entry_size;
Sector gpe(driver, _gpe_lba, length / driver.blk_size());
if (crc32(gpe.addr<void *>(), length) != _gpe_crc)
return false;
if (check_primary) {
/* check backup gpt header */
Sector backup_hdr(driver, _backup_hdr_lba, 1);
if (!backup_hdr.addr<Gpt_hdr*>()->valid(driver, false)) {
Genode::warning("Backup GPT header is corrupted");
}
}
return true;
}
/* the remainder of the LBA must be zero */
} __attribute__((packed));
/**
* GUID partition entry format
*/
struct Gpt_entry
{
enum { NAME_LEN = 36 };
Uuid _type; /* partition type GUID */
Uuid _guid; /* unique partition GUID */
Genode::uint64_t _lba_start; /* start of partition */
Genode::uint64_t _lba_end; /* end of partition */
Genode::uint64_t _attr; /* partition attributes */
Genode::uint16_t _name[NAME_LEN]; /* partition name in UNICODE-16 */
bool valid() const
{
if (_type.time_low == 0x00000000)
return false;
return true;
}
/**
* Extract all valid ASCII characters in the name entry
*/
char const *name()
{
static char buffer[NAME_LEN + 1];
char *p = buffer;
Genode::size_t i = 0;
for (Genode::size_t u = 0; u < NAME_LEN && _name[u] != 0; u++) {
Genode::uint32_t utfchar = _name[i++];
if ((utfchar & 0xf800) == 0xd800) {
unsigned int c = _name[i];
if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
utfchar = 0xfffd;
else
i++;
}
*p++ = (utfchar < 0x80) ? utfchar : '.';
}
*p++ = 0;
return buffer;
}
} __attribute__((packed));
/**
* Calculate free blocks until the start of the logical next entry
*
* \param header pointer to GPT header
* \param entry pointer to current entry
* \param entries pointer to entries
* \param num number of entries
*
* \return the number of free blocks to the next logical entry
*/
Genode::uint64_t _calculate_gap(Gpt_hdr const *header,
Gpt_entry const *entry,
Gpt_entry const *entries,
Genode::uint32_t num,
Genode::uint64_t total_blocks)
{
using namespace Genode;
/* add one block => end == start */
uint64_t const end_lba = entry->_lba_end + 1;
enum { INVALID_START = ~0ull, };
uint64_t next_start_lba = INVALID_START;
for (uint32_t i = 0; i < num; i++) {
Gpt_entry const *e = (entries + i);
if (!e->valid() || e == entry) { continue; }
/*
* Check if the entry starts afterwards and save the
* entry with the smallest distance.
*/
if (e->_lba_start >= end_lba) {
next_start_lba = min(next_start_lba, e->_lba_start);
}
}
/* sanity check if GPT is broken */
if (end_lba > header->_part_lba_end) { return 0; }
/* if the underyling Block device changes we might be able to expand more */
Genode::uint64_t const part_end = max(header->_part_lba_end, total_blocks);
/*
* Use stored next start LBA or paritions end LBA from header,
* if there is no other entry or we are the only one.
*/
return (next_start_lba == INVALID_START ? part_end
: next_start_lba) - end_lba;
}
/**
* Calculate total used blocks
*
* \param header pointer to GPT header
* \param entries pointer to entries
* \param num number of entries
*
* \return the number of used blocks
*/
Genode::uint64_t _calculate_used(Gpt_hdr const *,
Gpt_entry const *entries,
Genode::uint32_t num)
{
using namespace Genode;
uint64_t used = 0;
for (uint32_t i = 0; i < num; i++) {
Gpt_entry const *e = (entries + i);
if (!e->valid()) { continue; }
uint64_t const v = (e->_lba_end - e->_lba_start) + 1;
used += v;
}
return used;
}
/**
* Parse the GPT header
*/
void _parse_gpt(Gpt_hdr *gpt)
{
if (!(gpt->valid(driver)))
throw Genode::Exception();
Sector entry_array(driver, gpt->_gpe_lba,
gpt->_entries * gpt->_entry_size / driver.blk_size());
Gpt_entry *entries = entry_array.addr<Gpt_entry *>();
for (int i = 0; i < MAX_PARTITIONS; i++) {
Gpt_entry *e = (entries + i);
if (!e->valid())
continue;
Genode::uint64_t start = e->_lba_start;
Genode::uint64_t length = e->_lba_end - e->_lba_start + 1; /* [...) */
_part_list[i] = new (&heap) Block::Partition(start, length);
Genode::log("Partition ", i + 1, ": LBA ", start, " (", length,
" blocks) type: '", e->_type.to_string(),
"' name: '", e->name(), "'");
}
/* Report the partitions */
if (reporter.enabled())
{
Genode::Reporter::Xml_generator xml(reporter, [&] () {
xml.attribute("type", "gpt");
Genode::uint64_t const total_blocks = driver.blk_cnt();
xml.attribute("total_blocks", total_blocks);
Genode::uint64_t const gpt_total =
(gpt->_part_lba_end - gpt->_part_lba_start) + 1;
xml.attribute("gpt_total", gpt_total);
Genode::uint64_t const gpt_used =
_calculate_used(gpt, entries, gpt->_entries);
xml.attribute("gpt_used", gpt_used);
for (int i = 0; i < MAX_PARTITIONS; i++) {
Gpt_entry *e = (entries + i);
if (!e->valid()){
continue;
}
enum { BYTES = 4096, };
Sector fs(driver, e->_lba_start, BYTES / driver.blk_size());
Fs::Type fs_type = Fs::probe(fs.addr<Genode::uint8_t*>(), BYTES);
xml.node("partition", [&] () {
xml.attribute("number", i + 1);
xml.attribute("name", e->name());
xml.attribute("type", e->_type.to_string());
xml.attribute("guid", e->_guid.to_string());
xml.attribute("start", e->_lba_start);
xml.attribute("length", e->_lba_end - e->_lba_start + 1);
xml.attribute("block_size", driver.blk_size());
Genode::uint64_t const gap = _calculate_gap(gpt, e, entries,
gpt->_entries,
total_blocks);
if (gap) { xml.attribute("expandable", gap); }
if (fs_type.valid()) {
xml.attribute("file_system", fs_type);
}
});
}
});
}
}
public:
using Partition_table::Partition_table;
Block::Partition *partition(int num) {
return (num <= MAX_PARTITIONS && num > 0) ? _part_list[num-1] : 0; }
bool parse()
{
Sector s(driver, Gpt_hdr::HEADER_LBA, 1);
_parse_gpt(s.addr<Gpt_hdr *>());
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
if (_part_list[num])
return true;
return false;
}
};
#endif /* _PART_BLOCK__GUID_PARTITION_TABLE_H_ */

View File

@ -0,0 +1,143 @@
/*
* \brief Front end of the partition server
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \author Josef Soentgen
* \date 2011-05-30
*/
/*
* Copyright (C) 2011-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.
*/
#include <base/attached_rom_dataspace.h>
#include <block_session/rpc_object.h>
#include "component.h"
#include "driver.h"
#include "gpt.h"
#include "mbr.h"
void Block::Driver::_ready_to_submit() {
Block::Session_component::wake_up(); }
class Main
{
private:
Block::Partition_table & _table();
Genode::Env &_env;
Genode::Attached_rom_dataspace _config { _env, "config" };
Genode::Heap _heap { _env.ram(), _env.rm() };
Block::Driver _driver { _env, _heap };
Genode::Reporter _reporter { _env, "partitions" };
Mbr_partition_table _mbr { _heap, _driver, _reporter };
Gpt _gpt { _heap, _driver, _reporter };
Block::Root _root { _env, _config.xml(), _heap, _driver, _table() };
public:
struct No_partion_table : Genode::Exception { };
struct Ambiguous_tables : Genode::Exception { };
struct Invalid_config : Genode::Exception { };
Main(Genode::Env &env) : _env(env)
{
/*
* we read all partition information,
* now it's safe to turn in asynchronous mode
*/
_driver.work_asynchronously();
/* announce at parent */
env.parent().announce(env.ep().manage(_root));
}
};
Block::Partition_table & Main::_table()
{
using namespace Genode;
bool valid_mbr = false;
bool valid_gpt = false;
bool ignore_gpt = false;
bool ignore_mbr = false;
bool pmbr_found = false;
bool report = false;
try {
ignore_gpt = _config.xml().attribute_value("ignore_gpt", false);
ignore_mbr = _config.xml().attribute_value("ignore_mbr", false);
} catch(...) {}
if (ignore_gpt && ignore_mbr) {
error("invalid configuration: cannot ignore GPT as well as MBR");
throw Invalid_config();
}
try {
report = _config.xml().sub_node("report").attribute_value
("partitions", false);
if (report)
_reporter.enabled(true);
} catch(...) {}
/*
* Try to parse MBR as well as GPT first if not instructued
* to ignore either one of them.
*/
if (!ignore_mbr) {
try { valid_mbr = _mbr.parse(); }
catch (Mbr_partition_table::Protective_mbr_found) {
pmbr_found = true;
}
}
if (!ignore_gpt) {
try { valid_gpt = _gpt.parse(); }
catch (...) { }
}
/*
* Both tables are valid (although we would have expected a PMBR in
* conjunction with a GPT header - hybrid operation is not supported)
* and we will not decide which one to use, it is up to the user.
*/
if (valid_mbr && valid_gpt) {
error("ambigious tables: found valid MBR as well as valid GPT");
throw Ambiguous_tables();
}
if (valid_gpt && !pmbr_found) {
warning("will use GPT without proper protective MBR");
}
/* PMBR missing, i.e, MBR part[0] contains whole disk and GPT valid */
if (pmbr_found && ignore_gpt) {
warning("found protective MBR but GPT is to be ignored");
}
/*
* Return the appropriate table or abort if none is found.
*/
if (valid_gpt) return _gpt;
if (valid_mbr) return _mbr;
error("Aborting: no partition table found.");
throw No_partion_table();
}
void Component::construct(Genode::Env &env) { static Main main(env); }

View File

@ -0,0 +1,222 @@
/*
* \brief MBR partition table definitions
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \author Josef Soentgen
* \date 2013-12-04
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _PART_BLOCK__MBR_H_
#define _PART_BLOCK__MBR_H_
#include <base/env.h>
#include <base/log.h>
#include <block_session/client.h>
#include "partition_table.h"
#include "fsprobe.h"
struct Mbr_partition_table : public Block::Partition_table
{
public:
class Protective_mbr_found { };
private:
typedef Block::Partition_table::Sector Sector;
/**
* Partition table entry format
*/
struct Partition_record
{
enum {
INVALID = 0,
EXTENTED_CHS = 0x5, EXTENTED_LBA = 0xf, PROTECTIVE = 0xee
};
Genode::uint8_t _unused[4];
Genode::uint8_t _type; /* partition type */
Genode::uint8_t _unused2[3];
Genode::uint32_t _lba; /* logical block address */
Genode::uint32_t _sectors; /* number of sectors */
bool valid() { return _type != INVALID; }
bool extended() { return _type == EXTENTED_CHS
|| _type == EXTENTED_LBA; }
bool protective() { return _type == PROTECTIVE; }
} __attribute__((packed));
/**
* Master/Extented boot record format
*/
struct Mbr
{
Genode::uint8_t _unused[446];
Partition_record _records[4];
Genode::uint16_t _magic;
bool valid()
{
/* magic number of partition table */
enum { MAGIC = 0xaa55 };
return _magic == MAGIC;
}
} __attribute__((packed));
enum { MAX_PARTITIONS = 32 };
Block::Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid
partitions or 0 */
template <typename FUNC>
void _parse_extended(Partition_record *record, FUNC const &f)
{
Partition_record *r = record;
unsigned lba = r->_lba;
/* first logical partition number */
int nr = 5;
do {
Sector s(driver, lba, 1);
Mbr *ebr = s.addr<Mbr *>();
if (!(ebr->valid()))
return;
/* The first record is the actual logical partition. The lba of this
* partition is relative to the lba of the current EBR */
Partition_record *logical = &(ebr->_records[0]);
if (logical->valid() && nr < MAX_PARTITIONS) {
f(nr++, logical, lba);
}
/*
* the second record points to the next EBR
* (relative form this EBR)
*/
r = &(ebr->_records[1]);
lba += ebr->_records[1]._lba;
} while (r->valid());
}
template <typename FUNC>
void _parse_mbr(Mbr *mbr, FUNC const &f)
{
for (int i = 0; i < 4; i++) {
Partition_record *r = &(mbr->_records[i]);
if (!r->valid())
continue;
if (r->protective())
throw Protective_mbr_found();
f(i + 1, r, 0);
if (r->extended()) {
_parse_extended(r, f);
}
}
}
public:
using Partition_table::Partition_table;
Block::Partition *partition(int num) {
return (num < MAX_PARTITIONS) ? _part_list[num] : 0; }
bool parse()
{
Sector s(driver, 0, 1);
Mbr *mbr = s.addr<Mbr *>();
/* no partition table, use whole disc as partition 0 */
bool const mbr_valid = mbr->valid();
if (!mbr_valid) {
_part_list[0] = new (&heap)
Block::Partition(0, driver.blk_cnt() - 1);
} else {
_parse_mbr(mbr, [&] (int i, Partition_record *r, unsigned offset) {
Genode::log("Partition ", i, ": LBA ",
(unsigned int) r->_lba + offset, " (",
(unsigned int) r->_sectors, " blocks) type: ",
Genode::Hex(r->_type, Genode::Hex::OMIT_PREFIX));
if (!r->extended())
_part_list[i] = new (&heap)
Block::Partition(r->_lba + offset, r->_sectors);
});
}
/* Report the partitions */
if (reporter.enabled()) {
enum { PROBE_BYTES = 4096, };
Genode::size_t const block_size = driver.blk_size();
Genode::Reporter::Xml_generator xml(reporter, [&] () {
if (mbr_valid) {
xml.attribute("type", "mbr");
_parse_mbr(mbr, [&] (int i, Partition_record *r, unsigned offset) {
Sector fs(driver, r->_lba + offset, PROBE_BYTES / block_size);
Fs::Type fs_type = Fs::probe(fs.addr<Genode::uint8_t*>(), PROBE_BYTES);
xml.node("partition", [&] {
xml.attribute("number", i);
xml.attribute("type", r->_type);
xml.attribute("start", r->_lba + offset);
xml.attribute("length", r->_sectors);
xml.attribute("block_size", driver.blk_size());
if (fs_type.valid()) {
xml.attribute("file_system", fs_type);
}
});
});
} else {
xml.attribute("type", "disk");
enum { PART_NUM = 0, };
Block::Partition const &disk = *_part_list[PART_NUM];
Sector fs(driver, disk.lba, PROBE_BYTES / block_size);
Fs::Type fs_type = Fs::probe(fs.addr<Genode::uint8_t*>(), PROBE_BYTES);
xml.node("partition", [&] {
xml.attribute("number", PART_NUM);
xml.attribute("start", disk.lba);
xml.attribute("length", disk.sectors + 1);
xml.attribute("block_size", driver.blk_size());
if (fs_type.valid()) {
xml.attribute("file_system", fs_type);
}
});
}
});
}
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
if (_part_list[num])
return true;
return false;
}
};
#endif /* _PART_BLOCK__MBR_H_ */

View File

@ -0,0 +1,86 @@
/*
* \brief Partition table definitions
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \date 2013-12-04
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _PART_BLOCK__PARTITION_TABLE_H_
#define _PART_BLOCK__PARTITION_TABLE_H_
#include <base/env.h>
#include <base/log.h>
#include <block_session/client.h>
#include <os/reporter.h>
#include "driver.h"
namespace Block {
struct Partition;
class Partition_table;
}
struct Block::Partition
{
Genode::uint64_t lba; /* logical block address on device */
Genode::uint64_t sectors; /* number of sectors in patitions */
Partition(Genode::uint64_t l, Genode::uint64_t s)
: lba(l), sectors(s) { }
};
struct Block::Partition_table : Genode::Interface
{
class Sector
{
private:
Session_client &_session;
Packet_descriptor _p;
public:
Sector(Driver &driver,
unsigned long blk_nr,
unsigned long count,
bool write = false)
: _session(driver.session()),
_p(_session.dma_alloc_packet(driver.blk_size() * count),
write ? Packet_descriptor::WRITE : Packet_descriptor::READ,
blk_nr, count)
{
_session.tx()->submit_packet(_p);
_p = _session.tx()->get_acked_packet();
if (!_p.succeeded())
Genode::error("Could not access block ",
(unsigned long long)_p.block_number());
}
~Sector() { _session.tx()->release_packet(_p); }
template <typename T> T addr() {
return reinterpret_cast<T>(_session.tx()->packet_content(_p)); }
};
Genode::Heap & heap;
Driver & driver;
Genode::Reporter & reporter;
Partition_table(Genode::Heap & h, Driver & d, Genode::Reporter & r)
: heap(h), driver(d), reporter(r) {}
virtual Partition *partition(int num) = 0;
virtual bool parse() = 0;
};
#endif /* _PART_BLOCK__PARTITION_TABLE_H_ */

View File

@ -0,0 +1,3 @@
TARGET = part_block
LIBS = base
SRC_CC = main.cc