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 { for_each_async, reload_systemd, encode_filename } from "../utils.js";
11 : import { dialog_open } from "../dialog.jsx";
12 : import { TangKeyVerification } from "../crypto/tang.jsx";
13 :
14 113 : const _ = cockpit.gettext;
15 :
16 9 : export function validate_pool_name(pool, name) {
17 9 : if (name == "")
18 0 : return _("Name can not be empty.");
19 2 : if ((!pool || name != pool.Name) && client.stratis_poolnames_pool[name])
20 0 : return _("A pool with this name exists already.");
21 9 : }
22 :
23 15 : export function std_reply(result, code, message) {
24 15 : if (code)
25 2 : return Promise.reject(message);
26 : else
27 14 : return Promise.resolve(result);
28 15 : }
29 :
30 : export function with_keydesc(client, pool, func) {
31 : if (!pool.KeyDescription ||
32 : !pool.KeyDescription[0] ||
33 : !pool.KeyDescription[1][0]) {
34 : return func(false);
35 : } else {
36 : const keydesc = pool.KeyDescription[1][1];
37 : return client.stratis_manager.ListKeys()
38 : .catch(() => []) // not-covered: internal error
39 : .then(keys => func(keydesc, keys.indexOf(keydesc) >= 0));
40 : }
41 : }
42 :
43 1 : export function with_stored_passphrase(client, keydesc, passphrase, func) {
44 1 : return client.stratis_store_passphrase(keydesc, passphrase)
45 1 : .then(func)
46 1 : .finally(() => {
47 1 : return client.stratis_manager.UnsetKey(keydesc)
48 1 : .then(std_reply)
49 0 : .catch(ex => { console.warn("Failed to remove passphrase from key ring", ex.toString()) }); // not-covered: internal error
50 1 : });
51 1 : }
52 :
53 1 : export function get_unused_keydesc(client, desc_prefix) {
54 1 : return client.stratis_manager.ListKeys()
55 0 : .catch(() => []) // not-covered: internal error
56 1 : .then(keys => {
57 1 : let desc;
58 1 : for (let i = 0; i < 1000; i++) {
59 0 : desc = desc_prefix + (i > 0 ? "." + i.toFixed() : "");
60 1 : if (keys.indexOf(desc) == -1)
61 1 : break;
62 1 : }
63 1 : return desc;
64 1 : });
65 1 : }
66 :
67 1 : export function confirm_tang_trust(url, adv, action) {
68 1 : dialog_open({
69 1 : Title: _("Verify key"),
70 1 : Body: <TangKeyVerification url={url} adv={adv} />,
71 1 : Action: {
72 1 : Title: _("Trust key"),
73 1 : action
74 1 : }
75 1 : });
76 1 : }
77 :
78 : export function check_stratis_warnings(client, enter_warning) {
79 : for (const p in client.stratis_pools) {
80 : const blockdevs = client.stratis_pool_blockdevs[p] || [];
81 : const pool = client.stratis_pools[p];
82 : if (blockdevs.some(bd => bd.NewPhysicalSize[0] && Number(bd.NewPhysicalSize[1]) > Number(bd.TotalPhysicalSize)))
83 : enter_warning(p, { warning: "unused-blockdevs" });
84 : if (pool.AvailableActions && pool.AvailableActions !== "fully_operational")
85 : enter_warning(p, { warning: "not-fully-operational" });
86 : }
87 : }
88 :
89 8 : function teardown_block(block) {
90 6 : return for_each_async(block.Configuration, c => block.RemoveConfigurationItem(c, {}));
91 8 : }
92 :
93 8 : export function destroy_filesystem(fsys) {
94 8 : const block = client.slashdevs_block[fsys.Devnode];
95 8 : const pool = client.stratis_pools[fsys.Pool];
96 :
97 8 : return teardown_block(block).then(() => pool.DestroyFilesystems([fsys.path]).then(std_reply));
98 8 : }
99 :
100 9 : export function validate_fs_name(fsys, name, filesystems) {
101 9 : if (name == "")
102 0 : return _("Name can not be empty.");
103 2 : if (!fsys || name != fsys.Name) {
104 4 : for (const fs of filesystems) {
105 4 : if (fs.Name == name)
106 0 : return _("A filesystem with this name exists already in this pool.");
107 4 : }
108 9 : }
109 9 : }
110 :
111 8 : export function set_mount_options(path, vals, forced_options) {
112 8 : let mount_options = [];
113 :
114 3 : if (vals.variant == "nomount" || vals.at_boot == "never")
115 8 : mount_options.push("noauto");
116 7 : if (vals.mount_options?.ro)
117 0 : mount_options.push("ro");
118 8 : if (vals.at_boot == "never")
119 3 : mount_options.push("x-cockpit-never-auto");
120 8 : if (vals.at_boot == "nofail")
121 7 : mount_options.push("nofail");
122 8 : if (vals.at_boot == "netdev")
123 1 : mount_options.push("_netdev");
124 7 : if (vals.mount_options?.extra)
125 0 : mount_options.push(vals.mount_options.extra);
126 :
127 8 : mount_options = mount_options.concat(forced_options);
128 :
129 8 : let mount_point = vals.mount_point;
130 8 : if (mount_point == "")
131 6 : return Promise.resolve();
132 4 : if (mount_point[0] != "/")
133 0 : mount_point = "/" + mount_point;
134 4 : mount_point = client.add_mount_point_prefix(mount_point);
135 :
136 4 : const config =
137 4 : ["fstab",
138 4 : {
139 4 : dir: { t: 'ay', v: encode_filename(mount_point) },
140 4 : type: { t: 'ay', v: encode_filename("auto") },
141 0 : opts: { t: 'ay', v: encode_filename(mount_options.join(",") || "defaults") },
142 8 : freq: { t: 'i', v: 0 },
143 8 : passno: { t: 'i', v: 0 },
144 8 : }
145 8 : ];
146 :
147 4 : function udisks_block_for_stratis_fsys() {
148 4 : const fsys = client.stratis_filesystems[path];
149 4 : return fsys && client.slashdevs_block[fsys.Devnode];
150 4 : }
151 :
152 8 : return client.wait_for(udisks_block_for_stratis_fsys)
153 4 : .then(block => {
154 : // HACK - need a explicit "change" event
155 4 : return block.Rescan({})
156 4 : .then(() => {
157 4 : return client.wait_for(() => client.blocks_fsys[block.path])
158 4 : .then(fsys => {
159 4 : return block.AddConfigurationItem(config, {})
160 4 : .then(reload_systemd)
161 4 : .then(() => {
162 4 : if (vals.variant != "nomount")
163 0 : return client.mount_at(block, mount_point);
164 : else
165 1 : return Promise.resolve();
166 4 : });
167 4 : });
168 4 : });
169 4 : });
170 8 : }
|