/* * \brief Common representation of all storage devices * \author Norman Feske * \date 2018-05-02 */ /* * 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 _MODEL__STORAGE_DEVICE_H_ #define _MODEL__STORAGE_DEVICE_H_ #include "types.h" #include "partition.h" #include "capacity.h" #include "xml.h" namespace Sculpt { struct Storage_device; }; struct Sculpt::Storage_device { enum State { UNKNOWN, /* partition information not yet known */ USED, /* part_block is running and has reported partition info */ RELEASED, /* partition info is known but part_block is not running */ FAILED /* driver failed to access the device */ }; Allocator &_alloc; typedef String<32> Label; Label const label; Capacity capacity; /* non-const because USB storage devices need to update it */ State state { UNKNOWN }; bool whole_device = false; Reconstructible whole_device_partition { Partition::Args::whole_device(capacity) }; Partitions partitions { }; Attached_rom_dataspace _partitions_rom; unsigned _part_block_version = 0; /** * Trigger the rediscovery of the device, e.g., after partitioning of after * formatting the whole device. */ void rediscover() { state = UNKNOWN; _part_block_version++; Partition_update_policy policy(_alloc); partitions.update_from_xml(policy, Xml_node("")); } void process_part_block_report() { _partitions_rom.update(); Xml_node const report = _partitions_rom.xml(); if (!report.has_type("partitions")) return; whole_device = (report.attribute_value("type", String<16>()) == "disk"); Partition_update_policy policy(_alloc); partitions.update_from_xml(policy, report); /* * Import whole-device partition information. * * Ignore reports that come in while the device is in use. Otherwise, * the reconstruction of 'whole_device_partition' would wrongly reset * the partition state such as the 'file_system_inspected' flag. */ if (!whole_device_partition.constructed() || whole_device_partition->idle()) { whole_device_partition.construct(Partition::Args::whole_device(capacity)); report.for_each_sub_node("partition", [&] (Xml_node partition) { if (partition.attribute_value("number", Partition::Number()) == "0") whole_device_partition.construct(Partition::Args::from_xml(partition)); }); } /* finish initial discovery phase */ if (state == UNKNOWN) state = RELEASED; } /** * Constructor * * \param label label of block device * \param sigh signal handler to be notified on partition-info updates */ Storage_device(Env &env, Allocator &alloc, Label const &label, Capacity capacity, Signal_context_capability sigh) : _alloc(alloc), label(label), capacity(capacity), _partitions_rom(env, String<80>("report -> runtime/", label, ".part_block/partitions").string()) { _partitions_rom.sigh(sigh); process_part_block_report(); } ~Storage_device() { /* release partition info */ rediscover(); } bool part_block_needed_for_discovery() const { return state == UNKNOWN; } bool part_block_needed_for_access() const { bool needed_for_access = false; partitions.for_each([&] (Partition const &partition) { needed_for_access |= partition.check_in_progress; needed_for_access |= partition.format_in_progress; needed_for_access |= partition.file_system_inspected; needed_for_access |= partition.fs_resize_in_progress; }); if (whole_device_partition->format_in_progress || whole_device_partition->check_in_progress) { needed_for_access = false; } return needed_for_access; } /** * Generate content of start node for part_block * * \param service_name name of server that provides the block device, or * if invalid, request block device from parent. */ inline void gen_part_block_start_content(Xml_generator &xml, Label const &server_name) const; template void for_each_partition(FN const &fn) const { fn(*whole_device_partition); partitions.for_each([&] (Partition const &partition) { fn(partition); }); } template void for_each_partition(FN const &fn) { fn(*whole_device_partition); partitions.for_each([&] (Partition &partition) { fn(partition); }); } bool all_partitions_idle() const { bool idle = true; partitions.for_each([&] (Partition const &partition) { idle &= partition.idle(); }); return idle; } bool relabel_in_progress() const { bool result = false; partitions.for_each([&] (Partition const &partition) { result |= partition.relabel_in_progress(); }); return result; } bool gpt_expand_in_progress() const { bool result = false; partitions.for_each([&] (Partition const &partition) { result |= partition.gpt_expand_in_progress; }); return result; } bool fs_resize_in_progress() const { bool result = false; partitions.for_each([&] (Partition const &partition) { result |= partition.fs_resize_in_progress; }); return result; } bool expand_in_progress() const { return gpt_expand_in_progress() || fs_resize_in_progress(); } Start_name relabel_start_name() const { return Start_name(label, ".relabel"); } Start_name expand_start_name() const { return Start_name(label, ".expand"); } }; void Sculpt::Storage_device::gen_part_block_start_content(Xml_generator &xml, Label const &server_name) const { xml.attribute("version", _part_block_version); gen_common_start_content(xml, Label(label, ".part_block"), Cap_quota{100}, Ram_quota{8*1024*1024}); gen_named_node(xml, "binary", "part_block"); xml.node("config", [&] () { xml.node("report", [&] () { xml.attribute("partitions", "yes"); }); for (unsigned i = 1; i < 10; i++) { xml.node("policy", [&] () { xml.attribute("label", i); xml.attribute("partition", i); xml.attribute("writeable", "yes"); }); } }); gen_provides(xml); xml.node("route", [&] () { gen_service_node(xml, [&] () { if (server_name.valid()) gen_named_node(xml, "child", server_name); else xml.node("parent", [&] () { xml.attribute("label", label); }); }); gen_parent_rom_route(xml, "part_block"); gen_parent_rom_route(xml, "ld.lib.so"); gen_parent_route (xml); gen_parent_route (xml); gen_parent_route (xml); gen_service_node(xml, [&] () { xml.attribute("label", "partitions"); xml.node("parent", [&] () { }); }); }); } #endif /* _MODEL__STORAGE_DEVICE_H_ */