Merge pull request 'Arena allocator for DirEntry management' (#23) from development into main

Reviewed-on: #23
This commit is contained in:
Bas Wiel, van de 2024-05-12 16:25:21 +02:00
commit c64fe1f457
12 changed files with 446 additions and 295 deletions

71
Cargo.lock generated
View File

@ -83,9 +83,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.2.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backtrace"
@ -149,9 +149,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.96"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
[[package]]
name = "cfg-if"
@ -325,9 +325,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
@ -439,9 +439,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
@ -724,9 +724,9 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.2.18"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
@ -838,9 +838,9 @@ checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "proc-macro2"
version = "1.0.81"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
@ -896,9 +896,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustix"
@ -924,9 +924,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "schannel"
@ -939,11 +939,11 @@ dependencies = [
[[package]]
name = "security-framework"
version = "2.10.0"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
dependencies = [
"bitflags 1.3.2",
"bitflags 2.5.0",
"core-foundation",
"core-foundation-sys",
"libc",
@ -952,9 +952,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.10.0"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
dependencies = [
"core-foundation-sys",
"libc",
@ -962,18 +962,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.200"
version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.200"
version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
dependencies = [
"proc-macro2",
"quote",
@ -982,9 +982,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.116"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
@ -1054,9 +1054,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.60"
version = "2.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
dependencies = [
"proc-macro2",
"quote",
@ -1104,18 +1104,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.59"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.59"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
dependencies = [
"proc-macro2",
"quote",
@ -1164,16 +1164,15 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.10"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]

View File

@ -3,7 +3,7 @@ use crate::{
partition::{Partition, PartitionStatus},
Disk,
},
fat::Fat,
fat::{direntry::NodeRef, Fat},
};
pub struct Analyzer {
@ -20,6 +20,8 @@ impl Analyzer {
}
pub fn print_metadata(&mut self) {
let rootdir_ref = NodeRef::new(self.filesystem.root_directory, &self.filesystem.arena);
println!("Disk metadata according to VHD footer\n-------------------------------------\n");
println!("{}", self.disk.get_metadata());
println!("Partition table according to the MBR\n------------------------------------\n");
@ -27,10 +29,7 @@ impl Analyzer {
println!("Filesystem information\n-----------------------------------------------\n");
println!("{}", self.filesystem);
println!("Root directory information\n----------------------------------\n");
println!("{}", self.filesystem.root_directory);
for child in self.filesystem.root_directory.get_children() {
println!("{}", child);
}
println!("{}", rootdir_ref);
}
fn get_boot_partition(disk: &Disk) -> Option<Partition> {
@ -41,7 +40,7 @@ impl Analyzer {
_ => println!("Nothing found"),
}
}
if partitions.len() == 0 {
if partitions.is_empty() {
return None;
}

View File

@ -7,7 +7,7 @@ use crate::{
},
downloader::ZipDownloader,
fat::{
direntry::{DirEntry, DirEntryType},
direntry::{DirEntry, DirEntryType, NodeId},
vbr::Vbr,
Fat,
},
@ -44,7 +44,7 @@ impl DiskBuilder {
filesystem,
partition: root_partition,
operating_system,
manifest: manifest,
manifest,
})
}
@ -113,41 +113,44 @@ impl DiskBuilder {
/// and that `file.allocated_clusters` contains a valid list of disk addresses. It also
/// assumes that each address in `file.allocated_clusters` can hold a cluster of data
/// of size defined by `filesystem.get_sectors_per_cluster() * 512`.
pub fn write_file_to_storage(&mut self, file: &DirEntry) -> Result<(), std::io::Error> {
if file.entry_type != DirEntryType::File
&& file.entry_type != DirEntryType::SysFile
&& file.entry_type != DirEntryType::Directory
{
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"This method only works on files, not on other entry types.",
));
}
let bytes = file.get_data(); // Assuming this returns a &[u8]
if bytes.len() == 0 {
// Don't touch anything if we don't have any bytes.
pub fn write_entry_to_storage(&mut self, entry_id: NodeId) -> Result<(), std::io::Error> {
let cluster_size = self.get_cluster_size();
let entry = self.filesystem.arena.get_node(entry_id).ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::NotFound, "Entry node not found")
})?;
let entry = &entry.value.clone();
let bytes = match entry.entry_type {
DirEntryType::File => entry.get_data().to_vec(),
DirEntryType::SysFile => entry.get_data().to_vec(),
DirEntryType::Directory => entry.children_bytes(&self.filesystem.arena)?,
};
if bytes.is_empty() {
return Ok(());
}
let cluster_size = self.filesystem.get_sectors_per_cluster() * 512;
let chunks = bytes.chunks(cluster_size);
let addresses = &file.allocated_clusters; // Use a reference
let chunks = bytes.chunks(cluster_size).collect::<Vec<_>>(); // Collect into Vec
let addresses = &entry.allocated_clusters; // Use a reference
// Ensure the number of allocated clusters matches the number of chunks
if addresses.len() != chunks.len() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Number of allocated clusters does not match the number of data chunks.",
));
if entry.entry_type == DirEntryType::Directory && (entry.directory_name() == "." || entry.directory_name() == "..") {
return Ok(());
}
// Iterate over addresses and chunks to write data directly
for (address, chunk) in addresses.iter().zip(chunks) {
// Call write_cluster using mutable filesystem
self.write_cluster(*address, chunk)?;
}
Ok(())
}
fn get_cluster_size(&self) -> usize {
self.filesystem.get_sectors_per_cluster() * 512
}
/// Writes the data of a cluster to the disk.
///
/// This method takes the data of a cluster, represented as a `&[u8]`, and writes it to the disk.
@ -177,9 +180,9 @@ impl DiskBuilder {
// Calculate the base offset to figure out where the cluster should live on the disk
let mut sector_address = (cluster_address as usize
* self.filesystem.get_sectors_per_cluster() as usize
* self.filesystem.get_sectors_per_cluster()
+ self.filesystem.get_first_data_sector() as usize)
- 2 * self.filesystem.get_sectors_per_cluster() as usize;
- 2 * self.filesystem.get_sectors_per_cluster();
for chunk in bytes.chunks_exact(512) {
let mut chunk_array = [0u8; 512];
@ -230,7 +233,7 @@ impl DiskBuilder {
)?;
}
counter += 1;
sector_offset = sector_offset + self.filesystem.sectors_per_fat;
sector_offset += self.filesystem.sectors_per_fat;
}
Ok(())
@ -258,47 +261,55 @@ impl DiskBuilder {
/// - Errors can include but are not limited to I/O errors like disk write failures.
///
pub fn write_dirs(&mut self) -> Result<(), std::io::Error> {
self.write_root_dir()?;
// Assuming root_directory is a NodeId pointing to the root directory in the Arena
let root_id = self.filesystem.root_directory;
for entry in &self.filesystem.root_directory.children.clone() {
if entry.is_directory() {
self.write_directory_recursive(entry)?;
} else {
self.write_file_to_storage(entry)?;
}
}
Ok(())
// Recursively write the root directory and all its subdirectories and files
self.write_directory_recursive(root_id)
}
// General method to write any directory recursively
fn write_directory_recursive(&mut self, dir: &DirEntry) -> Result<(), std::io::Error> {
self.write_file_to_storage(dir)?;
for entry in &dir.children {
if entry.is_directory() {
if entry.directory_name() != "." && entry.directory_name() != ".." {
self.write_file_to_storage(entry)?;
}
self.write_directory_recursive(entry)?;
} else {
if entry.directory_name() != "." && entry.directory_name() != ".." {
self.write_file_to_storage(entry)?;
}
}
/// Recursively writes a directory and its contents.
fn write_directory_recursive(&mut self, dir_id: NodeId) -> Result<(), std::io::Error> {
// Write the directory itself to storage
self.write_entry_to_storage(dir_id)?;
// Get the directory node from the arena
let child_ids = {
let dir_node = self.filesystem.arena.get_node(dir_id).ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::NotFound, "Directory node not found")
})?;
// Clone child IDs to avoid borrowing issues during recursion
dir_node.value.children.clone()
};
// Now iterate over each child ID and recursively write directories or files
for &child_id in &child_ids {
self.write_directory_recursive(child_id)?;
}
Ok(())
}
fn write_root_dir(&mut self) -> Result<(), std::io::Error> {
// Calculate the starting sector for the root directory.
let sector_offset: u32 = self.filesystem.reserved_sector_count as u32
+ (self.filesystem.fat_copies as u32 * self.filesystem.sectors_per_fat as u32);
let bytes = self.filesystem.root_directory.children_bytes()?;
let mut sectors: Vec<[u8; 512]> = Vec::new();
// Write the bytes off to disk
// Assuming root_directory is a NodeId pointing to the root directory in the Arena
let root_id = self.filesystem.root_directory;
let root_node = self.filesystem.arena.get_node(root_id).ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::NotFound, "Root directory not found")
})?;
// Serialize children bytes from the root directory
let bytes = self.serialize_directory_children(&root_node.value)?;
// Prepare to write bytes to sectors
let mut sectors: Vec<[u8; 512]> = Vec::new();
let mut current_chunk: [u8; 512] = [0; 512];
let mut current_index = 0;
// Write the bytes off to disk
for &byte in bytes.iter() {
current_chunk[current_index] = byte;
current_index += 1;
@ -318,16 +329,28 @@ impl DiskBuilder {
// Write sectors to disk
for (index, sector) in sectors.iter().enumerate() {
self.partition.write_sector(
&mut self.disk,
sector_offset as u32 + index as u32,
*sector,
)?;
self.partition
.write_sector(&mut self.disk, sector_offset + index as u32, *sector)?;
}
Ok(())
}
/// Serialize children bytes for a given directory entry
fn serialize_directory_children(
&self,
directory: &DirEntry,
) -> Result<Vec<u8>, std::io::Error> {
let mut bytes = Vec::new();
for &child_id in &directory.children {
let child = self.filesystem.arena.get_node(child_id).ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::NotFound, "Child directory not found")
})?;
bytes.extend(child.value.as_bytes()?); // Assuming DirEntry has an as_bytes method
}
Ok(bytes)
}
/// The builder is the struct that does all manner of validation before trying to
/// create a Disk so that what we create will make sense.
pub fn build(&mut self) -> Result<(), std::io::Error> {
@ -371,14 +394,10 @@ impl DiskBuilder {
}
}
// Write all files to storage
for file in self.filesystem.root_directory.children.clone() {
self.write_file_to_storage(&file)?;
}
// REMOVE THIS
self.filesystem.mkdir(None, "DOS")?;
// Last action: write the allocation table copies to the disk
self.write_root_dir().unwrap();
self.write_dirs()?;
self.write_allocation_tables()?;
self.disk.commit_storage()?;

