Skip to content

Commit 3d93701

Browse files
authored
Merge pull request #1299 from ckyrouac/reinstall-mount-warning
Reinstall mount warning
2 parents 233e24b + 118dced commit 3d93701

File tree

7 files changed

+163
-16
lines changed

7 files changed

+163
-16
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: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,29 +45,28 @@ 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
}
5252

53-
fn run_findmnt(args: &[&str], path: &str) -> Result<Findmnt> {
54-
let o: Findmnt = Command::new("findmnt")
55-
.args([
56-
"-J",
57-
"-v",
58-
// If you change this you probably also want to change the Filesystem struct above
59-
"--output=SOURCE,TARGET,MAJ:MIN,FSTYPE,OPTIONS,UUID",
60-
])
61-
.args(args)
62-
.arg(path)
63-
.log_debug()
64-
.run_and_parse_json()?;
53+
pub fn run_findmnt(args: &[&str], path: Option<&str>) -> Result<Findmnt> {
54+
let mut cmd = Command::new("findmnt");
55+
cmd.args([
56+
"-J",
57+
"-v",
58+
// If you change this you probably also want to change the Filesystem struct above
59+
"--output=SOURCE,TARGET,MAJ:MIN,FSTYPE,OPTIONS,UUID",
60+
])
61+
.args(args)
62+
.args(path);
63+
let o: Findmnt = cmd.log_debug().run_and_parse_json()?;
6564
Ok(o)
6665
}
6766

6867
// Retrieve a mounted filesystem from a device given a matching path
6968
fn findmnt_filesystem(args: &[&str], path: &str) -> Result<Filesystem> {
70-
let o = run_findmnt(args, path)?;
69+
let o = run_findmnt(args, Some(path))?;
7170
o.filesystems
7271
.into_iter()
7372
.next()
@@ -90,7 +89,7 @@ pub fn inspect_filesystem_by_uuid(uuid: &str) -> Result<Filesystem> {
9089
// Check if a specified device contains an already mounted filesystem
9190
// in the root mount namespace
9291
pub fn is_mounted_in_pid1_mountns(path: &str) -> Result<bool> {
93-
let o = run_findmnt(&["-N"], "1")?;
92+
let o = run_findmnt(&["-N"], Some("1"))?;
9493

9594
let mounted = o.filesystems.iter().any(|fs| is_source_mounted(path, fs));
9695

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"

system-reinstall-bootc/src/btrfs.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use anyhow::Result;
2+
use bootc_mount::Filesystem;
3+
4+
pub(crate) fn check_root_siblings() -> Result<Vec<String>> {
5+
let mounts = bootc_mount::run_findmnt(&[], None)?;
6+
let problem_filesystems: Vec<String> = mounts
7+
.filesystems
8+
.iter()
9+
.filter(|fs| fs.target == "/")
10+
.flat_map(|root| {
11+
let children: Vec<&Filesystem> = root
12+
.children
13+
.iter()
14+
.flatten()
15+
.filter(|child| child.source == root.source)
16+
.collect();
17+
children
18+
})
19+
.map(|zs| {
20+
format!(
21+
"Type: {}, Mount Point: {}, Source: {}",
22+
zs.fstype, zs.target, zs.source
23+
)
24+
})
25+
.collect();
26+
Ok(problem_filesystems)
27+
}

system-reinstall-bootc/src/lvm.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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+
#[derive(Debug, Deserialize)]
9+
pub(crate) struct Lvs {
10+
report: Vec<LvsReport>,
11+
}
12+
13+
#[derive(Debug, Deserialize)]
14+
pub(crate) struct LvsReport {
15+
lv: Vec<LogicalVolume>,
16+
}
17+
18+
#[derive(Debug, Deserialize, Clone)]
19+
pub(crate) struct LogicalVolume {
20+
lv_name: String,
21+
lv_size: String,
22+
lv_path: String,
23+
vg_name: String,
24+
}
25+
26+
pub(crate) fn parse_volumes(group: Option<&str>) -> Result<Vec<LogicalVolume>> {
27+
if which::which("podman").is_err() {
28+
tracing::debug!("lvs binary not found. Skipping logical volume check.");
29+
return Ok(Vec::<LogicalVolume>::new());
30+
}
31+
32+
let mut cmd = Command::new("lvs");
33+
cmd.args([
34+
"--reportformat=json",
35+
"-o",
36+
"lv_name,lv_size,lv_path,vg_name",
37+
])
38+
.args(group);
39+
40+
let output: Lvs = cmd.run_and_parse_json()?;
41+
42+
Ok(output
43+
.report
44+
.iter()
45+
.flat_map(|r| r.lv.iter().cloned())
46+
.collect())
47+
}
48+
49+
pub(crate) fn check_root_siblings() -> Result<Vec<String>> {
50+
let all_volumes = parse_volumes(None)?;
51+
52+
// first look for a lv mounted to '/'
53+
// then gather all the sibling lvs in the vg along with their mount points
54+
let siblings: Vec<String> = all_volumes
55+
.iter()
56+
.filter(|lv| {
57+
let mount = run_findmnt(&["-S", &lv.lv_path], None).unwrap_or_default();
58+
if let Some(fs) = mount.filesystems.first() {
59+
&fs.target == "/"
60+
} else {
61+
false
62+
}
63+
})
64+
.flat_map(|root_lv| parse_volumes(Some(root_lv.vg_name.as_str())).unwrap_or_default())
65+
.try_fold(Vec::new(), |mut acc, r| -> anyhow::Result<_> {
66+
let mount = run_findmnt(&["-S", &r.lv_path], None)?;
67+
let mount_path = if let Some(fs) = mount.filesystems.first() {
68+
&fs.target
69+
} else {
70+
""
71+
};
72+
73+
if mount_path != "/" {
74+
acc.push(format!(
75+
"Type: LVM, Mount Point: {}, LV: {}, VG: {}, Size: {}",
76+
mount_path, r.lv_name, r.vg_name, r.lv_size
77+
))
78+
};
79+
80+
Ok(acc)
81+
})?;
82+
83+
Ok(siblings)
84+
}

system-reinstall-bootc/src/main.rs

Lines changed: 7 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

@@ -48,6 +52,9 @@ fn run() -> Result<()> {
4852
println!();
4953
println!("{}", reinstall_podman_command.to_string_pretty());
5054

55+
println!();
56+
println!("After reboot, the current root will be available in the /sysroot directory. Existing mounts will not be automatically mounted by the bootc system unless they are defined in the bootc image. Some automatic cleanup of the previous root will be performed.");
57+
5158
prompt::temporary_developer_protection_prompt()?;
5259

5360
reinstall_podman_command

system-reinstall-bootc/src/prompt.rs

Lines changed: 29 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,34 @@ pub(crate) fn ask_yes_no(prompt: &str, default: bool) -> Result<bool> {
9292
.context("prompting")
9393
}
9494

95+
pub(crate) fn press_enter() {
96+
println!();
97+
println!("Press <enter> to continue.");
98+
99+
loop {
100+
if let Event::Key(_) = event::read().unwrap() {
101+
break;
102+
}
103+
}
104+
}
105+
106+
pub(crate) fn mount_warning() -> Result<()> {
107+
let mut mounts = btrfs::check_root_siblings()?;
108+
mounts.extend(lvm::check_root_siblings()?);
109+
110+
if !mounts.is_empty() {
111+
println!();
112+
println!("NOTICE: the following mounts are left unchanged by this tool and will not be automatically mounted unless specified in the bootc image. Consult the bootc documentation to determine the appropriate action for your system.");
113+
println!();
114+
for m in mounts {
115+
println!("{m}");
116+
}
117+
press_enter();
118+
}
119+
120+
Ok(())
121+
}
122+
95123
/// Gather authorized keys for all user's of the host system
96124
/// prompt the user to select which users's keys will be imported
97125
/// into the target system's root user's authorized_keys file

0 commit comments

Comments
 (0)