part_block: split reporting from parsing

Previously the 'partitions' report was implicitly generated after
parsing the partition-table by accessing the referenced reporter
object.

Now the report is explicitly created by calling the 'generate_report'
member function. At this point we no longer have access to all
required information so the 'Partition' class was extended to
accommodate for the data needed by MBR as well as GPT reports.

Fixes #4786.
This commit is contained in:
Josef Söntgen 2023-03-13 16:24:59 +01:00 committed by Christian Helmuth
parent bd3936c7ed
commit f7b910b298
5 changed files with 213 additions and 162 deletions

View File

@ -90,7 +90,8 @@ struct Ahdi
for (unsigned i = 0; i < MAX_PARTITIONS; i++) {
Partition_record const &part = root.partitions[i];
if (part.valid())
fn(i + 1, Block::Partition(part.start.value(), part.length.value()));
fn(i + 1, Block::Partition(part.start.value(), part.length.value(),
Fs::Type(), 0));
}
}
};

View File

@ -23,7 +23,6 @@
#include <util/utf8.h>
#include "partition_table.h"
#include "fsprobe.h"
static bool constexpr verbose = false;
@ -193,6 +192,12 @@ class Block::Gpt : public Block::Partition_table
};
/* state needed for generating the partitions report */
uint64_t _gpt_part_lba_end { 0 };
uint64_t _gpt_total { 0 };
uint64_t _gpt_used { 0 };
/**
* GUID partition entry format
*/
@ -239,47 +244,57 @@ class Block::Gpt : public Block::Partition_table
};
/**
* Iterate over each constructed partition and execute FN
*/
template <typename FN>
void _for_each_valid_partition(FN const &fn) const
{
for (unsigned i = 0; i < MAX_PARTITIONS; i++)
if (_part_list[i].constructed())
fn(i);
};
/**
* 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
* \param entry number of current entry
* \param total number of total blocks on the device
*
* \return the number of free blocks to the next logical entry
*/
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)
uint64_t _calculate_gap(uint32_t entry,
uint64_t total_blocks) const
{
Partition const &current = *_part_list[entry];
/* add one block => end == start */
uint64_t const end_lba = entry.lba_end() + 1;
uint64_t const end_lba = current.lba + current.sectors;
enum { INVALID_START = ~0ull, };
uint64_t next_start_lba = INVALID_START;
for (uint32_t i = 0; i < num; i++) {
Gpt_entry const e(entries.base() + i * header.entry_size());
_for_each_valid_partition([&] (unsigned i) {
if (!e.valid() || e.base() == entry.base()) { continue; }
if (i == entry)
return;
Partition const &p = *_part_list[i];
/*
* 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());
}
}
if (p.lba >= end_lba)
next_start_lba = min(next_start_lba, p.lba);
});
/* sanity check if GPT is broken */
if (end_lba > header.part_lba_end()) { return 0; }
if (end_lba > _gpt_part_lba_end) { return 0; }
/* if the underyling Block device changes we might be able to expand more */
uint64_t const part_end = max(header.part_lba_end(), total_blocks);
/* if the underlying Block device changes we might be able to expand more */
uint64_t const part_end = max(_gpt_part_lba_end, total_blocks);
/*
* Use stored next start LBA or paritions end LBA from header,
@ -299,9 +314,9 @@ class Block::Gpt : public Block::Partition_table
*
* \return the number of used blocks
*/
uint64_t _calculate_used(Gpt_hdr const &header,
Gpt_entry const &entries,
uint32_t num)
uint64_t _calculate_used(Gpt_hdr const &header,
Gpt_entry const &entries,
uint32_t num)
{
uint64_t used = 0;
@ -330,6 +345,10 @@ class Block::Gpt : public Block::Partition_table
gpt.entries() * gpt.entry_size() / block.info().block_size);
Gpt_entry entries(entry_array.addr<addr_t>());
_gpt_part_lba_end = gpt.part_lba_end();
_gpt_total = (gpt.part_lba_end() - gpt.part_lba_start()) + 1;
_gpt_used = _calculate_used(gpt, entries, gpt.entries());
for (int i = 0; i < MAX_PARTITIONS; i++) {
Gpt_entry e(entries.base() + i * gpt.entry_size());
@ -337,69 +356,23 @@ class Block::Gpt : public Block::Partition_table
if (!e.valid())
continue;
block_number_t start = e.lba_start();
block_count_t length = (block_count_t)(e.lba_end() - e.lba_start() + 1); /* [...) */
block_number_t const lba_start = e.lba_start();
block_count_t const length = (block_count_t)(e.lba_end() - lba_start + 1); /* [...) */
_part_list[i].construct(start, length);
enum { BYTES = 4096, };
Sector fs(data, lba_start, BYTES / block.info().block_size);
Fs::Type fs_type = Fs::probe(fs.addr<uint8_t*>(), BYTES);
log("GPT Partition ", i + 1, ": LBA ", start, " (", length,
" blocks) type: '", e.type(),
"' name: '", e, "'");
}
String<40> guid { e.guid() };
String<40> type { e.type() };
String<Gpt_entry::NAME_LEN> name { e };
/* Report the partitions */
if (reporter.constructed())
{
reporter->generate([&] (Xml_generator &xml) {
xml.attribute("type", "gpt");
_part_list[i].construct(lba_start, length, fs_type,
guid, type, name);
uint64_t const total_blocks = block.info().block_count;
xml.attribute("total_blocks", total_blocks);
uint64_t const gpt_total =
(gpt.part_lba_end() - gpt.part_lba_start()) + 1;
xml.attribute("gpt_total", gpt_total);
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.base() + i * gpt.entry_size());
if (!e.valid()){
continue;
}
enum { BYTES = 4096, };
Sector fs(data, e.lba_start(), BYTES / block.info().block_size);
Fs::Type fs_type = Fs::probe(fs.addr<uint8_t*>(), BYTES);
String<40> guid { e.guid() };
String<40> type { e.type() };
String<Gpt_entry::NAME_LEN> name { e };
xml.node("partition", [&] () {
xml.attribute("number", i + 1);
xml.attribute("name", name);
xml.attribute("type", type);
xml.attribute("guid", guid);;
xml.attribute("start", e.lba_start());
xml.attribute("length", e.lba_end() - e.lba_start() + 1);
xml.attribute("block_size", block.info().block_size);
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);
}
});
}
});
log("GPT Partition ", i + 1, ": LBA ", lba_start, " (", length,
" blocks) type: '", type,
"' name: '", name, "'");
}
}
@ -425,14 +398,51 @@ class Block::Gpt : public Block::Partition_table
block.sigh(io_sigh);
Sector s(data, Gpt_hdr::Hdr_lba::LBA, 1);
Gpt_hdr hdr(s.addr<addr_t>());
_parse_gpt(hdr);
Gpt_hdr gpt_hdr(s.addr<addr_t>());
_parse_gpt(gpt_hdr);
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
if (_part_list[num].constructed())
return true;
return false;
}
void generate_report(Xml_generator &xml) const override
{
xml.attribute("type", "gpt");
uint64_t const total_blocks = block.info().block_count;
xml.attribute("total_blocks", total_blocks);
xml.attribute("gpt_total", _gpt_total);
xml.attribute("gpt_used", _gpt_used);
size_t const block_size = block.info().block_size;
_for_each_valid_partition([&] (unsigned i) {
Block::Partition const &part = *_part_list[i];
xml.node("partition", [&] () {
xml.attribute("number", i + 1);
xml.attribute("name", part.name);
xml.attribute("type", part.gpt_type);
xml.attribute("guid", part.guid);
xml.attribute("start", part.lba);
xml.attribute("length", part.sectors);
xml.attribute("block_size", block_size);
uint64_t const gap =
_calculate_gap(i, total_blocks);
if (gap)
xml.attribute("expandable", gap);
if (part.fs_type.valid())
xml.attribute("file_system", part.fs_type);
});
});
}
};
#endif /* _PART_BLOCK__GUID_PARTITION_TABLE_H_ */