View File

@ -156,7 +156,7 @@ impl Disk {
}
pub fn get_image_type(&self) -> &VhdDisktype {
&self.metadata.get_type()
self.metadata.get_type()
}
/// Read a sector-sized block from the cache

View File

@ -51,7 +51,7 @@ impl ZipDownloader {
// Check if the filename matches the provided name
if file.get_name() == name {
// If found, return a reference to the DownloadedFile
return Some(&file);
return Some(file);
}
}
// If the file is not found, return None

View File

@ -1,9 +1,10 @@
use std::fmt;
use self::allocationtable::AllocationTable;
use self::direntry::{DirEntry, DirEntryType};
use self::direntry::{Arena, DirEntry, DirEntryType, NodeId};
use crate::disk::partition::{Partition, PartitionType};
use crate::disk::vhd::VhdDisktype;
use crate::fat::direntry::Node;
use crate::os::OperatingSystem;
use crate::Disk;
@ -23,7 +24,8 @@ pub struct Fat {
/// Holds the allocation table, which manages cluster allocation and deallocation.
pub allocation_table: AllocationTable,
/// Holds the root directory entries.
pub root_directory: DirEntry,
pub arena: Arena<DirEntry>,
pub root_directory: NodeId,
/// Stores the OEM name of the filesystem.
oem_name: String,
/// Indicates the number of bytes per sector in the filesystem.
@ -167,19 +169,15 @@ impl Fat {
None => [0u8; 446],
};
let mut arena = Arena::new();
let root_directory = arena.insert_node(Node::new_directory("root", Vec::new().as_slice()));
Ok(Fat {
fat_type,
allocation_table: AllocationTable::new(
sector_count as u32 / sectors_per_cluster as u32,
sector_count / sectors_per_cluster as u32,
),
root_directory: DirEntry::new(
"ROOTDIR",
DirEntryType::RootDirectory,
None,
&Vec::new(),
0,
None,
)?,
arena,
root_directory,
oem_name: String::from("IBM 2.0"),
bytes_per_sector: SECTOR_SIZE,
sectors_per_cluster,
@ -247,6 +245,9 @@ impl Fat {
let vbr_address = partition.get_start_sector();
let vbr_lba = vbr_address.to_lba(&disk_geometry)?;
let vbr_bytes = disk.read_sector(vbr_lba)?;
let mut arena = Arena::new();
let root_directory =
arena.insert_node(Node::new_directory("rootdir", Vec::new().as_slice()));
if vbr_bytes[0] == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
@ -263,14 +264,8 @@ impl Fat {
sector_count as u32 / sectors_per_cluster as u32,
),
oem_name: Self::oem_name_from_bytes(&vbr_bytes),
root_directory: DirEntry::new(
"ROOTDIR",
DirEntryType::RootDirectory,
None,
&Vec::new(),
0,
None,
)?,
arena,
root_directory,
bytes_per_sector: 512,
sectors_per_cluster,
reserved_sector_count: u16::from_le_bytes([vbr_bytes[0x00e], vbr_bytes[0x00f]]),
@ -333,7 +328,7 @@ impl Fat {
/// Create a new file, give the data as a byte slice.
pub fn mkfile(
&mut self,
parent: Option<&mut DirEntry>,
parent_id: Option<NodeId>,
filename: &str,
sysfile: bool,
data: &[u8],
@ -350,84 +345,87 @@ impl Fat {
// Write the proper values to the allocation table
if let Some((last, elements)) = allocated_clusters.split_last() {
// Iterate over all but the last element with their indices
for (i, &current_cluster) in elements.iter().enumerate() {
let next_cluster = allocated_clusters[i + 1];
self.allocation_table
.allocate_cluster(current_cluster, next_cluster)?;
}
// Handle the last cluster, which points to the end of the chain
self.allocation_table.mark_end_of_chain(*last)?;
}
// Create a new directory entry
let file_entry = DirEntry::new(
filename,
entry_type,
None,
&allocated_clusters,
data.len() as u32,
Some(data),
)?;
// Insert the new file into the arena and get its NodeId
let node_id = match entry_type {
DirEntryType::Directory => self
.arena
.insert_node(Node::new_directory(filename, allocated_clusters.as_slice())),
DirEntryType::File => self.arena.insert_node(Node::new_file(
filename,
None,
allocated_clusters,
data.len() as u32,
data.to_vec(),
false,
)?),
DirEntryType::SysFile => self.arena.insert_node(Node::new_file(
filename,
None,
allocated_clusters,
data.len() as u32,
data.to_vec(),
true,
)?),
};
// Add the new entry to the parent directory or use the root directory
if let Some(parent_dir) = parent {
parent_dir.add_child(file_entry);
} else {
self.root_directory.add_child(file_entry);
}
if let Some(pid) = parent_id {
if let Some(parent_node) = self.arena.get_node_mut(pid) {
parent_node.value.children.push(node_id);
} else {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Parent node not found",
));
}
} else
if let Some(root) = self.arena
.get_node_mut(self.root_directory) { root.value.children.push(node_id) }
Ok(())
}
/// Equivalent of the MS-DOS "MKDIR" command. Provide a name, the system will do the rest.
pub fn mkdir(
&mut self,
parent: Option<&mut DirEntry>,
parent: Option<NodeId>,
dir_name: &str,
) -> Result<(), std::io::Error> {
) -> Result<NodeId, std::io::Error> {
// Set the parent to the root dir if none is given
let new_start_cluster = self.allocate_cluster()?;
self.allocation_table.mark_end_of_chain(new_start_cluster)?;
let allocated_clusters: Vec<u32> = vec![new_start_cluster];
let parent_entry = parent.unwrap_or(&mut self.root_directory);
let parent_clusters = vec![parent_entry.get_start_cluster()];
let parent_entry = parent.unwrap_or(self.root_directory);
let parent_clusters = if let Some(parent_node) = self.arena.get_node(parent_entry) {
parent_node.value.get_allocated_clusters().clone()
} else {
vec![2] // Default to empty if parent isn't found, though this should be handled more robustly
};
// Instantiate the new entry.
let mut new_entry = DirEntry::new(
dir_name,
DirEntryType::Directory,
None,
&allocated_clusters,
0,
None,
)?;
let new_dir_node = Node::new_directory(dir_name, allocated_clusters.as_slice());
let new_dir_id = self.arena.insert_node(new_dir_node);
self.arena.add_child(parent_entry, new_dir_id);
// Create the "." entry as a child.
new_entry.add_child(DirEntry::new(
".",
DirEntryType::Directory,
None,
&allocated_clusters,
0,
None,
)?);
let self_dot_node = Node::new_directory(".", allocated_clusters.as_slice());
let self_dot_id = self.arena.insert_node(self_dot_node);
let parent_dot_node = Node::new_directory("..", parent_clusters.as_slice());
let parent_dot_id = self.arena.insert_node(parent_dot_node);
self.arena.add_child(new_dir_id, self_dot_id);
self.arena.add_child(new_dir_id, parent_dot_id);
// Create the ".." entry as a child.
new_entry.add_child(DirEntry::new(
"..",
DirEntryType::Directory,
None,
&parent_clusters,
0,
None,
)?);
// Add the complete new entry to its parent
parent_entry.add_child(new_entry);
Ok(())
Ok(new_dir_id)
}
/// Generate a BIOS Parameter Block (BPB) compatible with DOS 2.00.
@ -526,12 +524,12 @@ impl Fat {
/// Allocate a single free cluster for things like directory entries.
fn allocate_cluster(&mut self) -> Result<u32, std::io::Error> {
if let Some(free_cluster) = self.allocation_table.get_free_cluster() {
return Ok(free_cluster);
Ok(free_cluster)
} else {
return Err(std::io::Error::new(
Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"No free cluster available. File system is full.",
));
))
}
}
@ -663,6 +661,32 @@ impl Fat {
+ rootdir_sector_count;
first_data_sector as u32
}
// Gets children of a directory entry by NodeId and returns references to their DirEntry
pub fn get_directory_entries(&self, directory_id: NodeId) -> Vec<&DirEntry> {
// Attempt to get the directory entry from the arena
if let Some(dir_entry) = self.arena.get_node(directory_id) {
// Map over the NodeIds in the children vector to fetch the actual DirEntry references
dir_entry
.value
.children
.iter()
.filter_map(|&child_id| self.arena.get_node(child_id).map(|node| &node.value))
.collect()
} else {
// If the directory node is not found, return an empty vector
vec![]
}
}
/// Retrieves the allocated clusters for a directory entry identified by `node_id`.
pub fn get_allocated_clusters(&self, node_id: NodeId) -> Option<&Vec<u32>> {
// Access the node from the arena using the node ID
self.arena
.get_node(node_id)
// If the node exists, retrieve the allocated clusters
.map(|node| node.value.get_allocated_clusters())
}
}
/// Enum representing different types of FAT (File Allocation Table) file systems.

View File

@ -69,7 +69,8 @@ impl AllocationTable {
"Attempted to reserve an unavailable cluster.",
));
}
Ok(cluster.set_reserved())
cluster.set_reserved();
Ok(())
}
// Nothing lives at index yet, place a reserved cluster there.
else {
@ -132,10 +133,10 @@ impl AllocationTable {
if let Some(cluster) = self.clusters.get(&cluster_address) {
Ok(*cluster)
} else {
return Err(std::io::Error::new(
Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Fudgies!!",
));
))
}
}
@ -183,7 +184,6 @@ impl AllocationTable {
bytes.push(decomposed[0]);
bytes.push(decomposed[1]);
}
println!("Allocation table bytes: {:?}", bytes);
Ok(bytes)
}

