Skip to content

Commit c1d3495

Browse files
committed
reinstall: Add warnings about mounts
This uses findmnt to locate filesystem mounts that are on the same source as the root mount. If any are found, the user is warned these filesystems will persist unmounted in the bootc system. The user must hit <enter> to proceed. This does the same for logical volumes in the same group as root. It also adds a generic warning to help the user understand what will happen after rebooting into the bootc system. Signed-off-by: ckyrouac <[email protected]>
1 parent 36fceed commit c1d3495

File tree

7 files changed

+209
-2
lines changed

7 files changed

+209
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mount/src/mount.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub struct Filesystem {
4545
pub children: Option<Vec<Filesystem>>,
4646
}
4747

48-
#[derive(Deserialize, Debug)]
48+
#[derive(Deserialize, Debug, Default)]
4949
pub struct Findmnt {
5050
pub filesystems: Vec<Filesystem>,
5151
}

system-reinstall-bootc/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ platforms = ["*-unknown-linux-gnu"]
1616

1717
[dependencies]
1818
anyhow = { workspace = true }
19+
bootc-mount = { path = "../mount" }
1920
bootc-utils = { path = "../utils" }
2021
clap = { workspace = true, features = ["derive"] }
2122
crossterm = "0.29.0"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use anyhow::Result;
2+
use bootc_mount::Filesystem;
3+
4+
use crate::prompt::press_enter;
5+
6+
pub(crate) struct ProblemMount {
7+
target: String,
8+
fs_type: String,
9+
source: String,
10+
}
11+
12+
impl std::fmt::Display for ProblemMount {
13+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14+
f.write_str(
15+
format!(
16+
"Type: {}, Target: {}, Source: {}",
17+
self.fs_type, self.target, self.source
18+
)
19+
.as_str(),
20+
)
21+
}
22+
}
23+
24+
pub(crate) fn check_root_siblings() -> Result<Vec<ProblemMount>> {
25+
let mounts = bootc_mount::run_findmnt(&[], None)?;
26+
let problem_filesystems: Vec<ProblemMount> = mounts
27+
.filesystems
28+
.iter()
29+
.filter(|fs| fs.target == "/")
30+
.flat_map(|root| {
31+
let children: Vec<&Filesystem> = root
32+
.children
33+
.iter()
34+
.flatten()
35+
.filter(|child| child.source == root.source)
36+
.collect();
37+
children
38+
})
39+
.map(|fs| ProblemMount {
40+
target: fs.target.clone(),
41+
fs_type: fs.fstype.clone(),
42+
source: fs.source.clone(),
43+
})
44+
.collect();
45+
Ok(problem_filesystems)
46+
}
47+
48+
pub(crate) fn print_warning(mounts: Vec<ProblemMount>) {
49+
if !mounts.is_empty() {
50+
println!();
51+
println!("NOTICE: the following filesystems are currently mounted on the same drive as root. After reboot, these will not be automatically mounted unless defined in the bootc image. The filesystems will be preserved and continue to consume disk space. Consult the bootc documentation to determine the appropriate action for your system.");
52+
println!();
53+
for m in mounts {
54+
println!("{}", m);
55+
}
56+
press_enter();
57+
}
58+
}

