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