View File

@ -52,7 +52,6 @@ impl Cluster {
/// Allocate the cluster
pub fn allocate(&mut self, value: u32) -> Result<(), std::io::Error> {
if self.state == ClusterState::Allocated {
println!("{:?}", self);
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"The current cluster was already allocated. Refusing to proceed.",

View File

@ -1,6 +1,27 @@
use super::{attributes::Attributes, filename::FileName, timedate::TimeDate};
use std::{cmp::min, fmt};
pub struct NodeRef<'a, T> {
node_id: NodeId,
arena: &'a Arena<T>,
}
impl<'a, T> NodeRef<'a, T> {
pub fn new(node_id: NodeId, arena: &'a Arena<T>) -> Self {
NodeRef { node_id, arena }
}
}
impl<'a> fmt::Display for NodeRef<'a, DirEntry> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(node) = self.arena.get_node(self.node_id) {
write!(f, "{}", node.value)
} else {
write!(f, "Invalid NodeId")
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct DirEntry {
filename: FileName,
@ -8,11 +29,117 @@ pub struct DirEntry {
create_timedate: TimeDate,
pub file_size: u32,
pub entry_type: DirEntryType,
pub children: Vec<DirEntry>,
pub children: Vec<NodeId>,
pub allocated_clusters: Vec<u32>,
data: Vec<u8>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Node<T> {
pub value: T,
pub parent: Option<NodeId>, // Optionally keep track of the parent
}
impl Node<DirEntry> {
/// Creates a new file node with given properties.
pub fn new_file(
filename: &str,
create_timedate: Option<TimeDate>,
allocated_clusters: Vec<u32>,
file_size: u32,
data: Vec<u8>,
sysfile: bool, // Indicates whether the file is a system file or regular file
) -> Result<Self, std::io::Error> {
// Create the file entry using the DirEntry's create method
let file_entry = DirEntry::create_file(
filename,
create_timedate,
&allocated_clusters,
file_size,
data,
sysfile,
)?;
// Return the new Node encapsulating the DirEntry
Ok(Node {
value: file_entry,
parent: None, // Initially, no parent is set
})
}
pub fn new_directory(dir_name: &str, allocated_clusters: &[u32]) -> Self {
Node {
value: DirEntry::new(
dir_name,
DirEntryType::Directory,
None,
&allocated_clusters.to_vec(),
0,
None,
)
.expect("Boom!"),
parent: None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Arena<T> {
nodes: Vec<Node<T>>,
}
impl Default for Arena<DirEntry> {
fn default() -> Self {
Self::new()
}
}
impl Arena<DirEntry> {
pub fn new() -> Self {
Arena { nodes: Vec::new() }
}
pub fn new_node(&mut self, entry: DirEntry) -> NodeId {
let node_id = NodeId(self.nodes.len());
self.nodes.push(Node {
value: entry,
parent: None,
});
node_id
}
pub fn add_child(&mut self, parent_id: NodeId, child_id: NodeId) {
if let Some(node) = self.nodes.get_mut(parent_id.0) {
node.value.children.push(child_id);
}
if let Some(node) = self.nodes.get_mut(child_id.0) {
node.parent = Some(parent_id);
}
}
/// Inserts a new node into the arena and returns a NodeId that can be used to reference it.
pub fn insert_node(&mut self, node: Node<DirEntry>) -> NodeId {
let node_id = NodeId(self.nodes.len());
self.nodes.push(node);
node_id
}
/// Retrieves an immutable reference to the node specified by `node_id`.
/// Returns `None` if no node exists at that index.
pub fn get_node(&self, node_id: NodeId) -> Option<&Node<DirEntry>> {
self.nodes.get(node_id.0)
}
/// Retrieves a mutable reference to the node specified by `node_id`.
/// Returns `None` if no node exists at that index.
pub fn get_node_mut(&mut self, node_id: NodeId) -> Option<&mut Node<DirEntry>> {
self.nodes.get_mut(node_id.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NodeId(usize);
/// Enum to differentiate between different types of DirEntry. These types streamline
/// the definition of default values.
#[derive(Clone, Copy, Debug, PartialEq)]
@ -20,7 +147,6 @@ pub enum DirEntryType {
File,
SysFile,
Directory,
RootDirectory,
}
impl fmt::Display for DirEntryType {
@ -28,7 +154,6 @@ impl fmt::Display for DirEntryType {
match self {
DirEntryType::Directory => write!(f, "Subdirectory"),
DirEntryType::File => write!(f, "Regular file"),
DirEntryType::RootDirectory => write!(f, "Root Directory"),
DirEntryType::SysFile => write!(f, "System file"),
}
}
@ -65,104 +190,64 @@ impl DirEntry {
allocated_clusters,
file_size,
data.unwrap().to_vec(),
false,
),
DirEntryType::SysFile => Self::create_file(
filename,
create_timedate,
allocated_clusters,
file_size,
data.unwrap().to_vec(),
true,
),
DirEntryType::SysFile => {
Self::create_sysfile(filename, create_timedate, file_size, allocated_clusters, data)
}
DirEntryType::Directory => {
Self::create_directory(filename, create_timedate, allocated_clusters)
}
DirEntryType::RootDirectory => Self::create_rootdir(),
}
}
/// Deliver a standard system file with attribs System, Hidden and ReadOnly set plus sensible values for a file.
/// Create a regular file with Archive set, all other attributes unset.
/// If you don't provide a TimeDate, the current system time on the host machine will be used.
fn create_sysfile(
filename: &str,
create_timedate: Option<TimeDate>,
file_size: u32,
allocated_clusters: &Vec<u32>,
data: Option<&[u8]>,
) -> Result<Self, std::io::Error> {
// Set of attributes appropriate for a system file
let attributes = Attributes::new(true, true, true, true, false, false, false);
match create_timedate {
None => Ok(DirEntry {
filename: FileName::new(filename)?,
attributes,
create_timedate: TimeDate::now(),
file_size,
entry_type: DirEntryType::SysFile,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data: data.unwrap().to_vec(),
}),
_ => Ok(DirEntry {
filename: FileName::new(filename)?,
attributes,
create_timedate: create_timedate.unwrap(),
file_size,
entry_type: DirEntryType::SysFile,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data: data.unwrap().to_vec(),
}),
}
}
/// Deliver a regular file with Archive set, all other attribs unset. If you don't provide a TimeDate, the current system
/// time on the host machine will be used.
fn create_file(
filename: &str,
create_timedate: Option<TimeDate>,
allocated_clusters: &Vec<u32>,
allocated_clusters: &[u32],
file_size: u32,
data: Vec<u8>,
sysfile: bool,
) -> Result<Self, std::io::Error> {
// Set of attributes appropriate to a regular file
let attributes = Attributes::new(true, false, false, false, false, false, false);
match create_timedate {
None => Ok(DirEntry {
filename: FileName::new(filename)?,
attributes,
create_timedate: TimeDate::now(),
file_size,
entry_type: DirEntryType::File,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data,
}),
_ => Ok(DirEntry {
filename: FileName::new(filename)?,
attributes,
create_timedate: create_timedate.unwrap(),
file_size,
entry_type: DirEntryType::File,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data,
}),
}
}
let attributes = match sysfile {
false => Attributes::new(true, false, false, false, false, false, false),
true => Attributes::new(true, true, true, true, false, false, false),
};
let entry_type = match sysfile {
true => DirEntryType::SysFile,
false => DirEntryType::File,
};
// Either use the provided TimeDate or get the current system TimeDate
let effective_timedate = create_timedate.unwrap_or_else(TimeDate::now);
fn create_rootdir() -> Result<Self, std::io::Error> {
Ok(DirEntry {
filename: FileName::new("ROOTDIR")?,
attributes: Attributes::new(false, false, false, false, false, true, false),
create_timedate: TimeDate::now(),
file_size: 0,
entry_type: DirEntryType::RootDirectory,
children: Vec::new(),
allocated_clusters: Vec::new(),
data: Vec::new(),
})
// Create the file name, handling potential errors
let file_name_result = FileName::new(filename);
match file_name_result {
Ok(file_name) => Ok(DirEntry {
filename: file_name,
attributes,
create_timedate: effective_timedate,
file_size,
entry_type,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data,
}),
Err(e) => Err(e),
}
}
fn create_directory(
filename: &str,
create_timedate: Option<TimeDate>,
allocated_clusters: &Vec<u32>,
allocated_clusters: &[u32],
) -> Result<Self, std::io::Error> {
// Set of attributes appropriate to a directory
let attributes = Attributes::new(false, false, false, false, false, true, false);
@ -170,7 +255,7 @@ impl DirEntry {
let new_entry = match create_timedate {
None => DirEntry {
filename: FileName::new(filename)?,
attributes: attributes.clone(),
attributes,
create_timedate: TimeDate::now(),
file_size: 0,
entry_type: DirEntryType::Directory,
@ -180,7 +265,7 @@ impl DirEntry {
},
_ => DirEntry {
filename: FileName::new(filename)?,
attributes: attributes.clone(),
attributes,
create_timedate: create_timedate.unwrap(),
file_size: 0,
entry_type: DirEntryType::Directory,
@ -221,7 +306,7 @@ impl DirEntry {
/// when reading a directory from disk. DOES NOT WORK YET!! [TODO]
pub fn from_bytes(bytes: &[u8; 32]) -> Result<DirEntry, std::io::Error> {
let filename_bytes = &bytes[0..11];
let filename = FileName::from_bytes(&filename_bytes)?;
let filename = FileName::from_bytes(filename_bytes)?;
let mut datetime_bytes = [0u8; 4];
datetime_bytes[0..2].copy_from_slice(&bytes[2..4]);
datetime_bytes[2..4].copy_from_slice(&bytes[0..2]);
@ -306,6 +391,10 @@ impl DirEntry {
/// assert_eq!(bytes, [0x32, 0x04]); // Assuming the start cluster number is 1234
/// ```
pub fn start_cluster_as_bytes(&self) -> [u8; 2] {
if self.allocated_clusters.is_empty() {
// Our parent is the root directory
return [0,0];
}
let cluster: u16 = min(self.allocated_clusters[0], 65535) as u16;
cluster.to_le_bytes()
}
@ -334,20 +423,38 @@ impl DirEntry {
/// The start cluster is either 0, or the first entry in the allocated clusters vec.
pub fn get_start_cluster(&self) -> u32 {
if self.allocated_clusters.len() == 0 {
return 0;
if self.allocated_clusters.is_empty() {
0
} else {
self.allocated_clusters[0]
}
}
pub fn get_children(&mut self) -> &Vec<DirEntry> {
&mut self.children
pub fn get_allocated_clusters(&self) -> &Vec<u32> {
&self.allocated_clusters
}
pub fn add_child(&mut self, child: DirEntry) {
self.children.push(child);
self.data = self.children_bytes().unwrap();
pub fn get_children(&mut self) -> &Vec<NodeId> {
&self.children
}
/// Adds a child node to this directory entry and updates necessary metadata
pub fn add_child(
&mut self,
child_id: NodeId,
arena: &mut Arena<DirEntry>,
) -> Result<(), std::io::Error> {
// Add the child NodeId to the children list
self.children.push(child_id);
// Attempt to update the data field with the serialized bytes of all children
match self.children_bytes(arena) {
Ok(bytes) => {
self.data = bytes.to_vec();
Ok(())
}
Err(e) => Err(e),
}
}
pub fn count_children(&self) -> usize {
@ -363,25 +470,32 @@ impl DirEntry {
}
pub fn get_data(&self) -> &[u8] {
&self.data.as_slice()
self.data.as_slice()
}
pub fn is_directory(&self) -> bool {
match self.get_type() {
DirEntryType::Directory => true,
DirEntryType::RootDirectory => true,
_ => false,
}
matches!(self.get_type(), DirEntryType::Directory)
}
pub fn children_bytes(&self) -> Result<Vec<u8>, std::io::Error> {
pub fn children_bytes(&self, arena: &Arena<DirEntry>) -> Result<Vec<u8>, std::io::Error> {
let mut bytes = Vec::new();
for child in &self.children {
bytes.extend_from_slice(&child.as_bytes()?);
for child_id in &self.children {
// Fetch the child DirEntry from the arena using the NodeId
if let Some(child_node) = arena.get_node(*child_id) {
// Serialize the child DirEntry
// `as_bytes` needs to be a method implemented for serializing DirEntry instances
bytes.extend_from_slice(&child_node.value.as_bytes()?);
} else {
// Handle the case where the child node cannot be found in the arena
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Child node not found in the arena",
));
}
}
Ok(bytes)
}
}
#[cfg(test)]
@ -405,7 +519,7 @@ mod tests {
file_size: 4608,
entry_type: DirEntryType::File, // Dummy placeholder
children: Vec::new(),
allocated_clusters: vec![2,3,4],
allocated_clusters: vec![2, 3, 4],
data: Vec::new(),
};
// Semi-representative entry that reflects IBMBIO.COM on April 4 1983.

View File

@ -202,8 +202,7 @@ impl TimeDate {
let second = if second % 2 == 1 { second - 1 } else { second };
// Validate year, month, day, hour, minute, and second
if year < 1980
|| year > 2107
if !(1980..=2107).contains(&year)
|| day > 31
|| month > 12
|| hour > 23

View File

@ -34,10 +34,10 @@ impl Application {
pub fn get_label(&self) -> String {
if self.label.is_some() {
return self.label.clone().unwrap();
self.label.clone().unwrap()
}
else {
return self.url.to_owned();
self.url.to_owned()
}
}
}