LCOV - code coverage report
Current view: top level - pkg/storaged/filesystem - mismounting.jsx Coverage Total Hit
Test: cockpit Lines: 94.8 % 172 163
Test Date: 2026-07-17 07:33:01

            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.js";
       9              : 
      10              : import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js";
      11              : 
      12              : import {
      13              :     encode_filename,
      14              :     parse_options, unparse_options, extract_option, reload_systemd,
      15              :     set_crypto_auto_option, get_mount_points,
      16              :     BTRFS_TOOL_MOUNT_PATH
      17              : } from "../utils.js";
      18              : import { StorageButton } from "../storage-controls.jsx";
      19              : 
      20              : import { mounting_dialog } from "./mounting-dialog.jsx";
      21              : import { get_cryptobacking_noauto } from "./utils.jsx";
      22              : 
      23          113 : const _ = cockpit.gettext;
      24              : 
      25          108 : export function check_mismounted_fsys(backing_block, content_block, fstab_config, subvol) {
      26          108 :     if (client.in_anaconda_mode())
      27          108 :         return;
      28              : 
      29          101 :     const block_fsys = content_block && client.blocks_fsys[content_block.path];
      30          108 :     const [, dir, opts] = fstab_config;
      31              : 
      32           57 :     if (!(block_fsys || dir))
      33          108 :         return;
      34              : 
      35           75 :     function ignore_mount(m) {
      36              :         // We don't complain about the rootfs, it's probably
      37              :         // configured somewhere else, like in the bootloader.
      38           75 :         if (m == "/")
      39            2 :             return true;
      40              : 
      41              :         // This is the mount point used for monitoring btrfs filesystems.
      42           75 :         if (m.startsWith(BTRFS_TOOL_MOUNT_PATH))
      43            2 :             return true;
      44              : 
      45           75 :         return false;
      46           75 :     }
      47              : 
      48          101 :     const mounted_at = get_mount_points(client, block_fsys, subvol);
      49          101 :     const split_options = parse_options(opts);
      50          101 :     const opt_noauto = extract_option(split_options, "noauto");
      51          101 :     const opt_noauto_intent = extract_option(split_options, "x-cockpit-never-auto");
      52          101 :     const opt_systemd_automount = split_options.indexOf("x-systemd.automount") >= 0;
      53          101 :     const is_mounted = mounted_at.indexOf(dir) >= 0;
      54           83 :     const other_mounts = mounted_at.filter(m => m != dir && !ignore_mount(m));
      55          101 :     const crypto_backing_noauto = get_cryptobacking_noauto(client, backing_block);
      56              : 
      57          101 :     let type;
      58          101 :     if (dir) {
      59           13 :         if (!is_mounted && other_mounts.length > 0) {
      60           13 :             if (!opt_noauto)
      61           13 :                 type = "change-mount-on-boot";
      62              :             else
      63           13 :                 type = "mounted-no-config";
      64           11 :         } else if (crypto_backing_noauto && !opt_noauto)
      65           18 :             type = "locked-on-boot-mount";
      66           51 :         else if (!is_mounted && !opt_noauto)
      67           43 :             type = "mount-on-boot";
      68           15 :         else if (is_mounted && opt_noauto && !opt_noauto_intent && !opt_systemd_automount)
      69           15 :             type = "no-mount-on-boot";
      70           18 :     } else if (other_mounts.length > 0) {
      71           18 :         type = "mounted-no-config";
      72           18 :     }
      73              : 
      74          101 :     if (type)
      75           49 :         return { warning: "mismounted-fsys", type, other: other_mounts[0] };
      76          108 : }
      77              : 
      78           10 : export const MismountAlert = ({ warning, fstab_config, forced_options, backing_block, content_block, subvol }) => {
      79           10 :     if (!warning)
      80            0 :         return null;
      81              : 
      82           10 :     const { type, other } = warning;
      83           10 :     const [old_config, old_dir, old_opts, old_parents] = fstab_config;
      84           10 :     const split_options = parse_options(old_opts);
      85           10 :     extract_option(split_options, "noauto");
      86           10 :     const opt_ro = extract_option(split_options, "ro");
      87           10 :     const opt_nofail = extract_option(split_options, "nofail");
      88           10 :     const opt_netdev = extract_option(split_options, "_netdev");
      89           10 :     const split_options_for_fix_config = split_options.slice();
      90           10 :     if (forced_options)
      91            1 :         for (const opt of forced_options)
      92            1 :             extract_option(split_options, opt);
      93              : 
      94            6 :     function fix_config() {
      95            6 :         let opts = [];
      96            6 :         if (type == "mount-on-boot")
      97            4 :             opts.push("noauto");
      98            2 :         if (type == "locked-on-boot-mount") {
      99            2 :             opts.push("noauto");
     100            2 :             opts.push("x-cockpit-never-auto");
     101            2 :         }
     102            6 :         if (opt_ro)
     103            0 :             opts.push("ro");
     104            6 :         if (opt_nofail)
     105            2 :             opts.push("nofail");
     106            6 :         if (opt_netdev)
     107            1 :             opts.push("_netdev");
     108            2 :         if (subvol) {
     109            2 :             opts.push(`subvol=${subvol.pathname}`);
     110            2 :         }
     111              : 
     112              :         // Add the forced options, but only to new entries.  We
     113              :         // don't want to modify existing entries beyond what we
     114              :         // say on the button.
     115            2 :         if (!old_config && forced_options)
     116            1 :             opts = opts.concat(forced_options);
     117              : 
     118            6 :         const new_opts = unparse_options(opts.concat(split_options_for_fix_config));
     119            6 :         let all_new_opts;
     120            6 :         if (new_opts && old_parents)
     121            0 :             all_new_opts = new_opts + "," + old_parents;
     122            4 :         else if (new_opts)
     123            0 :             all_new_opts = new_opts;
     124              :         else
     125            0 :             all_new_opts = old_parents;
     126              : 
     127            6 :         let new_dir = old_dir;
     128            6 :         if (type == "change-mount-on-boot" || type == "mounted-no-config")
     129            4 :             new_dir = other;
     130              : 
     131            6 :         const new_config = [
     132            6 :             "fstab", {
     133            1 :                 fsname: old_config ? old_config[1].fsname : undefined,
     134            6 :                 dir: { t: 'ay', v: encode_filename(new_dir) },
     135            6 :                 type: { t: 'ay', v: encode_filename("auto") },
     136            0 :                 opts: { t: 'ay', v: encode_filename(all_new_opts || "defaults") },
     137            6 :                 freq: { t: 'i', v: 0 },
     138            6 :                 passno: { t: 'i', v: 0 },
     139            6 :                 "track-parents": { t: 'b', v: !old_config }
     140            6 :             }];
     141              : 
     142            6 :         function fixup_crypto_backing() {
     143            6 :             if (!backing_block)
     144            6 :                 return;
     145            6 :             if (type == "no-mount-on-boot")
     146            3 :                 return set_crypto_auto_option(backing_block, true);
     147            6 :             if (type == "locked-on-boot-mount")
     148            2 :                 return set_crypto_auto_option(backing_block, false);
     149            6 :         }
     150              : 
     151            6 :         function fixup_fsys() {
     152            6 :             if (old_config)
     153            1 :                 return backing_block.UpdateConfigurationItem(old_config, new_config, {}).then(reload_systemd);
     154              :             else
     155            2 :                 return backing_block.AddConfigurationItem(new_config, {}).then(reload_systemd);
     156            6 :         }
     157              : 
     158            6 :         return fixup_fsys().then(fixup_crypto_backing);
     159            6 :     }
     160              : 
     161            5 :     function fix_mount() {
     162            5 :         const crypto_backing_crypto = client.blocks_crypto[backing_block.path];
     163              : 
     164            5 :         function do_mount() {
     165            5 :             if (!content_block)
     166            0 :                 mounting_dialog(client, backing_block, "mount", forced_options);
     167              :             else
     168            4 :                 return client.mount_at(content_block, old_dir);
     169            5 :         }
     170              : 
     171            3 :         function do_unmount(dir) {
     172            3 :             return client.unmount_at(dir)
     173            3 :                     .then(() => {
     174            3 :                         if (backing_block != content_block)
     175            1 :                             return crypto_backing_crypto.Lock({});
     176            3 :                     });
     177            3 :         }
     178              : 
     179            5 :         if (type == "change-mount-on-boot")
     180            2 :             return client.unmount_at(other).then(() => client.mount_at(content_block, old_dir));
     181            5 :         else if (type == "mount-on-boot")
     182            3 :             return do_mount();
     183            3 :         else if (type == "no-mount-on-boot")
     184            3 :             return do_unmount(old_dir);
     185            3 :         else if (type == "mounted-no-config")
     186            0 :             return do_unmount(other);
     187            1 :         else if (type == "locked-on-boot-mount") {
     188            1 :             if (backing_block != content_block)
     189            1 :                 return set_crypto_auto_option(backing_block, true);
     190            1 :         }
     191            5 :     }
     192              : 
     193           10 :     let text;
     194           10 :     let fix_config_text;
     195           10 :     let fix_mount_text;
     196              : 
     197            2 :     if (type == "change-mount-on-boot") {
     198            2 :         text = cockpit.format(_("The filesystem is currently mounted on $0 but will be mounted on $1 on the next boot."), other, old_dir);
     199            2 :         fix_config_text = cockpit.format(_("Mount automatically on $0 on boot"), other);
     200            2 :         fix_mount_text = cockpit.format(_("Mount on $0 now"), old_dir);
     201            2 :     } else if (type == "mount-on-boot") {
     202            6 :         text = _("The filesystem is currently not mounted but will be mounted on the next boot.");
     203            6 :         fix_config_text = _("Do not mount automatically on boot");
     204            6 :         fix_mount_text = _("Mount now");
     205            3 :     } else if (type == "no-mount-on-boot") {
     206            3 :         text = _("The filesystem is currently mounted but will not be mounted after the next boot.");
     207            3 :         fix_config_text = _("Mount also automatically on boot");
     208            3 :         fix_mount_text = _("Unmount now");
     209            2 :     } else if (type == "mounted-no-config") {
     210            6 :         text = cockpit.format(_("The filesystem is currently mounted on $0 but will not be mounted after the next boot."), other);
     211            6 :         fix_config_text = cockpit.format(_("Mount automatically on $0 on boot"), other);
     212            6 :         fix_mount_text = _("Unmount now");
     213            0 :     } else if (type == "locked-on-boot-mount") {
     214            2 :         text = _("The filesystem is configured to be automatically mounted on boot but its encryption container will not be unlocked at that time.");
     215            2 :         fix_config_text = _("Do not mount automatically on boot");
     216            2 :         fix_mount_text = _("Unlock automatically on boot");
     217            2 :     }
     218              : 
     219           10 :     return (
     220           10 :         <Alert variant="warning" isInline
     221           10 :                title={_("Inconsistent filesystem mount")}>
     222           10 :             {text}
     223           10 :             <div className="storage-alert-actions">
     224           10 :                 <StorageButton onClick={fix_config}>{fix_config_text}</StorageButton>
     225           10 :                 { fix_mount_text && <StorageButton onClick={fix_mount}>{fix_mount_text}</StorageButton> }
     226           10 :             </div>
     227           10 :         </Alert>);
     228           10 : };
        

Generated by: LCOV version 2.0-1