Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Commit

Permalink
feat(dev): creation of Bitmap structure
Browse files Browse the repository at this point in the history
  • Loading branch information
RatCornu committed Feb 18, 2024
1 parent 7e15dec commit c00ef1e
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 48 deletions.
159 changes: 159 additions & 0 deletions src/dev/bitmap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//! Bitmap manipulation for devices.
//!
//! Bitmap are frequently used data types, so this is a general interface to manipulate them.

use alloc::vec::Vec;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};

use super::celled::Celled;
use super::sector::Address;
use super::Device;
use crate::error::Error;

/// Generic bitmap structure.
///
/// It can handles any [`Copy`] structure directly written onto a [`Device`].
///
/// See [the Wikipedia page](https://en.wikipedia.org/wiki/Bit_array) for more general informations.
pub struct Bitmap<T: Copy, E: core::error::Error, Dev: Device<T, E>> {
/// Device containing the bitmap.
device: Celled<Dev>,

/// Inner elements.
inner: Vec<T>,

/// Starting address of the bitmap on the device.
starting_addr: Address,

/// Length of the bitmap.
length: usize,

/// Phantom data to use the `E` generic.
phantom: PhantomData<E>,
}

impl<E: core::error::Error, T: Copy, Dev: Device<T, E>> Bitmap<T, E, Dev> {
/// Creates a new [`Bitmap`] instance from the device on which it is located, its starting address on the device and its length.
///
/// # Errors
///
/// Returns an [`Error`] if the device cannot be read.
#[inline]
pub fn new(celled_device: Celled<Dev>, starting_addr: Address, length: usize) -> Result<Self, Error<E>> {
let inner = celled_device.borrow().slice(starting_addr..(starting_addr + length))?.to_vec();
Ok(Self {
device: celled_device,
inner,
starting_addr,
length,
phantom: PhantomData,
})
}

/// Returns the length of the bitmap.
#[inline]
#[must_use]
pub const fn length(&self) -> usize {
self.length
}

/// Returns the starting address of the bitmap.
#[inline]
#[must_use]
pub const fn starting_address(&self) -> Address {
self.starting_addr
}

/// Writes back the current state of the bitmap onto the device.
///
/// # Errors
///
/// Returns an [`Error`] if the device cannot be written.
#[inline]
pub fn write_back(&mut self) -> Result<(), Error<E>> {
let mut device = self.device.borrow_mut();
let mut slice = device.slice(self.starting_addr..(self.starting_addr + self.length))?;
slice.clone_from_slice(&self.inner);
let commit = slice.commit();
device.commit(commit)?;

Ok(())
}

/// Finds the first elements `el` such that the sum of all `count(el)` is greater than or equal to `n`.
///
/// Returns the indices and the value of those elements, keeping only the ones satisfying `count(el) > 0`.
///
/// If the sum of all `count(el)` is lesser than `n`, returns all the elements `el` such that `count(el) > 0`.
#[inline]
pub fn find_to_count<F: Fn(&T) -> usize>(&self, n: usize, count: F) -> Vec<(usize, T)> {
let mut counter = 0_usize;
let mut element_taken = Vec::new();

for (index, element) in self.inner.iter().enumerate() {
let element_count = count(element);
if element_count > 0 {
counter += element_count;
element_taken.push((index, *element));
if counter >= n {
return element_taken;
}
}
}

element_taken
}
}

impl<E: core::error::Error, Dev: Device<u8, E>> Bitmap<u8, E, Dev> {
/// Specialization of [`find_to_count`] to find the first bytes such that the sum of set bits is at least `n`.
#[inline]
#[must_use]
pub fn find_n_set_bits(&self, n: usize) -> Vec<(usize, u8)> {
self.find_to_count(n, |byte| {
let mut count = byte - ((byte >> 1_u8) & 0x55);
count = (count & 0x33) + ((count >> 2_u8) & 0x33);
count = (count + (count >> 4_u8)) & 0x0F;
count as usize
})
}

/// Specialization of [`find_to_count`] to find the first bytes such that the sum of unset bits is at least `n`.
#[inline]
#[must_use]
pub fn find_n_unset_bits(&self, n: usize) -> Vec<(usize, u8)> {
self.find_to_count(n, |byte| {
let mut count = byte - ((byte >> 1_u8) & 0x55);
count = (count & 0x33) + ((count >> 2_u8) & 0x33);
count = (count + (count >> 4_u8)) & 0x0F;
8 - count as usize
})
}
}

impl<E: core::error::Error, T: Copy, Dev: Device<T, E>> IntoIterator for Bitmap<T, E, Dev> {
type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
type Item = T;

#[inline]
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}