View File

@ -222,8 +222,8 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
Allocator_avl _block_alloc { &_heap };
Block_connection _block { _env, &_block_alloc, _io_buffer_size };
Io_signal_handler<Main> _io_sigh { _env.ep(), *this, &Main::_handle_io };
Mbr_partition_table _mbr { _env, _block, _heap, _reporter };
Gpt _gpt { _env, _block, _heap, _reporter };
Mbr_partition_table _mbr { _env, _block, _heap };
Gpt _gpt { _env, _block, _heap };
Partition_table &_partition_table { _table() };
enum { MAX_SESSIONS = 128 };
@ -494,12 +494,16 @@ Block::Partition_table & Block::Main::_table()
throw Invalid_config();
}
config.with_optional_sub_node("report", [&] (Xml_node const &node) {
report = node.attribute_value("partitions", false); });
try {
report = _config.xml().sub_node("report").attribute_value
("partitions", false);
if (report)
_reporter.construct(_env, "partitions", "partitions");
} catch(...) {}
} catch (...) {
error("cannot construct partitions reporter: abort");
throw;
}
/*
* Try to parse MBR as well as GPT first if not instructued
@ -537,6 +541,13 @@ Block::Partition_table & Block::Main::_table()
warning("found protective MBR but GPT is to be ignored");
}
/* generate appropriate report */
if (_reporter.constructed())
_reporter->generate([&] (Xml_generator &xml) {
if (valid_gpt) _gpt.generate_report(xml);
if (valid_mbr) _mbr.generate_report(xml);
});
/*
* Return the appropriate table or abort if none is found.
*/

