@@ -311,8 +311,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
311311 unsupported ( )
312312}
313313
314- pub fn exists ( _path : & Path ) -> io:: Result < bool > {
315- unsupported ( )
314+ pub fn exists ( path : & Path ) -> io:: Result < bool > {
315+ let f = uefi_fs:: File :: from_path ( path, r_efi:: protocols:: file:: MODE_READ , 0 ) ;
316+ match f {
317+ Ok ( _) => Ok ( true ) ,
318+ Err ( e) if e. kind ( ) == io:: ErrorKind :: NotFound => Ok ( false ) ,
319+ Err ( e) => Err ( e) ,
320+ }
316321}
317322
318323pub fn readlink ( _p : & Path ) -> io:: Result < PathBuf > {
@@ -342,3 +347,186 @@ pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
342347pub fn copy ( _from : & Path , _to : & Path ) -> io:: Result < u64 > {
343348 unsupported ( )
344349}
350+
351+ mod uefi_fs {
352+ use r_efi:: protocols:: { device_path, file, simple_file_system} ;
353+
354+ use super :: super :: helpers;
355+ use crate :: boxed:: Box ;
356+ use crate :: io;
357+ use crate :: mem:: MaybeUninit ;
358+ use crate :: path:: { Path , PathBuf } ;
359+ use crate :: ptr:: NonNull ;
360+ use crate :: sys:: unsupported_err;
361+
362+ const COLON : u8 = b':' ;
363+
364+ pub ( crate ) struct File ( NonNull < file:: Protocol > ) ;
365+
366+ impl File {
367+ pub ( crate ) fn from_path ( path : & Path , open_mode : u64 , attr : u64 ) -> io:: Result < Self > {
368+ let absoulte = absolute_path ( path) ?;
369+
370+ let p = helpers:: OwnedDevicePath :: from_text ( absoulte. as_os_str ( ) ) ?;
371+ let ( vol, mut path_remaining) = Self :: open_volume_from_device_path ( p. borrow ( ) ) ?;
372+
373+ vol. open ( & mut path_remaining, open_mode, attr)
374+ }
375+
376+ fn open_volume_from_device_path (
377+ path : helpers:: BorrowedDevicePath < ' _ > ,
378+ ) -> io:: Result < ( Self , Box < [ u16 ] > ) > {
379+ let handles = match helpers:: locate_handles ( simple_file_system:: PROTOCOL_GUID ) {
380+ Ok ( x) => x,
381+ Err ( e) => return Err ( e) ,
382+ } ;
383+ for handle in handles {
384+ let volume_device_path: NonNull < device_path:: Protocol > =
385+ match helpers:: open_protocol ( handle, device_path:: PROTOCOL_GUID ) {
386+ Ok ( x) => x,
387+ Err ( _) => continue ,
388+ } ;
389+ let volume_device_path = helpers:: BorrowedDevicePath :: new ( volume_device_path) ;
390+
391+ if let Some ( left_path) = path_best_match ( & volume_device_path, & path) {
392+ return Ok ( ( Self :: open_volume ( handle) ?, left_path) ) ;
393+ }
394+ }
395+
396+ Err ( io:: const_error!( io:: ErrorKind :: NotFound , "Volume Not Found" ) )
397+ }
398+
399+ // Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL
400+ fn open_volume ( device_handle : NonNull < crate :: ffi:: c_void > ) -> io:: Result < Self > {
401+ let simple_file_system_protocol = helpers:: open_protocol :: < simple_file_system:: Protocol > (
402+ device_handle,
403+ simple_file_system:: PROTOCOL_GUID ,
404+ ) ?;
405+
406+ let mut file_protocol: MaybeUninit < * mut file:: Protocol > = MaybeUninit :: uninit ( ) ;
407+ let r = unsafe {
408+ ( ( * simple_file_system_protocol. as_ptr ( ) ) . open_volume ) (
409+ simple_file_system_protocol. as_ptr ( ) ,
410+ file_protocol. as_mut_ptr ( ) ,
411+ )
412+ } ;
413+ if r. is_error ( ) {
414+ return Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) ;
415+ }
416+
417+ // Since no error was returned, file protocol should be non-NULL.
418+ let p = NonNull :: new ( unsafe { file_protocol. assume_init ( ) } ) . unwrap ( ) ;
419+ Ok ( Self ( p) )
420+ }
421+
422+ fn open ( & self , path : & mut [ u16 ] , open_mode : u64 , attr : u64 ) -> io:: Result < Self > {
423+ let file_ptr = self . 0 . as_ptr ( ) ;
424+ let mut file_opened: MaybeUninit < * mut file:: Protocol > = MaybeUninit :: uninit ( ) ;
425+
426+ let r = unsafe {
427+ ( ( * file_ptr) . open ) (
428+ file_ptr,
429+ file_opened. as_mut_ptr ( ) ,
430+ path. as_mut_ptr ( ) ,
431+ open_mode,
432+ attr,
433+ )
434+ } ;
435+
436+ if r. is_error ( ) {
437+ return Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) ;
438+ }
439+
440+ // Since no error was returned, file protocol should be non-NULL.
441+ let p = NonNull :: new ( unsafe { file_opened. assume_init ( ) } ) . unwrap ( ) ;
442+ Ok ( File ( p) )
443+ }
444+ }
445+
446+ impl Drop for File {
447+ fn drop ( & mut self ) {
448+ let file_ptr = self . 0 . as_ptr ( ) ;
449+ let _ = unsafe { ( ( * self . 0 . as_ptr ( ) ) . close ) ( file_ptr) } ;
450+ }
451+ }
452+
453+ fn path_best_match < ' a > (
454+ source : & helpers:: BorrowedDevicePath < ' a > ,
455+ target : & helpers:: BorrowedDevicePath < ' a > ,
456+ ) -> Option < Box < [ u16 ] > > {
457+ let mut source_iter = source. iter ( ) . take_while ( |x| !x. is_end_instance ( ) ) ;
458+ let mut target_iter = target. iter ( ) . take_while ( |x| !x. is_end_instance ( ) ) ;
459+
460+ loop {
461+ match ( source_iter. next ( ) , target_iter. next ( ) ) {
462+ ( Some ( x) , Some ( y) ) if x == y => continue ,
463+ ( None , Some ( y) ) => {
464+ let p = y. to_path ( ) . to_text ( ) . ok ( ) ?;
465+ return helpers:: os_string_to_raw ( & p) ;
466+ }
467+ _ => return None ,
468+ }
469+ }
470+ }
471+
472+ /// Get device path protocol associated with shell mapping.
473+ ///
474+ /// returns None in case no such mapping is exists
475+ fn get_device_path_from_map ( map : & Path ) -> io:: Result < helpers:: BorrowedDevicePath < ' static > > {
476+ let shell = helpers:: open_shell ( )
477+ . ok_or ( io:: const_error!( io:: ErrorKind :: NotFound , "UEFI Shell not found" ) ) ?;
478+ let mut path = helpers:: os_string_to_raw ( map. as_os_str ( ) ) . ok_or ( io:: const_error!(
479+ io:: ErrorKind :: InvalidFilename ,
480+ "Invalid UEFI shell mapping"
481+ ) ) ?;
482+
483+ let protocol = unsafe { ( ( * shell. as_ptr ( ) ) . get_device_path_from_map ) ( path. as_mut_ptr ( ) ) } ;
484+ let protocol = NonNull :: new ( protocol)
485+ . ok_or ( io:: const_error!( io:: ErrorKind :: NotFound , "UEFI Shell mapping not found" ) ) ?;
486+
487+ Ok ( helpers:: BorrowedDevicePath :: new ( protocol) )
488+ }
489+
490+ fn absolute_path ( path : & Path ) -> io:: Result < PathBuf > {
491+ const FORWARD_SLASH : u8 = b'/' ;
492+
493+ // Absolute Shell Path
494+ if path. as_os_str ( ) . as_encoded_bytes ( ) . contains ( & COLON ) {
495+ let mut path_components = path. components ( ) ;
496+ // Since path is not empty, it has at least one Component
497+ let prefix = path_components. next ( ) . unwrap ( ) ;
498+
499+ let dev_path = get_device_path_from_map ( prefix. as_ref ( ) ) ?;
500+ let dev_path_text = dev_path. to_text ( ) . map_err ( |_| unsupported_err ( ) ) ?;
501+
502+ let mut ans = PathBuf :: new ( ) ;
503+ ans. push ( & dev_path_text) ;
504+ // UEFI Shell does not seem to end device path with `/`
505+ if * dev_path_text. as_encoded_bytes ( ) . last ( ) . unwrap ( ) != FORWARD_SLASH {
506+ ans. push ( "/" ) ;
507+ }
508+ ans. push ( path_components) ;
509+
510+ return Ok ( ans) ;
511+ }
512+
513+ // Absolute Device Path
514+ if path. as_os_str ( ) . as_encoded_bytes ( ) . contains ( & FORWARD_SLASH ) {
515+ return Ok ( path. to_path_buf ( ) ) ;
516+ }
517+
518+ // cur_dir() always returns something
519+ let cur_dir = crate :: env:: current_dir ( ) . unwrap ( ) ;
520+ let mut path_components = path. components ( ) ;
521+
522+ // Relative Root
523+ if path_components. next ( ) . unwrap ( ) == crate :: path:: Component :: RootDir {
524+ let mut ans = PathBuf :: new ( ) ;
525+ ans. push ( cur_dir. components ( ) . next ( ) . unwrap ( ) ) ;
526+ ans. push ( path_components) ;
527+ return absolute_path ( & ans) ;
528+ }
529+
530+ absolute_path ( & cur_dir. join ( path) )
531+ }
532+ }
0 commit comments