Skip to content

Commit

Permalink
add support for extending the size of a regular file through a write …
Browse files Browse the repository at this point in the history
…as long as no new blocks need to be allocated for it
  • Loading branch information
tsatke committed Jun 22, 2024
1 parent ed18c06 commit 202d282
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
12 changes: 12 additions & 0 deletions ext2/src/inode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ macro_rules! inode_type {
&self.1
}

pub fn inode_mut(&mut self) -> &mut Inode {
&mut self.1
}

pub fn into_inner(self) -> (InodeAddress, Inode) {
(self.0, self.1)
}
Expand Down Expand Up @@ -147,6 +151,14 @@ impl Inode {
Flags::from_bits_truncate(self.flags)
}

pub fn set_file_size_lower(&mut self, size: u32) {
self.byte_size_lower = size;
}

pub fn set_file_size_upper(&mut self, size: u32) {
self.byte_size_upper_or_dir_acl = size;
}

pub fn direct_ptr(&self, index: usize) -> Option<BlockAddress> {
BlockAddress::new(match index {
0 => self.direct_block_ptr_0,
Expand Down
8 changes: 6 additions & 2 deletions ext2/src/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ where
Ok(total_read)
}

fn read_blocks_from_inode(&self, inode: &Inode, start_block: usize, end_block: usize, buf: &mut [u8]) -> Result<usize, Error> {
pub(crate) fn read_blocks_from_inode(&self, inode: &Inode, start_block: usize, end_block: usize, buf: &mut [u8]) -> Result<usize, Error> {
let block_size = self.superblock.block_size() as usize;
assert_eq!(buf.len(), (end_block - start_block + 1) * block_size, "buf.len() must be equal to the number of blocks you want to read");

Expand Down Expand Up @@ -79,6 +79,11 @@ where
}

pub fn is_block_allocated(&self, inode: &Inode, block_index: u32) -> Result<bool, Error> {
self.resolve_block_index(inode, block_index)
.map(|block| block.is_some())
}

pub fn resolve_block_index(&self, inode: &Inode, block_index: u32) -> Result<Option<BlockAddress>, Error> {
let (direct_limit, indirect_limit, double_indirect_limit) = self.indirect_pointer_limits();

Ok(
Expand All @@ -91,7 +96,6 @@ where
} else {
self.resolve_triple_indirect_ptr(inode.triple_indirect_ptr(), block_index - double_indirect_limit)?
}
.is_some()
)
}

Expand Down
51 changes: 41 additions & 10 deletions ext2/src/write.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc::vec;

use filesystem::BlockDevice;

use crate::{Error, Ext2Fs, RegularFile};
Expand All @@ -9,7 +11,7 @@ where
#[allow(unused_variables)]
pub fn write_to_file(
&mut self,
file: &RegularFile,
file: &mut RegularFile,
offset: usize,
buf: &[u8],
) -> Result<usize, Error> {
Expand All @@ -21,22 +23,51 @@ where
let relative_offset = (offset % block_size) as usize;
let block_count = (end_block - start_block + 1) as usize;

assert_eq!(buf.len() % block_size as usize, 0, "buf.len() must be a multiple of block_size for now"); // TODO: make this more flexible
// This is the data that we want to write. We pad the data with data from the disk
// to align it with the block boundaries (start and length). This data can be written
// back to disk (block aligned) as is.
let data = {
let mut data = vec![0_u8; block_count * block_size as usize];
self.read_blocks_from_inode(&file, start_block as usize, end_block as usize, &mut data)?; // TODO: we don't need to read what will be overwritten anyways
// overwrite the part that should be written
data[relative_offset..relative_offset + buf.len()].copy_from_slice(buf);
data
};

for (i, block) in (start_block..=end_block).enumerate() {
if !self.is_block_allocated(file, block)? {
// TODO: we don't need to allocate if the full content of this block would be zero if the fs allows sparse files
let free_block_address = self.allocate_block()?;
if free_block_address.is_none() {
return Err(Error::NoSpace);
}
let free_block_address = free_block_address.unwrap();
// TODO: write the block address to the inode
todo!("allocate block")
// // TODO: we don't need to allocate if the full content of this block would be zero if the fs allows sparse files
// let free_block_address = self.allocate_block()?;
// if free_block_address.is_none() {
// return Err(Error::NoSpace);
// }
// let free_block_address = free_block_address.unwrap();
// // TODO: write the block address to the inode
}
}

// we can now be certain that all blocks that we want to write into are allocated

todo!()
let mut chunks = data.chunks_exact(block_size as usize);
for (block, data) in (start_block..=end_block).zip(&mut chunks) {
let block_address = self.resolve_block_index(&file, block)?.expect("we should have just allocated this block, it should be present");
self.write_block(block_address, data)?;
}
debug_assert_eq!(chunks.remainder().len(), 0, "data to write was not block aligned");

if file.len() < offset as usize + buf.len() {
let new_size = offset as usize + buf.len();
let new_size_lower = new_size as u32;
let new_size_upper = (new_size >> 32) as u32;

let inode = file.inode_mut();
inode.set_file_size_lower(new_size_lower);
inode.set_file_size_upper(new_size_upper);

self.write_inode(file.inode_address(), file)?;
}

Ok(buf.len())
}
}

0 comments on commit 202d282

Please sign in to comment.