View File

@ -158,6 +158,10 @@ struct Block::Mbr_partition_table : public Block::Partition_table
}
}
/* state for partitions report */
bool _mbr_valid { false };
bool _ahdi_valid { false };
public:
using Partition_table::Partition_table;
@ -189,86 +193,44 @@ struct Block::Mbr_partition_table : public Block::Partition_table
/* check for MBR */
Mbr const mbr(s.addr<addr_t>());
bool const mbr_valid = mbr.valid();
if (mbr_valid) {
_mbr_valid = mbr.valid();
if (_mbr_valid) {
_parse_mbr(mbr, [&] (int i, Partition_record const &r, unsigned offset) {
log("MBR Partition ", i, ": LBA ",
r.lba() + offset, " (",
r.sectors(), " blocks) type: ",
Hex(r.type(), Hex::OMIT_PREFIX));
if (!r.extended())
_part_list[i].construct(Partition(r.lba() + offset, r.sectors()));
if (!r.extended()) {
block_number_t const lba = r.lba() + offset;
/* probe for known file-system types */
enum { PROBE_BYTES = 4096, };
Sector fs(data, lba , PROBE_BYTES / block.info().block_size);
Fs::Type const fs_type =
Fs::probe(fs.addr<uint8_t*>(), PROBE_BYTES);
_part_list[i].construct(
Partition(lba, r.sectors(), fs_type, r.type()));
}
});
}
/* check for AHDI partition table */
bool const ahdi_valid = !mbr_valid && Ahdi::valid(s);
if (ahdi_valid)
_ahdi_valid = !_mbr_valid && Ahdi::valid(s);
if (_ahdi_valid)
Ahdi::for_each_partition(s, [&] (unsigned i, Partition info) {
if (i < MAX_PARTITIONS)
_part_list[i].construct(
Partition(info.lba, info.sectors)); });
Partition(info.lba, info.sectors, Fs::Type(), 0));
});
/* no partition table, use whole disc as partition 0 */
if (!mbr_valid && !ahdi_valid)
if (!_mbr_valid && !_ahdi_valid)
_part_list[0].construct(
Partition(0, (block_count_t)(block.info().block_count - 1)));
/* report the partitions */
if (reporter.constructed()) {
auto gen_partition_attr = [&] (Xml_generator &xml, unsigned i)
{
Partition const &part = *_part_list[i];
size_t const block_size = block.info().block_size;
xml.attribute("number", i);
xml.attribute("start", part.lba);
xml.attribute("length", part.sectors);
xml.attribute("block_size", block_size);
/* probe for known file-system types */
enum { PROBE_BYTES = 4096, };
Sector fs(data, part.lba, PROBE_BYTES / block_size);
Fs::Type const fs_type =
Fs::probe(fs.addr<uint8_t*>(), PROBE_BYTES);
if (fs_type.valid())
xml.attribute("file_system", fs_type);
};
reporter->generate([&] (Xml_generator &xml) {
xml.attribute("type", mbr_valid ? "mbr" :
ahdi_valid ? "ahdi" :
"disk");
if (mbr_valid) {
_parse_mbr(mbr, [&] (int i, Partition_record const &r, unsigned) {
/* nullptr if extended */
if (!_part_list[i].constructed())
return;
xml.node("partition", [&] {
gen_partition_attr(xml, i);
xml.attribute("type", r.type()); });
});
} else if (ahdi_valid) {
_for_each_valid_partition([&] (unsigned i) {
xml.node("partition", [&] {
gen_partition_attr(xml, i);
xml.attribute("type", "bgm"); }); });
} else {
xml.node("partition", [&] {
gen_partition_attr(xml, 0); });
}
});
}
Partition(0, (block_count_t)(block.info().block_count - 1),
Fs::Type(), 0));
bool any_partition_valid = false;
_for_each_valid_partition([&] (unsigned) {
@ -276,6 +238,44 @@ struct Block::Mbr_partition_table : public Block::Partition_table
return any_partition_valid;
}
void generate_report(Xml_generator &xml) const override
{
auto gen_partition_attr = [&] (Xml_generator &xml, unsigned i)
{
Partition const &part = *_part_list[i];
size_t const block_size = block.info().block_size;
xml.attribute("number", i);
xml.attribute("start", part.lba);
xml.attribute("length", part.sectors);
xml.attribute("block_size", block_size);
if (_mbr_valid)
xml.attribute("type", part.mbr_type);
else if (_ahdi_valid)
xml.attribute("type", "bgm");
if (part.fs_type.valid())
xml.attribute("file_system", part.fs_type);
};
xml.attribute("type", _mbr_valid ? "mbr" :
_ahdi_valid ? "ahdi" :
"disk");
if (_mbr_valid || _ahdi_valid) {
_for_each_valid_partition([&] (unsigned i) {
xml.node("partition", [&] {
gen_partition_attr(xml, i); }); });
} else {
xml.node("partition", [&] {
gen_partition_attr(xml, 0); });
}
}
};
#endif /* _PART_BLOCK__MBR_H_ */

