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_ */