system-reinstall-bootc/src/lvm.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use std::process::Command;
2+
3+
use anyhow::Result;
4+
use bootc_mount::run_findmnt;
5+
use bootc_utils::CommandRunExt;
6+
use serde::Deserialize;
7+
8+
use crate::prompt::press_enter;
9+
10+
#[derive(Debug, Deserialize)]
11+
pub(crate) struct Lvs {
12+
report: Vec<LvsReport>,
13+
}
14+
15+
#[derive(Debug, Deserialize)]
16+
pub(crate) struct LvsReport {
17+
lv: Vec<LogicalVolume>,
18+
}
19+
20+
#[derive(Debug, Deserialize, Clone)]
21+
pub(crate) struct LogicalVolume {
22+
lv_name: String,
23+
lv_size: String,
24+
lv_path: String,
25+
vg_name: String,
26+
#[serde(default)]
27+
mount_path: String,
28+
}
29+
30+
pub(crate) fn parse_volumes(group: Option<String>) -> Result<Vec<LogicalVolume>> {
31+
let mut cmd = Command::new("lvs");
32+
cmd.args([
33+
"--reportformat=json",
34+
"-o",
35+
"lv_name,lv_size,lv_path,vg_name",
36+
]);
37+
38+
if let Some(group) = group {
39+
cmd.arg(group);
40+
}
41+
42+
let output: Lvs = cmd.run_and_parse_json()?;
43+
44+
Ok(output
45+
.report
46+
.iter()
47+
.flat_map(|r| r.lv.iter().cloned())
48+
.collect())
49+
}
50+
51+
pub(crate) fn check_root_siblings() -> Result<Vec<LogicalVolume>> {
52+
let all_volumes = parse_volumes(None)?;
53+
54+
// first look for a lv mounted to '/'
55+
// then gather all the sibling lvs in the vg along with their mount points
56+
let siblings: Vec<LogicalVolume> = all_volumes
57+
.iter()
58+
.filter(|lv| {
59+
let mount = run_findmnt(&["-S", &lv.lv_path], None).unwrap_or_default();
60+
if let Some(fs) = mount.filesystems.first() {
61+
&fs.target == "/"
62+
} else {
63+
false
64+
}
65+
})
66+
.flat_map(|root_lv| {
67+
tracing::warn!("inside flat_map");
68+
parse_volumes(Some(root_lv.vg_name.clone())).unwrap_or_default()
69+
})
70+
.map(|lv| {
71+
tracing::warn!("inside map");
72+
let mount = run_findmnt(&["-S", &lv.lv_path], None).unwrap();
73+
let mount_path = if let Some(fs) = mount.filesystems.first() {
74+
&fs.target
75+
} else {
76+
""
77+
};
78+
LogicalVolume {
79+
lv_name: lv.lv_name,
80+
lv_size: lv.lv_size,
81+
lv_path: lv.lv_path,
82+
vg_name: lv.vg_name,
83+
mount_path: mount_path.to_string(),
84+
}
85+
})
86+
.filter(|lv| lv.mount_path != "/")
87+
.collect();
88+
89+
Ok(siblings)
90+
}
91+
92+
pub(crate) fn print_warning(volumes: Vec<LogicalVolume>) {
93+
println!();
94+
println!("NOTICE: the following logical volumes are in the same volume group as root. After reboot, these will not be automatically mounted unless defined in the bootc image. The filesystems will be preserved and continue to consume disk space. Consult the bootc documentation to determine the appropriate action for your system.");
95+
println!();
96+
for vol in volumes {
97+
println!(
98+
"Mount Path: {}, Name: {}, Size: {}, Group: {}",
99+
vol.mount_path, vol.lv_name, vol.lv_size, vol.vg_name
100+
);
101+
}
102+
press_enter();
103+
}

system-reinstall-bootc/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use anyhow::{ensure, Context, Result};
44
use bootc_utils::CommandRunExt;
55
use rustix::process::getuid;
66

7+
mod btrfs;
78
mod config;
9+
mod lvm;
810
mod podman;
911
mod prompt;
1012
pub(crate) mod users;
@@ -40,6 +42,8 @@ fn run() -> Result<()> {
4042

4143
prompt::get_ssh_keys(ssh_key_file_path)?;
4244

45+
prompt::mount_warning()?;
46+
4347
let mut reinstall_podman_command =
4448
podman::reinstall_command(&config.bootc_image, ssh_key_file_path);
4549

system-reinstall-bootc/src/prompt.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{prompt, users::get_all_users_keys};
1+
use crate::{btrfs, lvm, prompt, users::get_all_users_keys};
22
use anyhow::{ensure, Context, Result};
33

44
use crossterm::event::{self, Event};
@@ -92,6 +92,46 @@ pub(crate) fn ask_yes_no(prompt: &str, default: bool) -> Result<bool> {
9292
.context("prompting")
9393
}
9494

95+
fn check_lvm_mounts() -> Result<()> {
96+
let siblings = lvm::check_root_siblings()?;
97+
if !siblings.is_empty() {
98+
lvm::print_warning(siblings);
99+
}
100+
Ok(())
101+
}
102+
103+
fn check_btrfs_mounts() -> Result<()> {
104+
let siblings = btrfs::check_root_siblings()?;
105+
if !siblings.is_empty() {
106+
btrfs::print_warning(siblings);
107+
}
108+
Ok(())
109+
}
110+
111+
pub(crate) fn press_enter() {
112+
println!();
113+
println!("Press <enter> to continue.");
114+
115+
loop {
116+
if let Event::Key(_) = event::read().unwrap() {
117+
break;
118+
}
119+
}
120+
}
121+
122+
fn generic_warning() {
123+
println!();
124+
println!("NOTICE: the reinstall procedure will switch to use the bootc image as the system root. The current root will be available in the /sysroot directory. Some automatic cleanup of the old root is performed on reboot. Any existing mounts will not be automatically mounted by the bootc system unless they are defined in the bootc image.");
125+
press_enter()
126+
}
127+
128+
pub(crate) fn mount_warning() -> Result<()> {
129+
check_lvm_mounts()?;
130+
check_btrfs_mounts()?;
131+
generic_warning();
132+
Ok(())
133+
}
134+
95135
/// Gather authorized keys for all user's of the host system
96136
/// prompt the user to select which users's keys will be imported
97137
/// into the target system's root user's authorized_keys file

0 commit comments

Comments
 (0)