From ef7c51548d5f11bccbfd2ae196926dbb8e9eca92 Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Wed, 3 Aug 2016 10:35:32 +0200 Subject: [PATCH] part_blk: transition to the new base API Ref #1987 Ref #2051 --- repos/os/src/server/part_blk/component.h | 94 ++++++++-------- repos/os/src/server/part_blk/driver.h | 34 +++--- repos/os/src/server/part_blk/gpt.h | 85 +++++++-------- repos/os/src/server/part_blk/main.cc | 102 ++++++++++-------- repos/os/src/server/part_blk/mbr.h | 48 ++++----- .../os/src/server/part_blk/partition_table.h | 26 +++-- 6 files changed, 192 insertions(+), 197 deletions(-) diff --git a/repos/os/src/server/part_blk/component.h b/repos/os/src/server/part_blk/component.h index b6c75b673b..7fa69067d9 100644 --- a/repos/os/src/server/part_blk/component.h +++ b/repos/os/src/server/part_blk/component.h @@ -14,8 +14,9 @@ #ifndef _PART_BLK__COMPONENT_H_ #define _PART_BLK__COMPONENT_H_ -#include #include +#include +#include #include #include @@ -36,15 +37,16 @@ class Block::Session_component : public Block::Session_rpc_object, { private: - Ram_dataspace_capability _rq_ds; - addr_t _rq_phys; - Partition *_partition; - Signal_dispatcher _sink_ack; - Signal_dispatcher _sink_submit; - bool _req_queue_full; - bool _ack_queue_full; - Packet_descriptor _p_to_handle; - unsigned _p_in_fly; + Ram_dataspace_capability _rq_ds; + addr_t _rq_phys; + Partition *_partition; + Signal_handler _sink_ack; + Signal_handler _sink_submit; + bool _req_queue_full; + bool _ack_queue_full; + Packet_descriptor _p_to_handle; + unsigned _p_in_fly; + Block::Driver &_driver; /** * Acknowledge a packet already handled @@ -52,7 +54,7 @@ class Block::Session_component : public Block::Session_rpc_object, inline void _ack_packet(Packet_descriptor &packet) { if (!tx_sink()->ready_to_ack()) - PERR("Not ready to ack!"); + error("Not ready to ack!"); tx_sink()->acknowledge_packet(packet); _p_in_fly--; @@ -83,7 +85,7 @@ class Block::Session_component : public Block::Session_rpc_object, size_t cnt = _p_to_handle.block_count(); void* addr = tx_sink()->packet_content(_p_to_handle); try { - Driver::driver().io(write, off, cnt, addr, *this, _p_to_handle); + _driver.io(write, off, cnt, addr, *this, _p_to_handle); } catch (Block::Session::Tx::Source::Packet_alloc_failed) { _req_queue_full = true; Session_component::wait_queue().insert(this); @@ -93,7 +95,7 @@ class Block::Session_component : public Block::Session_rpc_object, /** * Triggered when a packet was placed into the empty submit queue */ - void _packet_avail(unsigned) + void _packet_avail() { _ack_queue_full = _p_in_fly >= tx_sink()->ack_slots_free(); @@ -111,7 +113,7 @@ class Block::Session_component : public Block::Session_rpc_object, /** * Triggered when an ack got removed from the full ack queue */ - void _ready_to_ack(unsigned) { _packet_avail(0); } + void _ready_to_ack() { _packet_avail(); } public: @@ -120,17 +122,18 @@ class Block::Session_component : public Block::Session_rpc_object, */ Session_component(Ram_dataspace_capability rq_ds, Partition *partition, - Rpc_entrypoint &ep, - Signal_receiver &receiver) - : Session_rpc_object(rq_ds, ep), + Genode::Entrypoint &ep, + Block::Driver &driver) + : Session_rpc_object(rq_ds, ep.rpc_ep()), _rq_ds(rq_ds), _rq_phys(Dataspace_client(_rq_ds).phys_addr()), _partition(partition), - _sink_ack(receiver, *this, &Session_component::_ready_to_ack), - _sink_submit(receiver, *this, &Session_component::_packet_avail), + _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) + _p_in_fly(0), + _driver(driver) { _tx.sigh_ready_to_ack(_sink_ack); _tx.sigh_packet_avail(_sink_submit); @@ -142,16 +145,16 @@ class Block::Session_component : public Block::Session_rpc_object, { if (request.operation() == Block::Packet_descriptor::READ) { void *src = - Driver::driver().session().tx()->packet_content(reply); + _driver.session().tx()->packet_content(reply); Genode::size_t sz = - request.block_count() * Driver::driver().blk_size(); + request.block_count() * _driver.blk_size(); Genode::memcpy(tx_sink()->packet_content(request), src, sz); } request.succeeded(reply.succeeded()); _ack_packet(request); if (_ack_queue_full) - _packet_avail(0); + _packet_avail(); } static List& wait_queue() @@ -167,7 +170,7 @@ class Block::Session_component : public Block::Session_rpc_object, wait_queue().remove(c); c->_req_queue_full = false; c->_handle_packet(c->_p_to_handle); - c->_packet_avail(0); + c->_packet_avail(); } } @@ -179,11 +182,11 @@ class Block::Session_component : public Block::Session_rpc_object, Operations *ops) { *blk_count = _partition->sectors; - *blk_size = Driver::driver().blk_size(); - *ops = Driver::driver().ops(); + *blk_size = _driver.blk_size(); + *ops = _driver.ops(); } - void sync() { Driver::driver().session().sync(); } + void sync() { _driver.session().sync(); } }; @@ -195,8 +198,8 @@ class Block::Root : { private: - Rpc_entrypoint &_ep; - Signal_receiver &_receiver; + Genode::Env &_env; + Block::Driver &_driver; Block::Partition_table &_table; protected: @@ -217,15 +220,17 @@ class Block::Root : policy.attribute("partition").value(&num); } catch (Xml_node::Nonexistent_attribute) { - PERR("policy does not define partition number for for '%s'", label_str); + error("policy does not define partition number for for '", + label_str, "'"); throw Root::Unavailable(); } catch (Session_policy::No_policy_defined) { - PERR("rejecting session request, no matching policy for '%s'", label_str); + error("rejecting session request, no matching policy for '", + label_str, "'"); throw Root::Unavailable(); } if (!_table.partition(num)) { - PERR("Partition %ld unavailable for '%s'", num, label_str); + error("Partition ", num, " unavailable for '", label_str, "'"); throw Root::Unavailable(); } @@ -250,32 +255,27 @@ class Block::Root : * to handle a possible overflow of the sum of both sizes. */ if (tx_buf_size > ram_quota - session_size) { - PERR("insufficient 'ram_quota', got %zd, need %zd", - ram_quota, tx_buf_size + session_size); + error("insufficient 'ram_quota', got ", ram_quota, ", need ", + tx_buf_size + session_size); throw Root::Quota_exceeded(); } Ram_dataspace_capability ds_cap; - ds_cap = Genode::env()->ram_session()->alloc(tx_buf_size); + ds_cap = _env.ram().alloc(tx_buf_size); Session_component *session = new (md_alloc()) - Session_component(ds_cap, - _table.partition(num), - _ep, _receiver); + Session_component(ds_cap, _table.partition(num), + _env.ep(), _driver); - PLOG("session opened at partition %ld for '%s'", num, label_str); + log("session opened at partition ", num, " for '", label_str, "'"); return session; } public: - Root(Rpc_entrypoint *session_ep, Allocator *md_alloc, - Signal_receiver &receiver, Block::Partition_table& table) - : - Root_component(session_ep, md_alloc), - _ep(*session_ep), - _receiver(receiver), - _table(table) - { } + Root(Genode::Env &env, Genode::Heap &heap, + Block::Driver &driver, Block::Partition_table &table) + : Root_component(env.ep(), heap), _env(env), + _driver(driver), _table(table) { } }; #endif /* _PART_BLK__COMPONENT_H_ */ diff --git a/repos/os/src/server/part_blk/driver.h b/repos/os/src/server/part_blk/driver.h index 19bd564974..9f412f42e6 100644 --- a/repos/os/src/server/part_blk/driver.h +++ b/repos/os/src/server/part_blk/driver.h @@ -75,19 +75,19 @@ class Block::Driver enum { BLK_SZ = Session::TX_QUEUE_SIZE*sizeof(Request) }; - Genode::Tslab _r_slab; - Genode::List _r_list; - Genode::Allocator_avl _block_alloc; - Block::Connection _session; - Block::sector_t _blk_cnt; - Genode::size_t _blk_size; - Genode::Signal_dispatcher _source_ack; - Genode::Signal_dispatcher _source_submit; - Block::Session::Operations _ops; + Genode::Tslab _r_slab; + Genode::List _r_list; + Genode::Allocator_avl _block_alloc; + Block::Connection _session; + Block::sector_t _blk_cnt; + Genode::size_t _blk_size; + Genode::Signal_handler _source_ack; + Genode::Signal_handler _source_submit; + Block::Session::Operations _ops; - void _ready_to_submit(unsigned); + void _ready_to_submit(); - void _ack_avail(unsigned) + void _ack_avail() { /* check for acknowledgements */ while (_session.tx()->ack_avail()) { @@ -102,17 +102,17 @@ class Block::Driver _session.tx()->release_packet(p); } - _ready_to_submit(0); + _ready_to_submit(); } public: - Driver(Genode::Signal_receiver &receiver) - : _r_slab(Genode::env()->heap()), - _block_alloc(Genode::env()->heap()), + Driver(Genode::Entrypoint &ep, Genode::Heap &heap) + : _r_slab(&heap), + _block_alloc(&heap), _session(&_block_alloc, 4 * 1024 * 1024), - _source_ack(receiver, *this, &Driver::_ack_avail), - _source_submit(receiver, *this, &Driver::_ready_to_submit) + _source_ack(ep, *this, &Driver::_ack_avail), + _source_submit(ep, *this, &Driver::_ready_to_submit) { _session.info(&_blk_cnt, &_blk_size, &_ops); } diff --git a/repos/os/src/server/part_blk/gpt.h b/repos/os/src/server/part_blk/gpt.h index 4ca8478988..5e04685def 100644 --- a/repos/os/src/server/part_blk/gpt.h +++ b/repos/os/src/server/part_blk/gpt.h @@ -15,7 +15,7 @@ #define _PART_BLK__GPT_H_ #include -#include +#include #include #include "driver.h" @@ -24,7 +24,6 @@ namespace { static bool const verbose = false; -#define PLOGV(...) do { if (verbose) PLOG(__VA_ARGS__); } while (0) /* simple bitwise CRC32 checking */ static inline Genode::uint32_t crc32(void const * const buf, Genode::size_t size) @@ -110,23 +109,27 @@ class Gpt : public Block::Partition_table void dump_hdr(bool check_primary) { - PLOGV("GPT %s header:", check_primary ? "primary" : "backup"); - PLOGV(" rev: %u", _revision); - PLOGV(" size: %u", _hdr_size); - PLOGV(" crc: %x", _hdr_crc); - PLOGV(" reserved: %u", _reserved); - PLOGV(" hdr lba: %llu", _hdr_lba); - PLOGV(" bak lba: %llu", _backup_hdr_lba); - PLOGV(" part start lba: %llu", _part_lba_start); - PLOGV(" part end lba: %llu", _part_lba_end); - PLOGV(" guid: %s", _guid.to_string()); - PLOGV(" gpe lba: %llu", _gpe_lba); - PLOGV(" entries: %u", _entries); - PLOGV(" entry size: %u", _entry_size); - PLOGV(" gpe crc: %x", _gpe_crc); + 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(bool check_primary = true) + bool valid(Block::Driver &driver, bool check_primary = true) { dump_hdr(check_primary); @@ -138,7 +141,7 @@ class Gpt : public Block::Partition_table Genode::uint32_t crc = _hdr_crc; _hdr_crc = 0; if (crc32(this, _hdr_size) != crc) { - PERR("Wrong GPT header checksum"); + Genode::error("Wrong GPT header checksum"); return false; } @@ -149,15 +152,15 @@ class Gpt : public Block::Partition_table /* check GPT entry array */ Genode::size_t length = _entries * _entry_size; - Sector gpe(_gpe_lba, length / Block::Driver::driver().blk_size()); + Sector gpe(driver, _gpe_lba, length / driver.blk_size()); if (crc32(gpe.addr(), length) != _gpe_crc) return false; if (check_primary) { /* check backup gpt header */ - Sector backup_hdr(_backup_hdr_lba, 1); - if (!backup_hdr.addr()->valid(false)) { - PWRN("Backup GPT header is corrupted"); + Sector backup_hdr(driver, _backup_hdr_lba, 1); + if (!backup_hdr.addr()->valid(driver, false)) { + Genode::warning("Backup GPT header is corrupted"); } } @@ -226,11 +229,11 @@ class Gpt : public Block::Partition_table */ void _parse_gpt(Gpt_hdr *gpt) { - if (!(gpt->valid())) + if (!(gpt->valid(driver))) throw Genode::Exception(); - Sector entry_array(gpt->_gpe_lba, - gpt->_entries * gpt->_entry_size / Block::Driver::driver().blk_size()); + Sector entry_array(driver, gpt->_gpe_lba, + gpt->_entries * gpt->_entry_size / driver.blk_size()); Gpt_entry *entries = entry_array.addr(); for (int i = 0; i < MAX_PARTITIONS; i++) { @@ -242,43 +245,31 @@ class Gpt : public Block::Partition_table Genode::uint64_t start = e->_lba_start; Genode::uint64_t length = e->_lba_end - e->_lba_start + 1; /* [...) */ - _part_list[i] = new (Genode::env()->heap()) Block::Partition(start, length); + _part_list[i] = new (&heap) Block::Partition(start, length); - PINF("Partition %d: LBA %llu (%llu blocks) type: '%s' name: '%s'", - i + 1, start, length, e->_type.to_string(), e->name()); + Genode::log("Partition ", i + 1, ": LBA ", start, " (", length, + " blocks) type: '", e->_type.to_string(), + "' name: '", e->name(), "'"); } } - Gpt() - { - Sector s(Gpt_hdr::HEADER_LBA, 1); - _parse_gpt(s.addr()); - - /* - * we read all partition information, - * now it's safe to turn in asynchronous mode - */ - Block::Driver::driver().work_asynchronously(); - } - public: + using Partition_table::Partition_table; + Block::Partition *partition(int num) { return (num <= MAX_PARTITIONS && num > 0) ? _part_list[num-1] : 0; } - bool avail() + bool parse() { + Sector s(driver, Gpt_hdr::HEADER_LBA, 1); + _parse_gpt(s.addr()); + for (unsigned num = 0; num < MAX_PARTITIONS; num++) if (_part_list[num]) return true; return false; } - - static Gpt& table() - { - static Gpt table; - return table; - } }; #endif /* _PART_BLK__GUID_PARTITION_TABLE_H_ */ diff --git a/repos/os/src/server/part_blk/main.cc b/repos/os/src/server/part_blk/main.cc index eef01bdb75..5bab913267 100644 --- a/repos/os/src/server/part_blk/main.cc +++ b/repos/os/src/server/part_blk/main.cc @@ -7,84 +7,92 @@ */ /* - * Copyright (C) 2011-2014 Genode Labs GmbH + * Copyright (C) 2011-2016 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. */ +#include #include -#include -#include #include "component.h" #include "driver.h" #include "gpt.h" #include "mbr.h" -static Genode::Signal_receiver receiver; -Block::Driver& Block::Driver::driver() -{ - static Block::Driver driver(receiver); - return driver; -} - - -void Block::Driver::_ready_to_submit(unsigned) { +void Block::Driver::_ready_to_submit() { Block::Session_component::wake_up(); } -static bool _use_gpt() +class Main { - return Genode::config()->xml_node().attribute_value("use_gpt", false); -} + private: + + Block::Partition_table & _table(); + + Genode::Env & _env; + Genode::Heap _heap { _env.ram(), _env.rm() }; + Block::Driver _driver { _env.ep(), _heap }; + Mbr_partition_table _mbr { _heap, _driver }; + Gpt _gpt { _heap, _driver }; + Block::Root _root { _env, _heap, _driver, _table() }; + + public: + + class No_partion_table : 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)); + } +}; -int main() +Block::Partition_table & Main::_table() { - using namespace Genode; - bool valid_mbr = false; bool valid_gpt = false; - bool use_gpt = _use_gpt(); + bool use_gpt = false; + + try { + Genode::Attached_rom_dataspace config(_env, "config"); + use_gpt = config.xml().attribute_value("use_gpt", false); + } catch(...) {} if (use_gpt) - try { valid_gpt = Gpt::table().avail(); } catch (...) { } + try { valid_gpt = _gpt.parse(); } catch (...) { } /* fall back to MBR */ if (!valid_gpt) { - try { valid_mbr = Mbr_partition_table::table().avail(); } + try { valid_mbr = _mbr.parse(); } catch (Mbr_partition_table::Protective_mbr_found) { if (!use_gpt) - PERR("Aborting: found protective MBR but GPT usage was not requested."); - return 1; + Genode::error("Aborting: found protective MBR but ", + "GPT usage was not requested."); + throw; } } - Block::Partition_table *partition_table = 0; - if (valid_gpt) - partition_table = &Gpt::table(); - if (valid_mbr) - partition_table = &Mbr_partition_table::table(); + if (valid_gpt) return _gpt; + if (valid_mbr) return _mbr; - if (!partition_table) { - PERR("Aborting: no partition table found."); - return 1; - } - - enum { STACK_SIZE = 2048 * sizeof(Genode::size_t) }; - static Cap_connection cap; - static Rpc_entrypoint ep(&cap, STACK_SIZE, "part_ep"); - static Block::Root block_root(&ep, env()->heap(), receiver, - *partition_table); - - env()->parent()->announce(ep.manage(&block_root)); - - while (true) { - Signal s = receiver.wait_for_signal(); - static_cast(s.context())->dispatch(s.num()); - } - - return 0; + Genode::error("Aborting: no partition table found."); + throw No_partion_table(); } + + +Genode::size_t Component::stack_size() { + return 2048*sizeof(Genode::addr_t); } + + +void Component::construct(Genode::Env &env) { + static Main main(env); } diff --git a/repos/os/src/server/part_blk/mbr.h b/repos/os/src/server/part_blk/mbr.h index 7ecfd3a7f6..a8c5c41bf1 100644 --- a/repos/os/src/server/part_blk/mbr.h +++ b/repos/os/src/server/part_blk/mbr.h @@ -17,10 +17,9 @@ #define _PART_BLK__MBR_H_ #include -#include +#include #include -#include "driver.h" #include "partition_table.h" @@ -83,7 +82,7 @@ struct Mbr_partition_table : public Block::Partition_table /* first logical partition number */ int nr = 5; do { - Sector s(lba, 1); + Sector s(driver, lba, 1); Mbr *ebr = s.addr(); if (!(ebr->valid())) @@ -93,11 +92,12 @@ struct Mbr_partition_table : public Block::Partition_table * partition is relative to the lba of the current EBR */ Partition_record *logical = &(ebr->_records[0]); if (logical->valid() && nr < MAX_PARTITIONS) { - _part_list[nr++] = new (Genode::env()->heap()) + _part_list[nr++] = new (&heap) Block::Partition(logical->_lba + lba, logical->_sectors); - PINF("Partition %d: LBA %u (%u blocks) type %x", nr - 1, - logical->_lba + lba, logical->_sectors, logical->_type); + Genode::log("Partition ", nr - 1, ": LBA ", logical->_lba + lba, + " (", (unsigned int)logical->_sectors, " blocks) type ", + Genode::Hex(logical->_type, Genode::Hex::OMIT_PREFIX)); } /* @@ -114,8 +114,8 @@ struct Mbr_partition_table : public Block::Partition_table { /* no partition table, use whole disc as partition 0 */ if (!(mbr->valid())) - _part_list[0] = new(Genode::env()->heap()) - Block::Partition(0, Block::Driver::driver().blk_cnt() - 1); + _part_list[0] = new (&heap) + Block::Partition(0, driver.blk_cnt() - 1); for (int i = 0; i < 4; i++) { Partition_record *r = &(mbr->_records[i]); @@ -123,8 +123,10 @@ struct Mbr_partition_table : public Block::Partition_table if (!r->valid()) continue; - PINF("Partition %d: LBA %u (%u blocks) type: %x", - i + 1, r->_lba, r->_sectors, r->_type); + Genode::log("Partition ", i + 1, ": LBA ", + (unsigned int) r->_lba, " (", + (unsigned int) r->_sectors, " blocks) type: ", + Genode::Hex(r->_type, Genode::Hex::OMIT_PREFIX)); if (r->protective()) throw Protective_mbr_found(); @@ -134,41 +136,27 @@ struct Mbr_partition_table : public Block::Partition_table continue; } - _part_list[i + 1] = new(Genode::env()->heap()) + _part_list[i + 1] = new (&heap) Block::Partition(r->_lba, r->_sectors); } } - Mbr_partition_table() - { - Sector s(0, 1); - _parse_mbr(s.addr()); - - /* - * we read all partition information, - * now it's safe to turn in asynchronous mode - */ - Block::Driver::driver().work_asynchronously(); - } - public: + using Partition_table::Partition_table; + Block::Partition *partition(int num) { return (num < MAX_PARTITIONS) ? _part_list[num] : 0; } - bool avail() + bool parse() { + Sector s(driver, 0, 1); + _parse_mbr(s.addr()); for (unsigned num = 0; num < MAX_PARTITIONS; num++) if (_part_list[num]) return true; return false; } - - static Mbr_partition_table& table() - { - static Mbr_partition_table table; - return table; - } }; #endif /* _PART_BLK__MBR_H_ */ diff --git a/repos/os/src/server/part_blk/partition_table.h b/repos/os/src/server/part_blk/partition_table.h index d03aecfe0f..87eee30ad8 100644 --- a/repos/os/src/server/part_blk/partition_table.h +++ b/repos/os/src/server/part_blk/partition_table.h @@ -16,7 +16,7 @@ #define _PART_BLK__PARTITION_TABLE_H_ #include -#include +#include #include #include "driver.h" @@ -43,23 +43,25 @@ struct Block::Partition_table { private: - Session_client &_session; - Packet_descriptor _p; + Session_client &_session; + Packet_descriptor _p; public: - Sector(unsigned long blk_nr, + Sector(Driver &driver, + unsigned long blk_nr, unsigned long count, - bool write = false) - : _session(Driver::driver().session()), - _p(_session.dma_alloc_packet(Driver::driver().blk_size() * 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()) - PERR("Could not access block %llu", _p.block_number()); + Genode::error("Could not access block ", + (unsigned long long)_p.block_number()); } ~Sector() { _session.tx()->release_packet(_p); } @@ -68,9 +70,15 @@ struct Block::Partition_table return reinterpret_cast(_session.tx()->packet_content(_p)); } }; + Genode::Heap & heap; + Driver & driver; + + Partition_table(Genode::Heap & h, Driver & d) + : heap(h), driver(d) {} + virtual Partition *partition(int num) = 0; - virtual bool avail() = 0; + virtual bool parse() = 0; }; #endif /* _PART_BLK__PARTITION_TABLE_H_ */