From 1ead0ea3a73255202ba608f44291340b5bb6fbab Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 12 Mar 2019 15:58:32 +0100 Subject: [PATCH] sculpt: allow for removal of index files By clicking on a yellow checkbox in the depot selection dialog, the corresponding index files are removed. This way, index files can be update by removing and downloading them again. This patch also filters out sculpt-managed components from the graph to avoid erratic graph-position changes while the '+' menu is open. Fixes #3193 --- repos/gems/recipes/pkg/sculpt/archives | 1 + repos/gems/src/app/sculpt_manager/graph.h | 4 + repos/gems/src/app/sculpt_manager/main.cc | 53 +++++++++ .../sculpt_manager/model/child_exit_state.h | 4 + .../app/sculpt_manager/model/download_queue.h | 2 +- .../model/file_operation_queue.h | 107 ++++++++++++++++++ repos/gems/src/app/sculpt_manager/runtime.cc | 1 + repos/gems/src/app/sculpt_manager/runtime.h | 5 + .../src/app/sculpt_manager/runtime/fs_tool.cc | 61 ++++++++++ .../app/sculpt_manager/view/popup_dialog.cc | 2 + .../app/sculpt_manager/view/popup_dialog.h | 6 + 11 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 repos/gems/src/app/sculpt_manager/model/file_operation_queue.h create mode 100644 repos/gems/src/app/sculpt_manager/runtime/fs_tool.cc diff --git a/repos/gems/recipes/pkg/sculpt/archives b/repos/gems/recipes/pkg/sculpt/archives index 02eb34d20f..dcc769efb1 100644 --- a/repos/gems/recipes/pkg/sculpt/archives +++ b/repos/gems/recipes/pkg/sculpt/archives @@ -46,3 +46,4 @@ _/src/menu_view _/src/gpt_write _/src/sculpt_manager _/src/fs_query +_/src/fs_tool diff --git a/repos/gems/src/app/sculpt_manager/graph.h b/repos/gems/src/app/sculpt_manager/graph.h index eb114926ea..4803f0f338 100644 --- a/repos/gems/src/app/sculpt_manager/graph.h +++ b/repos/gems/src/app/sculpt_manager/graph.h @@ -135,6 +135,10 @@ struct Sculpt::Graph /* omit sculpt's helpers from the graph */ bool const blacklisted = (name == "runtime_view" || name == "launcher_query" + || name == "update" + || name == "fs_tool" + || name == "depot_rw" + || name == "public_rw" || name == "depot_rom" || name == "dynamic_depot_rom" || name == "depot_query"); diff --git a/repos/gems/src/app/sculpt_manager/main.cc b/repos/gems/src/app/sculpt_manager/main.cc index ab8650a510..77e8c9e0c8 100644 --- a/repos/gems/src/app/sculpt_manager/main.cc +++ b/repos/gems/src/app/sculpt_manager/main.cc @@ -24,6 +24,7 @@ /* local includes */ #include #include +#include #include #include #include @@ -50,6 +51,8 @@ struct Sculpt::Main : Input_event_handler, Heap _heap { _env.ram(), _env.rm() }; + Sculpt_version const _sculpt_version { _env }; + Constructible _nitpicker { }; Signal_handler
_input_handler { @@ -188,6 +191,10 @@ struct Sculpt::Main : Input_event_handler, Download_queue _download_queue { _heap }; + File_operation_queue _file_operation_queue { _heap }; + + Fs_tool_version _fs_tool_version { 0 }; + /***************** ** Depot query ** @@ -597,6 +604,21 @@ struct Sculpt::Main : Input_event_handler, generate_runtime_config(); } + void remove_index(Depot::Archive::User const &user) override + { + auto remove = [&] (Path const &path) { + _file_operation_queue.remove_file(path); }; + + remove(Path("/rw/depot/", user, "/index/", _sculpt_version)); + remove(Path("/rw/public/", user, "/index/", _sculpt_version, ".xz")); + remove(Path("/rw/public/", user, "/index/", _sculpt_version, ".xz.sig")); + + if (!_file_operation_queue.any_operation_in_progress()) + _file_operation_queue.schedule_next_operations(); + + generate_runtime_config(); + } + /** * Popup_dialog::Construction_info interface */ @@ -1146,6 +1168,30 @@ void Sculpt::Main::_handle_runtime_state() } } + /* schedule pending file operations to new fs_tool instance */ + { + Child_exit_state exit_state(state, "fs_tool"); + + if (exit_state.exited) { + + Child_exit_state::Version const expected_version(_fs_tool_version.value); + + if (exit_state.version == expected_version) { + + _file_operation_queue.schedule_next_operations(); + _fs_tool_version.value++; + reconfigure_runtime = true; + + /* + * The removal of an index file may have completed, re-query index + * files to reflect this change at the depot selection menu. + */ + if (_popup_dialog.interested_in_file_operations()) + trigger_depot_query(); + } + } + } + /* upgrade RAM and cap quota on demand */ state.for_each_sub_node("child", [&] (Xml_node child) { @@ -1243,6 +1289,13 @@ void Sculpt::Main::_generate_runtime_config(Xml_generator &xml) const chroot("depot", "/depot", READ_ONLY); } + /* execute file operations */ + if (_storage._sculpt_partition.valid()) + if (_file_operation_queue.any_operation_in_progress()) + xml.node("start", [&] () { + gen_fs_tool_start_content(xml, _fs_tool_version, + _file_operation_queue); }); + _network.gen_runtime_start_nodes(xml); if (_update_running()) diff --git a/repos/gems/src/app/sculpt_manager/model/child_exit_state.h b/repos/gems/src/app/sculpt_manager/model/child_exit_state.h index 7302f815fd..a3465250bd 100644 --- a/repos/gems/src/app/sculpt_manager/model/child_exit_state.h +++ b/repos/gems/src/app/sculpt_manager/model/child_exit_state.h @@ -29,12 +29,16 @@ struct Sculpt::Child_exit_state int code = 0; typedef String<64> Name; + typedef String<16> Version; + + Version version { }; Child_exit_state(Xml_node init_state, Name const &name) { init_state.for_each_sub_node("child", [&] (Xml_node child) { if (child.attribute_value("name", Name()) == name) { exists = true; + version = child.attribute_value("version", Version()); if (child.has_attribute("exited")) { exited = true; code = child.attribute_value("exited", 0L); diff --git a/repos/gems/src/app/sculpt_manager/model/download_queue.h b/repos/gems/src/app/sculpt_manager/model/download_queue.h index 81f373b642..c7a2d96141 100644 --- a/repos/gems/src/app/sculpt_manager/model/download_queue.h +++ b/repos/gems/src/app/sculpt_manager/model/download_queue.h @@ -122,4 +122,4 @@ struct Sculpt::Download_queue : Noncopyable } }; -#endif /* _MODEL__ROUTE_H_ */ +#endif /* _MODEL__DOWNLOAD_QUEUE_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/model/file_operation_queue.h b/repos/gems/src/app/sculpt_manager/model/file_operation_queue.h new file mode 100644 index 0000000000..5c1934de19 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/model/file_operation_queue.h @@ -0,0 +1,107 @@ +/* + * \brief List of file operations that are currently in flight + * \author Norman Feske + * \date 2019-03-12 + */ + +/* + * Copyright (C) 2019 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__FILE_OPERATION_QUEUE_H_ +#define _MODEL__FILE_OPERATION_QUEUE_H_ + +#include +#include + +namespace Sculpt { struct File_operation_queue; } + + +struct Sculpt::File_operation_queue : Noncopyable +{ + struct Operation : Interface + { + enum class State { + PENDING, /* scheduled for next fs_tool instance */ + IN_PROGRESS /* processed by current fs_tool instance */ + }; + + State state { State::PENDING }; + + enum class Type { REMOVE_FILE } type; + + Path const path; + + Operation(Type type, Path const &path) : type(type), path(path) { } + + void gen_fs_tool_config(Xml_generator &xml) const + { + if (state != State::IN_PROGRESS) + return; + + xml.node("remove-file", [&] () { + xml.attribute("path", path); }); + } + }; + + Allocator &_alloc; + + Registry > _operations { }; + + File_operation_queue(Allocator &alloc) : _alloc(alloc) { } + + void remove_file(Path const &path) + { + bool already_exists = false; + _operations.for_each([&] (Operation const &operation) { + if (operation.type == Operation::Type::REMOVE_FILE && operation.path == path) + already_exists = true; }); + + if (already_exists) + return; + + new (_alloc) Registered(_operations, Operation::Type::REMOVE_FILE, path); + } + + bool any_operation_in_progress() const + { + bool any_in_progress = false; + _operations.for_each([&] (Operation const &operation) { + if (operation.state == Operation::State::IN_PROGRESS) + any_in_progress = true; }); + + return any_in_progress; + } + + void schedule_next_operations() + { + /* + * All operations that were in progress are complete. So they can + * be removed from the queue. All pending operations become the + * operations-in-progress in the next iteration. + */ + _operations.for_each([&] (Registered &operation) { + switch (operation.state) { + + case Operation::State::IN_PROGRESS: + destroy(_alloc, &operation); + break; + + case Operation::State::PENDING: + operation.state = Operation::State::IN_PROGRESS; + break; + } + }); + } + + void gen_fs_tool_config(Xml_generator &xml) const + { + _operations.for_each([&] (Operation const &operation) { + operation.gen_fs_tool_config(xml); }); + } +}; + +#endif /* _MODEL__FILE_OPERATION_QUEUE_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/runtime.cc b/repos/gems/src/app/sculpt_manager/runtime.cc index b1a5675626..54f4b359fc 100644 --- a/repos/gems/src/app/sculpt_manager/runtime.cc +++ b/repos/gems/src/app/sculpt_manager/runtime.cc @@ -29,3 +29,4 @@ #include #include #include +#include diff --git a/repos/gems/src/app/sculpt_manager/runtime.h b/repos/gems/src/app/sculpt_manager/runtime.h index 1b67410673..eb37adedc6 100644 --- a/repos/gems/src/app/sculpt_manager/runtime.h +++ b/repos/gems/src/app/sculpt_manager/runtime.h @@ -73,6 +73,11 @@ namespace Sculpt { struct Prepare_version { unsigned value; }; void gen_prepare_start_content(Xml_generator &, Prepare_version); + struct Fs_tool_version { unsigned value; }; + struct File_operation_queue; + void gen_fs_tool_start_content(Xml_generator &, Fs_tool_version, + File_operation_queue const &); + void gen_ram_fs_start_content(Xml_generator &, Ram_fs_state const &); void gen_update_start_content(Xml_generator &); diff --git a/repos/gems/src/app/sculpt_manager/runtime/fs_tool.cc b/repos/gems/src/app/sculpt_manager/runtime/fs_tool.cc new file mode 100644 index 0000000000..fdf54ecbea --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/runtime/fs_tool.cc @@ -0,0 +1,61 @@ +/* + * \brief XML configuration for fs_tool + * \author Norman Feske + * \date 2018-05-08 + */ + +/* + * 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. + */ + +#include +#include + +void Sculpt::gen_fs_tool_start_content(Xml_generator &xml, Fs_tool_version version, + File_operation_queue const &operations) +{ + xml.attribute("version", version.value); + + gen_common_start_content(xml, "fs_tool", Cap_quota{200}, Ram_quota{5*1024*1024}); + + gen_named_node(xml, "binary", "fs_tool"); + + xml.node("config", [&] () { + + xml.attribute("exit", "yes"); + xml.attribute("verbose", "yes"); + + xml.node("vfs", [&] () { + + gen_named_node(xml, "dir", "rw", [&] () { + xml.node("fs", [&] () { xml.attribute("label", "target"); }); }); + + gen_named_node(xml, "dir", "config", [&] () { + xml.node("fs", [&] () { xml.attribute("label", "config"); }); }); + }); + + operations.gen_fs_tool_config(xml); + }); + + xml.node("route", [&] () { + + gen_service_node<::File_system::Session>(xml, [&] () { + xml.attribute("label", "target"); + gen_named_node(xml, "child", "default_fs_rw"); }); + + gen_parent_rom_route(xml, "fs_tool"); + gen_parent_rom_route(xml, "ld.lib.so"); + gen_parent_rom_route(xml, "vfs.lib.so"); + gen_parent_route (xml); + gen_parent_route (xml); + gen_parent_route (xml); + gen_parent_route (xml); + + gen_service_node<::File_system::Session>(xml, [&] () { + xml.attribute("label", "config"); + xml.node("parent", [&] () { xml.attribute("label", "config"); }); }); + }); +} diff --git a/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc b/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc index 15eeec1c61..61e5faad1e 100644 --- a/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc +++ b/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc @@ -307,6 +307,8 @@ void Popup_dialog::click(Action &action) if (!_index_avail(clicked)) action.trigger_download(_index_path(clicked)); + else + action.remove_index(clicked); } } diff --git a/repos/gems/src/app/sculpt_manager/view/popup_dialog.h b/repos/gems/src/app/sculpt_manager/view/popup_dialog.h index 36c6eb0d64..8f290e4e2c 100644 --- a/repos/gems/src/app/sculpt_manager/view/popup_dialog.h +++ b/repos/gems/src/app/sculpt_manager/view/popup_dialog.h @@ -98,6 +98,7 @@ struct Sculpt::Popup_dialog virtual void launch_construction() = 0; virtual void trigger_download(Path const &) = 0; + virtual void remove_index(Depot::Archive::User const &) = 0; }; Construction_info const &_construction_info; @@ -481,6 +482,11 @@ struct Sculpt::Popup_dialog return _state >= PKG_REQUESTED && _pkg_missing; } + + bool interested_in_file_operations() const + { + return _state == DEPOT_SELECTION; + } }; #endif /* _VIEW__POPUP_DIALOG_H_ */