diff --git a/pkg/storaged/block/actions.jsx b/pkg/storaged/block/actions.jsx index 06f8abdb8faf..0128cbf1c266 100644 --- a/pkg/storaged/block/actions.jsx +++ b/pkg/storaged/block/actions.jsx @@ -21,7 +21,7 @@ import cockpit from "cockpit"; import client from "../client"; import { format_disk } from "./format-disk-dialog.jsx"; -import { format_dialog, add_encryption_dialog, encrypted_format_dialog } from "./format-dialog.jsx"; +import { format_dialog } from "./format-dialog.jsx"; import { erase_dialog } from "./erase-dialog.jsx"; const _ = cockpit.gettext; @@ -37,7 +37,7 @@ export function block_actions(block, kind) { if (kind != "crypto") { actions.push({ title: _("Add encryption"), - action: () => add_encryption_dialog(client, block), + action: () => format_dialog(block, { add_encryption: true }), excuse, }); } @@ -54,14 +54,14 @@ export function block_actions(block, kind) { if (kind == "crypto") { actions.push({ title: _("Format cleartext device"), - action: () => encrypted_format_dialog(client, block), + action: () => format_dialog(block, { is_encrypted: true }), primary: true, excuse, }); } else { actions.push({ title: _("Format as filesystem"), - action: () => format_dialog(client, block.path), + action: () => format_dialog(block), primary: true, excuse, }); diff --git a/pkg/storaged/block/format-dialog.jsx b/pkg/storaged/block/format-dialog.jsx index 6018038bdad2..3b3f11c98473 100644 --- a/pkg/storaged/block/format-dialog.jsx +++ b/pkg/storaged/block/format-dialog.jsx @@ -81,18 +81,6 @@ export function initial_mount_options(client, block) { return initial_tab_options(client, block, true); } -export function format_dialog(client, path, start, size, enable_dos_extended) { - return format_dialog_internal(client, client.blocks[path], { start, size, enable_dos_extended }); -} - -export function add_encryption_dialog(client, block) { - return format_dialog_internal(client, block, { add_encryption: true }); -} - -export function encrypted_format_dialog(client, block) { - return format_dialog_internal(client, block, { is_encrypted: true }); -} - function find_root_fsys_block() { const root = client.anaconda?.mount_point_prefix || "/"; for (const p in client.blocks) { @@ -104,14 +92,14 @@ function find_root_fsys_block() { return null; } -function format_dialog_internal(client, block, options) { - const { start, size, enable_dos_extended, add_encryption } = options; +export function format_dialog(block, options) { + const { free_spaces, enable_dos_extended, add_encryption } = options || { }; const is_already_encrypted = options.is_encrypted; const block_part = client.blocks_part[block.path]; const block_ptable = client.blocks_ptable[block.path] || client.blocks_ptable[block_part?.Table]; const content_block = block.IdUsage == "crypto" ? client.blocks_cleartext[block.path] : block; - const create_partition = (start !== undefined); + const create_partition = (free_spaces !== undefined); let title; if (add_encryption) @@ -282,18 +270,20 @@ function format_dialog_internal(client, block, options) { ]; } + let max_size = 0; + if (create_partition) + max_size = Math.max(...free_spaces.map(f => f.size)); + dialog_open({ Title: title, Teardown: TeardownMessage(usage), Fields: [ SizeSlider("size", _("Size"), { - value: size, - max: size, + value: max_size, + max: max_size, round: 1024 * 1024, - visible: function () { - return create_partition; - } + visible: () => create_partition, }), SelectOne("type", _("Type"), { @@ -497,6 +487,13 @@ function format_dialog_internal(client, block, options) { function format() { if (create_partition) { + let start = free_spaces[0].start; + for (const fs of free_spaces) { + if (fs.size >= vals.size) { + start = fs.start; + break; + } + } if (type == "dos-extended") return block_ptable.CreatePartition(start, vals.size, "0x05", "", { }); else diff --git a/pkg/storaged/partitions/partition-table.jsx b/pkg/storaged/partitions/partition-table.jsx index e2873b660fd0..3d9bbad5b744 100644 --- a/pkg/storaged/partitions/partition-table.jsx +++ b/pkg/storaged/partitions/partition-table.jsx @@ -32,7 +32,7 @@ import { make_partition_card, delete_partition } from "./partition.jsx"; const _ = cockpit.gettext; -function make_partition_pages(parent, block) { +function make_partition_pages(parent, block, partitions) { const block_ptable = client.blocks_ptable[block.path]; let counter = 0; @@ -45,8 +45,7 @@ function make_partition_pages(parent, block) { actions: [ { title: _("Create partition"), - action: () => format_dialog(client, block.path, start, size, - enable_dos_extended), + action: () => format_dialog(block, { free_spaces: [{ start, size }], enable_dos_extended }), } ], }); @@ -80,12 +79,24 @@ function make_partition_pages(parent, block) { } } - process_partitions(parent, get_partitions(client, block), - block_ptable.Type == 'dos'); + process_partitions(parent, partitions, block_ptable.Type == 'dos'); +} + +function get_free_spaces(partitions) { + let result = []; + for (const p of partitions) { + if (p.type == 'free') + result.push({ start: p.start, size: p.size }); + else if (p.type == 'container') + result = result.concat(get_free_spaces(p.partitions)); + } + return result; } export function make_partition_table_page(parent, block, next_card) { const block_ptable = client.blocks_ptable[block.path]; + const partitions = get_partitions(client, block); + const free_spaces = get_free_spaces(partitions); const parts_card = new_card({ title: (block_ptable.Type @@ -94,10 +105,18 @@ export function make_partition_table_page(parent, block, next_card) { next: next_card, component: PartitionsCard, props: { }, + actions: [ + { + title: _("Create partition"), + action: () => format_dialog(block, { free_spaces, enable_dos_extended: false }), + primary: true, + excuse: free_spaces.length == 0 ? _("No free space") : null, + }, + ], }); const p = new_page(parent, parts_card, { sorted: false }); - make_partition_pages(p, block); + make_partition_pages(p, block, partitions); } const PartitionsCard = ({ card }) => {