some more basic stuff for the filesystem
This commit is contained in:
parent
d02674a179
commit
999336ff9e
4 changed files with 546 additions and 6 deletions
|
@ -6,5 +6,4 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
vapfs = { path = "../vapfs" }
|
||||
embedded-crc32c = "0.1.0"
|
||||
vapfs = { path = "../vapfs" }
|
62
src/crc32.rs
Normal file
62
src/crc32.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
based upon code written by Gary S. Brown.
|
||||
code and tables are in the public domain.
|
||||
*/
|
||||
|
||||
/// precomputed crc32 table taken from crc32.c
|
||||
pub const CRC_TAB: [u32; 256] = [0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
];
|
||||
|
||||
/// function that calculates crc32 using the above table
|
||||
pub const fn crc32(buf: &[u8]) -> u32 {
|
||||
let mut crc: u32 = !0;
|
||||
let mut i = 0;
|
||||
while i < buf.len() {
|
||||
crc = CRC_TAB[(crc ^ buf[i] as u32) as usize] ^ (crc >> 8);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
!crc
|
||||
}
|
198
src/lib.rs
198
src/lib.rs
|
@ -2,13 +2,205 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
use vapfs::BlockDevice;
|
||||
use crate::structs::Superblock;
|
||||
use alloc::vec::Vec;
|
||||
use vapfs::{BlockDevice, Index};
|
||||
use crate::structs::{Inode, Superblock};
|
||||
|
||||
pub mod btree;
|
||||
pub mod structs;
|
||||
pub mod bitmap;
|
||||
pub mod crc32;
|
||||
|
||||
/// Reads the superblock (located at byte offset 1024 of the block device) and returns it.
|
||||
/// Returns None if the block device is too small to contain a superblock.
|
||||
pub fn get_superblock(bd: &mut dyn BlockDevice) -> Option<Superblock> {
|
||||
todo!()
|
||||
let mut buf: [u8; core::mem::size_of::<Superblock>()] = [0; core::mem::size_of::<Superblock>()];
|
||||
bd.seek(1024);
|
||||
let read_count = bd.read_blocks(&mut buf);
|
||||
if read_count < core::mem::size_of::<Superblock>() {
|
||||
return None;
|
||||
}
|
||||
let mut superblock = unsafe { core::ptr::read(buf.as_ptr() as *const Superblock) };
|
||||
superblock.convert_big_endian_to_native();
|
||||
Some(superblock)
|
||||
}
|
||||
|
||||
/// Performs a direct write of a superblock to the block device.
|
||||
/// # Safety
|
||||
/// unsafe because it does not journal the write, and does not update any other metadata.
|
||||
pub unsafe fn write_superblock(mut sb: Superblock, bd: &mut dyn BlockDevice) -> bool {
|
||||
sb.convert_native_to_big_endian();
|
||||
let mut buf: [u8; core::mem::size_of::<Superblock>()] = [0; core::mem::size_of::<Superblock>()];
|
||||
core::ptr::write(buf.as_mut_ptr() as *mut Superblock, sb);
|
||||
bd.seek(1024);
|
||||
let write_count = bd.write_blocks(&buf);
|
||||
write_count == core::mem::size_of::<Superblock>()
|
||||
}
|
||||
|
||||
/// Reads the inode at the given index and returns it.
|
||||
pub fn read_inode(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice) -> Option<Inode> {
|
||||
let mut buf: [u8; core::mem::size_of::<Inode>()] = [0; core::mem::size_of::<Inode>()];
|
||||
bd.seek((sb.first_inode_block * sb.block_size as u64) + (index * core::mem::size_of::<Inode>() as u64));
|
||||
let read_count = bd.read_blocks(&mut buf);
|
||||
if read_count < core::mem::size_of::<Inode>() {
|
||||
return None;
|
||||
}
|
||||
let mut inode = unsafe { core::ptr::read(buf.as_ptr() as *const Inode) };
|
||||
inode.convert_big_endian_to_native();
|
||||
Some(inode)
|
||||
}
|
||||
|
||||
/// Performs a direct write of an inode to the block device.
|
||||
/// # Safety
|
||||
/// unsafe because it does not journal the write, and does not update any other metadata.
|
||||
pub unsafe fn write_inode(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice, mut inode: Inode) -> bool {
|
||||
inode.convert_native_to_big_endian();
|
||||
let mut buf: [u8; core::mem::size_of::<Inode>()] = [0; core::mem::size_of::<Inode>()];
|
||||
core::ptr::write(buf.as_mut_ptr() as *mut Inode, inode);
|
||||
bd.seek((sb.first_inode_block * sb.block_size as u64) + (index * core::mem::size_of::<Inode>() as u64));
|
||||
let write_count = bd.write_blocks(&buf);
|
||||
write_count == core::mem::size_of::<Inode>()
|
||||
}
|
||||
|
||||
/// Reads a single datablock into memory, may return less than the block size if the end of the block device is reached.
|
||||
pub fn read_datablock(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
buf.resize(sb.block_size as usize, 0);
|
||||
bd.seek((sb.first_data_block * sb.block_size as u64) + (index * sb.block_size as u64));
|
||||
bd.read_blocks(&mut buf);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Performs a direct write of a datablock to the block device.
|
||||
/// # Safety
|
||||
/// unsafe because it does not journal the write, and does not update any other metadata.
|
||||
pub unsafe fn write_datablock(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice, buf: &[u8]) -> bool {
|
||||
bd.seek((sb.first_data_block * sb.block_size as u64) + (index * sb.block_size as u64));
|
||||
let write_count = bd.write_blocks(buf);
|
||||
write_count == sb.block_size as usize
|
||||
}
|
||||
|
||||
/// Checks if a datablock is allocated.
|
||||
/// Will return None if the index is out of bounds or if the block device cannot fill the buffer.
|
||||
pub fn is_datablock_allocated(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice) -> Option<bool> {
|
||||
// datablock bitmap is at 1024 + 156 byte offset, length is data_block_count / 8 rounded up
|
||||
let bitmap_offset = 1024 + 156;
|
||||
let bitmap_length = (sb.data_block_count + 7) / 8;
|
||||
if index >= bitmap_length {
|
||||
return None;
|
||||
}
|
||||
let mut bitmap_buf: Vec<u8> = Vec::new();
|
||||
bitmap_buf.resize(bitmap_length as usize, 0);
|
||||
bd.seek(bitmap_offset);
|
||||
let read_count = bd.read_blocks(&mut bitmap_buf);
|
||||
if read_count < bitmap_length as usize {
|
||||
return None;
|
||||
}
|
||||
Some(bitmap::get_bit(&bitmap_buf, index as usize) == bitmap::SET)
|
||||
}
|
||||
|
||||
/// Checks if an inode is allocated.
|
||||
/// Will return None if the index is out of bounds or if the block device cannot fill the buffer.
|
||||
pub fn is_inode_allocated(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice) -> Option<bool> {
|
||||
// inode bitmap is at 1024 + 156 + datablock_bitmap_length byte offset,
|
||||
// length is inode_count / 8 rounded up
|
||||
let bitmap_offset = 1024 + 156 + ((sb.data_block_count + 7) / 8);
|
||||
let bitmap_length = (sb.inode_count + 7) / 8;
|
||||
if index >= bitmap_length {
|
||||
return None;
|
||||
}
|
||||
let mut bitmap_buf: Vec<u8> = Vec::new();
|
||||
bitmap_buf.resize(bitmap_length as usize, 0);
|
||||
bd.seek(bitmap_offset);
|
||||
let read_count = bd.read_blocks(&mut bitmap_buf);
|
||||
if read_count < bitmap_length as usize {
|
||||
return None;
|
||||
}
|
||||
Some(bitmap::get_bit(&bitmap_buf, index as usize) == bitmap::SET)
|
||||
}
|
||||
|
||||
/// Finds the first unallocated datablock and returns its index.
|
||||
/// Will return None if no unallocated datablock is found, or if the block device cannot fill the buffer.
|
||||
pub fn find_first_unallocated_datablock(sb: &Superblock, bd: &mut dyn BlockDevice) -> Option<Index> {
|
||||
// datablock bitmap is at 1024 + 156 byte offset, length is data_block_count / 8 rounded up
|
||||
let bitmap_offset = 1024 + 156;
|
||||
let bitmap_length = (sb.data_block_count + 7) / 8;
|
||||
let mut bitmap_buf: Vec<u8> = Vec::new();
|
||||
bitmap_buf.resize(bitmap_length as usize, 0);
|
||||
bd.seek(bitmap_offset);
|
||||
let read_count = bd.read_blocks(&mut bitmap_buf);
|
||||
if read_count < bitmap_length as usize {
|
||||
return None;
|
||||
}
|
||||
bitmap::find_first_bit_equal_to(&bitmap_buf, bitmap::UNSET).map(|index| index as Index)
|
||||
}
|
||||
|
||||
/// Finds the first unallocated inode and returns its index.
|
||||
/// Will return None if no unallocated inode is found, or if the block device cannot fill the buffer.
|
||||
pub fn find_first_unallocated_inode(sb: &Superblock, bd: &mut dyn BlockDevice) -> Option<Index> {
|
||||
// inode bitmap is at 1024 + 156 + datablock_bitmap_length byte offset,
|
||||
// length is inode_count / 8 rounded up
|
||||
let bitmap_offset = 1024 + 156 + ((sb.data_block_count + 7) / 8);
|
||||
let bitmap_length = (sb.inode_count + 7) / 8;
|
||||
let mut bitmap_buf: Vec<u8> = Vec::new();
|
||||
bitmap_buf.resize(bitmap_length as usize, 0);
|
||||
bd.seek(bitmap_offset);
|
||||
let read_count = bd.read_blocks(&mut bitmap_buf);
|
||||
if read_count < bitmap_length as usize {
|
||||
return None;
|
||||
}
|
||||
bitmap::find_first_bit_equal_to(&bitmap_buf, bitmap::UNSET).map(|index| index as Index)
|
||||
}
|
||||
|
||||
/// Sets the allocation status of a datablock.
|
||||
/// # Safety
|
||||
/// unsafe because it does not journal the write, and does not update any other metadata.
|
||||
pub unsafe fn set_datablock_allocation_status(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice, allocated: bool) -> bool {
|
||||
// todo! we should maybe optimise this to only write the byte that contains the bit, instead of the whole bitmap
|
||||
// todo! see how much time this saves?
|
||||
|
||||
// datablock bitmap is at 1024 + 156 byte offset, length is data_block_count / 8 rounded up
|
||||
let bitmap_offset = 1024 + 156;
|
||||
let bitmap_length = (sb.data_block_count + 7) / 8;
|
||||
if index >= bitmap_length {
|
||||
return false;
|
||||
}
|
||||
let mut bitmap_buf: Vec<u8> = Vec::new();
|
||||
bitmap_buf.resize(bitmap_length as usize, 0);
|
||||
bd.seek(bitmap_offset);
|
||||
let read_count = bd.read_blocks(&mut bitmap_buf);
|
||||
if read_count < bitmap_length as usize {
|
||||
return false;
|
||||
}
|
||||
bitmap::set_bit(&mut bitmap_buf, index as usize, allocated);
|
||||
bd.seek(bitmap_offset);
|
||||
let write_count = bd.write_blocks(&bitmap_buf);
|
||||
write_count == bitmap_length as usize
|
||||
}
|
||||
|
||||
/// Sets the allocation status of an inode.
|
||||
/// # Safety
|
||||
/// unsafe because it does not journal the write, and does not update any other metadata.
|
||||
pub unsafe fn set_inode_allocation_status(index: Index, sb: &Superblock, bd: &mut dyn BlockDevice, allocated: bool) -> bool {
|
||||
// todo! we should maybe optimise this to only write the byte that contains the bit, instead of the whole bitmap
|
||||
// todo! see how much time this saves?
|
||||
|
||||
// inode bitmap is at 1024 + 156 + datablock_bitmap_length byte offset,
|
||||
// length is inode_count / 8 rounded up
|
||||
let bitmap_offset = 1024 + 156 + ((sb.data_block_count + 7) / 8);
|
||||
let bitmap_length = (sb.inode_count + 7) / 8;
|
||||
if index >= bitmap_length {
|
||||
return false;
|
||||
}
|
||||
let mut bitmap_buf: Vec<u8> = Vec::new();
|
||||
bitmap_buf.resize(bitmap_length as usize, 0);
|
||||
bd.seek(bitmap_offset);
|
||||
let read_count = bd.read_blocks(&mut bitmap_buf);
|
||||
if read_count < bitmap_length as usize {
|
||||
return false;
|
||||
}
|
||||
bitmap::set_bit(&mut bitmap_buf, index as usize, allocated);
|
||||
bd.seek(bitmap_offset);
|
||||
let write_count = bd.write_blocks(&bitmap_buf);
|
||||
write_count == bitmap_length as usize
|
||||
}
|
289
src/structs.rs
289
src/structs.rs
|
@ -1,4 +1,5 @@
|
|||
use vapfs::{Index, Timestamp};
|
||||
use crate::crc32;
|
||||
|
||||
/// # Magic
|
||||
/// Constant value that identifies the Superblock as a valid VapUFS filesystem
|
||||
|
@ -37,12 +38,85 @@ pub struct Superblock {
|
|||
pub creation_time: Timestamp,
|
||||
/// timestamp of last modification
|
||||
pub last_modification_time: Timestamp,
|
||||
/// crc32 checksum of this Superblock
|
||||
pub checksum: u32,
|
||||
/// reserved u32
|
||||
pub reserved_1: u32,
|
||||
/// reserved values for expansion
|
||||
pub reserved: [u64; 8],
|
||||
pub reserved: [u64; 7],
|
||||
// 156 bytes used so far
|
||||
// rest of the block is used by the free data blocks bitmap and the free inodes bitmap
|
||||
}
|
||||
|
||||
impl Superblock {
|
||||
/// in-place conversion from the storage representation (big endian) to the native representation
|
||||
pub fn convert_big_endian_to_native(&mut self) {
|
||||
#[cfg(target_endian = "little")]
|
||||
{
|
||||
self.magic = u64::from_be(self.magic);
|
||||
self.block_size = u32::from_be(self.block_size);
|
||||
self.first_data_block = u64::from_be(self.first_data_block);
|
||||
self.first_inode_block = u64::from_be(self.first_inode_block);
|
||||
self.first_journal_block = u64::from_be(self.first_journal_block);
|
||||
self.inode_count = u64::from_be(self.inode_count);
|
||||
self.data_block_count = u64::from_be(self.data_block_count);
|
||||
self.journal_block_count = u64::from_be(self.journal_block_count);
|
||||
self.allocated_inode_count = u64::from_be(self.allocated_inode_count);
|
||||
self.allocated_data_block_count = u64::from_be(self.allocated_data_block_count);
|
||||
self.creation_time = u64::from_be(self.creation_time);
|
||||
self.last_modification_time = u64::from_be(self.last_modification_time);
|
||||
for i in 0..8 {
|
||||
self.reserved[i] = u64::from_be(self.reserved[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// in-place conversion from the native representation to the storage representation (big endian)
|
||||
pub fn convert_native_to_big_endian(&mut self) {
|
||||
#[cfg(target_endian = "little")]
|
||||
{
|
||||
self.magic = u64::to_be(self.magic);
|
||||
self.block_size = u32::to_be(self.block_size);
|
||||
self.first_data_block = u64::to_be(self.first_data_block);
|
||||
self.first_inode_block = u64::to_be(self.first_inode_block);
|
||||
self.first_journal_block = u64::to_be(self.first_journal_block);
|
||||
self.inode_count = u64::to_be(self.inode_count);
|
||||
self.data_block_count = u64::to_be(self.data_block_count);
|
||||
self.journal_block_count = u64::to_be(self.journal_block_count);
|
||||
self.allocated_inode_count = u64::to_be(self.allocated_inode_count);
|
||||
self.allocated_data_block_count = u64::to_be(self.allocated_data_block_count);
|
||||
self.creation_time = u64::to_be(self.creation_time);
|
||||
self.last_modification_time = u64::to_be(self.last_modification_time);
|
||||
for i in 0..8 {
|
||||
self.reserved[i] = u64::to_be(self.reserved[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// returns true if the crc32 checksum is currently valid
|
||||
/// returns false otherwise
|
||||
pub fn is_checksum_valid(&self) -> bool {
|
||||
let mut buf = [0; core::mem::size_of::<Self>() - (8 * 8)]; // don't hash last 7 u64s, the reserved u32, or the checksum
|
||||
unsafe {
|
||||
core::ptr::copy(self as *const Self as *const u8, buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
let checksum = crc32::crc32(&buf);
|
||||
|
||||
checksum == self.checksum
|
||||
}
|
||||
|
||||
/// updates the crc32 checksum
|
||||
pub fn recalculate_checksum(&mut self) {
|
||||
let mut buf = [0; core::mem::size_of::<Self>() - (8 * 8)]; // don't hash last 7 u64s, the reserved u32, or the checksum
|
||||
unsafe {
|
||||
core::ptr::copy(self as *const Self as *const u8, buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
let checksum = crc32::crc32(&buf);
|
||||
|
||||
self.checksum = checksum;
|
||||
}
|
||||
}
|
||||
|
||||
/// # UNIXPermissions
|
||||
/// UNIX permissions
|
||||
#[repr(u16)]
|
||||
|
@ -123,9 +197,82 @@ pub struct Inode {
|
|||
pub flags: u32,
|
||||
/// Direct-Block-Addresses, last three are indirect if `InodeFlags::INDIRECT` is set
|
||||
/// Indirect blocks are Indexes to other blocks, their contents are a u64 count "N" followed by N u64 indexes
|
||||
/// both are pointers to data blocks, starting from data block 0
|
||||
pub direct_block_addresses: [Index; 12],
|
||||
/// CRC32 checksum of this inode
|
||||
pub checksum: u32,
|
||||
// 172 bytes used so far
|
||||
}
|
||||
|
||||
impl Inode {
|
||||
/// in-place conversion from the storage representation (big endian) to the native representation
|
||||
pub fn convert_big_endian_to_native(&mut self) {
|
||||
#[cfg(target_endian = "little")]
|
||||
{
|
||||
self.mode = u16::from_be(self.mode);
|
||||
self.link_count = u16::from_be(self.link_count);
|
||||
self.uid = u32::from_be(self.uid);
|
||||
self.gid = u32::from_be(self.gid);
|
||||
self.size = u64::from_be(self.size);
|
||||
self.block_count = u64::from_be(self.block_count);
|
||||
self.creation_time = u64::from_be(self.creation_time);
|
||||
self.last_access_time = u64::from_be(self.last_access_time);
|
||||
self.last_modification_time = u64::from_be(self.last_modification_time);
|
||||
self.last_inode_modification_time = u64::from_be(self.last_inode_modification_time);
|
||||
self.deletion_time = u64::from_be(self.deletion_time);
|
||||
self.flags = u32::from_be(self.flags);
|
||||
for i in 0..12 {
|
||||
self.direct_block_addresses[i] = u64::from_be(self.direct_block_addresses[i]);
|
||||
}
|
||||
self.checksum = u32::from_be(self.checksum);
|
||||
}
|
||||
}
|
||||
|
||||
/// in-place conversion from the native representation to the storage representation (big endian)
|
||||
pub fn convert_native_to_big_endian(&mut self) {
|
||||
#[cfg(target_endian = "little")]
|
||||
{
|
||||
self.mode = u16::to_be(self.mode);
|
||||
self.link_count = u16::to_be(self.link_count);
|
||||
self.uid = u32::to_be(self.uid);
|
||||
self.gid = u32::to_be(self.gid);
|
||||
self.size = u64::to_be(self.size);
|
||||
self.block_count = u64::to_be(self.block_count);
|
||||
self.creation_time = u64::to_be(self.creation_time);
|
||||
self.last_access_time = u64::to_be(self.last_access_time);
|
||||
self.last_modification_time = u64::to_be(self.last_modification_time);
|
||||
self.last_inode_modification_time = u64::to_be(self.last_inode_modification_time);
|
||||
self.deletion_time = u64::to_be(self.deletion_time);
|
||||
self.flags = u32::to_be(self.flags);
|
||||
for i in 0..12 {
|
||||
self.direct_block_addresses[i] = u64::to_be(self.direct_block_addresses[i]);
|
||||
}
|
||||
self.checksum = u32::to_be(self.checksum);
|
||||
}
|
||||
}
|
||||
|
||||
/// returns true if the crc32 checksum is currently valid
|
||||
/// returns false otherwise
|
||||
pub fn is_checksum_valid(&self) -> bool {
|
||||
let mut buf = [0; core::mem::size_of::<Self>() - 4]; // don't hash the checksum
|
||||
unsafe {
|
||||
core::ptr::copy(self as *const Self as *const u8, buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
let checksum = crc32::crc32(&buf);
|
||||
|
||||
checksum == self.checksum
|
||||
}
|
||||
|
||||
/// updates the crc32 checksum
|
||||
pub fn recalculate_checksum(&mut self) {
|
||||
let mut buf = [0; core::mem::size_of::<Self>() - 4]; // don't hash the checksum
|
||||
unsafe {
|
||||
core::ptr::copy(self as *const Self as *const u8, buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
let checksum = crc32::crc32(&buf);
|
||||
|
||||
self.checksum = checksum;
|
||||
}
|
||||
}
|
||||
|
||||
/// # InodeFlags
|
||||
|
@ -136,4 +283,144 @@ pub enum InodeFlags {
|
|||
CORRUPT = 1 << 0,
|
||||
/// File uses indirect blocks
|
||||
INDIRECT = 1 << 1,
|
||||
}
|
||||
|
||||
/// # JournalEntry
|
||||
/// A Journal Entry
|
||||
#[repr(C)]
|
||||
pub struct JournalHeader {
|
||||
pub flags: u32,
|
||||
pub operation: JournalOperation,
|
||||
/// crc32 hash of the content with flags set to zero, used to verify journal was fully written without verifying the stage
|
||||
pub zeroed_content_crc32: u32,
|
||||
pub content: JournalEntry,
|
||||
}
|
||||
|
||||
/// # JournalOperation
|
||||
/// Type of operation performed by a Journal Entry
|
||||
#[repr(u32)]
|
||||
pub enum JournalOperation {
|
||||
/// A single block write, described by a `JournalBlockWrite`
|
||||
SingleBlockWrite = 0,
|
||||
/// A multi-block write, described by a `JournalMultiblockWrite`
|
||||
MultiblockWrite = 1,
|
||||
}
|
||||
|
||||
/// # JournalBlockWrite
|
||||
/// writes are performed as follows:
|
||||
/// 1. create and write the journal entry
|
||||
/// 2. allocate a data block for the data to be written
|
||||
/// 3. set the entry's allocated flag
|
||||
/// 4. write the data to the allocated data block
|
||||
/// 5. set the entry's stored flag
|
||||
/// == the following steps will be performed upon a journal flush, i.e. when the driver has decided that it's time to finally "commit" the journal ==
|
||||
/// 6. depending on the destination of the write, either:
|
||||
/// - update inode metadata to point to the new data block
|
||||
/// - copy the data from the old data block to the new data block
|
||||
/// 7. set the entry's written flag
|
||||
/// 8. deallocate the old data block
|
||||
/// 9. set the entry's deallocated flag
|
||||
/// == done! ==
|
||||
/// ideally, with this schema the filesystem shall never become corrupt
|
||||
/// all fields in the entry should be modified individually on the block device instead of rewriting the entire entry
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct JournalBlockWrite {
|
||||
/// flags stating how far the write has progressed
|
||||
pub flags: JBRFlags,
|
||||
/// are we writing to an inode instead of a data block?
|
||||
pub target_is_inode: bool,
|
||||
/// block number of target
|
||||
pub target_block: Index,
|
||||
/// block number of source data block
|
||||
pub source_block: Index,
|
||||
/// crc32 hash of the source data block
|
||||
pub source_block_crc32: u32,
|
||||
}
|
||||
|
||||
/// # JBRFlags
|
||||
/// Flags field of a JournalBlockWrite
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum JBRFlags {
|
||||
/// source data block has been allocated
|
||||
Allocated = 1,
|
||||
/// data has been written to the source data block
|
||||
Stored = 2,
|
||||
/// source data block has either replaced an old data block or has been written to an inode
|
||||
Written = 3,
|
||||
/// source data block (in the case of a write to an inode) or old data block (in the case of a write to a data block) has been deallocated
|
||||
/// (i.e. this journal entry has been fully committed)
|
||||
CompleteAndDeallocated = 4,
|
||||
}
|
||||
|
||||
/// # JournalMultiblockWrite
|
||||
/// a special entry for writing to multiple blocks at once,
|
||||
/// used for circumstances where it is very important that all blocks are either
|
||||
/// written successfully, or not written at all (i.e. directory blocks)
|
||||
/// writes are performed as follows:
|
||||
/// 1. create and write the journal entry
|
||||
/// 2. allocate a data block to store a MultiblockWriteList
|
||||
/// 3. set the entry's allocated_list flag
|
||||
/// 4. allocate data blocks to store the data to be written
|
||||
/// 5. set the entry's allocated_data flag
|
||||
/// 6. write the data to the allocated data blocks and the list to the allocated list block
|
||||
/// 7. set the entry's stored flag
|
||||
/// == the following steps will be performed upon a journal flush ==
|
||||
/// 8. update inode metadata to point to the new data blocks
|
||||
/// 9. set the entry's written flag
|
||||
/// 10. deallocate the old data blocks & list block
|
||||
/// 11. set the entry's deallocated flag
|
||||
/// == done! ==
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct JournalMultiblockWrite {
|
||||
/// flags stating how far the write has progressed
|
||||
pub flags: JMWFlags,
|
||||
/// block number of first target block
|
||||
pub target_block: Index,
|
||||
/// number of target blocks
|
||||
pub target_block_count: Index,
|
||||
/// block number of list block structure
|
||||
pub list_block: Index,
|
||||
/// crc32 hash of the list block
|
||||
pub list_block_crc32: u32,
|
||||
}
|
||||
|
||||
/// # JMWFlags
|
||||
/// Flags field of a JournalMultiblockWrite
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum JMWFlags {
|
||||
/// list block has been allocated
|
||||
AllocatedList = 1,
|
||||
/// data blocks have been allocated
|
||||
AllocatedData = 2,
|
||||
/// data has been written to the data blocks and the list block
|
||||
Stored = 3,
|
||||
/// data blocks have replaced old data blocks
|
||||
Written = 4,
|
||||
/// data blocks and list block have been deallocated
|
||||
/// (i.e. this journal entry has been fully committed)
|
||||
CompleteAndDeallocated = 5,
|
||||
}
|
||||
|
||||
/// # ListBlock
|
||||
/// a list of data blocks for a journaled multiblock write, similar in structure to an inode
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ListBlock {
|
||||
pub using_indirect_blocks: bool,
|
||||
/// Direct-Block-Addresses, last three are indirect if `using_indirect_blocks` is set
|
||||
/// Indirect blocks are Indexes to other blocks, their contents are a u64 count "N" followed by N u64 indexes
|
||||
/// both are pointers to data blocks, starting from data block 0
|
||||
pub direct_block_addresses: [Index; 12],
|
||||
}
|
||||
|
||||
/// # JournalEntry
|
||||
/// union of all possible journal entries
|
||||
#[repr(C)]
|
||||
pub union JournalEntry {
|
||||
pub block_write: JournalBlockWrite,
|
||||
pub multiblock_write: JournalMultiblockWrite,
|
||||
}
|
Loading…
Add table
Reference in a new issue