morpheus_bootloader/tui/distro_downloader/
manifest_io.rsuse crate::uefi::file_system::{
ascii_to_utf16, close_file, create_directory, create_file, flush_file, get_loaded_image,
open_file_read, write_file, FileProtocol, EFI_FILE_MODE_READ,
};
use crate::BootServices;
use alloc::string::String;
use alloc::vec::Vec;
use morpheus_core::iso::{IsoManifest, IsoStorageManager, MAX_MANIFEST_SIZE};
const MANIFEST_DIR: &str = "\\.iso";
const MAX_MANIFESTS: usize = 16;
pub type ManifestIoResult<T> = Result<T, ManifestIoError>;
#[derive(Debug, Clone, Copy)]
pub enum ManifestIoError {
EspAccessFailed,
DirectoryCreateFailed,
FileCreateFailed,
WriteFailed,
ReadFailed,
SerializeFailed,
DeserializeFailed,
NotFound,
}
pub unsafe fn persist_manifest(
bs: &BootServices,
image_handle: *mut (),
manifest: &IsoManifest,
) -> ManifestIoResult<()> {
let root = get_esp_root(bs, image_handle)?;
let mut iso_path = [0u16; 32];
ascii_to_utf16("\\.iso", &mut iso_path);
let _ = create_directory(root, &iso_path); let name = manifest.name_str();
let manifest_filename = morpheus_core::fs::generate_8_3_manifest_name(name);
let mut filename = String::new();
filename.push_str("\\.iso\\");
filename.push_str(&manifest_filename);
let mut path_utf16 = [0u16; 128];
ascii_to_utf16(&filename, &mut path_utf16);
let file = create_file(root, &path_utf16).map_err(|_| ManifestIoError::FileCreateFailed)?;
let mut buffer = [0u8; MAX_MANIFEST_SIZE];
let size = manifest
.serialize(&mut buffer)
.map_err(|_| ManifestIoError::SerializeFailed)?;
write_file(file, &buffer[..size]).map_err(|_| ManifestIoError::WriteFailed)?;
let _ = flush_file(file);
let _ = close_file(file);
let _ = close_file(root);
morpheus_core::logger::log("Manifest persisted to ESP");
Ok(())
}
pub unsafe fn load_manifests_from_esp(
bs: &BootServices,
image_handle: *mut (),
storage: &mut IsoStorageManager,
) -> ManifestIoResult<usize> {
let root = get_esp_root(bs, image_handle)?;
let mut dir_path = [0u16; 32];
ascii_to_utf16("\\.iso", &mut dir_path);
let mut dir: *mut FileProtocol = core::ptr::null_mut();
let status = ((*root).open)(root, &mut dir, dir_path.as_ptr(), EFI_FILE_MODE_READ, 0);
if status != 0 || dir.is_null() {
let _ = close_file(root);
morpheus_core::logger::log("Manifest dir not found or cannot open");
return Ok(0);
}
morpheus_core::logger::log("Scanning manifest directory...");
let mut count = 0;
let mut buffer = [0u8; 512]; loop {
let mut size = buffer.len();
let status = ((*dir).read)(dir, &mut size, buffer.as_mut_ptr());
if status != 0 || size == 0 {
break; }
if size < 82 {
continue;
}
let attributes = u64::from_le_bytes([
buffer[0x48],
buffer[0x49],
buffer[0x4A],
buffer[0x4B],
buffer[0x4C],
buffer[0x4D],
buffer[0x4E],
buffer[0x4F],
]);
if attributes & 0x10 != 0 {
continue;
}
let filename = extract_filename_from_file_info(&buffer);
morpheus_core::logger::log(alloc::format!("Found file: {}", filename).leak());
let filename_upper = filename.to_uppercase();
if !filename_upper.ends_with(".MFS") && !filename_upper.ends_with(".MANIFEST") {
morpheus_core::logger::log(" -> Not a manifest file, skipping");
continue;
}
morpheus_core::logger::log(alloc::format!("Loading manifest: {}", filename).leak());
match load_single_manifest(root, &filename) {
Ok(manifest) => {
morpheus_core::logger::log(
alloc::format!(
" -> Loaded OK: name='{}', size={}, flags=0x{:02x}",
manifest.name_str(),
manifest.total_size,
manifest.flags
)
.leak(),
);
if storage.add_entry(manifest).is_ok() {
count += 1;
morpheus_core::logger::log(" -> Added to storage");
if count >= MAX_MANIFESTS {
break;
}
} else {
morpheus_core::logger::log(" -> Failed to add to storage");
}
}
Err(e) => {
morpheus_core::logger::log(alloc::format!(" -> FAILED to load: {:?}", e).leak());
}
}
}
let _ = close_file(dir);
let _ = close_file(root);
morpheus_core::logger::log(alloc::format!("Loaded {} manifests from ESP", count).leak());
Ok(count)
}
unsafe fn load_single_manifest(
root: *mut FileProtocol,
filename: &str,
) -> ManifestIoResult<IsoManifest> {
let mut full_path = String::new();
full_path.push_str("\\.iso\\");
full_path.push_str(filename);
let mut path_utf16 = [0u16; 128];
ascii_to_utf16(&full_path, &mut path_utf16);
let mut file: *mut FileProtocol = core::ptr::null_mut();
let status = ((*root).open)(root, &mut file, path_utf16.as_ptr(), EFI_FILE_MODE_READ, 0);
if status != 0 || file.is_null() {
return Err(ManifestIoError::NotFound);
}
let mut buffer = [0u8; MAX_MANIFEST_SIZE];
let mut size = buffer.len();
let status = ((*file).read)(file, &mut size, buffer.as_mut_ptr());
let _ = close_file(file);
if status != 0 || size == 0 {
return Err(ManifestIoError::ReadFailed);
}
IsoManifest::deserialize(&buffer[..size]).map_err(|_| ManifestIoError::DeserializeFailed)
}
unsafe fn get_esp_root(
bs: &BootServices,
image_handle: *mut (),
) -> ManifestIoResult<*mut FileProtocol> {
let loaded_image =
get_loaded_image(bs, image_handle).map_err(|_| ManifestIoError::EspAccessFailed)?;
let device_handle = (*loaded_image).device_handle;
let mut fs_protocol: *mut () = core::ptr::null_mut();
let status = (bs.handle_protocol)(
device_handle,
&crate::uefi::file_system::SIMPLE_FILE_SYSTEM_PROTOCOL_GUID,
&mut fs_protocol,
);
if status != 0 || fs_protocol.is_null() {
return Err(ManifestIoError::EspAccessFailed);
}
let fs = fs_protocol as *mut crate::uefi::file_system::SimpleFileSystemProtocol;
let mut root: *mut FileProtocol = core::ptr::null_mut();
let status = ((*fs).open_volume)(fs, &mut root);
if status != 0 || root.is_null() {
return Err(ManifestIoError::EspAccessFailed);
}
Ok(root)
}
fn extract_filename_from_file_info(buffer: &[u8]) -> String {
let mut filename = String::new();
let mut offset = 0x50; while offset + 1 < buffer.len() {
let c = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
if c == 0 {
break;
}
if let Some(ch) = char::from_u32(c as u32) {
filename.push(ch);
}
offset += 2;
}
filename
}
pub unsafe fn delete_manifest(
bs: &BootServices,
image_handle: *mut (),
name: &str,
) -> ManifestIoResult<()> {
let root = get_esp_root(bs, image_handle)?;
let manifest_filename = morpheus_core::fs::generate_8_3_manifest_name(name);
let mut filename = String::new();
filename.push_str("\\.iso\\");
filename.push_str(&manifest_filename);
let mut path_utf16 = [0u16; 128];
ascii_to_utf16(&filename, &mut path_utf16);
let mut file: *mut FileProtocol = core::ptr::null_mut();
let status = ((*root).open)(
root,
&mut file,
path_utf16.as_ptr(),
EFI_FILE_MODE_READ | crate::uefi::file_system::EFI_FILE_MODE_WRITE,
0,
);
if status != 0 || file.is_null() {
let _ = close_file(root);
return Err(ManifestIoError::NotFound);
}
let status = ((*file).delete)(file);
let _ = close_file(root);
if status != 0 {
return Err(ManifestIoError::WriteFailed);
}
Ok(())
}