From f8fd202a1c0ae0bbae7bf782d76d90a65f7ca805 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 24 Mar 2023 17:14:41 +0100 Subject: [PATCH] depot_download: option for unverified downloads This patch equips the depot_download subsystem with the option to explicitly skip the signature verification for downloads by specifying the attribute 'verify="no"' for an item. This is useful in scenarios where the lack of integrity of downloaded content does not pose a risk, e.g., for untrusted applications that are rigidly sandboxed, or during development. Note that this option does not entirely discarge the signature checking. Whenever an download has dependencies that are verifyable - for which the public key exists in the depot - the dependencies are still verified. This allows untrusted content to depend of verifyable content while protecting the integrity the verifyable content. Issue #4804 --- .../depot_download_manager/gen_depot_query.cc | 13 ++- .../app/depot_download_manager/gen_extract.cc | 2 +- .../depot_download_manager/gen_fetchurl.cc | 47 +++++----- .../src/app/depot_download_manager/import.h | 71 ++++++++++----- .../src/app/depot_download_manager/main.cc | 24 +++-- .../src/app/depot_download_manager/types.h | 25 ++++++ .../gems/src/app/depot_download_manager/xml.h | 2 +- repos/gems/src/app/depot_query/main.cc | 33 ++++--- repos/gems/src/app/depot_query/main.h | 87 ++++++++++++++----- .../src/app/depot_query/query_image_index.cc | 5 +- 10 files changed, 219 insertions(+), 90 deletions(-) diff --git a/repos/gems/src/app/depot_download_manager/gen_depot_query.cc b/repos/gems/src/app/depot_download_manager/gen_depot_query.cc index 00469e1882..98fc3bc5f5 100644 --- a/repos/gems/src/app/depot_download_manager/gen_depot_query.cc +++ b/repos/gems/src/app/depot_download_manager/gen_depot_query.cc @@ -58,11 +58,18 @@ void Depot_download_manager::gen_depot_query_start_content(Xml_generator &xml, fn(node); }); }; + auto propagate_verify_attr = [&] (Xml_generator &xml, Xml_node const &node) + { + if (node.attribute_value("verify", true) == false) + xml.attribute("require_verify", "no"); + }; + for_each_install_sub_node("archive", [&] (Xml_node const &archive) { xml.node("dependencies", [&] () { xml.attribute("path", archive.attribute_value("path", Archive::Path())); xml.attribute("source", archive.attribute_value("source", true)); xml.attribute("binary", archive.attribute_value("binary", true)); + propagate_verify_attr(xml, archive); }); }); @@ -75,6 +82,7 @@ void Depot_download_manager::gen_depot_query_start_content(Xml_generator &xml, xml.node("index", [&] () { xml.attribute("user", Archive::user(path)); xml.attribute("version", Archive::_path_element(path, 2)); + propagate_verify_attr(xml, index); }); }); @@ -87,6 +95,7 @@ void Depot_download_manager::gen_depot_query_start_content(Xml_generator &xml, xml.node("image", [&] () { xml.attribute("user", Archive::user(path)); xml.attribute("name", Archive::name(path)); + propagate_verify_attr(xml, image); }); }); @@ -97,7 +106,9 @@ void Depot_download_manager::gen_depot_query_start_content(Xml_generator &xml, return; } xml.node("image_index", [&] () { - xml.attribute("user", Archive::user(path)); }); + xml.attribute("user", Archive::user(path)); + propagate_verify_attr(xml, image_index); + }); }); if (next_user.valid()) diff --git a/repos/gems/src/app/depot_download_manager/gen_extract.cc b/repos/gems/src/app/depot_download_manager/gen_extract.cc index 3a5406479c..53be0ea29f 100644 --- a/repos/gems/src/app/depot_download_manager/gen_extract.cc +++ b/repos/gems/src/app/depot_download_manager/gen_extract.cc @@ -55,7 +55,7 @@ void Depot_download_manager::gen_extract_start_content(Xml_generator &xml, }); }); - import.for_each_verified_archive([&] (Archive::Path const &path) { + import.for_each_verified_or_blessed_archive([&] (Archive::Path const &path) { typedef String<160> Path; diff --git a/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc b/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc index 5e131f16bd..a3f59b63ce 100644 --- a/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc +++ b/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc @@ -16,6 +16,7 @@ void Depot_download_manager::gen_fetchurl_start_content(Xml_generator &xml, Import const &import, Url const ¤t_user_url, + Pubkey_known pubkey_known, Fetchurl_version version) { xml.attribute("version", version.value); @@ -23,40 +24,40 @@ void Depot_download_manager::gen_fetchurl_start_content(Xml_generator &xml, gen_common_start_content(xml, "fetchurl", Cap_quota{500}, Ram_quota{8*1024*1024}); - xml.node("config", [&] () { - xml.node("libc", [&] () { + xml.node("config", [&] { + xml.node("libc", [&] { xml.attribute("stdout", "/dev/log"); xml.attribute("stderr", "/dev/log"); xml.attribute("rtc", "/dev/rtc"); xml.attribute("socket", "/socket"); }); - xml.node("report", [&] () { + xml.node("report", [&] { xml.attribute("progress", "yes"); xml.attribute("delay_ms", 250); }); - xml.node("vfs", [&] () { - xml.node("dir", [&] () { + xml.node("vfs", [&] { + xml.node("dir", [&] { xml.attribute("name", "download"); - xml.node("fs", [&] () { + xml.node("fs", [&] { xml.attribute("buffer_size", 144u << 10); xml.attribute("label", "download"); }); }); - xml.node("dir", [&] () { + xml.node("dir", [&] { xml.attribute("name", "dev"); - xml.node("log", [&] () { }); - xml.node("null", [&] () { }); - xml.node("inline", [&] () { + xml.node("log", [&] { }); + xml.node("null", [&] { }); + xml.node("inline", [&] { xml.attribute("name", "rtc"); String<64> date("2000-01-01 00:00"); xml.append(date.string()); }); - xml.node("inline", [&] () { + xml.node("inline", [&] { xml.attribute("name", "random"); String<64> entropy("01234567890123456789"); xml.append(entropy.string()); }); }); - xml.node("fs", [&] () { + xml.node("fs", [&] { xml.attribute("label", "tcpip"); }); }); @@ -69,29 +70,31 @@ void Depot_download_manager::gen_fetchurl_start_content(Xml_generator &xml, Remote const remote (current_user_url, "/", file_path); Local const local ("/download/", file_path); - xml.node("fetch", [&] () { + xml.node("fetch", [&] { xml.attribute("url", remote); xml.attribute("path", local); }); - xml.node("fetch", [&] () { - xml.attribute("url", Remote(remote, ".sig")); - xml.attribute("path", Local (local, ".sig")); - }); + if (pubkey_known.value) { + xml.node("fetch", [&] { + xml.attribute("url", Remote(remote, ".sig")); + xml.attribute("path", Local (local, ".sig")); + }); + } }); }); - xml.node("route", [&] () { - xml.node("service", [&] () { + xml.node("route", [&] { + xml.node("service", [&] { xml.attribute("name", File_system::Session::service_name()); xml.attribute("label", "download"); - xml.node("parent", [&] () { + xml.node("parent", [&] { xml.attribute("label", "public_rw"); }); }); - xml.node("service", [&] () { + xml.node("service", [&] { xml.attribute("name", File_system::Session::service_name()); xml.attribute("label", "tcpip"); - xml.node("parent", [&] () { + xml.node("parent", [&] { xml.attribute("label", "tcpip"); }); }); gen_parent_unscoped_rom_route(xml, "fetchurl"); diff --git a/repos/gems/src/app/depot_download_manager/import.h b/repos/gems/src/app/depot_download_manager/import.h index 1e16a723e8..fa56b36d26 100644 --- a/repos/gems/src/app/depot_download_manager/import.h +++ b/repos/gems/src/app/depot_download_manager/import.h @@ -63,12 +63,15 @@ class Depot_download_manager::Import Archive::Path const path; + bool const require_verify; + enum State { DOWNLOAD_IN_PROGRESS, DOWNLOAD_COMPLETE, DOWNLOAD_UNAVAILABLE, VERIFICATION_IN_PROGRESS, VERIFIED, VERIFICATION_FAILED, + BLESSED, /* verification deliberately skipped */ UNPACKED }; State state = DOWNLOAD_IN_PROGRESS; @@ -78,12 +81,15 @@ class Depot_download_manager::Import return state == DOWNLOAD_IN_PROGRESS || state == DOWNLOAD_COMPLETE || state == VERIFICATION_IN_PROGRESS - || state == VERIFIED; + || state == VERIFIED + || state == BLESSED; } - Item(Registry ®istry, Archive::Path const &path) + Item(Registry ®istry, Archive::Path const &path, + Require_verify require_verify) : - _element(registry, *this), path(path) + _element(registry, *this), path(path), + require_verify(require_verify.value) { } char const *state_text() const @@ -95,6 +101,7 @@ class Depot_download_manager::Import case VERIFICATION_IN_PROGRESS: return "verify"; case VERIFIED: return "extract"; case VERIFICATION_FAILED: return "corrupted"; + case BLESSED: return "extract"; case UNPACKED: return "done"; }; return ""; @@ -103,6 +110,8 @@ class Depot_download_manager::Import Allocator &_alloc; + bool const _pubkey_known; + Registry _items { }; template @@ -155,16 +164,16 @@ class Depot_download_manager::Import FN const &fn) { dependencies.for_each_sub_node("missing", [&] (Xml_node const &item) { - fn(_depdendency_path(item)); }); + fn(_depdendency_path(item), Require_verify::from_xml(item)); }); index.for_each_sub_node("missing", [&] (Xml_node const &item) { - fn(_index_path(item)); }); + fn(_index_path(item), Require_verify::from_xml(item)); }); image.for_each_sub_node("missing", [&] (Xml_node const &item) { - fn(_image_path(item)); }); + fn(_image_path(item), Require_verify::from_xml(item)); }); image_index.for_each_sub_node("missing", [&] (Xml_node const &item) { - fn(_image_index_path(item)); }); + fn(_image_index_path(item), Require_verify::from_xml(item)); }); } public: @@ -200,18 +209,20 @@ class Depot_download_manager::Import * items that match the 'user'. The remaining sub nodes are imported in * a future iteration. */ - Import(Allocator &alloc, Archive::User const &user, - Xml_node const &dependencies, - Xml_node const &index, - Xml_node const &image, - Xml_node const &image_index) + Import(Allocator &alloc, + Archive::User const &user, + Pubkey_known const pubkey_known, + Xml_node const &dependencies, + Xml_node const &index, + Xml_node const &image, + Xml_node const &image_index) : - _alloc(alloc) + _alloc(alloc), _pubkey_known(pubkey_known.value) { _for_each_missing_depot_path(dependencies, index, image, image_index, - [&] (Archive::Path const &path) { + [&] (Archive::Path const &path, Require_verify require_verify) { if (Archive::user(path) == user) - new (alloc) Item(_items, path); }); + new (alloc) Item(_items, path, require_verify); }); } ~Import() @@ -234,9 +245,10 @@ class Depot_download_manager::Import return _item_state_exists(Item::VERIFICATION_IN_PROGRESS); } - bool verified_archives_available() const + bool verified_or_blessed_archives_available() const { - return _item_state_exists(Item::VERIFIED); + return _item_state_exists(Item::VERIFIED) + || _item_state_exists(Item::BLESSED); } template @@ -252,9 +264,10 @@ class Depot_download_manager::Import } template - void for_each_verified_archive(FN const &fn) const + void for_each_verified_or_blessed_archive(FN const &fn) const { _for_each_item(Item::VERIFIED, fn); + _for_each_item(Item::BLESSED, fn); } template @@ -277,11 +290,23 @@ class Depot_download_manager::Import item.state = Item::DOWNLOAD_COMPLETE; }); } - void verify_all_downloaded_archives() + void verify_or_bless_all_downloaded_archives() { _items.for_each([&] (Item &item) { - if (item.state == Item::DOWNLOAD_COMPLETE) - item.state = Item::VERIFICATION_IN_PROGRESS; }); + if (item.state == Item::DOWNLOAD_COMPLETE) { + + /* + * If verification is not required, still verify whenever + * a depot user's public key exists. This way, verifiable + * archives referred to by non-verified archives end up in + * verified form in the depot. + */ + if (item.require_verify || _pubkey_known) + item.state = Item::VERIFICATION_IN_PROGRESS; + else + item.state = Item::BLESSED; + } + }); } void apply_download_progress(Download_progress const &progress) @@ -319,10 +344,10 @@ class Depot_download_manager::Import item.state = Item::VERIFICATION_FAILED; }); } - void all_verified_archives_extracted() + void all_verified_or_blessed_archives_extracted() { _items.for_each([&] (Item &item) { - if (item.state == Item::VERIFIED) + if (item.state == Item::VERIFIED || item.state == Item::BLESSED) item.state = Item::UNPACKED; }); } diff --git a/repos/gems/src/app/depot_download_manager/main.cc b/repos/gems/src/app/depot_download_manager/main.cc index ac04270dc2..7dc2e6a814 100644 --- a/repos/gems/src/app/depot_download_manager/main.cc +++ b/repos/gems/src/app/depot_download_manager/main.cc @@ -87,6 +87,11 @@ struct Depot_download_manager::Main : Import::Download_progress return _current_user.xml().attribute_value("name", Archive::User()); } + Pubkey_known _current_user_has_pubkey() const + { + return Pubkey_known { _current_user.xml().has_sub_node("pubkey") }; + } + Path _current_user_path() const { return Path("/depot/", _current_user_name()); @@ -326,7 +331,9 @@ void Depot_download_manager::Main::_generate_init_config(Xml_generator &xml) if (fetchurl_running) { try { xml.node("start", [&] () { - gen_fetchurl_start_content(xml, *_import, _current_user_url(), + gen_fetchurl_start_content(xml, *_import, + _current_user_url(), + _current_user_has_pubkey(), _fetchurl_count); }); } catch (Invalid_download_url) { @@ -338,7 +345,7 @@ void Depot_download_manager::Main::_generate_init_config(Xml_generator &xml) xml.node("start", [&] () { gen_verify_start_content(xml, *_import, _current_user_path()); }); - if (_import.constructed() && _import->verified_archives_available()) { + if (_import.constructed() && _import->verified_or_blessed_archives_available()) { xml.node("start", [&] () { gen_chroot_start_content(xml, _current_user_name()); }); @@ -370,8 +377,7 @@ void Depot_download_manager::Main::_handle_query_result() Archive::User const name = user.attribute_value("name", Archive::User()); - bool const user_info_complete = user.has_sub_node("url") - && user.has_sub_node("pubkey"); + bool const user_info_complete = user.has_sub_node("url"); if (name.valid() && !user_info_complete) { @@ -397,6 +403,8 @@ void Depot_download_manager::Main::_handle_query_result() Xml_node const image = _image.xml(); Xml_node const image_index = _image_index.xml(); + log("query result index: ", index); + /* mark jobs referring to existing depot content as unneccessary */ Import::for_each_present_depot_path(dependencies, index, image, image_index, [&] (Archive::Path const &path) { @@ -458,7 +466,7 @@ void Depot_download_manager::Main::_handle_query_result() } /* start new import */ - _import.construct(_heap, _current_user_name(), + _import.construct(_heap, _current_user_name(), _current_user_has_pubkey(), dependencies, index, image, image_index); /* mark imported jobs as started */ @@ -514,7 +522,7 @@ void Depot_download_manager::Main::_handle_init_state() } if (!import.downloads_in_progress() && import.completed_downloads_available()) { - import.verify_all_downloaded_archives(); + import.verify_or_bless_all_downloaded_archives(); reconfigure_init = true; } @@ -545,7 +553,7 @@ void Depot_download_manager::Main::_handle_init_state() }); } - if (import.verified_archives_available()) { + if (import.verified_or_blessed_archives_available()) { Child_exit_state const extract_state(_init_state.xml(), "extract"); @@ -553,7 +561,7 @@ void Depot_download_manager::Main::_handle_init_state() error("extract failed with exit code ", extract_state.code); if (extract_state.exited && extract_state.code == 0) - import.all_verified_archives_extracted(); + import.all_verified_or_blessed_archives_extracted(); } /* flag failed jobs to prevent re-attempts in subsequent import iterations */ diff --git a/repos/gems/src/app/depot_download_manager/types.h b/repos/gems/src/app/depot_download_manager/types.h index 07ca1cc9c3..58f73ab232 100644 --- a/repos/gems/src/app/depot_download_manager/types.h +++ b/repos/gems/src/app/depot_download_manager/types.h @@ -28,8 +28,33 @@ namespace Depot_download_manager { struct Depot_query_version { unsigned value; }; struct Fetchurl_version { unsigned value; }; + + struct Require_verify; + + struct Pubkey_known { bool value; }; } + +/** + * Argument type for propagating 'verify' install attributes to imports + */ +struct Depot_download_manager::Require_verify +{ + bool value; + + static Require_verify from_xml(Xml_node const &node) + { + return Require_verify { node.attribute_value("require_verify", true) }; + } + + void gen_attr(Xml_generator &xml) const + { + if (!value) + xml.attribute("require_verify", "no"); + } +}; + + namespace Genode { /** diff --git a/repos/gems/src/app/depot_download_manager/xml.h b/repos/gems/src/app/depot_download_manager/xml.h index 2504f5d8e8..4181adf90f 100644 --- a/repos/gems/src/app/depot_download_manager/xml.h +++ b/repos/gems/src/app/depot_download_manager/xml.h @@ -89,7 +89,7 @@ namespace Depot_download_manager { List_model const &); void gen_fetchurl_start_content(Xml_generator &, Import const &, Url const &, - Fetchurl_version); + Pubkey_known, Fetchurl_version); void gen_verify_start_content(Xml_generator &, Import const &, Path const &); diff --git a/repos/gems/src/app/depot_query/main.cc b/repos/gems/src/app/depot_query/main.cc index b5d994f05e..94f5d1ac97 100644 --- a/repos/gems/src/app/depot_query/main.cc +++ b/repos/gems/src/app/depot_query/main.cc @@ -191,6 +191,7 @@ void Depot_query::Main::_query_blueprint(Directory::Path const &pkg_path, Xml_ge void Depot_query::Main::_collect_source_dependencies(Archive::Path const &path, Dependencies &dependencies, + Require_verify require_verify, Recursion_limit recursion_limit) { try { Archive::type(path); } @@ -199,14 +200,15 @@ void Depot_query::Main::_collect_source_dependencies(Archive::Path const &path, return; } - dependencies.record(path); + dependencies.record(path, require_verify); switch (Archive::type(path)) { case Archive::PKG: { _with_file_content(path, "archives", [&] (File_content const &archives) { archives.for_each_line([&] (Archive::Path const &path) { - _collect_source_dependencies(path, dependencies, recursion_limit); }); }); + _collect_source_dependencies(path, dependencies, require_verify, + recursion_limit); }); }); break; } @@ -214,7 +216,8 @@ void Depot_query::Main::_collect_source_dependencies(Archive::Path const &path, typedef String<160> Api; _with_file_content(path, "used_apis", [&] (File_content const &used_apis) { used_apis.for_each_line([&] (Api const &api) { - dependencies.record(Archive::Path(Archive::user(path), "/api/", api)); }); }); + dependencies.record(Archive::Path(Archive::user(path), "/api/", api), + require_verify); }); }); break; } @@ -227,6 +230,7 @@ void Depot_query::Main::_collect_source_dependencies(Archive::Path const &path, void Depot_query::Main::_collect_binary_dependencies(Archive::Path const &path, Dependencies &dependencies, + Require_verify require_verify, Recursion_limit recursion_limit) { try { Archive::type(path); } @@ -238,22 +242,23 @@ void Depot_query::Main::_collect_binary_dependencies(Archive::Path const &path, switch (Archive::type(path)) { case Archive::PKG: - dependencies.record(path); + dependencies.record(path, require_verify); _with_file_content(path, "archives", [&] (File_content const &archives) { archives.for_each_line([&] (Archive::Path const &archive_path) { - _collect_binary_dependencies(archive_path, dependencies, recursion_limit); }); }); + _collect_binary_dependencies(archive_path, dependencies, + require_verify, recursion_limit); }); }); break; case Archive::SRC: dependencies.record(Archive::Path(Archive::user(path), "/bin/", _architecture, "/", Archive::name(path), "/", - Archive::version(path))); + Archive::version(path)), require_verify); break; case Archive::RAW: - dependencies.record(path); + dependencies.record(path, require_verify); break; case Archive::IMAGE: @@ -363,13 +368,16 @@ void Depot_query::Main::_gen_index_for_arch(Xml_generator &xml, void Depot_query::Main::_query_index(Archive::User const &user, Archive::Version const &version, - bool const content, Xml_generator &xml) + bool const content, + Require_verify const require_verify, + Xml_generator &xml) { Directory::Path const index_path("depot/", user, "/index/", version); if (!_root.file_exists(index_path)) { xml.node("missing", [&] () { xml.attribute("user", user); xml.attribute("version", version); + require_verify.gen_attr(xml); }); return; } @@ -377,6 +385,7 @@ void Depot_query::Main::_query_index(Archive::User const &user, xml.node("index", [&] () { xml.attribute("user", user); xml.attribute("version", version); + require_verify.gen_attr(xml); if (content) { try { @@ -392,9 +401,10 @@ void Depot_query::Main::_query_index(Archive::User const &user, } -void Depot_query::Main::_query_image(Archive::User const &user, - Archive::Name const &name, - Xml_generator &xml) +void Depot_query::Main::_query_image(Archive::User const &user, + Archive::Name const &name, + Require_verify const require_verify, + Xml_generator &xml) { Directory::Path const image_path("depot/", user, "/image/", name); char const *node_type = _root.directory_exists(image_path) @@ -402,6 +412,7 @@ void Depot_query::Main::_query_image(Archive::User const &user, xml.node(node_type, [&] () { xml.attribute("user", user); xml.attribute("name", name); + require_verify.gen_attr(xml); }); } diff --git a/repos/gems/src/app/depot_query/main.h b/repos/gems/src/app/depot_query/main.h index f60449a741..603b1fb0da 100644 --- a/repos/gems/src/app/depot_query/main.h +++ b/repos/gems/src/app/depot_query/main.h @@ -32,6 +32,7 @@ namespace Depot_query { typedef String<64> Rom_label; + struct Require_verify; struct Directory_cache; struct Recursion_limit; struct Dependencies; @@ -39,6 +40,26 @@ namespace Depot_query { } +/** + * Argument type for propagating 'require_verify' query attributes to results + */ +struct Depot_query::Require_verify +{ + bool value; + + static Require_verify from_xml(Xml_node const &node) + { + return Require_verify { node.attribute_value("require_verify", true) }; + } + + void gen_attr(Xml_generator &xml) const + { + if (!value) + xml.attribute("require_verify", "no"); + } +}; + + struct Depot_query::Directory_cache : Noncopyable { Allocator &_alloc; @@ -165,11 +186,28 @@ class Depot_query::Dependencies { private: + using Path = Archive::Path; + struct Collection : Noncopyable { Allocator &_alloc; - typedef Registered_no_delete Entry; + struct Dependency + { + Path path; + Require_verify require_verify; + + Dependency(Path const &path, Require_verify require_verify) + : path(path), require_verify(require_verify) { } + + void gen_attr(Xml_generator &xml) const + { + xml.attribute("path", path); + require_verify.gen_attr(xml); + } + }; + + using Entry = Registered_no_delete; Registry _entries { }; @@ -180,20 +218,20 @@ class Depot_query::Dependencies _entries.for_each([&] (Entry &e) { destroy(_alloc, &e); }); } - bool known(Archive::Path const &path) const + bool known(Path const &path) const { bool result = false; _entries.for_each([&] (Entry const &entry) { - if (path == entry) + if (path == entry.path) result = true; }); return result; } - void insert(Archive::Path const &path) + void insert(Path const &path, Require_verify require_verify) { if (!known(path)) - new (_alloc) Entry(_entries, path); + new (_alloc) Entry(_entries, path, require_verify); } template @@ -212,26 +250,26 @@ class Depot_query::Dependencies _depot(depot), _present(alloc), _missing(alloc) { } - bool known(Archive::Path const &path) const + bool known(Path const &path) const { return _present.known(path) || _missing.known(path); } - void record(Archive::Path const &path) + void record(Path const &path, Require_verify require_verify) { if (_depot.directory_exists(path)) - _present.insert(path); + _present.insert(path, require_verify); else - _missing.insert(path); + _missing.insert(path, require_verify); } void xml(Xml_generator &xml) const { - _present.for_each([&] (Archive::Path const &path) { - xml.node("present", [&] () { xml.attribute("path", path); }); }); + _present.for_each([&] (Collection::Entry const &entry) { + xml.node("present", [&] () { entry.gen_attr(xml); }); }); - _missing.for_each([&] (Archive::Path const &path) { - xml.node("missing", [&] () { xml.attribute("path", path); }); }); + _missing.for_each([&] (Collection::Entry const &entry) { + xml.node("missing", [&] () { entry.gen_attr(xml); }); }); } }; @@ -336,15 +374,15 @@ struct Depot_query::Main void _gen_inherited_rom_path_nodes(Xml_generator &, Xml_node const &, Archive::Path const &, Recursion_limit); void _query_blueprint(Directory::Path const &, Xml_generator &); - void _collect_source_dependencies(Archive::Path const &, Dependencies &, Recursion_limit); - void _collect_binary_dependencies(Archive::Path const &, Dependencies &, Recursion_limit); + void _collect_source_dependencies(Archive::Path const &, Dependencies &, Require_verify, Recursion_limit); + void _collect_binary_dependencies(Archive::Path const &, Dependencies &, Require_verify, Recursion_limit); void _scan_user(Archive::User const &, Xml_generator &); void _query_user(Archive::User const &, Xml_generator &); void _gen_index_node_rec(Xml_generator &, Xml_node const &, unsigned) const; void _gen_index_for_arch(Xml_generator &, Xml_node const &) const; - void _query_index(Archive::User const &, Archive::Version const &, bool, Xml_generator &); - void _query_image(Archive::User const &, Archive::Name const &, Xml_generator &); - void _query_image_index(Xml_node const &, Xml_generator &); + void _query_index(Archive::User const &, Archive::Version const &, bool, Require_verify, Xml_generator &); + void _query_image(Archive::User const &, Archive::Name const &, Require_verify, Xml_generator &); + void _query_image_index(Xml_node const &, Require_verify, Xml_generator &); void _handle_config() { @@ -431,13 +469,16 @@ struct Depot_query::Main Dependencies dependencies(_heap, _depot_dir); query.for_each_sub_node("dependencies", [&] (Xml_node node) { - Archive::Path const path = node.attribute_value("path", Archive::Path()); + Archive::Path const path = node.attribute_value("path", Archive::Path()); + Require_verify const require_verify = Require_verify::from_xml(node); if (node.attribute_value("source", false)) - _collect_source_dependencies(path, dependencies, Recursion_limit{8}); + _collect_source_dependencies(path, dependencies, require_verify, + Recursion_limit{8}); if (node.attribute_value("binary", false)) - _collect_binary_dependencies(path, dependencies, Recursion_limit{8}); + _collect_binary_dependencies(path, dependencies, require_verify, + Recursion_limit{8}); }); dependencies.xml(xml); }); @@ -457,17 +498,19 @@ struct Depot_query::Main _query_index(node.attribute_value("user", Archive::User()), node.attribute_value("version", Archive::Version()), node.attribute_value("content", false), + Require_verify::from_xml(node), xml); }); }); _gen_versioned_report(_image_reporter, version, [&] (Xml_generator &xml) { query.for_each_sub_node("image", [&] (Xml_node node) { _query_image(node.attribute_value("user", Archive::User()), node.attribute_value("name", Archive::Name()), + Require_verify::from_xml(node), xml); }); }); _gen_versioned_report(_image_index_reporter, version, [&] (Xml_generator &xml) { query.for_each_sub_node("image_index", [&] (Xml_node node) { - _query_image_index(node, xml); }); }); + _query_image_index(node, Require_verify::from_xml(node), xml); }); }); } Main(Env &env) : _env(env) diff --git a/repos/gems/src/app/depot_query/query_image_index.cc b/repos/gems/src/app/depot_query/query_image_index.cc index 22cbca80d8..7a19565e1f 100644 --- a/repos/gems/src/app/depot_query/query_image_index.cc +++ b/repos/gems/src/app/depot_query/query_image_index.cc @@ -19,6 +19,7 @@ void Depot_query::Main::_query_image_index(Xml_node const &index_query, + Require_verify require_verify, Xml_generator &xml) { using User = Archive::User; @@ -150,7 +151,9 @@ void Depot_query::Main::_query_image_index(Xml_node const &index_query, * file. */ xml.node(index_exists ? "present" : "missing", [&] () { - xml.attribute("user", user); }); + xml.attribute("user", user); + require_verify.gen_attr(xml); + }); /* * Report aggregated image information with the newest version first.