about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/std/src/sys/fs/uefi.rs140
-rw-r--r--library/std/src/sys/pal/uefi/helpers.rs3
2 files changed, 138 insertions, 5 deletions
diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs
index 56aed7dfd8e..defc1681d38 100644
--- a/library/std/src/sys/fs/uefi.rs
+++ b/library/std/src/sys/fs/uefi.rs
@@ -286,8 +286,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
     unsupported()
 }
 
-pub fn exists(_path: &Path) -> io::Result<bool> {
-    unsupported()
+pub fn exists(path: &Path) -> io::Result<bool> {
+    let f = uefi_fs::File::from_path(path, r_efi::protocols::file::MODE_READ, 0);
+    match f {
+        Ok(_) => Ok(true),
+        Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false),
+        Err(e) => Err(e),
+    }
 }
 
 pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
@@ -317,3 +322,134 @@ pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
 pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
     unsupported()
 }
+
+mod uefi_fs {
+    use r_efi::protocols::{device_path, file, simple_file_system};
+
+    use crate::boxed::Box;
+    use crate::io;
+    use crate::path::Path;
+    use crate::ptr::NonNull;
+    use crate::sys::helpers;
+
+    pub(crate) struct File(NonNull<file::Protocol>);
+
+    impl File {
+        pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result<Self> {
+            let absolute = crate::path::absolute(path)?;
+
+            let p = helpers::OwnedDevicePath::from_text(absolute.as_os_str())?;
+            let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?;
+
+            vol.open(&mut path_remaining, open_mode, attr)
+        }
+
+        /// Open Filesystem volume given a devicepath to the volume, or a file/directory in the
+        /// volume. The path provided should be absolute UEFI device path, without any UEFI shell
+        /// mappings.
+        ///
+        /// Returns
+        /// 1. The volume as a UEFI File
+        /// 2. Path relative to the volume.
+        ///
+        /// For example, given "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi",
+        /// this will open the volume "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)"
+        /// and return the remaining file path "\abc\run.efi".
+        fn open_volume_from_device_path(
+            path: helpers::BorrowedDevicePath<'_>,
+        ) -> io::Result<(Self, Box<[u16]>)> {
+            let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            };
+            for handle in handles {
+                let volume_device_path: NonNull<device_path::Protocol> =
+                    match helpers::open_protocol(handle, device_path::PROTOCOL_GUID) {
+                        Ok(x) => x,
+                        Err(_) => continue,
+                    };
+                let volume_device_path = helpers::BorrowedDevicePath::new(volume_device_path);
+
+                if let Some(left_path) = path_best_match(&volume_device_path, &path) {
+                    return Ok((Self::open_volume(handle)?, left_path));
+                }
+            }
+
+            Err(io::const_error!(io::ErrorKind::NotFound, "Volume Not Found"))
+        }
+
+        // Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL
+        fn open_volume(device_handle: NonNull<crate::ffi::c_void>) -> io::Result<Self> {
+            let simple_file_system_protocol = helpers::open_protocol::<simple_file_system::Protocol>(
+                device_handle,
+                simple_file_system::PROTOCOL_GUID,
+            )?;
+
+            let mut file_protocol = crate::ptr::null_mut();
+            let r = unsafe {
+                ((*simple_file_system_protocol.as_ptr()).open_volume)(
+                    simple_file_system_protocol.as_ptr(),
+                    &mut file_protocol,
+                )
+            };
+            if r.is_error() {
+                return Err(io::Error::from_raw_os_error(r.as_usize()));
+            }
+
+            // Since no error was returned, file protocol should be non-NULL.
+            let p = NonNull::new(file_protocol).unwrap();
+            Ok(Self(p))
+        }
+
+        fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result<Self> {
+            let file_ptr = self.0.as_ptr();
+            let mut file_opened = crate::ptr::null_mut();
+
+            let r = unsafe {
+                ((*file_ptr).open)(file_ptr, &mut file_opened, path.as_mut_ptr(), open_mode, attr)
+            };
+
+            if r.is_error() {
+                return Err(io::Error::from_raw_os_error(r.as_usize()));
+            }
+
+            // Since no error was returned, file protocol should be non-NULL.
+            let p = NonNull::new(file_opened).unwrap();
+            Ok(File(p))
+        }
+    }
+
+    impl Drop for File {
+        fn drop(&mut self) {
+            let file_ptr = self.0.as_ptr();
+            let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) };
+        }
+    }
+
+    /// A helper to check that target path is a descendent of source. It is expected to be used with
+    /// absolute UEFI device paths without any UEFI shell mappings.
+    ///
+    /// Returns the path relative to source
+    ///
+    /// For example, given "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/" and
+    /// "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi", this will return
+    /// "\abc\run.efi"
+    fn path_best_match(
+        source: &helpers::BorrowedDevicePath<'_>,
+        target: &helpers::BorrowedDevicePath<'_>,
+    ) -> Option<Box<[u16]>> {
+        let mut source_iter = source.iter().take_while(|x| !x.is_end_instance());
+        let mut target_iter = target.iter().take_while(|x| !x.is_end_instance());
+
+        loop {
+            match (source_iter.next(), target_iter.next()) {
+                (Some(x), Some(y)) if x == y => continue,
+                (None, Some(y)) => {
+                    let p = y.to_path().to_text().ok()?;
+                    return helpers::os_string_to_raw(&p);
+                }
+                _ => return None,
+            }
+        }
+    }
+}
diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs
index 60c33c637d7..2faa733f23f 100644
--- a/library/std/src/sys/pal/uefi/helpers.rs
+++ b/library/std/src/sys/pal/uefi/helpers.rs
@@ -374,7 +374,6 @@ impl<'a> BorrowedDevicePath<'a> {
         device_path_to_text(self.protocol)
     }
 
-    #[expect(dead_code)]
     pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> {
         DevicePathIterator::new(DevicePathNode::new(self.protocol))
     }
@@ -452,7 +451,6 @@ impl<'a> DevicePathNode<'a> {
             && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE
     }
 
-    #[expect(dead_code)]
     pub(crate) const fn is_end_instance(&self) -> bool {
         self.node_type() == r_efi::protocols::device_path::TYPE_END
             && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_INSTANCE
@@ -468,7 +466,6 @@ impl<'a> DevicePathNode<'a> {
         Self::new(node)
     }
 
-    #[expect(dead_code)]
     pub(crate) fn to_path(&'a self) -> BorrowedDevicePath<'a> {
         BorrowedDevicePath::new(self.protocol)
     }