File System

Accessing files and directories using UEFI file system protocols.

File System Architecture

flowchart TB
    subgraph protocols["UEFI File System Protocols"]
        SFS["SimpleFileSystem<br/>Protocol"]
        FP["File Protocol"]
    end

    subgraph storage["Storage"]
        DISK["Block Device"]
        PART["Partition"]
        FS["FAT32 / Other FS"]
    end

    SFS -->|"OpenVolume()"| FP
    FP -->|"Read/Write"| FS
    FS --> PART
    PART --> DISK

SimpleFileSystem Protocol

The entry point for file access:

use uefi::proto::media::fs::SimpleFileSystem;
use uefi::proto::media::file::{File, FileMode, FileAttribute, Directory};
use uefi::table::boot::SearchType;

fn open_filesystem(bt: &BootServices) -> uefi::Result<Directory> {
    // Find all file system handles
    let handles = bt.locate_handle_buffer(
        SearchType::ByProtocol(&SimpleFileSystem::GUID)
    )?;

    log::info!("Found {} file systems", handles.len());

    // Open first file system
    if let Some(&handle) = handles.first() {
        let fs = bt.open_protocol_exclusive::<SimpleFileSystem>(handle)?;

        // Open the root directory
        let root = fs.open_volume()?;

        log::info!("Opened root directory");
        return Ok(root);
    }

    Err(uefi::Status::NOT_FOUND.into())
}

Opening Files

Read a File

use uefi::proto::media::file::{File, FileMode, FileAttribute, RegularFile};
use uefi::CStr16;

fn read_file(root: &mut Directory, path: &CStr16) -> uefi::Result<alloc::vec::Vec<u8>> {
    // Open file for reading
    let handle = root.open(
        path,
        FileMode::Read,
        FileAttribute::empty(),
    )?;

    // Convert to regular file
    let mut file = handle.into_regular_file().ok_or(uefi::Status::INVALID_PARAMETER)?;

    // Get file size
    let mut info_buffer = [0u8; 256];
    let info = file.get_info::<FileInfo>(&mut info_buffer)?;
    let size = info.file_size() as usize;

    log::info!("File size: {} bytes", size);

    // Read file contents
    let mut buffer = alloc::vec![0u8; size];
    let bytes_read = file.read(&mut buffer)?;
    buffer.truncate(bytes_read);

    log::info!("Read {} bytes", bytes_read);

    Ok(buffer)
}

Write a File

fn write_file(root: &mut Directory, path: &CStr16, data: &[u8]) -> uefi::Result {
    // Open or create file for writing
    let handle = root.open(
        path,
        FileMode::CreateReadWrite,
        FileAttribute::empty(),
    )?;

    let mut file = handle.into_regular_file().ok_or(uefi::Status::INVALID_PARAMETER)?;

    // Write data
    file.write(data)?;

    // Flush to ensure data is written
    file.flush()?;

    log::info!("Wrote {} bytes to file", data.len());

    Ok(())
}

Create a New File

fn create_file(root: &mut Directory, name: &CStr16) -> uefi::Result<RegularFile> {
    let handle = root.open(
        name,
        FileMode::CreateReadWrite,
        FileAttribute::empty(),
    )?;

    let file = handle.into_regular_file().ok_or(uefi::Status::INVALID_PARAMETER)?;

    log::info!("Created file");

    Ok(file)
}

Directory Operations

List Directory Contents

use uefi::proto::media::file::FileInfo;

fn list_directory(dir: &mut Directory) -> uefi::Result {
    // Reset to beginning
    dir.reset_entry_readout()?;

    log::info!("Directory contents:");

    loop {
        // Buffer for file info
        let mut buffer = [0u8; 512];

        match dir.read_entry(&mut buffer) {
            Ok(Some(info)) => {
                let name = info.file_name();
                let size = info.file_size();
                let is_dir = info.attribute().contains(FileAttribute::DIRECTORY);

                if is_dir {
                    log::info!("  [DIR]  {}", name);
                } else {
                    log::info!("  {:>8} bytes  {}", size, name);
                }
            }
            Ok(None) => break, // End of directory
            Err(e) => return Err(e),
        }
    }

    Ok(())
}

Create Directory

fn create_directory(root: &mut Directory, name: &CStr16) -> uefi::Result<Directory> {
    let handle = root.open(
        name,
        FileMode::CreateReadWrite,
        FileAttribute::DIRECTORY,
    )?;

    let dir = handle.into_directory().ok_or(uefi::Status::INVALID_PARAMETER)?;

    log::info!("Created directory");

    Ok(dir)
}

Open Subdirectory

fn open_subdirectory(parent: &mut Directory, name: &CStr16) -> uefi::Result<Directory> {
    let handle = parent.open(
        name,
        FileMode::Read,
        FileAttribute::empty(),
    )?;

    let dir = handle.into_directory().ok_or(uefi::Status::NOT_A_DIRECTORY)?;

    Ok(dir)
}

File Information

Get File Info

use uefi::proto::media::file::{FileInfo, FileSystemInfo};

fn get_file_info(file: &mut RegularFile) -> uefi::Result {
    let mut buffer = [0u8; 512];
    let info = file.get_info::<FileInfo>(&mut buffer)?;

    log::info!("File Information:");
    log::info!("  Name: {}", info.file_name());
    log::info!("  Size: {} bytes", info.file_size());
    log::info!("  Physical Size: {} bytes", info.physical_size());
    log::info!("  Read-only: {}", info.attribute().contains(FileAttribute::READ_ONLY));
    log::info!("  Hidden: {}", info.attribute().contains(FileAttribute::HIDDEN));
    log::info!("  System: {}", info.attribute().contains(FileAttribute::SYSTEM));

    Ok(())
}

