mirror of
https://github.com/edera-dev/sprout.git
synced 2026-11-04 19:52:22 +00:00
fix(extractors): skip filesystems that can't be checked for a match
filesystem-device-match failed as soon as any filesystem couldn't be checked, such as a removable device without media or a filesystem that couldn't be opened exclusively. The fallback value was never used and boot was aborted. Log a warning and check the next filesystem instead. The filesystem protocol is now only opened when the label or item criteria need it, and the partition UUIDs are parsed once up front.
This commit is contained in:
1 parent
642236edec
commit
aed9903180
1 file changed
+77
-46
@@ -6,41 +6,25 @@ use core::ops::Deref;
|
|||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
use edera_sprout_config::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor;
|
use edera_sprout_config::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor;
|
||||||
use eficore::partition::PartitionGuidForm;
|
use eficore::partition::PartitionGuidForm;
|
||||||
|
use log::warn;
|
||||||
use uefi::fs::{FileSystem, Path};
|
use uefi::fs::{FileSystem, Path};
|
||||||
use uefi::proto::device_path::DevicePath;
|
use uefi::proto::device_path::DevicePath;
|
||||||
use uefi::proto::media::file::{File, FileSystemVolumeLabel};
|
use uefi::proto::media::file::{File, FileSystemVolumeLabel};
|
||||||
use uefi::proto::media::fs::SimpleFileSystem;
|
use uefi::proto::media::fs::SimpleFileSystem;
|
||||||
use uefi::{CString16, Guid};
|
use uefi::{CString16, Guid, Handle};
|
||||||
|
|
||||||
/// Extract a filesystem device path using the specified `context` and `extractor` configuration.
|
/// Check whether the filesystem at `handle` matches all the criteria of the `extractor`
|
||||||
pub fn extract(
|
/// configuration inside the `context`. The partition UUIDs are parsed ahead of time into
|
||||||
context: Rc<SproutContext>,
|
/// `partition_uuid` and `partition_type_uuid`.
|
||||||
|
fn is_match(
|
||||||
|
context: &Rc<SproutContext>,
|
||||||
extractor: &FilesystemDeviceMatchExtractor,
|
extractor: &FilesystemDeviceMatchExtractor,
|
||||||
) -> Result<String> {
|
handle: Handle,
|
||||||
// If no criteria are provided, bail with an error.
|
partition_uuid: Option<Guid>,
|
||||||
if extractor.has_label.is_none()
|
partition_type_uuid: Option<Guid>,
|
||||||
&& extractor.has_item.is_none()
|
) -> Result<bool> {
|
||||||
&& extractor.has_partition_uuid.is_none()
|
|
||||||
&& extractor.has_partition_type_uuid.is_none()
|
|
||||||
{
|
|
||||||
bail!("at least one criteria is required for filesystem-device-match");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all the filesystems inside the UEFI stack.
|
|
||||||
let handles = uefi::boot::find_handles::<SimpleFileSystem>()
|
|
||||||
.context("unable to find filesystem handles")?;
|
|
||||||
|
|
||||||
// Iterate over all the filesystems and check if they match the criteria.
|
|
||||||
for handle in handles {
|
|
||||||
// This defines whether a match has been found.
|
|
||||||
let mut has_match = false;
|
|
||||||
|
|
||||||
// Check if the partition info matches partition uuid criteria.
|
// Check if the partition info matches partition uuid criteria.
|
||||||
if let Some(ref has_partition_uuid) = extractor.has_partition_uuid {
|
if let Some(parsed_uuid) = partition_uuid {
|
||||||
// Parse the partition uuid from the extractor.
|
|
||||||
let parsed_uuid = Guid::from_str(has_partition_uuid)
|
|
||||||
.map_err(|e| anyhow!("unable to parse has-partition-uuid: {}", e))?;
|
|
||||||
|
|
||||||
// Fetch the root of the device.
|
// Fetch the root of the device.
|
||||||
let root = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
|
let root = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
|
||||||
.context("unable to fetch the device path of the filesystem")?
|
.context("unable to fetch the device path of the filesystem")?
|
||||||
@@ -53,19 +37,13 @@ pub fn extract(
|
|||||||
.context("unable to fetch the partition uuid of the filesystem")?;
|
.context("unable to fetch the partition uuid of the filesystem")?;
|
||||||
|
|
||||||
// Compare the partition uuid to the parsed uuid.
|
// Compare the partition uuid to the parsed uuid.
|
||||||
// If it does not match, continue to the next filesystem.
|
|
||||||
if partition_uuid != Some(parsed_uuid) {
|
if partition_uuid != Some(parsed_uuid) {
|
||||||
continue;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
has_match = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the partition info matches partition type uuid criteria.
|
// Check if the partition info matches partition type uuid criteria.
|
||||||
if let Some(ref has_partition_type_uuid) = extractor.has_partition_type_uuid {
|
if let Some(parsed_uuid) = partition_type_uuid {
|
||||||
// Parse the partition type uuid from the extractor.
|
|
||||||
let parsed_uuid = Guid::from_str(has_partition_type_uuid)
|
|
||||||
.map_err(|e| anyhow!("unable to parse has-partition-type-uuid: {}", e))?;
|
|
||||||
|
|
||||||
// Fetch the root of the device.
|
// Fetch the root of the device.
|
||||||
let root = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
|
let root = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
|
||||||
.context("unable to fetch the device path of the filesystem")?
|
.context("unable to fetch the device path of the filesystem")?
|
||||||
@@ -76,12 +54,16 @@ pub fn extract(
|
|||||||
let partition_type_uuid =
|
let partition_type_uuid =
|
||||||
eficore::partition::partition_guid(&root, PartitionGuidForm::PartitionType)
|
eficore::partition::partition_guid(&root, PartitionGuidForm::PartitionType)
|
||||||
.context("unable to fetch the partition uuid of the filesystem")?;
|
.context("unable to fetch the partition uuid of the filesystem")?;
|
||||||
|
|
||||||
// Compare the partition type uuid to the parsed uuid.
|
// Compare the partition type uuid to the parsed uuid.
|
||||||
// If it does not match, continue to the next filesystem.
|
|
||||||
if partition_type_uuid != Some(parsed_uuid) {
|
if partition_type_uuid != Some(parsed_uuid) {
|
||||||
continue;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
has_match = true;
|
}
|
||||||
|
|
||||||
|
// The remaining criteria need the filesystem itself.
|
||||||
|
if extractor.has_label.is_none() && extractor.has_item.is_none() {
|
||||||
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the filesystem protocol for this handle.
|
// Open the filesystem protocol for this handle.
|
||||||
@@ -100,9 +82,8 @@ pub fn extract(
|
|||||||
.context("unable to get filesystem volume label")?;
|
.context("unable to get filesystem volume label")?;
|
||||||
|
|
||||||
if label.volume_label() != want_label {
|
if label.volume_label() != want_label {
|
||||||
continue;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
has_match = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the filesystem matches item criteria.
|
// Check if the filesystem matches item criteria.
|
||||||
@@ -114,20 +95,70 @@ pub fn extract(
|
|||||||
// Check the metadata of the item.
|
// Check the metadata of the item.
|
||||||
// Ignore filesystem errors as we can't do anything useful with the error.
|
// Ignore filesystem errors as we can't do anything useful with the error.
|
||||||
let Some(metadata) = filesystem.metadata(Path::new(&want_item)).ok() else {
|
let Some(metadata) = filesystem.metadata(Path::new(&want_item)).ok() else {
|
||||||
continue;
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only check directories and files.
|
// Only check directories and files.
|
||||||
if !(metadata.is_directory() || metadata.is_regular_file()) {
|
if !(metadata.is_directory() || metadata.is_regular_file()) {
|
||||||
continue;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
has_match = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no match, continue to the next filesystem.
|
Ok(true)
|
||||||
if !has_match {
|
}
|
||||||
|
|
||||||
|
/// Extract a filesystem device path using the specified `context` and `extractor` configuration.
|
||||||
|
pub fn extract(
|
||||||
|
context: Rc<SproutContext>,
|
||||||
|
extractor: &FilesystemDeviceMatchExtractor,
|
||||||
|
) -> Result<String> {
|
||||||
|
// If no criteria are provided, bail with an error.
|
||||||
|
if extractor.has_label.is_none()
|
||||||
|
&& extractor.has_item.is_none()
|
||||||
|
&& extractor.has_partition_uuid.is_none()
|
||||||
|
&& extractor.has_partition_type_uuid.is_none()
|
||||||
|
{
|
||||||
|
bail!("at least one criteria is required for filesystem-device-match");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the partition uuid from the extractor.
|
||||||
|
let partition_uuid = extractor
|
||||||
|
.has_partition_uuid
|
||||||
|
.as_deref()
|
||||||
|
.map(Guid::from_str)
|
||||||
|
.transpose()
|
||||||
|
.map_err(|e| anyhow!("unable to parse has-partition-uuid: {}", e))?;
|
||||||
|
|
||||||
|
// Parse the partition type uuid from the extractor.
|
||||||
|
let partition_type_uuid = extractor
|
||||||
|
.has_partition_type_uuid
|
||||||
|
.as_deref()
|
||||||
|
.map(Guid::from_str)
|
||||||
|
.transpose()
|
||||||
|
.map_err(|e| anyhow!("unable to parse has-partition-type-uuid: {}", e))?;
|
||||||
|
|
||||||
|
// Find all the filesystems inside the UEFI stack.
|
||||||
|
let handles = uefi::boot::find_handles::<SimpleFileSystem>()
|
||||||
|
.context("unable to find filesystem handles")?;
|
||||||
|
|
||||||
|
// Iterate over all the filesystems and check if they match the criteria.
|
||||||
|
for handle in handles {
|
||||||
|
// A filesystem that can't be checked is skipped, as it should not prevent
|
||||||
|
// finding a match on the other filesystems or using the fallback value.
|
||||||
|
match is_match(
|
||||||
|
&context,
|
||||||
|
extractor,
|
||||||
|
handle,
|
||||||
|
partition_uuid,
|
||||||
|
partition_type_uuid,
|
||||||
|
) {
|
||||||
|
Ok(true) => {}
|
||||||
|
Ok(false) => continue,
|
||||||
|
Err(error) => {
|
||||||
|
warn!("skipping filesystem, unable to check it: {:#}", error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we have a match, return the device root path.
|
// If we have a match, return the device root path.
|
||||||
let path = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
|
let path = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
|
||||||
|
|||||||
Reference in new issue
Block a user