blob: 7fddcfdff7724a8e66b9bdb256933f1e8fd1cde0 [file] [log] [blame]
use r_efi::efi::protocols::{device_path, loaded_image_device_path};
use crate::ffi::{OsStr, OsString};
use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
use crate::path::{self, PathBuf};
use crate::sys::pal::{helpers, unsupported_err};
use crate::{fmt, io};
const PATHS_SEP: u16 = b';' as u16;
pub fn getcwd() -> io::Result<PathBuf> {
match helpers::open_shell() {
Some(shell) => {
// SAFETY: path_ptr is managed by UEFI shell and should not be deallocated
let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) };
helpers::os_string_from_raw(path_ptr)
.map(PathBuf::from)
.ok_or(io::const_error!(io::ErrorKind::InvalidData, "invalid path"))
}
None => {
let mut t = current_exe()?;
// SAFETY: This should never fail since the disk prefix will be present even for root
// executables
assert!(t.pop());
Ok(t)
}
}
}
pub fn chdir(p: &path::Path) -> io::Result<()> {
let shell = helpers::open_shell().ok_or(unsupported_err())?;
let mut p = helpers::os_string_to_raw(p.as_os_str())
.ok_or(io::const_error!(io::ErrorKind::InvalidData, "invalid path"))?;
let r = unsafe { ((*shell.as_ptr()).set_cur_dir)(crate::ptr::null_mut(), p.as_mut_ptr()) };
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}
pub struct SplitPaths<'a> {
data: crate::os::uefi::ffi::EncodeWide<'a>,
must_yield: bool,
}
pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
SplitPaths { data: unparsed.encode_wide(), must_yield: true }
}
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
let must_yield = self.must_yield;
self.must_yield = false;
let mut in_progress = Vec::new();
for b in self.data.by_ref() {
if b == PATHS_SEP {
self.must_yield = true;
break;
} else {
in_progress.push(b)
}
}
if !must_yield && in_progress.is_empty() {
None
} else {
Some(PathBuf::from(OsString::from_wide(&in_progress)))
}
}
}
#[derive(Debug)]
pub struct JoinPathsError;
// UEFI Shell Path variable is defined in Section 3.6.1
// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf).
pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
where
I: Iterator<Item = T>,
T: AsRef<OsStr>,
{
let mut joined = Vec::new();
for (i, path) in paths.enumerate() {
if i > 0 {
joined.push(PATHS_SEP)
}
let v = path.as_ref().encode_wide().collect::<Vec<u16>>();
if v.contains(&PATHS_SEP) {
return Err(JoinPathsError);
}
joined.extend_from_slice(&v);
}
Ok(OsString::from_wide(&joined))
}
impl fmt::Display for JoinPathsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"path segment contains `;`".fmt(f)
}
}
impl crate::error::Error for JoinPathsError {}
pub fn current_exe() -> io::Result<PathBuf> {
let protocol = helpers::image_handle_protocol::<device_path::Protocol>(
loaded_image_device_path::PROTOCOL_GUID,
)?;
helpers::device_path_to_text(protocol).map(PathBuf::from)
}
pub fn temp_dir() -> PathBuf {
panic!("UEFI doesn't have a dedicated temp directory")
}
pub fn home_dir() -> Option<PathBuf> {
None
}