diff --git a/lib/src/blockdev.rs b/lib/src/blockdev.rs index 7f737f151..0381128a8 100644 --- a/lib/src/blockdev.rs +++ b/lib/src/blockdev.rs @@ -27,10 +27,18 @@ pub(crate) struct Device { pub(crate) name: String, pub(crate) serial: Option, pub(crate) model: Option, + pub(crate) partlabel: Option, + pub(crate) children: Option>, + pub(crate) size: u64, + #[serde(rename = "maj:min")] + pub(crate) maj_min: Option, + // NOTE this one is not available on older util-linux, and + // will also not exist for whole blockdevs (as opposed to partitions). + pub(crate) start: Option, + + // Filesystem-related properties pub(crate) label: Option, pub(crate) fstype: Option, - pub(crate) children: Option>, - pub(crate) size: Option, } impl Device { @@ -43,6 +51,39 @@ impl Device { pub(crate) fn has_children(&self) -> bool { self.children.as_ref().map_or(false, |v| !v.is_empty()) } + + // The "start" parameter was only added in a version of util-linux that's only + // in Fedora 40 as of this writing. + fn backfill_start(&mut self) -> Result<()> { + let Some(majmin) = self.maj_min.as_deref() else { + // This shouldn't happen + return Ok(()); + }; + let sysfs_start_path = format!("/sys/dev/block/{majmin}/start"); + if Utf8Path::new(&sysfs_start_path).try_exists()? { + let start = std::fs::read_to_string(&sysfs_start_path) + .with_context(|| format!("Reading {sysfs_start_path}"))?; + tracing::debug!("backfilled start to {start}"); + self.start = Some( + start + .trim() + .parse() + .context("Parsing sysfs start property")?, + ); + } + Ok(()) + } + + /// Older versions of util-linux may be missing some properties. Backfill them if they're missing. + pub(crate) fn backfill_missing(&mut self) -> Result<()> { + // Add new properties to backfill here + self.backfill_start()?; + // And recurse to child devices + for child in self.children.iter_mut().flatten() { + child.backfill_missing()?; + } + Ok(()) + } } #[context("Failed to wipe {dev}")] @@ -56,13 +97,16 @@ pub(crate) fn wipefs(dev: &Utf8Path) -> Result<()> { fn list_impl(dev: Option<&Utf8Path>) -> Result> { let o = Command::new("lsblk") - .args(["-J", "-o", "NAME,SERIAL,MODEL,LABEL,FSTYPE,SIZE"]) + .args(["-J", "-b", "-O"]) .args(dev) .output()?; if !o.status.success() { return Err(anyhow::anyhow!("Failed to list block devices")); } - let devs: DevicesOutput = serde_json::from_reader(&*o.stdout)?; + let mut devs: DevicesOutput = serde_json::from_reader(&*o.stdout)?; + for dev in devs.blockdevices.iter_mut() { + dev.backfill_missing()?; + } Ok(devs.blockdevices) } diff --git a/lib/src/bootloader.rs b/lib/src/bootloader.rs index ed24dbd3f..810a2eb02 100644 --- a/lib/src/bootloader.rs +++ b/lib/src/bootloader.rs @@ -2,6 +2,7 @@ use anyhow::Result; use camino::Utf8Path; use fn_error_context::context; +use crate::blockdev::Device; use crate::task::Task; /// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel) @@ -9,18 +10,19 @@ pub(crate) const EFI_DIR: &str = "efi"; #[context("Installing bootloader")] pub(crate) fn install_via_bootupd( - device: &Utf8Path, + device: &Device, rootfs: &Utf8Path, configopts: &crate::install::InstallConfigOpts, ) -> Result<()> { let verbose = std::env::var_os("BOOTC_BOOTLOADER_DEBUG").map(|_| "-vvvv"); // bootc defaults to only targeting the platform boot method. let bootupd_opts = (!configopts.generic_image).then_some(["--update-firmware", "--auto"]); + let devpath = device.path(); let args = ["backend", "install", "--write-uuid"] .into_iter() .chain(verbose) .chain(bootupd_opts.iter().copied().flatten()) - .chain(["--device", device.as_str(), rootfs.as_str()]); + .chain(["--device", devpath.as_str(), rootfs.as_str()]); Task::new("Running bootupctl to install bootloader", "bootupctl") .args(args) .verbose() diff --git a/lib/src/install.rs b/lib/src/install.rs index 4f3fa66d7..9c7acee21 100644 --- a/lib/src/install.rs +++ b/lib/src/install.rs @@ -823,7 +823,7 @@ fn require_skopeo_with_containers_storage() -> Result<()> { pub(crate) struct RootSetup { luks_device: Option, - device: Utf8PathBuf, + device_info: crate::blockdev::Device, rootfs: Utf8PathBuf, rootfs_fd: Dir, rootfs_uuid: Option, @@ -1240,7 +1240,11 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re .context("Writing aleph version")?; } - crate::bootloader::install_via_bootupd(&rootfs.device, &rootfs.rootfs, &state.config_opts)?; + crate::bootloader::install_via_bootupd( + &rootfs.device_info, + &rootfs.rootfs, + &state.config_opts, + )?; tracing::debug!("Installed bootloader"); // Finalize mounted filesystems @@ -1594,6 +1598,7 @@ pub(crate) async fn install_to_filesystem( dev }; tracing::debug!("Backing device: {backing_device}"); + let device_info = crate::blockdev::list_dev(Utf8Path::new(&backing_device))?; let rootarg = format!("root={}", root_info.mount_spec); let mut boot = if let Some(spec) = fsopts.boot_mount_spec { @@ -1622,7 +1627,7 @@ pub(crate) async fn install_to_filesystem( matches!(fsopts.replace, Some(ReplaceMode::Alongside)) || fsopts.skip_finalize; let mut rootfs = RootSetup { luks_device: None, - device: backing_device.into(), + device_info, rootfs: fsopts.root_path, rootfs_fd, rootfs_uuid: inspect.uuid.clone(), diff --git a/lib/src/install/baseline.rs b/lib/src/install/baseline.rs index f60c6bc64..31abbe6ab 100644 --- a/lib/src/install/baseline.rs +++ b/lib/src/install/baseline.rs @@ -125,7 +125,7 @@ fn mkfs<'a>( opts: impl IntoIterator, ) -> Result { let devinfo = crate::blockdev::list_dev(dev.into())?; - let size = devinfo.size.as_deref().unwrap_or("(unknown)"); + let size = ostree_ext::glib::format_size(devinfo.size); let u = uuid::Uuid::new_v4(); let mut t = Task::new( &format!("Creating {label} filesystem ({fs}) on device {dev} (size={size})"), @@ -210,12 +210,8 @@ pub(crate) fn install_create_rootfs( }; let serial = device.serial.as_deref().unwrap_or(""); let model = device.model.as_deref().unwrap_or(""); - let size = device - .size - .as_deref() - .ok_or_else(|| anyhow::anyhow!("Missing size for blockdev"))?; println!("Block setup: {block_setup}"); - println!(" Size: {size}",); + println!(" Size: {}", device.size); println!(" Serial: {serial}"); println!(" Model: {model}"); @@ -443,9 +439,10 @@ pub(crate) fn install_create_rootfs( BlockSetup::Direct => None, BlockSetup::Tpm2Luks => Some(luks_name.to_string()), }; + let device_info = crate::blockdev::list_dev(&devpath)?; Ok(RootSetup { luks_device, - device: devpath, + device_info, rootfs, rootfs_fd, rootfs_uuid: Some(root_uuid.to_string()),