Line data Source code
1 : /*
2 : * Copyright (C) 2023 Red Hat, Inc.
3 : * SPDX-License-Identifier: LGPL-2.1-or-later
4 : */
5 :
6 113 : import cockpit from "cockpit";
7 113 : import React from "react";
8 : import client from "../client";
9 :
10 : import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js";
11 : import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js";
12 : import { useEvent } from "hooks";
13 :
14 : import {
15 : block_name, fmt_size, validate_fsys_label
16 : } from "../utils.js";
17 : import {
18 : dialog_open, TextInput,
19 : } from "../dialog.jsx";
20 : import { StorageLink, StorageUsageBar, StorageSize } from "../storage-controls.jsx";
21 : import { StorageCard, StorageDescription, new_card, useIsNarrow } from "../pages.jsx";
22 :
23 : import { std_format_action } from "../block/actions.jsx";
24 : import { is_mounted, MountPoint, mount_point_text, edit_mount_point } from "./utils.jsx";
25 : import { mounting_dialog } from "./mounting-dialog.jsx";
26 : import { check_mismounted_fsys, MismountAlert } from "./mismounting.jsx";
27 :
28 113 : const _ = cockpit.gettext;
29 :
30 : /* This page is used in a variety of cases, which can be distinguished
31 : * by looking at the "backing_block" and "content_block" parameters:
32 : *
33 : * not-encrypted: backing_block == content_block,
34 : * content_block != null
35 : *
36 : * encrypted and unlocked: backing_block != content_block,
37 : * backing_block != null,
38 : * content_block != null
39 : *
40 : * encrypted and locked: backing_block != null,
41 : * content_block == null
42 : *
43 : * "backing_block" is always non-null and always refers to the block
44 : * device that we want to talk about in the UI. "content_block" (when
45 : * non-null) is the block that we need to use for filesystem related
46 : * actions, such as mounting. It's the one with the
47 : * "o.fd.UDisks2.Filesystem" interface.
48 : *
49 : * When "content_block" is null, then "backing_block" is a locked LUKS
50 : * device, but we could figure out the fstab entry for the filesystem
51 : * that's on it.
52 : */
53 :
54 99 : const MountPointUsageBar = ({ mount_point, block, short }) => {
55 99 : useEvent(client.fsys_sizes, "changed");
56 99 : const narrow = useIsNarrow();
57 :
58 99 : const stats = client.fsys_sizes.data[mount_point];
59 99 : if (stats)
60 96 : return <StorageUsageBar stats={stats} critical={0.95} block={block_name(block)} short={short} />;
61 99 : else if (short && !narrow)
62 25 : return <StorageSize size={block.Size} />;
63 : else
64 25 : return fmt_size(block.Size);
65 99 : };
66 :
67 106 : export function make_filesystem_card(next, backing_block, content_block, fstab_config) {
68 106 : const [, mount_point] = fstab_config;
69 106 : const mismount_warning = check_mismounted_fsys(backing_block, content_block, fstab_config);
70 106 : const mounted = content_block && is_mounted(client, content_block);
71 :
72 106 : const mp_text = mount_point_text(mount_point, mounted);
73 106 : if (mp_text == null)
74 11 : return null;
75 :
76 106 : return new_card({
77 21 : title: content_block ? cockpit.format(_("$0 filesystem"), content_block.IdType) : _("Filesystem"),
78 106 : location: mp_text,
79 106 : next,
80 106 : page_size: <MountPointUsageBar mount_point={mount_point} block={backing_block} short />,
81 106 : has_warning: !!mismount_warning,
82 106 : component: FilesystemCard,
83 106 : props: { backing_block, content_block, fstab_config, mismount_warning },
84 106 : actions: [
85 106 : client.in_anaconda_mode() &&
86 0 : { title: _("Edit mount point"), action: () => edit_mount_point(content_block || backing_block) },
87 106 : content_block && mounted
88 8 : ? { title: _("Unmount"), action: () => mounting_dialog(client, content_block, "unmount") }
89 106 : : null,
90 106 : !(content_block && mounted) && !client.in_anaconda_mode()
91 4 : ? { title: _("Mount"), action: () => mounting_dialog(client, content_block || backing_block, "mount") }
92 106 : : null,
93 106 : std_format_action(backing_block, content_block),
94 106 : ]
95 106 : });
96 106 : }
97 :
98 37 : export const FilesystemCard = ({
99 37 : card, backing_block, content_block, fstab_config, mismount_warning
100 37 : }) => {
101 1 : function rename_dialog() {
102 : // assert(content_block)
103 1 : const block_fsys = client.blocks_fsys[content_block.path];
104 :
105 1 : dialog_open({
106 1 : Title: _("Filesystem name"),
107 1 : Fields: [
108 1 : TextInput("name", _("Name"),
109 1 : {
110 1 : validate: name => validate_fsys_label(name, content_block.IdType),
111 1 : value: content_block.IdLabel
112 1 : })
113 1 : ],
114 1 : Action: {
115 1 : Title: _("Save"),
116 1 : action: function (vals) {
117 1 : return block_fsys.SetLabel(vals.name, {});
118 1 : }
119 1 : }
120 1 : });
121 1 : }
122 :
123 37 : const [, mount_point] = fstab_config;
124 36 : const mounted = content_block && is_mounted(client, content_block);
125 :
126 37 : return (
127 37 : <StorageCard card={card}>
128 37 : <CardBody>
129 37 : <DescriptionList className="pf-m-horizontal-on-sm">
130 37 : <StorageDescription title={_("Name")}
131 18 : value={content_block?.IdLabel || "-"}
132 37 : action={<StorageLink onClick={rename_dialog}
133 8 : excuse={!content_block ? _("Filesystem is locked") : null}>
134 37 : {_("edit")}
135 37 : </StorageLink>} />
136 37 : <StorageDescription title={_("Mount point")}>
137 37 : <MountPoint fstab_config={fstab_config}
138 37 : backing_block={backing_block} content_block={content_block} />
139 37 : </StorageDescription>
140 37 : { mounted &&
141 25 : <StorageDescription title={_("Usage")}>
142 25 : <MountPointUsageBar mount_point={mount_point} block={backing_block} />
143 25 : </StorageDescription>
144 : }
145 37 : </DescriptionList>
146 37 : </CardBody>
147 37 : { mismount_warning &&
148 9 : <CardBody>
149 9 : <MismountAlert warning={mismount_warning}
150 9 : fstab_config={fstab_config}
151 9 : backing_block={backing_block} content_block={content_block} />
152 9 : </CardBody>
153 : }
154 37 : </StorageCard>);
155 37 : };
|