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