Get File System Info

fn get_fs_info(root: &mut Directory) -> uefi::Result {
    let mut buffer = [0u8; 512];
    let info = root.get_info::<FileSystemInfo>(&mut buffer)?;

    log::info!("File System Information:");
    log::info!("  Volume Label: {}", info.volume_label());
    log::info!("  Volume Size: {} bytes", info.volume_size());
    log::info!("  Free Space: {} bytes", info.free_space());
    log::info!("  Block Size: {} bytes", info.block_size());
    log::info!("  Read-only: {}", info.read_only());

    Ok(())
}

Set File Info

fn set_file_readonly(file: &mut RegularFile, readonly: bool) -> uefi::Result {
    let mut buffer = [0u8; 512];
    let info = file.get_info::<FileInfo>(&mut buffer)?;

    // Modify attributes
    let mut attr = info.attribute();
    if readonly {
        attr |= FileAttribute::READ_ONLY;
    } else {
        attr -= FileAttribute::READ_ONLY;
    }

    // Create new info with modified attributes
    // Note: This requires creating a new FileInfo structure
    // The actual API may vary

    Ok(())
}

File Positioning

fn file_seek_example(file: &mut RegularFile) -> uefi::Result {
    // Get current position
    let pos = file.get_position()?;
    log::info!("Current position: {}", pos);

    // Seek to beginning
    file.set_position(0)?;

    // Seek to end (use special value)
    file.set_position(u64::MAX)?;
    let size = file.get_position()?;
    log::info!("File size: {}", size);

    // Seek to middle
    file.set_position(size / 2)?;

    Ok(())
}

Delete Files

fn delete_file(root: &mut Directory, path: &CStr16) -> uefi::Result {
    let handle = root.open(
        path,
        FileMode::Read,  // Open existing
        FileAttribute::empty(),
    )?;

    // Delete the file
    handle.delete()?;

    log::info!("File deleted");

    Ok(())
}

Finding the Boot Volume

use uefi::proto::loaded_image::LoadedImage;
use uefi::proto::media::fs::SimpleFileSystem;

fn get_boot_volume(bt: &BootServices, image: Handle) -> uefi::Result<Directory> {
    // Get the loaded image protocol
    let loaded_image = bt.open_protocol_exclusive::<LoadedImage>(image)?;

    // Get the device handle we were loaded from
    let device = loaded_image.device().ok_or(uefi::Status::NOT_FOUND)?;

    // Open the file system on that device
    let fs = bt.open_protocol_exclusive::<SimpleFileSystem>(device)?;

    // Open root directory
    let root = fs.open_volume()?;

    log::info!("Opened boot volume");

    Ok(root)
}

Complete Example

#![no_main]
#![no_std]

extern crate alloc;

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

#[entry]
fn main(image: Handle, st: SystemTable<Boot>) -> Status {
    uefi::helpers::init().unwrap();

    let bt = st.boot_services();

    // Find file systems
    let handles = match bt.locate_handle_buffer(SearchType::ByProtocol(&SimpleFileSystem::GUID)) {
        Ok(h) => h,
        Err(_) => {
            log::error!("No file systems found");
            return Status::NOT_FOUND;
        }
    };

    log::info!("Found {} file systems", handles.len());

    // Open first file system
    if let Some(&handle) = handles.first() {
        let fs = bt.open_protocol_exclusive::<SimpleFileSystem>(handle).unwrap();
        let mut root = fs.open_volume().unwrap();

        // List root directory
        log::info!("\n=== Root Directory ===");
        root.reset_entry_readout().unwrap();

        let mut count = 0;
        loop {
            let mut buffer = [0u8; 512];
            match root.read_entry(&mut buffer) {
                Ok(Some(info)) => {
                    let name = info.file_name();
                    let is_dir = info.attribute().contains(FileAttribute::DIRECTORY);
                    if is_dir {
                        log::info!("[DIR]  {}", name);
                    } else {
                        log::info!("{:>8}  {}", info.file_size(), name);
                    }
                    count += 1;
                }
                Ok(None) => break,
                Err(_) => break,
            }
        }
        log::info!("\nTotal: {} entries", count);

        // Try to read a specific file
        log::info!("\n=== Reading File ===");
        let path = cstr16!("\\EFI\\BOOT\\BOOTX64.EFI");

        match root.open(path, FileMode::Read, FileAttribute::empty()) {
            Ok(handle) => {
                if let Some(mut file) = handle.into_regular_file() {
                    let mut info_buf = [0u8; 256];
                    if let Ok(info) = file.get_info::<FileInfo>(&mut info_buf) {
                        log::info!("Found: {}", info.file_name());
                        log::info!("Size: {} bytes", info.file_size());
                    }
                }
            }
            Err(_) => log::info!("File not found"),
        }
    }

    Status::SUCCESS
}

Error Handling

Status Meaning
NOT_FOUND File or directory doesn’t exist
ACCESS_DENIED Permission denied
VOLUME_FULL No space left
WRITE_PROTECTED Read-only media
MEDIA_CHANGED Media was removed/changed

Summary

Operation Method
Open volume SimpleFileSystem::open_volume()
Open file Directory::open()
Read file RegularFile::read()
Write file RegularFile::write()
List directory Directory::read_entry()
Get info File::get_info()
Delete FileHandle::delete()

See Also

Next Steps

Learn about Graphics output using the Graphics Output Protocol.


Back to top

Rust Programming Guide is not affiliated with the Rust Foundation. Content is provided for educational purposes.

This site uses Just the Docs, a documentation theme for Jekyll.