From be83b4d4e592fae7ec89d65cfb1ad5db551ed699 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 30 Nov 2021 16:56:54 -0500 Subject: [PATCH] bind-boot: look for ESPs on the same disk(s) as bootfs Right now, to find the ESP devices, we just query all the devices on the system with the matching partition type. This is clumsy though. E.g. in case multipathing is in use, this will match both the multipathed version and the non-multipathed versions. Another case is multiple re-installs of the OS on different disks: we're working to ensure that users don't have multiple boot filesystems, but haven't so far required them to also wipe old ESPs. So we want to ensure we only modify the ESPs on *our* boot disk. Rework things by trying to be smarter: since we know what the right boot filesystem is (`coreos-boot-edit.service` uses `--boot-mount`), use that knowledge to derive the ESPs by only looking for them in the same storage hierarchy. --- src/bin/rdcore/rootmap.rs | 2 +- src/blockdev.rs | 67 +++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/bin/rdcore/rootmap.rs b/src/bin/rdcore/rootmap.rs index 466c98010..ce32f1016 100644 --- a/src/bin/rdcore/rootmap.rs +++ b/src/bin/rdcore/rootmap.rs @@ -278,7 +278,7 @@ pub fn bind_boot(config: BindBootConfig) -> Result<()> { write_boot_uuid_grub2_dropin(&boot_uuid, grub_bios_path)?; } - for esp in find_esps()? { + for esp in find_colocated_esps(boot_mount.device())? { let mount = Mount::try_mount(&esp, "vfat", mount::MsFlags::empty())?; let vendor_dir = find_efi_vendor_dir(&mount)?; let grub_efi_path = vendor_dir.join("bootuuid.cfg"); diff --git a/src/blockdev.rs b/src/blockdev.rs index 4727f7c5e..95e847e13 100644 --- a/src/blockdev.rs +++ b/src/blockdev.rs @@ -877,30 +877,71 @@ pub fn lsblk(dev: &Path, with_deps: bool) -> Result> Ok(result) } -pub fn find_esps() -> Result> { - const ESP_TYPE_GUID: &str = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"; +/// This is a bit fuzzy, but... this function will return every block device in the parent +/// hierarchy of `device` capable of containing other partitions. So e.g. parent devices of type +/// "part" doesn't match, but "disk" and "mpath" does. +pub fn find_parent_devices(device: &str) -> Result> { let mut cmd = Command::new("lsblk"); - // Older lsblk, e.g. in CentOS 7.6, doesn't support PATH, but --paths option cmd.arg("--pairs") .arg("--paths") + .arg("--inverse") .arg("--output") - .arg("NAME,PARTTYPE"); + .arg("NAME,TYPE") + .arg(device); let output = cmd_output(&mut cmd)?; - output - .lines() - .filter_map(|line| { + let mut parents = Vec::new(); + // skip first line, which is the device itself + for line in output.lines().skip(1) { + let dev = split_lsblk_line(line); + let name = dev + .get("NAME") + .ok_or_else(|| anyhow!("device in hierarchy of {} missing NAME", device))?; + let kind = dev + .get("TYPE") + .ok_or_else(|| anyhow!("device in hierarchy of {} missing TYPE", device))?; + if kind == "disk" { + parents.push(name.clone()); + } else if kind == "mpath" { + parents.push(name.clone()); + // we don't need to know what disks back the multipath + break; + } + } + if parents.is_empty() { + bail!("no parent devices found for {}", device); + } + Ok(parents) +} + +/// Find ESP partitions which sit at the same hierarchy level as `device`. +pub fn find_colocated_esps(device: &str) -> Result> { + const ESP_TYPE_GUID: &str = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"; + + // first, get the parent device + let parent_devices = find_parent_devices(device) + .with_context(|| format!("while looking for colocated ESPs of '{}'", device))?; + + // now, look for all ESPs on those devices + let mut esps = Vec::new(); + for parent_device in parent_devices { + let mut cmd = Command::new("lsblk"); + cmd.arg("--pairs") + .arg("--paths") + .arg("--output") + .arg("NAME,PARTTYPE") + .arg(parent_device); + for line in cmd_output(&mut cmd)?.lines() { let dev = split_lsblk_line(line); if dev.get("PARTTYPE").map(|t| t.as_str()) == Some(ESP_TYPE_GUID) { - Some( + esps.push( dev.get("NAME") .cloned() - .ok_or_else(|| anyhow!("ESP device with missing NAME")), + .ok_or_else(|| anyhow!("ESP device with missing NAME"))?, ) - } else { - None } - }) - .collect() + } + } + Ok(esps) } /// This is basically a Rust version of: