@@ -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,186 @@ 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
+ 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