impl<E: core::error::Error, T: Copy, Dev: Device<T, E>> Deref for Bitmap<T, E, Dev> {
type Target = [T];

#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl<E: core::error::Error, T: Copy, Dev: Device<T, E>> DerefMut for Bitmap<T, E, Dev> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
1 change: 1 addition & 0 deletions src/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::dev::error::DevError;
use crate::error::Error;
use crate::io::{Base, Read, Seek, SeekFrom, Write};

pub mod bitmap;
pub mod celled;
pub mod error;
pub mod sector;
Expand Down
109 changes: 61 additions & 48 deletions src/fs/ext2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use self::file::{Directory, Regular, SymbolicLink};
use self::inode::{Inode, ROOT_DIRECTORY_INODE};
use self::superblock::{Superblock, SUPERBLOCK_START_BYTE};
use super::FileSystem;
use crate::dev::bitmap::Bitmap;
use crate::dev::celled::Celled;
use crate::dev::sector::Address;
use crate::dev::Device;
Expand Down Expand Up @@ -148,46 +149,14 @@ impl<Dev: Device<u8, Ext2Error>> Ext2<Dev> {
///
/// Returns the same errors as [`BlockGroupDescriptor::parse`](block_group/struct.BlockGroupDescriptor.html#method.parse).
#[inline]
pub fn get_block_bitmap(&self, block_group_number: u32) -> Result<Vec<u8>, Error<Ext2Error>> {
pub fn get_block_bitmap(&self, block_group_number: u32) -> Result<Bitmap<u8, Ext2Error, Dev>, Error<Ext2Error>> {
let superblock = self.superblock();

let block_group_descriptor = BlockGroupDescriptor::parse(&self.device, superblock, block_group_number)?;
let starting_addr = Address::new((block_group_descriptor.block_bitmap * superblock.block_size()) as usize);
let length = (superblock.base().blocks_per_group / 8) as usize;

Ok(self
.device
.borrow()
.slice(starting_addr..starting_addr + (superblock.base().blocks_per_group / 8) as usize)?
.as_ref()
.to_vec())
}

/// Sets the block bitmap for the given block group as the given bitmap.
///
/// # Errors
///
/// Returns the same errors as [`BlockGroupDescriptor::parse`](block_group/struct.BlockGroupDescriptor.html#method.parse).
///
/// # Panics
///
/// This will panic if `block_bitmap.len() == superblock.blocks_per_group` is false.
///
/// # Safety
///
/// Must ensure that the given `block_bitmap` is coherent with the current filesystem's state.
unsafe fn set_block_bitmap(&self, block_group_number: u32, block_bitmap: &[u8]) -> Result<(), Error<Ext2Error>> {
let superblock = self.superblock();

let block_group_descriptor = BlockGroupDescriptor::parse(&self.device, superblock, block_group_number)?;
let starting_addr = Address::new((block_group_descriptor.block_bitmap * superblock.block_size()) as usize);

let mut device = self.device.borrow_mut();
let mut slice = device.slice(starting_addr..starting_addr + (superblock.base().blocks_per_group / 8) as usize)?;
slice.clone_from_slice(block_bitmap);
let commit = slice.commit();
device.commit(commit)?;

Ok(())
Bitmap::new(self.device.clone(), starting_addr, length)
}

/// Returns a [`Vec`] containing the block numbers of `n` free blocks.
Expand Down Expand Up @@ -222,18 +191,18 @@ impl<Dev: Device<u8, Ext2Error>> Ext2<Dev> {
let block_group_descriptor = BlockGroupDescriptor::parse(&self.device, self.superblock(), block_group_count)?;
if block_group_descriptor.free_blocks_count > 0 {
let bitmap = self.get_block_bitmap(block_group_count)?;
for (index, byte) in bitmap.into_iter().enumerate() {
let group_free_block_index = bitmap.find_n_unset_bits(n as usize);

for (index, byte) in group_free_block_index {
// SAFETY: a block size is usually at most thousands of bytes, which is smaller than `u32::MAX`
let index = unsafe { u32::try_from(index).unwrap_unchecked() };

if byte != u8::MAX {
for bit in 0_u32..8 {
if (byte >> bit) & 1 == 0 {
free_blocks.push(block_group_count * self.superblock().base().blocks_per_group + index * 8 + bit);
for bit in 0_u32..8 {
if (byte >> bit) & 1 == 0 {
free_blocks.push(block_group_count * self.superblock().base().blocks_per_group + index * 8 + bit);

if free_blocks.len() as u64 == u64::from(n) {
return Ok(free_blocks);
}
if free_blocks.len() as u64 == u64::from(n) {
return Ok(free_blocks);
}
}
}
Expand Down Expand Up @@ -285,10 +254,10 @@ impl<Dev: Device<u8, Ext2Error>> Ext2<Dev> {
ext2: &Ext2<Dev>,
block_group_number: u32,
number_blocks_changed_in_group: u16,
bitmap: &[u8],
bitmap: &mut Bitmap<u8, Ext2Error, Dev>,
usage: bool,
) -> Result<(), Error<Ext2Error>> {
ext2.set_block_bitmap(block_group_number, bitmap)?;
bitmap.write_back()?;

let mut new_block_group_descriptor = BlockGroupDescriptor::parse(&ext2.device, ext2.superblock(), block_group_number)?;

Expand Down Expand Up @@ -321,7 +290,7 @@ impl<Dev: Device<u8, Ext2Error>> Ext2<Dev> {
if block / self.superblock().base().blocks_per_group != block_group_number {
// SAFETY: the state of the filesystem stays coherent within this function
unsafe {
update_block_group(self, block_group_number, number_blocks_changed_in_group, &bitmap, usage)?;
update_block_group(self, block_group_number, number_blocks_changed_in_group, &mut bitmap, usage)?;
};

number_blocks_changed_in_group = 0;
Expand Down Expand Up @@ -351,7 +320,7 @@ impl<Dev: Device<u8, Ext2Error>> Ext2<Dev> {

// SAFETY: the state of the filesystem stays coherent within this function
unsafe {
update_block_group(self, block_group_number, number_blocks_changed_in_group, &bitmap, usage)?;
update_block_group(self, block_group_number, number_blocks_changed_in_group, &mut bitmap, usage)?;
};
}

Expand Down Expand Up @@ -394,6 +363,50 @@ impl<Dev: Device<u8, Ext2Error>> Ext2<Dev> {
self.locate_blocks(blocks, false)
}

/// Returns the inode bitmap for the given block group.
///
/// # Errors
///
/// Returns the same errors as [`BlockGroupDescriptor::parse`](block_group/struct.BlockGroupDescriptor.html#method.parse).
#[inline]
pub fn get_inode_bitmap(&self, block_group_number: u32) -> Result<Bitmap<u8, Ext2Error, Dev>, Error<Ext2Error>> {
let superblock = self.superblock();

let block_group_descriptor = BlockGroupDescriptor::parse(&self.device, superblock, block_group_number)?;
let starting_addr = Address::new((block_group_descriptor.inode_bitmap * superblock.block_size()) as usize);
let length = (superblock.base().inodes_per_group / 8) as usize;

Bitmap::new(self.device.clone(), starting_addr, length)
}

/// Retuns the number of the first unused inode.
///
/// # Errors
///
/// Returns a [`NotEnoughInodes`](Ext2Error::NotEnoughInodes) if no inode is currently available.
///
/// Returns an [`Error`] if the device cannot be read or written.
#[inline]
pub fn free_inode(&mut self) -> Result<u32, Error<Ext2Error>> {
for block_group_number in 0..self.superblock().block_group_count() {
let inode_bitmap = self.get_inode_bitmap(block_group_number)?;
if let Some(index) = inode_bitmap.iter().position(|byte| *byte != u8::MAX) {
// SAFETY: the index has been given by the function `position`
let byte = *unsafe { inode_bitmap.get_unchecked(index) };
for bit in 0_u32..8 {
if (byte >> bit) & 1 == 0 {
// SAFETY: a block size is usually at most thousands of bytes, which is smaller than `u32::MAX`
let index = unsafe { u32::try_from(index).unwrap_unchecked() };

return Ok(block_group_number * self.superblock().base().inodes_per_group + 8 * index + bit);
}
}
}
}

Err(Error::Fs(FsError::Implementation(Ext2Error::NotEnoughInodes)))
}

/// Finds an unused inode number, writes an empty inode, sets the usage of this inode as `true` and returns the inode number.
///
/// # Errors
Expand Down Expand Up @@ -500,7 +513,7 @@ mod test {
let device = RefCell::new(File::options().read(true).write(true).open("./tests/fs/ext2/base.ext2").unwrap());
let ext2 = Ext2::new(device, 0).unwrap();

assert_eq!(ext2.get_block_bitmap(0).unwrap().len() * 8, ext2.superblock().base().blocks_per_group as usize);
assert_eq!(ext2.get_block_bitmap(0).unwrap().length() * 8, ext2.superblock().base().blocks_per_group as usize);
}

#[test]
Expand Down

0 comments on commit c00ef1e

Please sign in to comment.