| 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 | |
| } |