@@ -286,8 +286,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
286286 unsupported ( )
287287}
288288
289- pub fn exists ( _path : & Path ) -> io:: Result < bool > {
290- unsupported ( )
289+ pub fn exists ( path : & Path ) -> io:: Result < bool > {
290+ let f = uefi_fs:: File :: from_path ( path, r_efi:: protocols:: file:: MODE_READ , 0 ) ;
291+ match f {
292+ Ok ( _) => Ok ( true ) ,
293+ Err ( e) if e. kind ( ) == io:: ErrorKind :: NotFound => Ok ( false ) ,
294+ Err ( e) => Err ( e) ,
295+ }
291296}
292297
293298pub fn readlink ( _p : & Path ) -> io:: Result < PathBuf > {
@@ -317,3 +322,134 @@ pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
317322pub fn copy ( _from : & Path , _to : & Path ) -> io:: Result < u64 > {
318323 unsupported ( )
319324}
325+
326+ mod uefi_fs {
327+ use r_efi:: protocols:: { device_path, file, simple_file_system} ;
328+
329+ use crate :: boxed:: Box ;
330+ use crate :: io;
331+ use crate :: path:: Path ;
332+ use crate :: ptr:: NonNull ;
333+ use crate :: sys:: helpers;
334+
335+ pub ( crate ) struct File ( NonNull < file:: Protocol > ) ;
336+
337+ impl File {
338+ pub ( crate ) fn from_path ( path : & Path , open_mode : u64 , attr : u64 ) -> io:: Result < Self > {
339+ let absolute = crate :: path:: absolute ( path) ?;
340+
341+ let p = helpers:: OwnedDevicePath :: from_text ( absolute. as_os_str ( ) ) ?;
342+ let ( vol, mut path_remaining) = Self :: open_volume_from_device_path ( p. borrow ( ) ) ?;
343+
344+ vol. open ( & mut path_remaining, open_mode, attr)
345+ }
346+
347+ /// Open Filesystem volume given a devicepath to the volume, or a file/directory in the
348+ /// volume. The path provided should be absolute UEFI device path, without any UEFI shell
349+ /// mappings.
350+ ///
351+ /// Returns
352+ /// 1. The volume as a UEFI File
353+ /// 2. Path relative to the volume.
354+ ///
355+ /// For example, given "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi",
356+ /// this will open the volume "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)"
357+ /// and return the remaining file path "\abc\run.efi".
358+ fn open_volume_from_device_path (
359+ path : helpers:: BorrowedDevicePath < ' _ > ,
360+ ) -> io:: Result < ( Self , Box < [ u16 ] > ) > {
361+ let handles = match helpers:: locate_handles ( simple_file_system:: PROTOCOL_GUID ) {
362+ Ok ( x) => x,
363+ Err ( e) => return Err ( e) ,
364+ } ;
365+ for handle in handles {
366+ let volume_device_path: NonNull < device_path:: Protocol > =
367+ match helpers:: open_protocol ( handle, device_path:: PROTOCOL_GUID ) {
368+ Ok ( x) => x,
369+ Err ( _) => continue ,
370+ } ;
371+ let volume_device_path = helpers:: BorrowedDevicePath :: new ( volume_device_path) ;
372+
373+ if let Some ( left_path) = path_best_match ( & volume_device_path, & path) {
374+ return Ok ( ( Self :: open_volume ( handle) ?, left_path) ) ;
375+ }
376+ }
377+
378+ Err ( io:: const_error!( io:: ErrorKind :: NotFound , "Volume Not Found" ) )
379+ }
380+
381+ // Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL
382+ fn open_volume ( device_handle : NonNull < crate :: ffi:: c_void > ) -> io:: Result < Self > {
383+ let simple_file_system_protocol = helpers:: open_protocol :: < simple_file_system:: Protocol > (
384+ device_handle,
385+ simple_file_system:: PROTOCOL_GUID ,
386+ ) ?;
387+
388+ let mut file_protocol = crate :: ptr:: null_mut ( ) ;
389+ let r = unsafe {
390+ ( ( * simple_file_system_protocol. as_ptr ( ) ) . open_volume ) (
391+ simple_file_system_protocol. as_ptr ( ) ,
392+ & mut file_protocol,
393+ )
394+ } ;
395+ if r. is_error ( ) {
396+ return Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) ;
397+ }
398+
399+ // Since no error was returned, file protocol should be non-NULL.
400+ let p = NonNull :: new ( file_protocol) . unwrap ( ) ;
401+ Ok ( Self ( p) )
402+ }
403+
404+ fn open ( & self , path : & mut [ u16 ] , open_mode : u64 , attr : u64 ) -> io:: Result < Self > {
405+ let file_ptr = self . 0 . as_ptr ( ) ;
406+ let mut file_opened = crate :: ptr:: null_mut ( ) ;
407+
408+ let r = unsafe {
409+ ( ( * file_ptr) . open ) ( file_ptr, & mut file_opened, path. as_mut_ptr ( ) , open_mode, attr)
410+ } ;
411+
412+ if r. is_error ( ) {
413+ return Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) ;
414+ }
415+
416+ // Since no error was returned, file protocol should be non-NULL.
417+ let p = NonNull :: new ( file_opened) . unwrap ( ) ;
418+ Ok ( File ( p) )
419+ }
420+ }
421+
422+ impl Drop for File {
423+ fn drop ( & mut self ) {
424+ let file_ptr = self . 0 . as_ptr ( ) ;
425+ let _ = unsafe { ( ( * self . 0 . as_ptr ( ) ) . close ) ( file_ptr) } ;
426+ }
427+ }
428+
429+ /// A helper to check that target path is a descendent of source. It is expected to be used with
430+ /// absolute UEFI device paths without any UEFI shell mappings.
431+ ///
432+ /// Returns the path relative to source
433+ ///
434+ /// For example, given "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/" and
435+ /// "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi", this will return
436+ /// "\abc\run.efi"
437+ fn path_best_match (
438+ source : & helpers:: BorrowedDevicePath < ' _ > ,
439+ target : & helpers:: BorrowedDevicePath < ' _ > ,
440+ ) -> Option < Box < [ u16 ] > > {
441+ let mut source_iter = source. iter ( ) . take_while ( |x| !x. is_end_instance ( ) ) ;
442+ let mut target_iter = target. iter ( ) . take_while ( |x| !x. is_end_instance ( ) ) ;
443+
444+ loop {
445+ match ( source_iter. next ( ) , target_iter. next ( ) ) {
446+ ( Some ( x) , Some ( y) ) if x == y => continue ,
447+ ( None , Some ( y) ) => {
448+ let p = y. to_path ( ) . to_text ( ) . ok ( ) ?;
449+ return helpers:: os_string_to_raw ( & p) ;
450+ }
451+ _ => return None ,
452+ }
453+ }
454+ }
455+ }
0 commit comments