mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-29 20:46:07 +03:00
std: sys: fs: uefi: Implement rename
- Using the file_name field in `EFI_FILE_INFO` works for renaming, even when changing directories. - Does not work for cross-device rename, but that is already expected behaviour according to the docs: "This will not work if the new name is on a different mount point." - Also add some helper code for dealing with UefiBox<file::Info>. - Tested using OVMF in qemu. Signed-off-by: Ayush Singh <ayush@beagleboard.org>
This commit is contained in:
@@ -385,8 +385,43 @@ pub fn unlink(p: &Path) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
|
||||
unsupported()
|
||||
/// The implementation mirrors `mv` implementation in UEFI shell:
|
||||
/// https://github.com/tianocore/edk2/blob/66346d5edeac2a00d3cf2f2f3b5f66d423c07b3e/ShellPkg/Library/UefiShellLevel2CommandsLib/Mv.c#L455
|
||||
///
|
||||
/// In a nutshell we do the following:
|
||||
/// 1. Convert both old and new paths to absolute paths.
|
||||
/// 2. Check that both lie in the same disk.
|
||||
/// 3. Construct the target path relative to the current disk root.
|
||||
/// 4. Set this target path as the file_name in the file_info structure.
|
||||
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
|
||||
let old_absolute = crate::path::absolute(old)?;
|
||||
let new_absolute = crate::path::absolute(new)?;
|
||||
|
||||
let mut old_components = old_absolute.components();
|
||||
let mut new_components = new_absolute.components();
|
||||
|
||||
let Some(old_disk) = old_components.next() else {
|
||||
return Err(io::const_error!(io::ErrorKind::InvalidInput, "Old path is not valid"));
|
||||
};
|
||||
let Some(new_disk) = new_components.next() else {
|
||||
return Err(io::const_error!(io::ErrorKind::InvalidInput, "New path is not valid"));
|
||||
};
|
||||
|
||||
// Ensure that paths are on the same device.
|
||||
if old_disk != new_disk {
|
||||
return Err(io::const_error!(io::ErrorKind::CrossesDevices, "Cannot rename across device"));
|
||||
}
|
||||
|
||||
// Construct an path relative the current disk root.
|
||||
let new_relative =
|
||||
[crate::path::Component::RootDir].into_iter().chain(new_components).collect::<PathBuf>();
|
||||
|
||||
let f = uefi_fs::File::from_path(old, file::MODE_READ | file::MODE_WRITE, 0)?;
|
||||
let file_info = f.file_info()?;
|
||||
|
||||
let new_info = file_info.with_file_name(new_relative.as_os_str())?;
|
||||
|
||||
f.set_file_info(new_info)
|
||||
}
|
||||
|
||||
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
|
||||
@@ -751,12 +786,7 @@ pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time {
|
||||
}
|
||||
|
||||
pub(crate) fn file_name_from_uefi(info: &UefiBox<file::Info>) -> OsString {
|
||||
let file_name = {
|
||||
let size = unsafe { (*info.as_ptr()).size };
|
||||
let strlen = (size as usize - crate::mem::size_of::<file::Info<0>>() - 1) / 2;
|
||||
unsafe { crate::slice::from_raw_parts((*info.as_ptr()).file_name.as_ptr(), strlen) }
|
||||
};
|
||||
|
||||
OsString::from_wide(file_name)
|
||||
let fname = info.file_name();
|
||||
OsString::from_wide(&fname[..fname.len() - 1])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles)
|
||||
|
||||
use r_efi::efi::{self, Guid};
|
||||
use r_efi::protocols::{device_path, device_path_to_text, service_binding, shell};
|
||||
use r_efi::protocols::{device_path, device_path_to_text, file, service_binding, shell};
|
||||
|
||||
use crate::alloc::Layout;
|
||||
use crate::ffi::{OsStr, OsString};
|
||||
@@ -787,7 +787,7 @@ pub(crate) fn new(len: usize) -> io::Result<Self> {
|
||||
|
||||
match NonNull::new(ptr.cast()) {
|
||||
Some(inner) => Ok(Self { inner, size: len }),
|
||||
None => Err(io::Error::new(io::ErrorKind::OutOfMemory, "Allocation failed")),
|
||||
None => Err(const_error!(io::ErrorKind::OutOfMemory, "Allocation failed")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,3 +814,58 @@ fn drop(&mut self) {
|
||||
unsafe { crate::alloc::dealloc(self.inner.as_ptr().cast(), layout) };
|
||||
}
|
||||
}
|
||||
|
||||
impl UefiBox<file::Info> {
|
||||
fn size(&self) -> u64 {
|
||||
unsafe { (*self.as_ptr()).size }
|
||||
}
|
||||
|
||||
fn set_size(&mut self, s: u64) {
|
||||
unsafe { (*self.as_mut_ptr()).size = s }
|
||||
}
|
||||
|
||||
// Length of string (including NULL), not number of bytes.
|
||||
fn file_name_len(&self) -> usize {
|
||||
(self.size() as usize - size_of::<file::Info<0>>()) / size_of::<u16>()
|
||||
}
|
||||
|
||||
pub(crate) fn file_name(&self) -> &[u16] {
|
||||
unsafe {
|
||||
crate::slice::from_raw_parts((*self.as_ptr()).file_name.as_ptr(), self.file_name_len())
|
||||
}
|
||||
}
|
||||
|
||||
fn file_name_mut(&mut self) -> &mut [u16] {
|
||||
unsafe {
|
||||
crate::slice::from_raw_parts_mut(
|
||||
(*self.as_mut_ptr()).file_name.as_mut_ptr(),
|
||||
self.file_name_len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_file_name(mut self, name: &OsStr) -> io::Result<Self> {
|
||||
// os_string_to_raw returns NULL terminated string. So no need to handle it separately.
|
||||
let fname = os_string_to_raw(name)
|
||||
.ok_or(const_error!(io::ErrorKind::OutOfMemory, "Allocation failed"))?;
|
||||
let new_size = size_of::<file::Info<0>>() + fname.len() * size_of::<u16>();
|
||||
|
||||
// Reuse the current structure if the new name can fit in it.
|
||||
if self.size() >= new_size as u64 {
|
||||
self.file_name_mut()[..fname.len()].copy_from_slice(&fname);
|
||||
self.set_size(new_size as u64);
|
||||
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
let mut new_box = UefiBox::new(new_size)?;
|
||||
|
||||
unsafe {
|
||||
crate::ptr::copy_nonoverlapping(self.as_ptr(), new_box.as_mut_ptr(), 1);
|
||||
}
|
||||
new_box.set_size(new_size as u64);
|
||||
new_box.file_name_mut().copy_from_slice(&fname);
|
||||
|
||||
Ok(new_box)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user