From 5b7a89caa953bd6c8d3b315d83380883c37de450 Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Tue, 2 Jan 2024 14:22:19 +0200 Subject: [PATCH] storage: Show partition types symbolically and allow editing --- pkg/storaged/partitions/partition.jsx | 89 ++++++++++++++++++++++++++- test/reference | 2 +- test/verify/check-storage-partitions | 62 +++++++++++++++++++ 3 files changed, 149 insertions(+), 4 deletions(-) diff --git a/pkg/storaged/partitions/partition.jsx b/pkg/storaged/partitions/partition.jsx index 5f2be9174bb4..5bb5cab85643 100644 --- a/pkg/storaged/partitions/partition.jsx +++ b/pkg/storaged/partitions/partition.jsx @@ -25,8 +25,12 @@ import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js"; import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js"; -import { StorageButton } from "../storage-controls.jsx"; -import { dialog_open, init_active_usage_processes, BlockingMessage, TeardownMessage } from "../dialog.jsx"; +import { StorageButton, StorageLink } from "../storage-controls.jsx"; +import { + dialog_open, + SelectOne, TextInput, + init_active_usage_processes, BlockingMessage, TeardownMessage +} from "../dialog.jsx"; import { block_name, fmt_size, get_active_usage, teardown_active_usage, reload_systemd } from "../utils.js"; import { check_unused_space, get_resize_info, free_space_after_part, grow_dialog, shrink_dialog } from "../block/resize.jsx"; import { StorageCard, StorageDescription, new_card, navigate_away_from_card } from "../pages.jsx"; @@ -106,9 +110,28 @@ export function make_partition_card(next, block) { return card; } +const gpt_type_names = { + "21686148-6449-6e6f-744e-656564454649": _("BIOS boot partition"), + "c12a7328-f81f-11d2-ba4b-00a0c93ec93b": _("EFI system partition"), + "9e1a2d38-c612-4316-aa26-8b49521e5a8b": _("PowerPC PReP boot partition"), + "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f": _("Linux swap space"), + "0fc63daf-8483-4772-8e79-3d69d8477de4": _("Linux filesystem data"), + "e6d6d379-f507-44c2-a23c-238f2a3df928": _("Logical Volume Manager partition"), +}; + +const mbr_type_names = { + ef: _("EFI system partition"), + 82: _("Linux swap space"), + 83: _("Linux filesystem data"), + "8e": _("Logical Volume Manager partition"), +}; + const PartitionCard = ({ card, block, unused_space_warning, resize_info }) => { const block_part = client.blocks_part[block.path]; const unused_space = !!unused_space_warning; + const block_ptable = client.blocks_ptable[block_part.Table]; + const type_names = block_ptable?.Type == "dos" ? mbr_type_names : gpt_type_names; + const type = block_part.Type.replace(/^0x/, "").toLowerCase(); function shrink_to_fit() { return shrink_dialog(client, block_part, resize_info, true); @@ -118,6 +141,62 @@ const PartitionCard = ({ card, block, unused_space_warning, resize_info }) => { return grow_dialog(client, block_part, resize_info, true); } + function set_type_dialog() { + const choices = Object.keys(type_names).map(k => ({ value: k, title: type_names[k] })); + choices.push({ title: _("Custom"), value: "custom" }); + + function validate_type(val) { + if (block_ptable.Type == "dos") { + const hex_rx = /^[a-fA-F0-9]{2}$/; + if (!hex_rx.test(val)) + return _("Type must contain exactly two hexadecimal characters (0 to 9, A to F)."); + } else { + /* We let people use any 128 bit value as a UUID, even + those that are not defined by RFC 4122. This is + what the rest of the storage stack does as well, + and the "BIOS boot" UUID is in fact invalid + according to RFC 4122. + + But we do insist that the dashes are in the right + place, because UDisks2 does as well. + */ + const uuid_rx_1 = /^[a-fA-F0-9-]*$/; + if (!uuid_rx_1.test(val)) + return _("Type can only contain the characters 0 to 9, A to F, and \"-\"."); + const uuid_rx_2 = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/; + if (!uuid_rx_2.test(val)) + return _("Type must be of the form NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN."); + } + } + + dialog_open({ + Title: cockpit.format(_("Set partition type of $0"), block_name(block)), + Fields: [ + SelectOne("type", _("Type"), + { + value: (type_names[type] ? type : "custom"), + choices, + }), + TextInput("custom", _("Custom type"), + { + value: type, + validate: validate_type, + visible: vals => vals.type == "custom", + }), + ], + Action: { + Danger: !client.in_anaconda_mode() && _("Changing partition types might prevent the system from booting."), + Title: _("Save"), + action: async function (vals) { + let t = vals.type == "custom" ? vals.custom : vals.type; + if (block_ptable?.Type == "dos") + t = "0x" + t; + await block_part.SetType(t, { }); + } + }, + }); + } + return ( { - + + {_("edit")} + } /> { !unused_space && } diff --git a/test/reference b/test/reference index 23d27828e014..5ec337bfa326 160000 --- a/test/reference +++ b/test/reference @@ -1 +1 @@ -Subproject commit 23d27828e014d9c20765b13d47d408ef5d6e1369 +Subproject commit 5ec337bfa326b33c20deee8e7ca7e7c4021a6212 diff --git a/test/verify/check-storage-partitions b/test/verify/check-storage-partitions index 3dadf3046da3..4e6ca66d8c07 100755 --- a/test/verify/check-storage-partitions +++ b/test/verify/check-storage-partitions @@ -173,6 +173,68 @@ class TestStoragePartitions(storagelib.StorageCase): b.wait_visible(self.card_button("Partition", "Grow") + ":disabled") b.wait_in_text(self.card_desc("Partition", "Size"), "103 MB") + def testType(self): + b = self.browser + + self.login_and_go("/storage") + + disk = self.add_ram_disk(100) + self.click_card_row("Storage", name=disk) + + # GPT + + self.click_card_dropdown("Solid State Drive", "Create partition table") + self.dialog({"type": "gpt"}) + b.wait_text(self.card_row_col("GPT partitions", 1, 1), "Free space") + + self.click_dropdown(self.card_row("GPT partitions", 1), "Create partition") + self.dialog({"type": "empty"}) + + self.click_card_row("GPT partitions", 1) + b.wait_text(self.card_desc("Partition", "Type"), "Linux filesystem data") + b.click(self.card_desc_action("Partition", "Type")) + self.dialog({"type": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"}) + b.wait_text(self.card_desc("Partition", "Type"), "EFI system partition") + b.click(self.card_desc_action("Partition", "Type")) + self.dialog_wait_open() + self.dialog_set_val("type", "custom") + self.dialog_set_val("custom", "bla bla") + self.dialog_apply() + self.dialog_wait_error("custom", "Type can only contain the characters 0 to 9, A to F, and \"-\".") + self.dialog_set_val("custom", "7D0359A3-02B3-4F0A865C-654403E70625") + self.dialog_apply() + self.dialog_wait_error("custom", "Type must be of the form NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN.") + self.dialog_set_val("custom", "7D0359A3-02B3-4F0A-865C-654403E70625") + self.dialog_apply() + self.dialog_wait_close() + b.wait_text(self.card_desc("Partition", "Type"), "7d0359a3-02b3-4f0a-865c-654403e70625") + + # DOS + + b.click(self.card_parent_link()) + self.click_card_dropdown("Solid State Drive", "Create partition table") + self.dialog({"type": "dos"}) + b.wait_text(self.card_row_col("DOS partitions", 1, 1), "Free space") + + self.click_dropdown(self.card_row("DOS partitions", 1), "Create partition") + self.dialog({"size": 100, "type": "empty"}) + + self.click_card_row("DOS partitions", 1) + b.wait_text(self.card_desc("Partition", "Type"), "Linux filesystem data") + b.click(self.card_desc_action("Partition", "Type")) + self.dialog({"type": "ef"}) + b.wait_text(self.card_desc("Partition", "Type"), "EFI system partition") + b.click(self.card_desc_action("Partition", "Type")) + self.dialog_wait_open() + self.dialog_set_val("type", "custom") + self.dialog_set_val("custom", "bla bla") + self.dialog_apply() + self.dialog_wait_error("custom", "Type must contain exactly two hexadecimal characters (0 to 9, A to F).") + self.dialog_set_val("custom", "C8") + self.dialog_apply() + self.dialog_wait_close() + b.wait_text(self.card_desc("Partition", "Type"), "c8") + if __name__ == '__main__': testlib.test_main()