|
1 |
| -use anyhow::Result; |
| 1 | +use anyhow::{anyhow, bail, Context, Result}; |
2 | 2 | use camino::{Utf8Path, Utf8PathBuf};
|
3 | 3 | use fn_error_context::context;
|
4 | 4 |
|
@@ -51,3 +51,80 @@ pub(crate) fn install_via_bootupd(
|
51 | 51 | .verbose()
|
52 | 52 | .run()
|
53 | 53 | }
|
| 54 | + |
| 55 | +#[context("Installing bootloader using zipl")] |
| 56 | +pub(crate) fn install_via_zipl(device: &PartitionTable, boot_uuid: &str) -> Result<()> { |
| 57 | + // Identify the target boot partition from UUID |
| 58 | + let fs = crate::mount::inspect_filesystem_by_uuid(boot_uuid)?; |
| 59 | + let boot_dir = Utf8Path::new(&fs.target); |
| 60 | + let maj_min = fs.maj_min; |
| 61 | + |
| 62 | + // Ensure that the found partition is a part of the target device |
| 63 | + let device_path = device.path(); |
| 64 | + |
| 65 | + let partitions = crate::blockdev::list_dev(device_path)? |
| 66 | + .children |
| 67 | + .with_context(|| format!("no partition found on {device_path}"))?; |
| 68 | + let boot_part = partitions |
| 69 | + .iter() |
| 70 | + .find(|part| part.maj_min.as_deref() == Some(maj_min.as_str())) |
| 71 | + .with_context(|| format!("partition device {maj_min} is not on {device_path}"))?; |
| 72 | + let boot_part_offset = boot_part.start.unwrap_or(0); |
| 73 | + |
| 74 | + // Find exactly one BLS configuration under /boot/loader/entries |
| 75 | + // TODO: utilize the BLS parser in ostree |
| 76 | + let bls_dir = boot_dir.join("boot/loader/entries"); |
| 77 | + let bls_entry = bls_dir |
| 78 | + .read_dir_utf8()? |
| 79 | + .try_fold(None, |acc, e| -> Result<_> { |
| 80 | + let e = e?; |
| 81 | + let name = Utf8Path::new(e.file_name()); |
| 82 | + if let Some("conf") = name.extension() { |
| 83 | + if acc.is_some() { |
| 84 | + bail!("more than one BLS configurations under {bls_dir}"); |
| 85 | + } |
| 86 | + Ok(Some(e.path().to_owned())) |
| 87 | + } else { |
| 88 | + Ok(None) |
| 89 | + } |
| 90 | + })? |
| 91 | + .with_context(|| format!("no BLS configuration under {bls_dir}"))?; |
| 92 | + |
| 93 | + let bls_path = bls_dir.join(bls_entry); |
| 94 | + let bls_conf = |
| 95 | + std::fs::read_to_string(&bls_path).with_context(|| format!("reading {bls_path}"))?; |
| 96 | + |
| 97 | + let mut kernel = None; |
| 98 | + let mut initrd = None; |
| 99 | + let mut options = None; |
| 100 | + |
| 101 | + for line in bls_conf.lines() { |
| 102 | + match line.split_once(char::is_whitespace) { |
| 103 | + Some(("linux", val)) => kernel = Some(val.trim().trim_start_matches('/')), |
| 104 | + Some(("initrd", val)) => initrd = Some(val.trim().trim_start_matches('/')), |
| 105 | + Some(("options", val)) => options = Some(val.trim()), |
| 106 | + _ => (), |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + let kernel = kernel.ok_or_else(|| anyhow!("missing 'linux' key in default BLS config"))?; |
| 111 | + let initrd = initrd.ok_or_else(|| anyhow!("missing 'initrd' key in default BLS config"))?; |
| 112 | + let options = options.ok_or_else(|| anyhow!("missing 'options' key in default BLS config"))?; |
| 113 | + |
| 114 | + let image = boot_dir.join(kernel).canonicalize_utf8()?; |
| 115 | + let ramdisk = boot_dir.join(initrd).canonicalize_utf8()?; |
| 116 | + |
| 117 | + // Execute the zipl command to install bootloader |
| 118 | + let zipl_desc = format!("running zipl to install bootloader on {device_path}"); |
| 119 | + let zipl_task = Task::new(&zipl_desc, "zipl") |
| 120 | + .args(["--target", boot_dir.as_str()]) |
| 121 | + .args(["--image", image.as_str()]) |
| 122 | + .args(["--ramdisk", ramdisk.as_str()]) |
| 123 | + .args(["--parameters", options]) |
| 124 | + .args(["--targetbase", device_path.as_str()]) |
| 125 | + .args(["--targettype", "SCSI"]) |
| 126 | + .args(["--targetblocksize", "512"]) |
| 127 | + .args(["--targetoffset", &boot_part_offset.to_string()]) |
| 128 | + .args(["--add-files", "--verbose"]); |
| 129 | + zipl_task.verbose().run().context(zipl_desc) |
| 130 | +} |
0 commit comments