Skip to content

Commit 8eb084d

Browse files
committed
add support for allocating new blocks
1 parent a2c2e1c commit 8eb084d

File tree

6 files changed

+128
-9
lines changed

6 files changed

+128
-9
lines changed

ext2/src/block_group.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use crate::{bytefield, bytefield_field_read, bytefield_field_write, check_is_implemented};
21
use alloc::vec::Vec;
32
use core::ops::{Deref, DerefMut};
43

4+
use crate::{bytefield, bytefield_field_read, bytefield_field_write, check_is_implemented};
5+
56
bytefield! {
67
pub struct BlockGroupDescriptor ([u8; 32]) {
78
block_usage_bitmap_block: u32 = 0,
@@ -30,6 +31,10 @@ impl BlockGroupDescriptor {
3031
self.num_unallocated_blocks
3132
}
3233

34+
pub fn num_unallocated_blocks_mut(&mut self) -> &mut u16 {
35+
&mut self.num_unallocated_blocks
36+
}
37+
3338
pub fn num_unallocated_inodes(&self) -> u16 {
3439
self.num_unallocated_inodes
3540
}

ext2/src/bytefield.rs

+6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ macro_rules! bytefield {
9898

9999
impl From<$name> for $source {
100100
fn from(value: $name) -> Self {
101+
Self::from(&value)
102+
}
103+
}
104+
105+
impl From<&$name> for $source {
106+
fn from(value: &$name) -> Self {
101107
let mut r: Self = Default::default();
102108
$(
103109
bytefield_field_write!($typ, value.$field, $offset, r);

ext2/src/error.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use core::fmt::{Debug, Display, Formatter};
44
pub enum Error {
55
InvalidSuperblock,
66
UnableToReadSuperblock,
7+
UnableToWriteSuperblock,
78
UnableToReadBlockGroupDescriptorTable,
9+
UnableToWriteBlockGroupDescriptorTable,
810
NotDirectory,
911
NotRegularFile,
1012
DeviceRead,

ext2/src/inode.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use core::ops::{Deref, DerefMut};
2+
3+
use bitflags::bitflags;
4+
15
use crate::{
2-
bytefield, bytefield_field_read, bytefield_field_write, check_is_implemented, BlockAddress,
6+
BlockAddress, bytefield, bytefield_field_read, bytefield_field_write, check_is_implemented,
37
InodeAddress,
48
};
5-
use bitflags::bitflags;
6-
use core::ops::{Deref, DerefMut};
79

810
macro_rules! inode_type {
911
($name:ident, $typ:expr) => {
@@ -62,6 +64,12 @@ inode_type!(UnixSocket, Type::UnixSocket);
6264

6365
pub struct InodeRawArray([u8; 128]);
6466

67+
impl InodeRawArray {
68+
pub fn as_slice(&self) -> &[u8] {
69+
&self.0
70+
}
71+
}
72+
6573
impl From<[u8; 128]> for InodeRawArray {
6674
fn from(value: [u8; 128]) -> Self {
6775
Self(value)

ext2/src/lib.rs

+91-4
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,18 @@ pub struct Ext2Fs<T> {
3232
bgdt: BlockGroupDescriptorTable,
3333
}
3434

35+
const SUPERBLOCK_OFFSET: usize = 1024;
36+
const BLOCK_GROUP_DESCRIPTOR_TABLE_OFFSET: usize = 2048;
37+
const BGD_SIZE: usize = 32; // 32 bytes per block group descriptor
38+
3539
impl<T> Ext2Fs<T>
3640
where
3741
T: BlockDevice,
3842
{
3943
pub fn try_new(block_device: T) -> Result<Self, Error> {
4044
let mut superblock_data = [0_u8; 1024];
4145
block_device
42-
.read_at(1024, &mut superblock_data)
46+
.read_at(SUPERBLOCK_OFFSET, &mut superblock_data)
4347
.map_err(|_| Error::UnableToReadSuperblock)?;
4448

4549
let superblock = Superblock::try_from(SuperblockArray::from(superblock_data)).unwrap();
@@ -48,11 +52,10 @@ where
4852

4953
let mut bgdt_data = vec![0_u8; superblock.block_size() as usize];
5054
block_device
51-
.read_at(2048, &mut bgdt_data)
55+
.read_at(BLOCK_GROUP_DESCRIPTOR_TABLE_OFFSET, &mut bgdt_data)
5256
.map_err(|_| Error::UnableToReadBlockGroupDescriptorTable)?;
5357
let mut bgdt = BlockGroupDescriptorTable::new();
5458
for i in 0..number_of_block_groups as usize {
55-
const BGD_SIZE: usize = 32; // 32 bytes per block group descriptor
5659
let offset = i * BGD_SIZE;
5760
let end = offset + BGD_SIZE;
5861
let bgd_data = TryInto::<[u8; BGD_SIZE]>::try_into(&bgdt_data[offset..end]).unwrap();
@@ -76,7 +79,7 @@ where
7679
.and_then(|inode| Directory::try_from(inode).map_err(|_| Error::NotDirectory))
7780
}
7881

79-
fn read_inode(&self, addr: InodeAddress) -> Result<(InodeAddress, Inode), Error> {
82+
pub fn read_inode(&self, addr: InodeAddress) -> Result<(InodeAddress, Inode), Error> {
8083
let inodes_per_group = self.superblock.inodes_per_group();
8184
let block_group_index = (addr.get() - 1) / inodes_per_group;
8285
let block_group = &self.bgdt[block_group_index as usize];
@@ -96,6 +99,25 @@ where
9699
Ok((addr, Inode::try_from(InodeRawArray::from(inode_buffer)).expect("inode conversion can't fail. if it does, the logic has changed and this should propagate the error")))
97100
}
98101

102+
pub fn write_inode(&mut self, addr: InodeAddress, inode: &Inode) -> Result<(), Error> {
103+
let inodes_per_group = self.superblock.inodes_per_group();
104+
let block_group_index = (addr.get() - 1) / inodes_per_group;
105+
let block_group = &self.bgdt[block_group_index as usize];
106+
let itable_start_block =
107+
BlockAddress::new(block_group.inode_table_starting_block()).unwrap();
108+
109+
let index = (addr.get() - 1) % inodes_per_group;
110+
let inode_size = self.superblock.inode_size();
111+
let address =
112+
self.resolve_block_offset(itable_start_block) + (index * inode_size as u32) as usize;
113+
114+
let inode_raw = InodeRawArray::from(inode);
115+
self.block_device
116+
.write_at(address, &inode_raw.as_slice())
117+
.map_err(|_| Error::DeviceWrite)
118+
.map(|_| ())
119+
}
120+
99121
pub fn read_block(&self, addr: BlockAddress, buf: &mut [u8]) -> Result<usize, Error> {
100122
let offset = self.resolve_block_offset(addr);
101123
self.block_device
@@ -113,4 +135,69 @@ where
113135
fn resolve_block_offset(&self, addr: BlockAddress) -> usize {
114136
(1024 + (addr.get() - 1) * self.superblock.block_size()) as usize
115137
}
138+
139+
pub fn allocate_block(&mut self) -> Result<Option<BlockAddress>, Error> {
140+
let block_size = self.superblock.block_size();
141+
let blocks_per_group = self.superblock.blocks_per_group();
142+
let num_groups = self.bgdt.len();
143+
144+
for group_index in 0..num_groups {
145+
let first_free_block_index = self.try_reserve_block_in_group(group_index)?;
146+
if first_free_block_index.is_none() {
147+
continue;
148+
}
149+
let first_free_block_index = first_free_block_index.unwrap();
150+
151+
let descriptor = &mut self.bgdt[group_index];
152+
*descriptor.num_unallocated_blocks_mut() -= 1;
153+
154+
// read the block group descriptor table
155+
let mut bgdt_data = vec![0_u8; block_size as usize];
156+
self.block_device
157+
.read_at(BLOCK_GROUP_DESCRIPTOR_TABLE_OFFSET, &mut bgdt_data)
158+
.map_err(|_| Error::UnableToReadBlockGroupDescriptorTable)?;
159+
// merge the changed descriptor back into the table
160+
let bgd_offset = group_index * BGD_SIZE;
161+
let bgd_end = bgd_offset + BGD_SIZE;
162+
let bgd_data = Into::<[u8; BGD_SIZE]>::into(&*descriptor);
163+
bgdt_data[bgd_offset..bgd_end].copy_from_slice(&bgd_data);
164+
// write the block group descriptor table back
165+
self.block_device
166+
.write_at(BLOCK_GROUP_DESCRIPTOR_TABLE_OFFSET, &bgdt_data)
167+
.map_err(|_| Error::UnableToWriteBlockGroupDescriptorTable)?;
168+
169+
170+
*self.superblock.num_unallocated_blocks_mut() -= 1;
171+
let superblock_data = Into::<SuperblockArray>::into(&self.superblock);
172+
self.block_device
173+
.write_at(SUPERBLOCK_OFFSET, superblock_data.as_slice())
174+
.map_err(|_| Error::UnableToWriteSuperblock)?;
175+
176+
let global_block_num = group_index as u32 * blocks_per_group + first_free_block_index as u32;
177+
return Ok(Some(BlockAddress::new(global_block_num).unwrap()));
178+
}
179+
180+
Ok(None)
181+
}
182+
183+
/// Tries to allocate a block in the given block group and returns the index in the group
184+
/// if successful. This reads the block usage bitmap from the block group, maybe modifies
185+
/// it and writes it back.
186+
fn try_reserve_block_in_group(&mut self, group_index: usize) -> Result<Option<usize>, Error> {
187+
let mut block_bitmap = vec![0_u8; self.superblock.block_size() as usize];
188+
let bitmap_block = self.bgdt[group_index].block_usage_bitmap_block();
189+
let bitmap_block_address = BlockAddress::new(bitmap_block).expect("bgdt does not have valid block address for bitmap block");
190+
self.read_block(bitmap_block_address, &mut block_bitmap)?;
191+
192+
for (i, byte) in block_bitmap.iter_mut().enumerate() {
193+
for bit_index in 0..8 {
194+
if *byte & (1_u8 << bit_index) == 0 {
195+
*byte |= 1 << bit_index;
196+
return Ok(Some(i * 8 + bit_index));
197+
}
198+
}
199+
}
200+
self.write_block(bitmap_block_address, &block_bitmap)?;
201+
Ok(None)
202+
}
116203
}

ext2/src/superblock.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
use core::ops::{Deref, DerefMut, Shl};
22

3-
use crate::{bytefield, bytefield_field_read, bytefield_field_write, check_is_implemented};
43
use bitflags::bitflags;
54

5+
use crate::{bytefield, bytefield_field_read, bytefield_field_write, check_is_implemented};
6+
67
pub struct SuperblockArray([u8; 1024]);
78

9+
impl SuperblockArray {
10+
pub fn as_slice(&self) -> &[u8] {
11+
&self.0
12+
}
13+
}
14+
815
impl From<[u8; 1024]> for SuperblockArray {
916
fn from(value: [u8; 1024]) -> Self {
1017
Self(value)
@@ -94,6 +101,10 @@ impl Superblock {
94101
self.num_unallocated_blocks
95102
}
96103

104+
pub fn num_unallocated_blocks_mut(&mut self) -> &mut u32 {
105+
&mut self.num_unallocated_blocks
106+
}
107+
97108
pub fn num_unallocated_inodes(&self) -> u32 {
98109
self.num_unallocated_inodes
99110
}

0 commit comments

Comments
 (0)