Skip to content

Commit

Permalink
Merge pull request coreos#824 from HuijingHei/sync-bootc-blockdev
Browse files Browse the repository at this point in the history
Add `bootc-blockdev` and local mod `blockdev`
  • Loading branch information
cgwalters authored Feb 6, 2025
2 parents 0ee8287 + 07c3661 commit f7e0c72
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 103 deletions.
21 changes: 20 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ path = "src/main.rs"
[dependencies]
anyhow = "1.0"
bincode = "1.3.2"
bootc-utils = { git = "https://github.com/containers/bootc", commit = "e8bb9f748fdfeb5164667046b3c6678c619ad46c" }
bootc-blockdev = { git = "https://github.com/containers/bootc", rev = "9a586935e3c88a3802ea4308b0ec364b6448c59e", package = "blockdev" }
bootc-utils = { git = "https://github.com/containers/bootc", rev = "9a586935e3c88a3802ea4308b0ec364b6448c59e" }
cap-std-ext = "4.0.4"
camino = "1.1.9"
chrono = { version = "0.4.39", features = ["serde"] }
Expand Down
125 changes: 24 additions & 101 deletions src/bios.rs
Original file line number Diff line number Diff line change
@@ -1,68 +1,21 @@
use anyhow::{bail, Result};
use std::io::prelude::*;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::process::Command;

use crate::blockdev;
use crate::component::*;
use crate::model::*;
use crate::packagesystem;
use anyhow::{bail, Result};

use crate::util;
use serde::{Deserialize, Serialize};

// grub2-install file path
pub(crate) const GRUB_BIN: &str = "usr/sbin/grub2-install";

