some more basic stuff for the filesystem

This commit is contained in:
husky 2023-07-27 09:28:06 -07:00
parent d02674a179
commit 999336ff9e
No known key found for this signature in database
GPG key ID: 6B3D8CB511646891
4 changed files with 546 additions and 6 deletions

View file

@ -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
View 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
}

View file

@ -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
}

View file

@ -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,
}