@@ -311,8 +311,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
311
311
unsupported ( )
312
312
}
313
313
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
+ }
316
321
}
317
322
318
323
pub fn readlink ( _p : & Path ) -> io:: Result < PathBuf > {
@@ -342,3 +347,185 @@ pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
342
347
pub fn copy ( _from : & Path , _to : & Path ) -> io:: Result < u64 > {
343
348
unsupported ( )
344
349
}
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
+ return y. to_path ( ) . to_text_raw ( ) . ok ( ) ;
465
+ }
466
+ _ => return None ,
467
+ }
468
+ }
469
+ }
470
+
471
+ /// Get device path protocol associated with shell mapping.
472
+ ///
473
+ /// returns None in case no such mapping is exists
474
+ fn get_device_path_from_map ( map : & Path ) -> io:: Result < helpers:: BorrowedDevicePath < ' static > > {
475
+ let shell = helpers:: open_shell ( )
476
+ . ok_or ( io:: const_error!( io:: ErrorKind :: NotFound , "UEFI Shell not found" ) ) ?;
477
+ let mut path = helpers:: os_string_to_raw ( map. as_os_str ( ) ) . ok_or ( io:: const_error!(
478
+ io:: ErrorKind :: InvalidFilename ,
479
+ "Invalid UEFI shell mapping"
480
+ ) ) ?;
481
+
482
+ let protocol = unsafe { ( ( * shell. as_ptr ( ) ) . get_device_path_from_map ) ( path. as_mut_ptr ( ) ) } ;
483
+ let protocol = NonNull :: new ( protocol)
484
+ . ok_or ( io:: const_error!( io:: ErrorKind :: NotFound , "UEFI Shell mapping not found" ) ) ?;
485
+
486
+ Ok ( helpers:: BorrowedDevicePath :: new ( protocol) )
487
+ }
488
+
489
+ fn absolute_path ( path : & Path ) -> io:: Result < PathBuf > {
490
+ const FORWARD_SLASH : u8 = b'/' ;
491
+
492
+ // Absoulte Shell Path
493
+ if path. as_os_str ( ) . as_encoded_bytes ( ) . contains ( & COLON ) {
494
+ let mut path_components = path. components ( ) ;
495
+ // Since path is not empty, it has at least one Component
496
+ let prefix = path_components. next ( ) . unwrap ( ) ;
497
+
498
+ let dev_path = get_device_path_from_map ( prefix. as_ref ( ) ) ?;
499
+ let dev_path_text = dev_path. to_text ( ) . map_err ( |_| unsupported_err ( ) ) ?;
500
+
501
+ let mut ans = PathBuf :: new ( ) ;
502
+ ans. push ( & dev_path_text) ;
503
+ // UEFI Shell does not seem to end device path with `/`
504
+ if * dev_path_text. as_encoded_bytes ( ) . last ( ) . unwrap ( ) != FORWARD_SLASH {
505
+ ans. push ( "/" ) ;
506
+ }
507
+ ans. push ( path_components) ;
508
+
509
+ return Ok ( ans) ;
510
+ }
511
+
512
+ // Absoulte Device Path
513
+ if path. as_os_str ( ) . as_encoded_bytes ( ) . contains ( & FORWARD_SLASH ) {
514
+ return Ok ( path. to_path_buf ( ) ) ;
515
+ }
516
+
517
+ // cur_dir() always returns something
518
+ let cur_dir = crate :: env:: current_dir ( ) . unwrap ( ) ;
519
+ let mut path_components = path. components ( ) ;
520
+
521
+ // Relative Root
522
+ if path_components. next ( ) . unwrap ( ) == crate :: path:: Component :: RootDir {
523
+ let mut ans = PathBuf :: new ( ) ;
524
+ ans. push ( cur_dir. components ( ) . next ( ) . unwrap ( ) ) ;
525
+ ans. push ( path_components) ;
526
+ return absolute_path ( & ans) ;
527
+ }
528
+
529
+ absolute_path ( & cur_dir. join ( path) )
530
+ }
531
+ }
0 commit comments