@@ -286,8 +286,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
286
286
unsupported ( )
287
287
}
288
288
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
+ }
291
296
}
292
297
293
298
pub fn readlink ( _p : & Path ) -> io:: Result < PathBuf > {
@@ -317,3 +322,141 @@ pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
317
322
pub fn copy ( _from : & Path , _to : & Path ) -> io:: Result < u64 > {
318
323
unsupported ( )
319
324
}
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