Unbroke the build. Need to sift through writing of files, sysfiles and dirs again now though.

This commit is contained in:
Bas Wiel, van de 2024-05-09 19:18:22 +02:00
parent 6fc4b6bcf6
commit 9fe9a4a2e0
4 changed files with 228 additions and 127 deletions

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> {

View File

@ -7,7 +7,7 @@ use crate::{
},
downloader::ZipDownloader,
fat::{
direntry::{DirEntry, DirEntryType},
direntry::{DirEntry, DirEntryType, Node, NodeId},
vbr::Vbr,
Fat,
},
@ -113,26 +113,25 @@ 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.
return Ok(());
pub fn write_file_to_storage(&mut self, file_id: NodeId) -> Result<(), std::io::Error> {
self.write_entry_to_storage(file_id)
}
/// Writes an entry (file or directory) to storage based on its allocated clusters.
pub fn write_entry_to_storage(&mut self, entry_id: NodeId) -> Result<(), std::io::Error> {
let entry = &self.filesystem.arena.get_node(entry_id)
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Entry node not found"))?
.value;
let bytes = entry.get_data(); // Ensure this returns a &[u8]
if bytes.is_empty() {
return Ok(()); // If no data, nothing to write
}
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 addresses = &entry.allocated_clusters;
// 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,
@ -140,9 +139,14 @@ impl DiskBuilder {
));
}
// Iterate over addresses and chunks to write data directly
let disk = &mut self.disk;
let partition = &mut self.partition;
// Write each chunk to its corresponding cluster
for (address, chunk) in addresses.iter().zip(chunks) {
self.write_cluster(*address, chunk)?;
let mut sector: [u8; 512] = [0; 512]; // Create a buffer filled with zeros
sector[..chunk.len()].copy_from_slice(chunk); // Copy the chunk into the buffer
partition.write_sector(disk, *address, sector)?; // Now pass a reference to &[u8; 512]
}
Ok(())
@ -258,47 +262,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 +330,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 +395,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

@ -1,5 +1,4 @@
use std::fmt;
use std::os::unix::process::parent_id;
use self::allocationtable::AllocationTable;
use self::direntry::{Arena, DirEntry, DirEntryType, NodeId};
@ -171,20 +170,14 @@ impl Fat {
};
let mut arena = Arena::new();
let root_directory = arena.insert_node(Node::new_directory("root", 0));
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,
),
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,
@ -252,6 +245,8 @@ 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,
@ -268,14 +263,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]]),
@ -338,7 +327,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],
@ -355,19 +344,17 @@ 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(
let new_file = DirEntry::new(
filename,
entry_type,
None,
@ -376,12 +363,25 @@ impl Fat {
Some(data),
)?;
// Insert the new file into the arena and get its NodeId
let file_id = self.arena.insert_node(Node::new_directory(filename, allocated_clusters.as_slice()));
// 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);
if let Some(pid) = parent_id {
if let Some(parent_node) = self.arena.get_node_mut(pid) {
parent_node.value.children.push(file_id);
} else {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Parent node not found",
));
}
} else {
self.root_directory.add_child(file_entry);
self.arena
.get_node_mut(self.root_directory)
.map(|root| root.value.children.push(file_id));
}
Ok(())
}
@ -397,20 +397,20 @@ impl Fat {
let allocated_clusters: Vec<u32> = vec![new_start_cluster];
let parent_entry = parent.unwrap_or(self.root_directory);
let parent_clusters = if let Some(parent_node) = self.arena.get_node(parent_id) {
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 new_dir_node = Node::new_directory(dir_name, allocated_clusters);
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);
let self_dot_node = Node::new_directory(".", allocated_clusters);
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);
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);
@ -654,15 +654,19 @@ impl Fat {
// 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> {
self.arena
.get_node(directory_id)
.map_or(vec![], |dir_entry| {
dir_entry
.children
.iter()
.filter_map(|&child_id| self.arena.get_node(child_id))
.collect()
})
// 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`.

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,
@ -20,13 +41,44 @@ pub struct Node<T> {
}
impl Node<DirEntry> {
pub fn new_directory(dir_name: &str, allocated_clusters: Vec<u32>) -> Self {
/// 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> {
// Determine the DirEntryType based on sysfile flag
let entry_type = if sysfile {
DirEntryType::SysFile
} else {
DirEntryType::File
};
// Create the file entry using the DirEntry's create method
let file_entry = DirEntry::create_file(
filename,
create_timedate,
&allocated_clusters,
file_size,
data,
)?;
// 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,
&allocated_clusters.to_vec(),
0,
None,
)
@ -42,6 +94,10 @@ pub struct Arena<T> {
}
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 {
@ -62,7 +118,7 @@ impl Arena<DirEntry> {
}
/// 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<T>) -> NodeId {
pub fn insert_node(&mut self, node: Node<DirEntry>) -> NodeId {
let node_id = NodeId(self.nodes.len());
self.nodes.push(node);
node_id
@ -70,13 +126,13 @@ impl Arena<DirEntry> {
/// 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<T>> {
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<T>> {
pub fn get_node_mut(&mut self, node_id: NodeId) -> Option<&mut Node<DirEntry>> {
self.nodes.get_mut(node_id.0)
}
}
@ -186,38 +242,35 @@ impl DirEntry {
}
}
/// 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.
/// 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_file(
filename: &str,
create_timedate: Option<TimeDate>,
allocated_clusters: &Vec<u32>,
allocated_clusters: &[u32],
file_size: u32,
data: Vec<u8>,
) -> 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)?,
// Either use the provided TimeDate or get the current system TimeDate
let effective_timedate = create_timedate.unwrap_or_else(TimeDate::now);
// 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: 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(),
create_timedate: effective_timedate,
file_size,
entry_type: DirEntryType::File,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data,
}),
Err(e) => Err(e),
}
}
@ -424,9 +477,23 @@ impl DirEntry {
&self.children
}
pub fn add_child(&mut self, child: DirEntry) {
self.children.push(child);
self.data = self.children_bytes().unwrap();
/// 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;
Ok(())
}
Err(e) => Err(e),
}
}
pub fn count_children(&self) -> usize {
@ -453,10 +520,21 @@ impl DirEntry {
}
}
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)
}