View File

@ -21,6 +21,8 @@
#include <block_session/connection.h>
#include <os/reporter.h>
#include "fsprobe.h"
namespace Block {
struct Partition;
class Partition_table;
@ -35,8 +37,35 @@ struct Block::Partition
block_number_t lba; /* logical block address on device */
block_count_t sectors; /* number of sectors in patitions */
Partition(block_number_t lba, block_count_t sectors)
: lba(lba), sectors(sectors) { }
Fs::Type fs_type { };
uint8_t mbr_type { 0 };
using Uuid = String<40>;
Uuid guid { };
Uuid gpt_type { };
using Name = String<72>; /* use GPT name antry length */
Name name { };
Partition(block_number_t lba,
block_count_t sectors,
Fs::Type fs_type,
uint8_t mbr_type)
:
lba(lba), sectors(sectors), fs_type(fs_type),
mbr_type(mbr_type)
{ }
Partition(block_number_t lba,
block_count_t sectors,
Fs::Type fs_type,
Uuid const &guid, Uuid const &gpt_type,
Name const &name)
:
lba(lba), sectors(sectors), fs_type(fs_type),
guid(guid), gpt_type(gpt_type), name(name)
{ }
};
@ -154,7 +183,6 @@ struct Block::Partition_table : Interface
Env &env;
Block_connection &block;
Constructible<Expanding_reporter> &reporter;
Sector_data data;
Io_signal_handler<Partition_table> io_sigh {
@ -167,14 +195,15 @@ struct Block::Partition_table : Interface
Partition_table(Env &env,
Block_connection &block,
Allocator &alloc,
Constructible<Expanding_reporter> &r)
: env(env), block(block), reporter(r), data(env, block, alloc)
Allocator &alloc)
: env(env), block(block), data(env, block, alloc)
{ }
virtual Partition &partition(long num) = 0;
virtual bool parse() = 0;
virtual void generate_report(Xml_generator &xml) const = 0;
};
#endif /* _PART_BLOCK__PARTITION_TABLE_H_ */