Sysfiles now also get their bytes written to disk.

This commit is contained in:
Bas Wiel, van de 2024-05-03 11:47:44 +02:00
parent db3fdb1ed1
commit b8aac70371
3 changed files with 91 additions and 51 deletions

View File

@ -6,7 +6,11 @@ use crate::{
Disk,
},
downloader::ZipDownloader,
fat::{vbr::Vbr, Fat},
fat::{
direntry::{DirEntry, DirEntryType},
vbr::Vbr,
Fat,
},
manifest::Manifest,
os::OperatingSystem,
};
@ -60,7 +64,7 @@ impl DiskBuilder {
}
}
/// Copy a system file from a downloaed ZIP to the VHD
/// Copy a file from a downloaded ZIP to the VHD
pub fn copy_sysfile(
&mut self,
downloader: &ZipDownloader,
@ -71,13 +75,75 @@ impl DiskBuilder {
.mkfile(None, source_file.get_name(), true, source_file.get_bytes())?;
Ok(())
} else {
// If the file was not found, nothing essentially bad happens. Just continue and do nothing for now.
Ok(())
}
}
/// Writes the contents of a `DirEntry` to the disk in clusters.
///
/// This function takes the data from a `DirEntry` and writes it to the disk,
/// segmenting the data into clusters based on the filesystem's cluster size.
/// Each segment of data is written to a specific disk cluster corresponding
/// to pre-allocated cluster addresses within the `DirEntry`.
///
/// # Parameters
/// - `file`: A reference to the `DirEntry` containing the data to be written.
/// The `DirEntry` must contain both the data (as a byte slice) and a list
/// of allocated cluster addresses where the data will be written.
///
/// # Returns
/// This function returns `Ok(())` if the data is successfully written to all
/// specified clusters. It returns an `Err(std::io::Error)` if there is a mismatch
/// in the number of allocated clusters and the number of data chunks, or if
/// any call to `write_cluster` fails.
///
/// # Errors
/// This function can error in the following scenarios:
/// - Returns an error if called on a directory entry type other than `File` or `SysFile`.
/// - If the number of clusters pre-allocated in `file.allocated_clusters` does
/// not match the number of chunks into which the data has been divided. This
/// situation triggers an error with `std::io::ErrorKind::Other`.
/// - If writing any cluster fails, the underlying `write_cluster` function
/// may also return an `std::io::Error` describing the specific problem encountered
/// during the write operation.
///
/// # Note
/// This function assumes that `file.get_data()` returns a slice of all data to be written
/// 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 {
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]
let cluster_size = self.filesystem.get_sectors_per_cluster() * 512;
let chunks = bytes.chunks(cluster_size);
let addresses = &file.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.",
));
}
// Iterate over addresses and chunks to write data directly
for (address, chunk) in addresses.iter().zip(chunks) {
self.write_cluster(*address, chunk)?;
}
Ok(())
}
/// Writes the data of a cluster to the disk.
///
/// This method takes the data of a cluster, represented as a `Vec<u8>`, and writes it to the disk.
/// This method takes the data of a cluster, represented as a `&[u8]`, and writes it to the disk.
/// If the provided data is shorter than the size of a cluster, it is padded with zeroes.
/// Each cluster is divided into sectors, and the data is written sector by sector.
///
@ -85,13 +151,15 @@ impl DiskBuilder {
///
/// * `data`: The data of the cluster to be written to the disk.
/// * `cluster_address`: The address of the cluster on the disk where the data will be written.
/// * `partition`: A reference to the partition where the cluster resides.
/// * `disk`: A mutable reference to the disk where the data will be written.
///
/// # Errors
///
/// This method returns an `std::io::Error` if there is an I/O error during writing.
pub fn write_cluster(&mut self, cluster_address: u32, data: &[u8]) -> Result<(), std::io::Error> {
pub fn write_cluster(
&mut self,
cluster_address: u32,
data: &[u8],
) -> Result<(), std::io::Error> {
let bytes_per_cluster = self.filesystem.get_sectors_per_cluster() * 512;
let mut bytes = data.to_vec();
@ -109,7 +177,8 @@ impl DiskBuilder {
for chunk in bytes.chunks_exact(512) {
let mut chunk_array = [0u8; 512];
chunk_array.copy_from_slice(chunk);
self.partition.write_sector(&mut self.disk, sector_address as u32, chunk_array)?;
self.partition
.write_sector(&mut self.disk, sector_address as u32, chunk_array)?;
sector_address += 1;
}
Ok(())
@ -163,8 +232,11 @@ impl DiskBuilder {
// Last action: write the allocation table copies to the disk
self.filesystem
.write_allocation_tables(&self.partition, &mut self.disk)?;
self.filesystem
.write_dirs(None, &self.partition, &mut self.disk)?;
for file in self.filesystem.root_directory.children.clone() {
self.write_file_to_storage(&file)?;
}
self.disk.commit_storage()?;
Ok(())
}

View File

@ -348,13 +348,6 @@ impl Fat {
// Attempt to allocate clusters and handle errors appropriately
let allocated_clusters = self.allocate_clusters(data)?;
println!(
"Writing {} with {} bytes into {} clusters.",
filename,
data.len(),
allocated_clusters.len()
);
// Create a new directory entry
let file_entry = DirEntry::new(
filename,
@ -371,7 +364,6 @@ impl Fat {
} else {
self.root_directory.add_child(file_entry);
}
Ok(())
}
@ -479,34 +471,6 @@ impl Fat {
Ok(())
}
pub fn write_dirs(
&self,
entry: Option<&DirEntry>,
partition: &Partition,
disk: &mut Disk,
) -> Result<(), std::io::Error> {
let directory = match entry {
None => self.root_directory.clone(),
Some(directory) => directory.clone(),
};
// Collect all the bytes in a Vec
let mut bytes: Vec<u8> = Vec::new();
// Loop over all the children and get them as bytes
for child in &directory.children {
bytes.extend_from_slice(&child.as_bytes()?);
// Do this again for all subdirs except for recursive system dirs
if child.get_type() == DirEntryType::Directory {
if child.directory_name() != "." && child.directory_name() != ".." {
self.write_dirs(Some(child), partition, disk)?;
}
}
}
Ok(())
}
/// Generate a BIOS Parameter Block (BPB) compatible with DOS 2.00.
///
/// Constructs a BPB suitable for DOS 2.00 based on the filesystem parameters.

View File

@ -9,7 +9,7 @@ pub struct DirEntry {
pub file_size: u32,
pub entry_type: DirEntryType,
pub children: Vec<DirEntry>,
allocated_clusters: Vec<u32>,
pub allocated_clusters: Vec<u32>,
data: Vec<u8>,
}
@ -67,7 +67,7 @@ impl DirEntry {
data.unwrap().to_vec(),
),
DirEntryType::SysFile => {
Self::create_sysfile(filename, create_timedate, file_size, allocated_clusters)
Self::create_sysfile(filename, create_timedate, file_size, allocated_clusters, data)
}
DirEntryType::Directory => {
Self::create_directory(filename, create_timedate, allocated_clusters)
@ -83,6 +83,7 @@ impl DirEntry {
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);
@ -95,7 +96,7 @@ impl DirEntry {
entry_type: DirEntryType::SysFile,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data: Vec::new(),
data: data.unwrap().to_vec(),
}),
_ => Ok(DirEntry {
filename: FileName::new(filename)?,
@ -105,7 +106,7 @@ impl DirEntry {
entry_type: DirEntryType::SysFile,
children: Vec::new(),
allocated_clusters: allocated_clusters.to_vec(),
data: Vec::new(),
data: data.unwrap().to_vec(),
}),
}
}
@ -206,13 +207,12 @@ impl DirEntry {
/// or an `std::io::Error` if an error occurs during the conversion process.
///
pub fn as_bytes(&self) -> Result<[u8; 32], std::io::Error> {
let start_cluster = self.allocated_clusters[0] as u16;
let mut result = [0u8; 32];
result[0..11].copy_from_slice(&self.filename.as_bytes());
result[11] = self.attributes.as_byte();
result[22..24].copy_from_slice(&self.create_timedate.creation_time_bytes()?);
result[24..26].copy_from_slice(&self.create_timedate.creation_date_bytes()?);
result[26..28].copy_from_slice(&start_cluster.to_le_bytes());
result[26..28].copy_from_slice(&self.start_cluster_as_bytes());
result[28..32].copy_from_slice(&self.file_size.to_le_bytes());
Ok(result)
}
@ -360,6 +360,10 @@ impl DirEntry {
pub fn get_type(&self) -> DirEntryType {
self.entry_type
}
pub fn get_data(&self) -> &[u8] {
&self.data.as_slice()
}
}
#[cfg(test)]