summaryrefslogtreecommitdiff
path: root/Userland/Applications
diff options
context:
space:
mode:
authorSamuel Bowman <sam@sambowman.tech>2022-06-27 22:48:30 -0400
committerLinus Groh <mail@linusgroh.de>2022-07-21 20:13:44 +0100
commit7a8953a833e5243df4809ae8b9bffc3a1bab10e1 (patch)
treedcb516fe7fb75f437061ba54716fab31dd401558 /Userland/Applications
parent7b8088c78d66f4d4a551c626971276d148907820 (diff)
downloadserenity-7a8953a833e5243df4809ae8b9bffc3a1bab10e1.zip
PartitionEditor: Add the beginnings of a partition editor :^)
This adds a new application PartitionEditor which will eventually be used to create and edit partition tables. Since LibPartition does not know how to write partition tables yet, it is currently read-only. Devices are discovered by scanning /dev for block device files. Since block devices are chmod 600, PartitionEditor be must run as root. By default Serenity uses the entire disk for the ext2 filesystem without a partition table. This isn't useful for testing as the partition list for the default disk will be empty. To test properly, I created a few disk images using various partitioning schemes (MBR, EBR, and GPT) and attached them using the following command: export SERENITY_EXTRA_QEMU_ARGS=" -drive file=/path/to/mbr.img,format=raw,index=1,media=disk -drive file=/path/to/ebr.img,format=raw,index=2,media=disk -drive file=/path/to/gpt.img,format=raw,index=3,media=disk"
Diffstat (limited to 'Userland/Applications')
-rw-r--r--Userland/Applications/CMakeLists.txt1
-rw-r--r--Userland/Applications/PartitionEditor/CMakeLists.txt15
-rw-r--r--Userland/Applications/PartitionEditor/PartitionEditorWindow.gml26
-rw-r--r--Userland/Applications/PartitionEditor/PartitionModel.cpp88
-rw-r--r--Userland/Applications/PartitionEditor/PartitionModel.h39
-rw-r--r--Userland/Applications/PartitionEditor/main.cpp83
6 files changed, 252 insertions, 0 deletions
diff --git a/Userland/Applications/CMakeLists.txt b/Userland/Applications/CMakeLists.txt
index 58a21bd8d9..04c4046326 100644
--- a/Userland/Applications/CMakeLists.txt
+++ b/Userland/Applications/CMakeLists.txt
@@ -23,6 +23,7 @@ add_subdirectory(Mail)
add_subdirectory(MailSettings)
add_subdirectory(MouseSettings)
add_subdirectory(NetworkSettings)
+add_subdirectory(PartitionEditor)
add_subdirectory(PDFViewer)
add_subdirectory(Piano)
add_subdirectory(PixelPaint)
diff --git a/Userland/Applications/PartitionEditor/CMakeLists.txt b/Userland/Applications/PartitionEditor/CMakeLists.txt
new file mode 100644
index 0000000000..67b9d19543
--- /dev/null
+++ b/Userland/Applications/PartitionEditor/CMakeLists.txt
@@ -0,0 +1,15 @@
+serenity_component(
+ PartitionEditor
+ TARGETS PartitionEditor
+)
+
+compile_gml(PartitionEditorWindow.gml PartitionEditorWindowGML.h partition_editor_window_gml)
+
+set(SOURCES
+ main.cpp
+ PartitionEditorWindowGML.h
+ PartitionModel.cpp
+)
+
+serenity_app(PartitionEditor ICON app-space-analyzer)
+target_link_libraries(PartitionEditor LibMain LibGUI LibPartition)
diff --git a/Userland/Applications/PartitionEditor/PartitionEditorWindow.gml b/Userland/Applications/PartitionEditor/PartitionEditorWindow.gml
new file mode 100644
index 0000000000..fb1341be0e
--- /dev/null
+++ b/Userland/Applications/PartitionEditor/PartitionEditorWindow.gml
@@ -0,0 +1,26 @@
+@GUI::Widget {
+ fill_with_background_color: true
+ layout: @GUI::VerticalBoxLayout {}
+
+ @GUI::ToolbarContainer {
+ @GUI::Toolbar {
+ layout: @GUI::HorizontalBoxLayout {
+ margins: [0, 4]
+ }
+
+ @GUI::Label {
+ text: "Device: "
+ autosize: true
+ }
+
+ @GUI::ComboBox {
+ name: "device_combobox"
+ fixed_width: 100
+ }
+ }
+ }
+
+ @GUI::TableView {
+ name: "partition_table_view"
+ }
+}
diff --git a/Userland/Applications/PartitionEditor/PartitionModel.cpp b/Userland/Applications/PartitionEditor/PartitionModel.cpp
new file mode 100644
index 0000000000..d0dae293a8
--- /dev/null
+++ b/Userland/Applications/PartitionEditor/PartitionModel.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2022, Samuel Bowman <sam@sambowman.tech>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Applications/PartitionEditor/PartitionModel.h>
+#include <LibPartition/EBRPartitionTable.h>
+#include <LibPartition/GUIDPartitionTable.h>
+#include <LibPartition/MBRPartitionTable.h>
+
+namespace PartitionEditor {
+
+String PartitionModel::column_name(int column) const
+{
+ switch (column) {
+ case Column::Partition:
+ return "Partition";
+ case Column::StartBlock:
+ return "Start Block";
+ case Column::EndBlock:
+ return "End Block";
+ default:
+ VERIFY_NOT_REACHED();
+ }
+}
+
+GUI::Variant PartitionModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const
+{
+ if (role != GUI::ModelRole::Display)
+ return {};
+ if (!m_partition_table)
+ return {};
+
+ auto optional_partition = m_partition_table->partition(index.row());
+ if (optional_partition.has_value()) {
+ auto partition = optional_partition.release_value();
+
+ switch (index.column()) {
+ case Column::Partition:
+ return index.row() + 1;
+ case Column::StartBlock:
+ return partition.start_block();
+ case Column::EndBlock:
+ // FIXME: Either MBR end block is off by one (if supposed to be exclusive bound)
+ // or GPT end block is off by one (if supposed to be inclusive bound).
+ // This is an issue in LibPartition.
+ return partition.end_block();
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ }
+
+ return {};
+}
+
+ErrorOr<void> PartitionModel::set_device_path(String const& path)
+{
+ auto file = TRY(Core::File::open(path, Core::OpenMode::ReadOnly));
+
+ auto mbr_table_or_error = Partition::MBRPartitionTable::try_to_initialize(file);
+ if (!mbr_table_or_error.is_error()) {
+ dbgln("Found MBR partition table on {}", path);
+ m_partition_table = move(mbr_table_or_error.value());
+ invalidate();
+ return {};
+ }
+
+ auto ebr_table_or_error = Partition::EBRPartitionTable::try_to_initialize(file);
+ if (!ebr_table_or_error.is_error()) {
+ dbgln("Found EBR partition table on {}", path);
+ m_partition_table = move(ebr_table_or_error.value());
+ invalidate();
+ return {};
+ }
+
+ auto guid_table_or_error = Partition::GUIDPartitionTable::try_to_initialize(file);
+ if (!guid_table_or_error.is_error()) {
+ dbgln("Found GUID partition table on {}", path);
+ m_partition_table = move(guid_table_or_error.value());
+ invalidate();
+ return {};
+ }
+
+ return Error::from_errno(ENOTSUP);
+}
+
+}
diff --git a/Userland/Applications/PartitionEditor/PartitionModel.h b/Userland/Applications/PartitionEditor/PartitionModel.h
new file mode 100644
index 0000000000..65be6dac7f
--- /dev/null
+++ b/Userland/Applications/PartitionEditor/PartitionModel.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022, Samuel Bowman <sam@sambowman.tech>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGUI/Model.h>
+#include <LibPartition/PartitionTable.h>
+
+namespace PartitionEditor {
+
+class PartitionModel final : public GUI::Model {
+public:
+ enum Column {
+ Partition,
+ StartBlock,
+ EndBlock,
+ __Count,
+ };
+
+ static NonnullRefPtr<PartitionModel> create() { return adopt_ref(*new PartitionModel()); }
+ virtual ~PartitionModel() override = default;
+
+ virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return m_partition_table->partitions_count(); }
+ virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return Column::__Count; }
+ virtual String column_name(int) const override;
+ virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override;
+
+ ErrorOr<void> set_device_path(String const&);
+
+private:
+ PartitionModel() = default;
+
+ OwnPtr<Partition::PartitionTable> m_partition_table;
+};
+
+}
diff --git a/Userland/Applications/PartitionEditor/main.cpp b/Userland/Applications/PartitionEditor/main.cpp
new file mode 100644
index 0000000000..c8e53f4a27
--- /dev/null
+++ b/Userland/Applications/PartitionEditor/main.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2022, Samuel Bowman <sam@sambowman.tech>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Applications/PartitionEditor/PartitionEditorWindowGML.h>
+#include <Applications/PartitionEditor/PartitionModel.h>
+#include <LibCore/DirIterator.h>
+#include <LibCore/System.h>
+#include <LibGUI/Application.h>
+#include <LibGUI/ComboBox.h>
+#include <LibGUI/ItemListModel.h>
+#include <LibGUI/Menu.h>
+#include <LibGUI/MessageBox.h>
+#include <LibGUI/TableView.h>
+
+static Vector<String> get_device_paths()
+{
+ auto device_paths = Vector<String>();
+ Core::DirIterator iterator("/dev", Core::DirIterator::SkipParentAndBaseDir);
+ while (iterator.has_next()) {
+ auto path = iterator.next_full_path();
+ if (Core::File::is_block_device(path))
+ device_paths.append(path);
+ }
+ return device_paths;
+}
+
+ErrorOr<int> serenity_main(Main::Arguments arguments)
+{
+ TRY(Core::System::pledge("stdio recvfd sendfd rpath unix"));
+
+ auto app = TRY(GUI::Application::try_create(arguments));
+
+ TRY(Core::System::pledge("stdio recvfd sendfd rpath"));
+ TRY(Core::System::unveil("/dev", "r"));
+ TRY(Core::System::unveil("/res", "r"));
+ TRY(Core::System::unveil(nullptr, nullptr));
+
+ // FIXME: PartitionEditor needs its own icon.
+ auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-space-analyzer"sv));
+
+ auto window = TRY(GUI::Window::try_create());
+ window->set_title("Partition Editor");
+ window->resize(640, 400);
+ window->set_icon(app_icon.bitmap_for_size(16));
+
+ // FIXME: Abort and show a dialog if not running as root.
+
+ auto widget = TRY(window->try_set_main_widget<GUI::Widget>());
+ widget->load_from_gml(partition_editor_window_gml);
+
+ auto device_paths = get_device_paths();
+
+ auto partition_model = PartitionEditor::PartitionModel::create();
+ TRY(partition_model->set_device_path(device_paths.first()));
+
+ auto& device_combobox = *widget->find_descendant_of_type_named<GUI::ComboBox>("device_combobox");
+ device_combobox.set_model(GUI::ItemListModel<String>::create(device_paths));
+ device_combobox.set_only_allow_values_from_model(true);
+ device_combobox.set_selected_index(0);
+ device_combobox.on_change = [&](auto const& path, auto const&) {
+ auto result = partition_model->set_device_path(path);
+ if (result.is_error())
+ GUI::MessageBox::show_error(window, String::formatted("No partition table found for device {}", path));
+ };
+
+ auto& partition_table_view = *widget->find_descendant_of_type_named<GUI::TableView>("partition_table_view");
+ partition_table_view.set_model(partition_model);
+ partition_table_view.set_focus(true);
+
+ auto& file_menu = window->add_menu("&File");
+ file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) {
+ app->quit();
+ }));
+
+ auto help_menu = TRY(window->try_add_menu("&Help"));
+ TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Partition Editor", app_icon, window)));
+
+ window->show();
+ return app->exec();
+}