#[derive(Serialize, Deserialize, Debug)]
struct BlockDevice {
path: String,
pttype: Option<String>,
parttypename: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Devices {
blockdevices: Vec<BlockDevice>,
}

#[derive(Default)]
pub(crate) struct Bios {}

impl Bios {
// get target device for running update
fn get_device(&self) -> Result<String> {
let mut cmd: Command;
#[cfg(target_arch = "x86_64")]
{
// find /boot partition
cmd = Command::new("findmnt");
cmd.arg("--noheadings")
.arg("--nofsroot")
.arg("--output")
.arg("SOURCE")
.arg("/boot");
let partition = util::cmd_output(&mut cmd)?;

// lsblk to find parent device
cmd = Command::new("lsblk");
cmd.arg("--paths")
.arg("--noheadings")
.arg("--output")
.arg("PKNAME")
.arg(partition.trim());
}

#[cfg(target_arch = "powerpc64")]
{
// get PowerPC-PReP-boot partition
cmd = Command::new("realpath");
cmd.arg("/dev/disk/by-partlabel/PowerPC-PReP-boot");
}

let device = util::cmd_output(&mut cmd)?;
Ok(device)
}

// Return `true` if grub2-modules installed
fn check_grub_modules(&self) -> Result<bool> {
let usr_path = Path::new("/usr/lib/grub");
Expand Down Expand Up @@ -115,37 +68,17 @@ impl Bios {
}

// check bios_boot partition on gpt type disk
fn get_bios_boot_partition(&self) -> Result<Option<String>> {
let target = self.get_device()?;
// lsblk to list children with bios_boot
let output = Command::new("lsblk")
.args([
"--json",
"--output",
"PATH,PTTYPE,PARTTYPENAME",
target.trim(),
])
.output()?;
if !output.status.success() {
std::io::stderr().write_all(&output.stderr)?;
bail!("Failed to run lsblk");
}

let output = String::from_utf8(output.stdout)?;
// Parse the JSON string into the `Devices` struct
let Ok(devices) = serde_json::from_str::<Devices>(&output) else {
bail!("Could not deserialize JSON output from lsblk");
};

// Find the device with the parttypename "BIOS boot"
for device in devices.blockdevices {
if let Some(parttypename) = &device.parttypename {
if parttypename == "BIOS boot" && device.pttype.as_deref() == Some("gpt") {
return Ok(Some(device.path));
}
fn get_bios_boot_partition(&self) -> Option<String> {
match blockdev::get_single_device("/") {
Ok(device) => {
let bios_boot_part =
blockdev::get_bios_boot_partition(&device).expect("get bios_boot part");
return bios_boot_part;
}
Err(e) => log::warn!("Get error: {}", e),
}
Ok(None)
log::debug!("Not found any bios_boot partition");
None
}
}

Expand Down Expand Up @@ -187,7 +120,7 @@ impl Component for Bios {

fn query_adopt(&self) -> Result<Option<Adoptable>> {
#[cfg(target_arch = "x86_64")]
if crate::efi::is_efi_booted()? && self.get_bios_boot_partition()?.is_none() {
if crate::efi::is_efi_booted()? && self.get_bios_boot_partition().is_none() {
log::debug!("Skip BIOS adopt");
return Ok(None);
}
Expand All @@ -199,9 +132,10 @@ impl Component for Bios {
anyhow::bail!("Failed to find adoptable system")
};

let device = self.get_device()?;
let device = device.trim();
self.run_grub_install("/", device)?;
let target_root = "/";
let device = blockdev::get_single_device(&target_root)?;
self.run_grub_install(target_root, &device)?;
log::debug!("Install grub modules on {device}");
Ok(InstalledContent {
meta: update.clone(),
filetree: None,
Expand All @@ -215,9 +149,13 @@ impl Component for Bios {

fn run_update(&self, sysroot: &openat::Dir, _: &InstalledContent) -> Result<InstalledContent> {
let updatemeta = self.query_update(sysroot)?.expect("update available");
let device = self.get_device()?;
let device = device.trim();
self.run_grub_install("/", device)?;
let dest_fd = format!("/proc/self/fd/{}", sysroot.as_raw_fd());
let dest_root = std::fs::read_link(dest_fd)?;
let device = blockdev::get_single_device(&dest_root)?;

let dest_root = dest_root.to_string_lossy().into_owned();
self.run_grub_install(&dest_root, &device)?;
log::debug!("Install grub modules on {device}");

let adopted_from = None;
Ok(InstalledContent {
Expand All @@ -235,18 +173,3 @@ impl Component for Bios {
Ok(None)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_deserialize_lsblk_output() {
let data = include_str!("../tests/fixtures/example-lsblk-output.json");
let devices: Devices = serde_json::from_str(&data).expect("JSON was not well-formatted");
assert_eq!(devices.blockdevices.len(), 7);
assert_eq!(devices.blockdevices[0].path, "/dev/sr0");
assert!(devices.blockdevices[0].pttype.is_none());
assert!(devices.blockdevices[0].parttypename.is_none());
}
}
101 changes: 101 additions & 0 deletions src/blockdev.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use camino::Utf8Path;
use std::path::Path;

use anyhow::{bail, Context, Result};
use bootc_blockdev::PartitionTable;
use fn_error_context::context;

#[context("get parent devices from mount point boot")]
pub fn get_devices<P: AsRef<Path>>(target_root: P) -> Result<Vec<String>> {
let target_root = target_root.as_ref();
let bootdir = target_root.join("boot");
if !bootdir.exists() {
bail!("{} does not exist", bootdir.display());
}
let bootdir = openat::Dir::open(&bootdir)?;
// Run findmnt to get the source path of mount point boot
let fsinfo = crate::filesystem::inspect_filesystem(&bootdir, ".")?;
// Find the parent devices of the source path
let parent_devices = bootc_blockdev::find_parent_devices(&fsinfo.source)
.with_context(|| format!("while looking for backing devices of {}", fsinfo.source))?;
log::debug!("Find parent devices: {parent_devices:?}");
Ok(parent_devices)
}

// Get single device for the target root
pub fn get_single_device<P: AsRef<Path>>(target_root: P) -> Result<String> {
let mut devices = get_devices(&target_root)?.into_iter();
let Some(parent) = devices.next() else {
anyhow::bail!("Failed to find parent device");
};

if let Some(next) = devices.next() {
anyhow::bail!("Found multiple parent devices {parent} and {next}; not currently supported");
}
Ok(parent)
}

/// Find esp partition on the same device
/// using sfdisk to get partitiontable
#[allow(dead_code)]
pub fn get_esp_partition(device: &str) -> Result<Option<String>> {
const ESP_TYPE_GUID: &str = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B";
let device_info: PartitionTable = bootc_blockdev::partitions_of(Utf8Path::new(device))?;
let esp = device_info
.partitions
.into_iter()
.find(|p| p.parttype.as_str() == ESP_TYPE_GUID);
if let Some(esp) = esp {
return Ok(Some(esp.node));
}
Ok(None)
}

/// Find all ESP partitions on the devices with mountpoint boot
#[allow(dead_code)]
pub fn find_colocated_esps<P: AsRef<Path>>(target_root: P) -> Result<Vec<String>> {
// first, get the parent device
let devices = get_devices(&target_root).with_context(|| "while looking for colocated ESPs")?;

// now, look for all ESPs on those devices
let mut esps = Vec::new();
for device in devices {
if let Some(esp) = get_esp_partition(&device)? {
esps.push(esp)
}
}
log::debug!("Find esp partitions: {esps:?}");
Ok(esps)
}

/// Find bios_boot partition on the same device
pub fn get_bios_boot_partition(device: &str) -> Result<Option<String>> {
const BIOS_BOOT_TYPE_GUID: &str = "21686148-6449-6E6F-744E-656564454649";
let device_info = bootc_blockdev::partitions_of(Utf8Path::new(device))?;
let bios_boot = device_info
.partitions
.into_iter()
.find(|p| p.parttype.as_str() == BIOS_BOOT_TYPE_GUID);
if let Some(bios_boot) = bios_boot {
return Ok(Some(bios_boot.node));
}
Ok(None)
}

/// Find all bios_boot partitions on the devices with mountpoint boot
#[allow(dead_code)]
pub fn find_colocated_bios_boot<P: AsRef<Path>>(target_root: P) -> Result<Vec<String>> {
// first, get the parent device
let devices =
get_devices(&target_root).with_context(|| "looking for colocated bios_boot parts")?;

// now, look for all bios_boot parts on those devices
let mut bios_boots = Vec::new();
for device in devices {
if let Some(bios) = get_bios_boot_partition(&device)? {
bios_boots.push(bios)
}
}
log::debug!("Find bios_boot partitions: {bios_boots:?}");
Ok(bios_boots)
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Refs:
mod backend;
#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))]
mod bios;
mod blockdev;
mod bootupd;
mod cli;
mod component;
Expand Down

0 comments on commit f7e0c72

Please sign in to comment.