@@ -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,134 @@ 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 :: 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