在uefi-rs中获取文件大小

问题描述 投票:0回答:1

我正在制作一个基本的 uefi 应用程序,该应用程序应该加载 elf 内核。我已经加载了填充文件和包含文件信息的缓冲区。但要实际读取文件并对其执行任何操作,我需要知道文件大小,以便为其创建缓冲区。我知道 uefi-rs 有一个

FileInfo
结构,但我不知道如何将我拥有的缓冲区转换为这个结构。

我尝试寻找类似问题的解决方案,遇到了这个 Transmuting u8 buffer to struct in Rust。这些解决方案都不起作用,我一直收到该页面上答案的错误,因为我无法将瘦 u8 指针转换为胖

FileInfo
指针。

这是我到目前为止的源代码:

#![no_main]
#![no_std]
#![feature(abi_efiapi)]
#![allow(stable_features)]

#[macro_use]
extern crate alloc;

use elf_rs::{Elf, ElfFile};
use log::info;
use uefi::{prelude::{entry, BootServices, cstr16}, Handle, table::{SystemTable, Boot}, Status, Char16, proto::{loaded_image::LoadedImage, media::{file::{File, FileHandle, FileMode, FileAttribute, FileInfo}, fs::SimpleFileSystem}}, CStr16, data_types::Align};

fn load_file(path: &CStr16, boot_services: &BootServices) -> FileHandle {
    let loaded_image = boot_services.open_protocol_exclusive::<LoadedImage>(boot_services.image_handle()).unwrap();

    let mut file_system = boot_services.open_protocol_exclusive::<SimpleFileSystem>(loaded_image.device()).unwrap();

    let mut directory = file_system.open_volume().unwrap();

    directory.open(path, FileMode::Read, FileAttribute::READ_ONLY).unwrap()
}

#[entry]
fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
    uefi_services::init(&mut system_table).unwrap();
    info!("Loading kernel...");
    let mut kernel = load_file(cstr16!("kernel.elf"), system_table.boot_services()).into_regular_file().unwrap();
    let mut small_buffer = vec![0u8; 0];
    let size = kernel.get_info::<FileInfo>(&mut small_buffer).err().unwrap().data().unwrap();
    let mut file_info = vec![0u8; size];
    kernel.get_info::<FileInfo>(&mut file_info);
    
    let info: FileInfo; //this is what I need


    let elf_buffer = vec![0u8; info.file_size().try_into().unwrap()];
    
    let elf = Elf::from_bytes(&mut elf_buffer).expect("Kernel loading failed!");

    info!("{:?} header: {:?}", elf, elf.elf_header());

    for p in elf.program_header_iter() {
        info!("{:x?}", p);
    }

    for s in elf.section_header_iter() {
        info!("{:x?}", s);
    }

    let s = elf.lookup_section(b".text");
    info!("s {:?}", s);

    system_table.boot_services().stall(100_000_000);
    Status::SUCCESS
}
rust operating-system elf uefi rust-no-std
1个回答
0
投票

有几种方法可以做到这一点。哪一种适合您取决于您需要对文件读取和读入内存进行多少控制。

最简单的选择可能是使用

uefi::fs::FileSystem
。它提供了一个
metadata
方法来获取文件信息,其中包括文件大小。更好的是,如果您只想要大小以便将文件读入 vec,您可以使用
FileSystem::read
来获取
Vec<u8>
。请注意,
uefi::fs
需要启用
alloc
箱的
uefi
功能。

您也可以使用

File::get_info
File::get_info_boxed
或文件查找来获取大小。以下是如何操作的示例:

#![no_main]
#![no_std]

extern crate alloc;

use alloc::vec;
use alloc::vec::Vec;
use uefi::prelude::*;
use uefi::proto::media::file::{File, FileAttribute, FileInfo, FileMode, RegularFile};
use uefi::proto::media::fs::SimpleFileSystem;
use uefi::table::boot::ScopedProtocol;
use uefi::{CStr16, Result};
use uefi_services::println;

/// Read via the `uefi::fs` module. Note that this requires the `alloc`
/// feature of the `uefi` crate to be enabled.
fn read_via_fs_module(path: &CStr16, simple_file_system: ScopedProtocol<SimpleFileSystem>) {
    let mut fs = uefi::fs::FileSystem::new(simple_file_system);

    let size = fs
        .metadata(path)
        .expect("failed to get metadata")
        .file_size();

    let vec = fs.read(path).expect("read failed");

    assert_eq!(vec.len(), usize::try_from(size).unwrap());

    println!(
        "successfully read {} into a Vec of length {}",
        path,
        vec.len()
    );
}

/// Read via the `SimpleFileSystem` protocol directly.
fn read_via_sfs(
    path: &CStr16,
    simple_file_system: &mut ScopedProtocol<SimpleFileSystem>,
) -> Result {
    let mut root = simple_file_system.open_volume()?;
    let mut file = root.open(
        path,
        FileMode::Read,
        // File attributes only matter when creating a file, so
        // use `empty`.
        FileAttribute::empty(),
    )?;

    // There are a few ways to get the length:

    // Option 1: `get_boxed_info`. This requires the `alloc` feature of
    // the `uefi` crate to be enabled.
    let size1 = file.get_boxed_info::<FileInfo>()?.file_size();

    // Option 2: `get_info` with a stack buffer that is sufficiently
    // large to hold the output. This call would fail if the buffer was
    // too small.
    let mut buf = [0; 128];
    let size2 = file.get_info::<FileInfo>(&mut buf).unwrap().file_size();

    // Option 3: `get_info` with a buffer allocated with the correct
    // size. We'll pass in an empty buffer initially to get the
    // size. Since the buffer is too small, we'll get an error -- and
    // that error will contain a `usize` with the required buffer size.
    let mut buf = Vec::new();
    let info_size = file
        .get_info::<FileInfo>(&mut buf)
        .unwrap_err()
        .data()
        .unwrap();
    buf.resize(info_size, 0);
    let size3 = file.get_info::<FileInfo>(&mut buf).unwrap().file_size();

    // Option 4: rather than using `FileInfo`, seek to the end of the
    // buffer. First we need to convert the file into a `RegularFile`
    // (the other type of file is a directory). We'd do that to read the
    // file anyway.
    let mut file = file.into_regular_file().unwrap();
    file.set_position(RegularFile::END_OF_FILE)?;
    let size4 = file.get_position()?;

    // Verify that all four methods of getting the size gave the same
    // value.
    assert_eq!(size1, size2);
    assert_eq!(size1, size3);
    assert_eq!(size1, size4);

    // Now we can create a buffer and read into it.
    let mut buf = vec![0; usize::try_from(size4).unwrap()];
    // Earlier we seeked to the end of the file. Seek back to the
    // beginning so we read the whole thing.
    file.set_position(0)?;
    file.read(&mut buf)?;

    println!("successfully read {} bytes from {}", size1, path);
    Ok(())
}

#[entry]
fn main(image: Handle, mut st: SystemTable<Boot>) -> Status {
    uefi_services::init(&mut st).unwrap();
    // Get the `SimpleFileSystem` protocol for the file system that the
    // current executable was loaded from, probably the ESP (EFI System
    // Partition).
    let mut simple_file_system = st.boot_services().get_image_file_system(image).unwrap();

    let path = cstr16!("kernel.elf");

    read_via_sfs(path, &mut simple_file_system).unwrap();

    read_via_fs_module(path, simple_file_system);
    Status::SUCCESS
}
© www.soinside.com 2019 - 2024. All rights reserved.