Skip to content

Commit

Permalink
storage: Show partition types symbolically and allow editing
Browse files Browse the repository at this point in the history
  • Loading branch information
mvollmer committed Jan 5, 2024
1 parent ea9a84d commit 5b7a89c
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 4 deletions.
89 changes: 86 additions & 3 deletions pkg/storaged/partitions/partition.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand All @@ -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 (
<StorageCard card={card}
alert={unused_space &&
Expand All @@ -139,7 +218,11 @@ const PartitionCard = ({ card, block, unused_space_warning, resize_info }) => {
<DescriptionList className="pf-m-horizontal-on-sm">
<StorageDescription title={_("Name")} value={block_part.Name || "-"} />
<StorageDescription title={_("UUID")} value={block_part.UUID} />
<StorageDescription title={_("Type")} value={block_part.Type} />
<StorageDescription title={_("Type")}
value={type_names[type] || type}
action={<StorageLink onClick={set_type_dialog}>
{_("edit")}
</StorageLink>} />
{ !unused_space &&
<StorageDescription title={_("Size")} value={fmt_size(block_part.Size)} />
}
Expand Down
62 changes: 62 additions & 0 deletions test/verify/check-storage-partitions
Original file line number Diff line number Diff line change
Expand Up @@ -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()

0 comments on commit 5b7a89c

Please sign in to comment.