From 5803fbe5e3b9de8fec2b86530f09c4085c050e52 Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Mon, 8 Apr 2024 11:12:33 +0300 Subject: [PATCH] storage: Confirmation before erasing actual data But only in Anaconda mode. --- pkg/storaged/block/format-dialog.jsx | 4 +- pkg/storaged/block/resize.jsx | 6 +- pkg/storaged/btrfs/subvolume.jsx | 5 +- pkg/storaged/dialog.jsx | 137 ++++++++++++++---- pkg/storaged/filesystem/mounting-dialog.jsx | 4 +- pkg/storaged/fsys-is-empty.sh | 28 ++++ pkg/storaged/legacy-vdo/legacy-vdo.jsx | 6 +- pkg/storaged/lvm2/block-logical-volume.jsx | 6 +- pkg/storaged/lvm2/volume-group.jsx | 12 +- pkg/storaged/mdraid/mdraid.jsx | 6 +- .../partitions/format-disk-dialog.jsx | 4 +- pkg/storaged/partitions/partition.jsx | 4 +- pkg/storaged/stratis/filesystem.jsx | 4 +- pkg/storaged/stratis/pool.jsx | 4 +- pkg/storaged/utils.js | 10 +- test/reference | 2 +- test/verify/check-storage-anaconda | 78 ++++++++++ test/verify/check-storage-unrecognized | 2 +- 18 files changed, 256 insertions(+), 66 deletions(-) create mode 100644 pkg/storaged/fsys-is-empty.sh diff --git a/pkg/storaged/block/format-dialog.jsx b/pkg/storaged/block/format-dialog.jsx index 6d72f69a1c55..b575dc6b4def 100644 --- a/pkg/storaged/block/format-dialog.jsx +++ b/pkg/storaged/block/format-dialog.jsx @@ -32,7 +32,7 @@ import { dialog_open, TextInput, PassInput, CheckBoxes, SelectOne, SizeSlider, BlockingMessage, TeardownMessage, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { get_fstab_config, is_valid_mount_point } from "../filesystem/utils.jsx"; @@ -630,7 +630,7 @@ function format_dialog_internal(client, path, start, size, enable_dos_extended, } }, Inits: [ - init_active_usage_processes(client, usage), + init_teardown_usage(client, usage), unlock_before_format ? init_existing_passphrase(block, true, type => { existing_passphrase_type = type }) : null diff --git a/pkg/storaged/block/resize.jsx b/pkg/storaged/block/resize.jsx index 77cf023daf3f..34706c43f52f 100644 --- a/pkg/storaged/block/resize.jsx +++ b/pkg/storaged/block/resize.jsx @@ -31,7 +31,7 @@ import { } from "../crypto/keyslots.jsx"; import { dialog_open, SizeSlider, BlockingMessage, TeardownMessage, SelectSpaces, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { std_reply } from "../stratis/utils.jsx"; import { pvs_to_spaces } from "../lvm2/utils.jsx"; @@ -534,7 +534,7 @@ export function grow_dialog(client, lvol_or_part, info, to_fit) { } }, Inits: [ - init_active_usage_processes(client, usage), + init_teardown_usage(client, usage), passphrase_fields.length ? init_existing_passphrase(block, false, pp => { recovered_passphrase = pp }) : null @@ -647,7 +647,7 @@ export function shrink_dialog(client, lvol_or_part, info, to_fit) { } }, Inits: [ - init_active_usage_processes(client, usage), + init_teardown_usage(client, usage), passphrase_fields.length ? init_existing_passphrase(block, false, pp => { recovered_passphrase = pp }) : null diff --git a/pkg/storaged/btrfs/subvolume.jsx b/pkg/storaged/btrfs/subvolume.jsx index b776dae9390a..659e42a95fd7 100644 --- a/pkg/storaged/btrfs/subvolume.jsx +++ b/pkg/storaged/btrfs/subvolume.jsx @@ -36,7 +36,7 @@ import { btrfs_usage, validate_subvolume_name, parse_subvol_from_options } from import { at_boot_input, update_at_boot_input, mounting_dialog, mount_options } from "../filesystem/mounting-dialog.jsx"; import { dialog_open, TextInput, - TeardownMessage, init_active_usage_processes, + TeardownMessage, init_teardown_usage, } from "../dialog.jsx"; import { check_mismounted_fsys, MismountAlert } from "../filesystem/mismounting.jsx"; import { @@ -204,6 +204,7 @@ function subvolume_delete(volume, subvol, mount_point_in_parent, card) { const paths_to_delete = []; const usage = []; + usage.Teardown = true; for (const sv of all_subvols) { const [config, mount_point] = get_fstab_config_with_client(client, block, false, sv); const fs_is_mounted = is_mounted(client, block, sv); @@ -241,7 +242,7 @@ function subvolume_delete(volume, subvol, mount_point_in_parent, card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/dialog.jsx b/pkg/storaged/dialog.jsx index fe5f64f1390c..45ceeb45b060 100644 --- a/pkg/storaged/dialog.jsx +++ b/pkg/storaged/dialog.jsx @@ -240,10 +240,16 @@ import { show_modal_dialog, apply_modal_dialog } from "cockpit-components-dialog import { ListingTable } from "cockpit-components-table.jsx"; import { FormHelper } from "cockpit-components-form-helper"; -import { fmt_size, block_name, format_size_and_text, format_delay, for_each_async } from "./utils.js"; +import { + decode_filename, fmt_size, block_name, format_size_and_text, format_delay, for_each_async, + is_available_block +} from "./utils.js"; import { fmt_to_fragments } from "utils.jsx"; + import client from "./client.js"; +import fsys_is_empty_sh from "./fsys-is-empty.sh"; + const _ = cockpit.gettext; function make_rows(fields, values, errors, onChange) { @@ -328,6 +334,23 @@ const Body = ({ body, teardown, fields, values, errors, isFormHorizontal, onChan ); }; +const ExtraConfirmation = ({ title, text, confirm_text, onChange }) => { + const [confirmed, setConfirmed] = useState(false); + + return ( + + {text} + { + setConfirmed(val); + onChange(val); + }} /> + ); +}; + function flatten_fields(fields) { return fields.reduce( (acc, val) => acc.concat([val]).concat(val.options && val.options.nested_fields @@ -340,6 +363,8 @@ export const dialog_open = (def) => { const nested_fields = def.Fields || []; const fields = flatten_fields(nested_fields); const values = { }; + let confirmation = null; + let confirmed = false; let errors = null; fields.forEach(f => { values[f.tag] = f.initial_value }); @@ -415,8 +440,10 @@ export const dialog_open = (def) => { caption: variant.Title, style: actions.length == 0 ? "primary" : "secondary", danger: def.Action.Danger || def.Action.DangerButton, - disabled: running_promise != null || (def.Action.disable_on_error && - errors && errors.toString() != "[object Object]"), + disabled: (running_promise != null || + (def.Action.disable_on_error && + errors && errors.toString() != "[object Object]") || + (confirmation && !confirmed)), clicked: progress_callback => run_action(progress_callback, variant.tag), }); } @@ -436,13 +463,21 @@ export const dialog_open = (def) => { } } - const extra = ( -
- { def.Action && def.Action.Danger - ? {def.Action.Danger} - : null - } -
); + let extra = null; + if (confirmation) { + extra = { + confirmed = val; + update_footer(); + }} />; + } else if (def.Action && def.Action.Danger) { + extra = ( +
+ {def.Action.Danger} +
); + } return { idle_message: (running_promise @@ -537,6 +572,12 @@ export const dialog_open = (def) => { update(); }, + need_confirmation: (conf) => { + confirmation = conf; + confirmed = false; + update_footer(); + }, + close: () => { dlg.footerProps.dialog_done(); } @@ -1207,13 +1248,14 @@ const teardown_block_name = use => { }; export const TeardownMessage = (usage, expect_single_unmount) => { - if (usage.length == 0) + if (!usage.Teardown) return null; if (is_expected_unmount(usage, expect_single_unmount)) return ; const rows = []; + let have_data = false; usage.forEach((use, index) => { if (use.block) { const name = teardown_block_name(use); @@ -1223,12 +1265,22 @@ export const TeardownMessage = (usage, expect_single_unmount) => { if (location === false) location = _("(Not part of target)"); } + if (use.data_warning) + have_data = true; rows.push({ columns: [name, location || "-", use.actions.length ? use.actions.join(", ") : "-", { - title: , + title: <> + + { use.data_warning && + + { "\n" } + { use.data_warning } + + } + , props: { className: "pf-v5-u-text-align-right" } } ] @@ -1247,6 +1299,11 @@ export const TeardownMessage = (usage, expect_single_unmount) => { { title: "" } ]} rows={rows} /> + { have_data && + <> +
+ + } ); }; @@ -1268,24 +1325,48 @@ export function teardown_danger_message(usage, expect_single_unmount) { } } -export function init_active_usage_processes(client, usage, expect_single_unmount) { +// XXX - rename to init_teardown_usage + +export function init_teardown_usage(client, usage, expect_single_unmount) { return { - title: _("Checking related processes"), - func: dlg => { - return for_each_async(usage, u => { + title: _("Checking filesystem usage"), + func: async function (dlg) { + let have_data = false; + for (const u of usage) { if (u.usage == "mounted") { - return client.find_mount_users(u.location) - .then(users => { - u.users = users; - }); - } else - return Promise.resolve(); - }).then(() => { - dlg.set_attribute("Teardown", TeardownMessage(usage, expect_single_unmount)); - const msg = teardown_danger_message(usage, expect_single_unmount); - if (msg) - dlg.add_danger(msg); - }); + u.users = await client.find_mount_users(u.location); + } + if (client.in_anaconda_mode() && !expect_single_unmount && u.block) { + if (u.block.IdUsage == "filesystem") { + const empty = await cockpit.script(fsys_is_empty_sh, + [decode_filename(u.block.PreferredDevice)], + { superuser: true, err: "message" }); + if (empty.trim() != "yes") + u.data_warning = _("Filesystem is not empty"); + } else if (u.block.IdUsage == "crypto" && !client.blocks_cleartext[u.block.path]) { + u.data_warning = _("Locked encrypted device might contain data"); + } else if (!client.blocks_ptable[u.block.path] && + u.block.IdUsage != "raid" && + !is_available_block(client, u.block)) { + u.data_warning = _("Device contains unrecognized data"); + } + if (u.data_warning) + have_data = true; + } + } + + if (have_data) { + usage.Teardown = true; + dlg.need_confirmation({ + Title: _("Important data might be deleted."), + Text: _("The device that you are about to erase or delete might contain important data. Please confirm that you really want to delete or erase this device."), + ConfirmText: _("Yes, delete my data."), + }); + } + dlg.set_attribute("Teardown", TeardownMessage(usage, expect_single_unmount)); + const msg = teardown_danger_message(usage, expect_single_unmount); + if (msg) + dlg.add_danger(msg); } }; } diff --git a/pkg/storaged/filesystem/mounting-dialog.jsx b/pkg/storaged/filesystem/mounting-dialog.jsx index bb21343a43d9..b7e033896ad3 100644 --- a/pkg/storaged/filesystem/mounting-dialog.jsx +++ b/pkg/storaged/filesystem/mounting-dialog.jsx @@ -36,7 +36,7 @@ import { dialog_open, TextInput, PassInput, CheckBoxes, SelectOne, TeardownMessage, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { init_existing_passphrase, unlock_with_type } from "../crypto/keyslots.jsx"; import { initial_tab_options } from "../block/format-dialog.jsx"; @@ -447,7 +447,7 @@ export function mounting_dialog(client, block, mode, forced_options, subvol) { } }, Inits: [ - init_active_usage_processes(client, usage, old_dir), + init_teardown_usage(client, usage, old_dir), init_existing_passphrase(block, true, type => { passphrase_type = type; update_explicit_passphrase(dlg.get_value("mount_options")?.ro ?? opt_ro); diff --git a/pkg/storaged/fsys-is-empty.sh b/pkg/storaged/fsys-is-empty.sh new file mode 100644 index 000000000000..505b4841b2a6 --- /dev/null +++ b/pkg/storaged/fsys-is-empty.sh @@ -0,0 +1,28 @@ +#! /bin/bash + +set -eux + +dev=$1 + +need_unmount="" +mp=$(findmnt -no TARGET "$dev" | cat) +if [ -z "$mp" ]; then + mp=$(mktemp -d) + need_unmount=$mp + mount "$dev" "$mp" -o ro +fi + +# A filesystem is empty if it only has directories in it. + +first=$(find "$mp" -not -type d | head -1) + +if [ -n "$need_unmount" ]; then + umount "$need_unmount" + rmdir "$need_unmount" +fi + +if [ -z "$first" ]; then + echo yes +else + echo no +fi diff --git a/pkg/storaged/legacy-vdo/legacy-vdo.jsx b/pkg/storaged/legacy-vdo/legacy-vdo.jsx index 198c3904295f..27b15552a823 100644 --- a/pkg/storaged/legacy-vdo/legacy-vdo.jsx +++ b/pkg/storaged/legacy-vdo/legacy-vdo.jsx @@ -27,7 +27,7 @@ import { DescriptionList, DescriptionListDescription, DescriptionListGroup, Desc import { block_short_name, get_active_usage, teardown_active_usage, fmt_size, decode_filename, reload_systemd } from "../utils.js"; import { - dialog_open, SizeSlider, BlockingMessage, TeardownMessage, init_active_usage_processes + dialog_open, SizeSlider, BlockingMessage, TeardownMessage, init_teardown_usage } from "../dialog.jsx"; import { StorageButton, StorageOnOff } from "../storage-controls.jsx"; @@ -68,7 +68,7 @@ export function make_legacy_vdo_page(parent, vdo, backing_block, next_card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } else { @@ -130,7 +130,7 @@ export function make_legacy_vdo_page(parent, vdo, backing_block, next_card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/lvm2/block-logical-volume.jsx b/pkg/storaged/lvm2/block-logical-volume.jsx index 2e386df20bfb..e8f834dc9496 100644 --- a/pkg/storaged/lvm2/block-logical-volume.jsx +++ b/pkg/storaged/lvm2/block-logical-volume.jsx @@ -34,7 +34,7 @@ import { StorageCard, StorageDescription, new_card, navigate_to_new_card_locatio import { block_name, fmt_size, get_active_usage, teardown_active_usage, reload_systemd } from "../utils.js"; import { dialog_open, TextInput, SelectSpaces, BlockingMessage, TeardownMessage, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { lvm2_create_snapshot_action } from "./volume-group.jsx"; @@ -85,7 +85,7 @@ export function lvol_delete(lvol, card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } @@ -166,7 +166,7 @@ function deactivate(lvol, block) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/lvm2/volume-group.jsx b/pkg/storaged/lvm2/volume-group.jsx index a8f6e7c52788..4f40a325ca19 100644 --- a/pkg/storaged/lvm2/volume-group.jsx +++ b/pkg/storaged/lvm2/volume-group.jsx @@ -44,7 +44,7 @@ import { import { dialog_open, SelectSpaces, TextInput, BlockingMessage, TeardownMessage, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { create_logical_volume } from "./create-logical-volume-dialog.jsx"; @@ -104,7 +104,7 @@ function vgroup_delete(client, vgroup, card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } @@ -328,8 +328,10 @@ const LVM2VolumeGroupCard = ({ card, vgroup }) => { a bit inconsistent, but *shrug*. */ - let usage = get_active_usage(client, vgroup.path, _("delete")); - usage = usage.filter(u => u.block && is_partial_linear_lvol(u.block)); + const all_usage = get_active_usage(client, vgroup.path, _("delete")); + const usage = all_usage.filter(u => u.block && is_partial_linear_lvol(u.block)); + usage.Blocking = all_usage.Blocking; + usage.Teardown = all_usage.Teardown; if (usage.Blocking) { dialog_open({ @@ -359,7 +361,7 @@ const LVM2VolumeGroupCard = ({ card, vgroup }) => { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/mdraid/mdraid.jsx b/pkg/storaged/mdraid/mdraid.jsx index 880ab76f1097..3fcda5afcade 100644 --- a/pkg/storaged/mdraid/mdraid.jsx +++ b/pkg/storaged/mdraid/mdraid.jsx @@ -43,7 +43,7 @@ import { import { dialog_open, SelectSpaces, BlockingMessage, TeardownMessage, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { partitionable_block_actions } from "../partitions/actions.jsx"; @@ -81,7 +81,7 @@ function mdraid_stop(mdraid) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); return; @@ -132,7 +132,7 @@ function mdraid_delete(mdraid, block, card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/partitions/format-disk-dialog.jsx b/pkg/storaged/partitions/format-disk-dialog.jsx index f1dede36d3bb..97e65f191847 100644 --- a/pkg/storaged/partitions/format-disk-dialog.jsx +++ b/pkg/storaged/partitions/format-disk-dialog.jsx @@ -24,7 +24,7 @@ import { dialog_open, SelectOne, CheckBoxes, BlockingMessage, TeardownMessage, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { get_active_usage, block_name, teardown_active_usage, reload_systemd } from "../utils.js"; import { job_progress_wrapper } from "../jobs-panel.jsx"; @@ -84,7 +84,7 @@ export function format_disk(block) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/partitions/partition.jsx b/pkg/storaged/partitions/partition.jsx index ec8a8e9806e7..d3783484331d 100644 --- a/pkg/storaged/partitions/partition.jsx +++ b/pkg/storaged/partitions/partition.jsx @@ -29,7 +29,7 @@ import { StorageButton, StorageLink } from "../storage-controls.jsx"; import { dialog_open, SelectOne, TextInput, - init_active_usage_processes, BlockingMessage, TeardownMessage + init_teardown_usage, 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"; @@ -64,7 +64,7 @@ export function delete_partition(block, card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/stratis/filesystem.jsx b/pkg/storaged/stratis/filesystem.jsx index d5b7cae73176..05dbb99368f4 100644 --- a/pkg/storaged/stratis/filesystem.jsx +++ b/pkg/storaged/stratis/filesystem.jsx @@ -26,7 +26,7 @@ import { DescriptionList } from "@patternfly/react-core/dist/esm/components/Desc import { dialog_open, TextInput, BlockingMessage, TeardownMessage, - init_active_usage_processes, + init_teardown_usage, } from "../dialog.jsx"; import { StorageUsageBar, StorageLink } from "../storage-controls.jsx"; import { @@ -142,7 +142,7 @@ export function make_stratis_filesystem_page(parent, pool, fsys, } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/stratis/pool.jsx b/pkg/storaged/stratis/pool.jsx index ca1e011cea88..883a6eb54001 100644 --- a/pkg/storaged/stratis/pool.jsx +++ b/pkg/storaged/stratis/pool.jsx @@ -45,7 +45,7 @@ import { import { dialog_open, SelectSpaces, TextInput, PassInput, SelectOne, SizeSlider, BlockingMessage, TeardownMessage, - init_active_usage_processes + init_teardown_usage } from "../dialog.jsx"; import { validate_url, get_tang_adv } from "../crypto/tang.jsx"; @@ -154,7 +154,7 @@ function delete_pool(pool, card) { } }, Inits: [ - init_active_usage_processes(client, usage) + init_teardown_usage(client, usage) ] }); } diff --git a/pkg/storaged/utils.js b/pkg/storaged/utils.js index f09a444f257d..0bfe8740e410 100644 --- a/pkg/storaged/utils.js +++ b/pkg/storaged/utils.js @@ -690,7 +690,7 @@ export function should_ignore(client, path) { GET_CHILDREN_FOR_TEARDOWN is similar but doesn't consider things like volume groups to be children of their physical volumes. This is appropriate for teardown processing, where tearing down a - physical volume does not imply tearing down the whole volume groups + physical volume does not imply tearing down the whole volume group with everything that it contains. */ @@ -978,15 +978,15 @@ export function get_active_usage(client, path, top_action, child_action, is_temp return usage; } - let usage = []; + const usage = []; get_usage(usage, path, 0); - if (usage.length == 1 && usage[0].level == 0 && usage[0].usage == "none") - usage = []; - usage.Blocking = usage.some(u => u.blocking); usage.Teardown = usage.some(u => !u.blocking); + if (usage.length == 1 && usage[0].level == 0 && usage[0].usage == "none") + usage.Teardown = false; + return usage; } diff --git a/test/reference b/test/reference index e11eafdd5c2e..612b0904a905 160000 --- a/test/reference +++ b/test/reference @@ -1 +1 @@ -Subproject commit e11eafdd5c2e1dcbb1d86fbbce85697c41b06d55 +Subproject commit 612b0904a905e7a8d67778f63601e14d38ac2086 diff --git a/test/verify/check-storage-anaconda b/test/verify/check-storage-anaconda index bd63558b1448..d8803b1c9ee4 100755 --- a/test/verify/check-storage-anaconda +++ b/test/verify/check-storage-anaconda @@ -549,6 +549,84 @@ class TestStorageAnaconda(storagelib.StorageCase): b.click(self.card_desc("MDRAID disk", "MDRAID device") + " button") b.wait_in_text("body", "Not found") + def testNonEmpty(self): + b = self.browser + m = self.machine + + disk = self.add_loopback_disk(name="loop10") + + anaconda_config = { + "mount_point_prefix": "/sysroot", + "available_devices": [disk], + } + + # Create some content on the disk + m.execute(f"echo einszweidrei | cryptsetup luksFormat --pbkdf-memory 32768 {disk}") + m.execute(f"echo einszweidrei | cryptsetup luksOpen {disk} dm-test") + m.execute(f"mkfs.ext4 /dev/mapper/dm-test; mount /dev/mapper/dm-test /mnt; echo Hi >/mnt/hello; umount /mnt") + m.execute("cryptsetup close dm-test") + + self.login_and_go("/storage") + self.enterAnacondaMode(anaconda_config) + + # Attempt to wipe it. This requires a extra confirmation + # because it is locked. + b.wait_text(self.card_row_col("Storage", 1, 3), "Locked data (encrypted)") + self.click_dropdown(self.card_row("Storage", 1), "Create partition table") + self.dialog_wait_open() + self.dialog_set_val("type", "empty") + b.wait_in_text("#dialog", "Locked encrypted device might contain data") + self.dialog_wait_apply_disabled() + b.assert_pixels('#dialog', "wipe") + b.set_checked("#dialog-confirm", True) + self.dialog_wait_apply_enabled() + self.dialog_cancel() + self.dialog_wait_close() + + # Unlock and confirm the extra warning, and actually wipe it + self.click_dropdown(self.card_row("Storage", 1), "Unlock") + self.dialog({ "passphrase": "einszweidrei" }) + b.wait_text(self.card_row_col("Storage", 1, 3), "ext4 filesystem (encrypted)") + self.click_dropdown(self.card_row("Storage", 1), "Create partition table") + self.dialog_wait_open() + self.dialog_set_val("type", "empty") + b.wait_in_text("#dialog", "Filesystem is not empty") + b.set_checked("#dialog-confirm", True) + self.dialog_wait_apply_enabled() + self.dialog_apply() + self.dialog_wait_close() + + # Put some unrecognized data on it + + # This is the superblock of a legacy VDO device. Cockpit does + # not recognize it. + + data = """ +ZG12ZG8wMDEFAAAABAAAAAAAAABdAAAAAAAAAJQJAgCGsH0mrQgGAC4WnB4G50Fzu20jY6J1rfwA +AAAAAQAAAAAAAAABAAAA2FwKAAAAAAAA////AAAAAAA7tw9zAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= +""" + m.execute(f"base64 -d >{disk}", input=data) + # Wait for udev to settle and re-trigger, udisks sometimes misses udev events on ubuntu 2204. + m.execute(f"udevadm settle; udevadm trigger {disk}") + + b.wait_text(self.card_row_col("Storage", 1, 3), "Unrecognized data") + self.click_dropdown(self.card_row("Storage", 1), "Create partition table") + self.dialog_wait_open() + self.dialog_set_val("type", "empty") + testlib.sit() + b.wait_in_text("#dialog", "Device contains unrecognized data") + b.set_checked("#dialog-confirm", True) + self.dialog_wait_apply_enabled() + self.dialog_apply() + self.dialog_wait_close() + if __name__ == '__main__': testlib.test_main() diff --git a/test/verify/check-storage-unrecognized b/test/verify/check-storage-unrecognized index 2deec8306894..71799fb5befb 100755 --- a/test/verify/check-storage-unrecognized +++ b/test/verify/check-storage-unrecognized @@ -53,7 +53,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= """ m.execute(f"base64 -d >{disk}", input=data) # Wait for udev to settle and re-trigger, udisks sometimes misses udev events on ubuntu 2204. - m.execute("udevadm settle; udevadm trigger /dev/sda") + m.execute(f"udevadm settle; udevadm trigger {disk}") b.wait_text(self.card_desc("Unrecognized data", "Usage"), "other") b.wait_text(self.card_desc("Unrecognized data", "Type"), "vdo")