mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 10:10:17 +00:00
implement support for filesystem extractor mechanism
This commit is contained in:
@@ -1,9 +1,12 @@
|
|||||||
version = 1
|
version = 1
|
||||||
|
|
||||||
|
[extractors.boot.filesystem]
|
||||||
|
item = "\\EFI\\BOOT\\kernel.efi"
|
||||||
|
|
||||||
[actions.chainload-kernel]
|
[actions.chainload-kernel]
|
||||||
chainload.path = "\\EFI\\BOOT\\kernel.efi"
|
chainload.path = "$boot\\EFI\\BOOT\\kernel.efi"
|
||||||
chainload.options = ["console=hvc0"]
|
chainload.options = ["console=hvc0"]
|
||||||
chainload.linux-initrd = "\\initramfs"
|
chainload.linux-initrd = "$boot\\initramfs"
|
||||||
|
|
||||||
[entries.kernel]
|
[entries.kernel]
|
||||||
title = "Boot Linux"
|
title = "Boot Linux"
|
||||||
|
|||||||
@@ -20,22 +20,20 @@ pub struct ChainloadConfiguration {
|
|||||||
|
|
||||||
pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfiguration) -> Result<()> {
|
pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfiguration) -> Result<()> {
|
||||||
let sprout_image = uefi::boot::image_handle();
|
let sprout_image = uefi::boot::image_handle();
|
||||||
let image_device_path_protocol =
|
let _image_device_path_protocol =
|
||||||
uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(sprout_image)
|
uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(sprout_image)
|
||||||
.context("unable to open loaded image device path protocol")?;
|
.context("unable to open loaded image device path protocol")?;
|
||||||
|
|
||||||
let mut full_path = utils::device_path_root(&image_device_path_protocol)?;
|
let resolved = utils::resolve_path(
|
||||||
|
context.root().loaded_image_path()?,
|
||||||
full_path.push_str(&context.stamp(&configuration.path));
|
&context.stamp(&configuration.path),
|
||||||
|
)
|
||||||
info!("path: {}", full_path);
|
.context("failed to resolve chainload path")?;
|
||||||
|
|
||||||
let device_path = utils::text_to_device_path(&full_path)?;
|
|
||||||
|
|
||||||
let image = uefi::boot::load_image(
|
let image = uefi::boot::load_image(
|
||||||
sprout_image,
|
sprout_image,
|
||||||
uefi::boot::LoadImageSource::FromDevicePath {
|
uefi::boot::LoadImageSource::FromDevicePath {
|
||||||
device_path: &device_path,
|
device_path: &resolved.full_path,
|
||||||
boot_policy: uefi::proto::BootPolicy::ExactMatch,
|
boot_policy: uefi::proto::BootPolicy::ExactMatch,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -77,8 +75,8 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
|
|||||||
let mut initrd_handle = None;
|
let mut initrd_handle = None;
|
||||||
if let Some(ref linux_initrd) = configuration.linux_initrd {
|
if let Some(ref linux_initrd) = configuration.linux_initrd {
|
||||||
let initrd_path = context.stamp(linux_initrd);
|
let initrd_path = context.stamp(linux_initrd);
|
||||||
let content =
|
let content = utils::read_file_contents(context.root().loaded_image_path()?, &initrd_path)
|
||||||
utils::read_file_contents(&initrd_path).context("failed to read linux initrd")?;
|
.context("failed to read linux initrd")?;
|
||||||
initrd_handle = Some(
|
initrd_handle = Some(
|
||||||
register_linux_initrd(content.into_boxed_slice())
|
register_linux_initrd(content.into_boxed_slice())
|
||||||
.context("failed to register linux initrd")?,
|
.context("failed to register linux initrd")?,
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ fn draw(image: DynamicImage) -> Result<()> {
|
|||||||
|
|
||||||
pub fn splash(context: Rc<SproutContext>, configuration: &SplashConfiguration) -> Result<()> {
|
pub fn splash(context: Rc<SproutContext>, configuration: &SplashConfiguration) -> Result<()> {
|
||||||
let image = context.stamp(&configuration.image);
|
let image = context.stamp(&configuration.image);
|
||||||
let image = read_file_contents(&image)?;
|
let image = read_file_contents(context.root().loaded_image_path()?, &image)?;
|
||||||
let image = ImageReader::with_format(Cursor::new(image), ImageFormat::Png)
|
let image = ImageReader::with_format(Cursor::new(image), ImageFormat::Png)
|
||||||
.decode()
|
.decode()
|
||||||
.context("failed to decode splash image")?;
|
.context("failed to decode splash image")?;
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use crate::actions::ActionDeclaration;
|
use crate::actions::ActionDeclaration;
|
||||||
use crate::drivers::DriverDeclaration;
|
use crate::drivers::DriverDeclaration;
|
||||||
|
use crate::extractors::ExtractorDeclaration;
|
||||||
use crate::generators::GeneratorDeclaration;
|
use crate::generators::GeneratorDeclaration;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use uefi::proto::device_path::LoadedImageDevicePath;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default, Clone)]
|
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||||
pub struct RootConfiguration {
|
pub struct RootConfiguration {
|
||||||
@@ -16,6 +19,8 @@ pub struct RootConfiguration {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub drivers: BTreeMap<String, DriverDeclaration>,
|
pub drivers: BTreeMap<String, DriverDeclaration>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub extractors: BTreeMap<String, ExtractorDeclaration>,
|
||||||
|
#[serde(default)]
|
||||||
pub actions: BTreeMap<String, ActionDeclaration>,
|
pub actions: BTreeMap<String, ActionDeclaration>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub entries: BTreeMap<String, EntryDeclaration>,
|
pub entries: BTreeMap<String, EntryDeclaration>,
|
||||||
@@ -49,8 +54,13 @@ pub struct PhaseConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load() -> Result<RootConfiguration> {
|
pub fn load() -> Result<RootConfiguration> {
|
||||||
let content =
|
let current_image_device_path_protocol =
|
||||||
utils::read_file_contents("sprout.toml").context("unable to read sprout.toml file")?;
|
uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(uefi::boot::image_handle())
|
||||||
|
.context("failed to get loaded image device path")?;
|
||||||
|
let path = current_image_device_path_protocol.deref().to_boxed();
|
||||||
|
|
||||||
|
let content = utils::read_file_contents(&path, "sprout.toml")
|
||||||
|
.context("unable to read sprout.toml file")?;
|
||||||
let config: RootConfiguration =
|
let config: RootConfiguration =
|
||||||
toml::from_slice(&content).context("unable to parse sprout.toml file")?;
|
toml::from_slice(&content).context("unable to parse sprout.toml file")?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
use crate::actions::ActionDeclaration;
|
use crate::actions::ActionDeclaration;
|
||||||
|
use anyhow::Result;
|
||||||
|
use anyhow::anyhow;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use uefi::proto::device_path::DevicePath;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RootContext {
|
pub struct RootContext {
|
||||||
actions: BTreeMap<String, ActionDeclaration>,
|
actions: BTreeMap<String, ActionDeclaration>,
|
||||||
|
loaded_image_path: Option<Box<DevicePath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RootContext {
|
impl RootContext {
|
||||||
pub fn new() -> Self {
|
pub fn new(loaded_image_device_path: Box<DevicePath>) -> Self {
|
||||||
Default::default()
|
RootContext {
|
||||||
|
actions: BTreeMap::new(),
|
||||||
|
loaded_image_path: Some(loaded_image_device_path),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn actions(&self) -> &BTreeMap<String, ActionDeclaration> {
|
pub fn actions(&self) -> &BTreeMap<String, ActionDeclaration> {
|
||||||
@@ -19,6 +26,12 @@ impl RootContext {
|
|||||||
pub fn actions_mut(&mut self) -> &mut BTreeMap<String, ActionDeclaration> {
|
pub fn actions_mut(&mut self) -> &mut BTreeMap<String, ActionDeclaration> {
|
||||||
&mut self.actions
|
&mut self.actions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn loaded_image_path(&self) -> Result<&DevicePath> {
|
||||||
|
self.loaded_image_path
|
||||||
|
.as_deref()
|
||||||
|
.ok_or_else(|| anyhow!("no loaded image path"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SproutContext {
|
pub struct SproutContext {
|
||||||
|
|||||||
20
src/extractors.rs
Normal file
20
src/extractors.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use crate::context::SproutContext;
|
||||||
|
use crate::extractors::filesystem::FileSystemExtractorConfiguration;
|
||||||
|
use anyhow::{Result, bail};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub mod filesystem;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||||
|
pub struct ExtractorDeclaration {
|
||||||
|
pub filesystem: Option<FileSystemExtractorConfiguration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract(context: Rc<SproutContext>, extractor: &ExtractorDeclaration) -> Result<String> {
|
||||||
|
if let Some(filesystem) = &extractor.filesystem {
|
||||||
|
filesystem::extract(context, filesystem)
|
||||||
|
} else {
|
||||||
|
bail!("unknown extractor configuration");
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/extractors/filesystem.rs
Normal file
66
src/extractors/filesystem.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use crate::context::SproutContext;
|
||||||
|
use crate::utils;
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use uefi::CString16;
|
||||||
|
use uefi::fs::{FileSystem, Path};
|
||||||
|
use uefi::proto::device_path::DevicePath;
|
||||||
|
use uefi::proto::media::file::{File, FileSystemVolumeLabel};
|
||||||
|
use uefi::proto::media::fs::SimpleFileSystem;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||||
|
pub struct FileSystemExtractorConfiguration {
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub item: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract(
|
||||||
|
context: Rc<SproutContext>,
|
||||||
|
device: &FileSystemExtractorConfiguration,
|
||||||
|
) -> Result<String> {
|
||||||
|
let handles = uefi::boot::find_handles::<SimpleFileSystem>()
|
||||||
|
.context("failed to find filesystem handles")?;
|
||||||
|
for handle in handles {
|
||||||
|
let mut filesystem = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(handle)
|
||||||
|
.context("failed to open filesystem protocol")?;
|
||||||
|
|
||||||
|
if let Some(ref label) = device.label {
|
||||||
|
let want_label = CString16::try_from(context.stamp(label).as_str())
|
||||||
|
.context("failed to convert label to CString16")?;
|
||||||
|
let mut root = filesystem
|
||||||
|
.open_volume()
|
||||||
|
.context("failed to open filesystem volume")?;
|
||||||
|
let label = root
|
||||||
|
.get_boxed_info::<FileSystemVolumeLabel>()
|
||||||
|
.context("failed to get filesystem volume label")?;
|
||||||
|
|
||||||
|
if label.volume_label() != want_label {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref item) = device.item {
|
||||||
|
let want_item = CString16::try_from(context.stamp(item).as_str())
|
||||||
|
.context("failed to convert item to CString16")?;
|
||||||
|
let mut filesystem = FileSystem::new(filesystem);
|
||||||
|
let metadata = filesystem.metadata(Path::new(&want_item));
|
||||||
|
|
||||||
|
if metadata.is_err() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata = metadata?;
|
||||||
|
if !(metadata.is_directory() || metadata.is_regular_file()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
|
||||||
|
.context("failed to open filesystem device path")?;
|
||||||
|
let path = path.deref();
|
||||||
|
return utils::device_path_root(path).context("failed to get device path root");
|
||||||
|
}
|
||||||
|
Ok(String::new())
|
||||||
|
}
|
||||||
@@ -21,6 +21,6 @@ pub fn generate(
|
|||||||
if let Some(matrix) = &generator.matrix {
|
if let Some(matrix) = &generator.matrix {
|
||||||
matrix::generate(context, matrix)
|
matrix::generate(context, matrix)
|
||||||
} else {
|
} else {
|
||||||
bail!("unknown action configuration");
|
bail!("unknown generator configuration");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/main.rs
30
src/main.rs
@@ -4,12 +4,17 @@ use crate::config::PhaseConfiguration;
|
|||||||
use crate::context::{RootContext, SproutContext};
|
use crate::context::{RootContext, SproutContext};
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use uefi::proto::device_path::LoadedImageDevicePath;
|
||||||
|
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
|
||||||
|
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod drivers;
|
pub mod drivers;
|
||||||
|
pub mod extractors;
|
||||||
pub mod generators;
|
pub mod generators;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
@@ -37,7 +42,19 @@ fn main() -> Result<()> {
|
|||||||
bail!("unsupported configuration version: {}", config.version);
|
bail!("unsupported configuration version: {}", config.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut root = RootContext::new();
|
let mut root = {
|
||||||
|
let current_image_device_path_protocol = uefi::boot::open_protocol_exclusive::<
|
||||||
|
LoadedImageDevicePath,
|
||||||
|
>(uefi::boot::image_handle())
|
||||||
|
.context("failed to get loaded image device path")?;
|
||||||
|
let loaded_image_path = current_image_device_path_protocol.deref().to_boxed();
|
||||||
|
info!(
|
||||||
|
"loaded image path: {}",
|
||||||
|
loaded_image_path.to_string(DisplayOnly(false), AllowShortcuts(false))?
|
||||||
|
);
|
||||||
|
RootContext::new(loaded_image_path)
|
||||||
|
};
|
||||||
|
|
||||||
root.actions_mut().extend(config.actions.clone());
|
root.actions_mut().extend(config.actions.clone());
|
||||||
|
|
||||||
let mut context = SproutContext::new(root);
|
let mut context = SproutContext::new(root);
|
||||||
@@ -46,6 +63,17 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
drivers::load(context.clone(), &config.drivers).context("failed to load drivers")?;
|
drivers::load(context.clone(), &config.drivers).context("failed to load drivers")?;
|
||||||
|
|
||||||
|
let mut extracted = BTreeMap::new();
|
||||||
|
for (name, extractor) in &config.extractors {
|
||||||
|
let value = extractors::extract(context.clone(), extractor)
|
||||||
|
.context(format!("failed to extract value {}", name))?;
|
||||||
|
info!("extracted value {}: {}", name, value);
|
||||||
|
extracted.insert(name.clone(), value);
|
||||||
|
}
|
||||||
|
let mut context = context.fork();
|
||||||
|
context.insert(&extracted);
|
||||||
|
let context = context.freeze();
|
||||||
|
|
||||||
phase(context.clone(), &config.phases.startup)?;
|
phase(context.clone(), &config.phases.startup)?;
|
||||||
|
|
||||||
let mut all_entries = Vec::new();
|
let mut all_entries = Vec::new();
|
||||||
|
|||||||
87
src/utils.rs
87
src/utils.rs
@@ -1,9 +1,10 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use uefi::CString16;
|
use std::ops::Deref;
|
||||||
use uefi::fs::{FileSystem, Path};
|
use uefi::fs::{FileSystem, Path};
|
||||||
use uefi::proto::device_path::text::{AllowShortcuts, DevicePathFromText, DisplayOnly};
|
use uefi::proto::device_path::text::{AllowShortcuts, DevicePathFromText, DisplayOnly};
|
||||||
use uefi::proto::device_path::{DevicePath, PoolDevicePath};
|
use uefi::proto::device_path::{DevicePath, PoolDevicePath};
|
||||||
use uefi::proto::media::fs::SimpleFileSystem;
|
use uefi::proto::media::fs::SimpleFileSystem;
|
||||||
|
use uefi::{CString16, Handle};
|
||||||
|
|
||||||
pub mod framebuffer;
|
pub mod framebuffer;
|
||||||
pub mod linux_media_initrd;
|
pub mod linux_media_initrd;
|
||||||
@@ -43,14 +44,84 @@ pub fn device_path_root(path: &DevicePath) -> Result<String> {
|
|||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_file_contents(path: &str) -> Result<Vec<u8>> {
|
pub fn device_path_subpath(path: &DevicePath) -> Result<String> {
|
||||||
let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(
|
let path = path
|
||||||
uefi::boot::get_handle_for_protocol::<SimpleFileSystem>()
|
.node_iter()
|
||||||
.context("no filesystem protocol")?,
|
.filter_map(|item| {
|
||||||
)
|
let item = item.to_string(DisplayOnly(false), AllowShortcuts(false));
|
||||||
.context("unable to open filesystem protocol")?;
|
if item
|
||||||
|
.as_ref()
|
||||||
|
.map(|item| item.to_string().contains("("))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(item.unwrap_or_default())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|item| item.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\\");
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ResolvedPath {
|
||||||
|
pub root_path: Box<DevicePath>,
|
||||||
|
pub sub_path: Box<DevicePath>,
|
||||||
|
pub full_path: Box<DevicePath>,
|
||||||
|
pub filesystem_handle: Handle,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_path(default_root_path: &DevicePath, input: &str) -> Result<ResolvedPath> {
|
||||||
|
let mut path = text_to_device_path(input).context("failed to convert text to path")?;
|
||||||
|
let path_has_device = path
|
||||||
|
.node_iter()
|
||||||
|
.next()
|
||||||
|
.map(|it| {
|
||||||
|
it.to_string(DisplayOnly(false), AllowShortcuts(false))
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.map(|it| it.to_string().contains("("))
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !path_has_device {
|
||||||
|
let mut input = input.to_string();
|
||||||
|
if !input.starts_with("\\") {
|
||||||
|
input.insert(0, '\\');
|
||||||
|
}
|
||||||
|
input.insert_str(
|
||||||
|
0,
|
||||||
|
device_path_root(default_root_path)
|
||||||
|
.context("failed to get loaded image device root")?
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
path = text_to_device_path(input.as_str()).context("failed to convert text to path")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = path.to_boxed();
|
||||||
|
let root = device_path_root(path.as_ref()).context("failed to convert root to path")?;
|
||||||
|
let root_path = text_to_device_path(root.as_str())
|
||||||
|
.context("failed to convert root to path")?
|
||||||
|
.to_boxed();
|
||||||
|
let mut root_path = root_path.as_ref();
|
||||||
|
let handle = uefi::boot::locate_device_path::<SimpleFileSystem>(&mut root_path)
|
||||||
|
.context("failed to locate filesystem device path")?;
|
||||||
|
let subpath = device_path_subpath(path.deref()).context("failed to get device subpath")?;
|
||||||
|
Ok(ResolvedPath {
|
||||||
|
root_path: root_path.to_boxed(),
|
||||||
|
sub_path: text_to_device_path(subpath.as_str())?.to_boxed(),
|
||||||
|
full_path: path,
|
||||||
|
filesystem_handle: handle,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_file_contents(default_root_path: &DevicePath, input: &str) -> Result<Vec<u8>> {
|
||||||
|
let resolved = resolve_path(default_root_path, input)?;
|
||||||
|
let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(resolved.filesystem_handle)
|
||||||
|
.context("unable to open filesystem protocol")?;
|
||||||
let mut fs = FileSystem::new(fs);
|
let mut fs = FileSystem::new(fs);
|
||||||
let path = CString16::try_from(path).context("unable to convert path to CString16")?;
|
let path = resolved
|
||||||
|
.sub_path
|
||||||
|
.to_string(DisplayOnly(false), AllowShortcuts(false))?;
|
||||||
let content = fs.read(Path::new(&path));
|
let content = fs.read(Path::new(&path));
|
||||||
content.context("unable to read file contents")
|
content.context("unable to read file contents")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user