* Copyright (C) 2023 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import cockpit from "cockpit";
import React from "react";
import client from "../client";
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 { useEvent } from "hooks";
import {
block_name, fmt_size, validate_fsys_label
} from "../utils.js";
import {
dialog_open, TextInput,
} from "../dialog.jsx";
import { StorageLink, StorageUsageBar, StorageSize } from "../storage-controls.jsx";
import { StorageCard, StorageDescription, new_card, useIsNarrow } from "../pages.jsx";
import { std_format_action } from "../block/actions.jsx";
import { is_mounted, MountPoint, mount_point_text, edit_mount_point } from "./utils.jsx";
import { mounting_dialog } from "./mounting-dialog.jsx";
import { check_mismounted_fsys, MismountAlert } from "./mismounting.jsx";
const _ = cockpit.gettext;
* by looking at the "backing_block" and "content_block" parameters:
*
* not-encrypted: backing_block == content_block,
* content_block != null
*
* encrypted and unlocked: backing_block != content_block,
* backing_block != null,
* content_block != null
*
* encrypted and locked: backing_block != null,
* content_block == null
*
* "backing_block" is always non-null and always refers to the block
* device that we want to talk about in the UI. "content_block" (when
* non-null) is the block that we need to use for filesystem related
* actions, such as mounting. It's the one with the
* "o.fd.UDisks2.Filesystem" interface.
*
* When "content_block" is null, then "backing_block" is a locked LUKS
* device, but we could figure out the fstab entry for the filesystem
* that's on it.
*/
const MountPointUsageBar = ({ mount_point, block, short }) => {
useEvent(client.fsys_sizes, "changed");
const narrow = useIsNarrow();
const stats = client.fsys_sizes.data[mount_point];
if (stats)
return <StorageUsageBar stats={stats} critical={0.95} block={block_name(block)} short={short} />;
else if (short && !narrow)
return <StorageSize size={block.Size} />;
else
return fmt_size(block.Size);
};
export function make_filesystem_card(next, backing_block, content_block, fstab_config) {
const [, mount_point] = fstab_config;
const mismount_warning = check_mismounted_fsys(backing_block, content_block, fstab_config);
const mounted = content_block && is_mounted(client, content_block);
const mp_text = mount_point_text(mount_point, mounted);
if (mp_text == null)
return null;
return new_card({
title: content_block ? cockpit.format(_("$0 filesystem"), content_block.IdType) : _("Filesystem"),
location: mp_text,
next,
page_size: <MountPointUsageBar mount_point={mount_point} block={backing_block} short />,
has_warning: !!mismount_warning,
component: FilesystemCard,
props: { backing_block, content_block, fstab_config, mismount_warning },
actions: [
client.in_anaconda_mode() &&
{ title: _("Edit mount point"), action: () => edit_mount_point(content_block || backing_block) },
content_block && mounted
? { title: _("Unmount"), action: () => mounting_dialog(client, content_block, "unmount") }
: null,
!(content_block && mounted) && !client.in_anaconda_mode()
? { title: _("Mount"), action: () => mounting_dialog(client, content_block || backing_block, "mount") }
: null,
std_format_action(backing_block, content_block),
]
});
}
export const FilesystemCard = ({
card, backing_block, content_block, fstab_config, mismount_warning
}) => {
function rename_dialog() {
const block_fsys = client.blocks_fsys[content_block.path];
dialog_open({
Title: _("Filesystem name"),
Fields: [
TextInput("name", _("Name"),
{
validate: name => validate_fsys_label(name, content_block.IdType),
value: content_block.IdLabel
})
],
Action: {
Title: _("Save"),
action: function (vals) {
return block_fsys.SetLabel(vals.name, {});
}
}
});
}
const [, mount_point] = fstab_config;
const mounted = content_block && is_mounted(client, content_block);
return (
<StorageCard card={card}>
<CardBody>
<DescriptionList className="pf-m-horizontal-on-sm">
<StorageDescription title={_("Name")}
value={content_block?.IdLabel || "-"}
action={<StorageLink onClick={rename_dialog}
excuse={!content_block ? _("Filesystem is locked") : null}>
{_("edit")}
</StorageLink>} />
<StorageDescription title={_("Mount point")}>
<MountPoint fstab_config={fstab_config}
backing_block={backing_block} content_block={content_block} />
</StorageDescription>
{ mounted &&
<StorageDescription title={_("Usage")}>
<MountPointUsageBar mount_point={mount_point} block={backing_block} />
</StorageDescription>
}
</DescriptionList>
</CardBody>
{ mismount_warning &&
<CardBody>
<MismountAlert warning={mismount_warning}
fstab_config={fstab_config}
backing_block={backing_block} content_block={content_block} />
</CardBody>
}
</StorageCard>);
};