| /// A wrapper around BlockDevice that adds helper methods for navigating a |
| /// BlobFS filesystem. |
| use crate::bit_vector::*; |
| use crate::block_device::*; |
| use crate::errors::*; |
| use crate::structs::*; |
| use crate::utils::*; |
| |
| use core::mem; |
| |
| pub struct BlobDevice<'a> { |
| pub bd: &'a mut dyn BlockDevice, |
| } |
| |
| impl<'a> BlobDevice<'a> { |
| const MAGIC_0: u64 = 0xac2153479e694d21; |
| const MAGIC_1: u64 = 0x985000d4d4d3d314; |
| |
| pub fn new(bd: &'a mut dyn BlockDevice) -> Self { |
| return BlobDevice { bd: bd }; |
| } |
| |
| pub fn sanity_check(&self) -> Result<(), BFSErr> { |
| return Ok(()); |
| } |
| |
| /// Base address fetchers |
| |
| pub fn superblock_base(&self) -> usize { |
| return self.bd.geom().block_size * 0; |
| } |
| |
| pub fn blockmap_base(&self) -> usize { |
| return self.bd.geom().block_size * 1; |
| } |
| |
| pub fn nodemap_base(&self) -> usize { |
| return self.bd.geom().block_size * 2; |
| } |
| |
| pub fn journal_base(&self) -> usize { |
| return self.bd.geom().block_size * 3; |
| } |
| |
| pub fn block_base(&self) -> usize { |
| return self.bd.geom().block_size * 4; |
| } |
| |
| /// Format a whole device to support BlobFS. |
| |
| pub fn format(&mut self) -> Result<(), BFSErr> { |
| // Erase the whole device. |
| for i in 0..self.bd.geom().block_count { |
| self.bd.erase_block(i)?; |
| } |
| |
| // Write the initial superblock. |
| self.format_superblock()?; |
| |
| // Write the initial blockmap. |
| self.format_blockmap()?; |
| |
| return Ok(()); |
| } |
| |
| pub fn read_superblock(&self, superblock: &mut Superblock) -> Result<(), BFSErr> { |
| let blob = as_unsafe_blob_mut(superblock); |
| self.bd.read_range(0, blob)?; |
| |
| // Sanity check the superblock |
| dcheck!(superblock.magic0 == BlobDevice::MAGIC_0, BFSErr::Corrupt); |
| dcheck!(superblock.magic1 == BlobDevice::MAGIC_1, BFSErr::Corrupt); |
| |
| return Ok(()); |
| } |
| |
| pub fn write_superblock(&mut self, superblock: &Superblock) -> Result<(), BFSErr> { |
| // Sanity check the superblock |
| dcheck!(superblock.magic0 == BlobDevice::MAGIC_0, BFSErr::Corrupt); |
| dcheck!(superblock.magic1 == BlobDevice::MAGIC_1, BFSErr::Corrupt); |
| |
| let blob = as_unsafe_blob(superblock); |
| self.bd.write_range(0, blob)?; |
| return Ok(()); |
| } |
| |
| pub fn format_superblock(&mut self) -> Result<(), BFSErr> { |
| let block_size = self.bd.geom().block_size; |
| let block_count = self.bd.geom().block_count; |
| |
| let superblock_count = 1; |
| let bitmap_block_count = 1; |
| let node_block_count = 1; |
| let journal_block_count = 1; |
| let data_block_count = block_count |
| - superblock_count |
| - bitmap_block_count |
| - node_block_count |
| - journal_block_count; |
| |
| let inodes_per_block = block_size / mem::size_of::<Inode>(); |
| |
| // Write the superblock to offset 0 |
| let superblock = Superblock { |
| magic0: BlobDevice::MAGIC_0, |
| magic1: BlobDevice::MAGIC_1, |
| version: 0, |
| flags: 0, |
| block_size: block_size as u32, |
| unused: 0, |
| data_block_count: data_block_count as u64, |
| journal_block_count: journal_block_count as u64, |
| inode_count: inodes_per_block as u64, |
| alloc_block_count: 0, |
| alloc_inode_count: 0, |
| }; |
| self.write_superblock(&superblock)?; |
| return Ok(()); |
| } |
| |
| pub fn format_blockmap(&mut self) -> Result<(), BFSErr> { |
| let bits: [u8; 1] = [0xF0]; |
| self.bd.write_range(self.blockmap_base(), &bits)?; |
| return Ok(()); |
| } |
| |
| pub fn read_blockmap(&self, bitmap: &mut BitVector) -> Result<(), BFSErr> { |
| self.bd |
| .read_range(self.blockmap_base(), slice_as_unsafe_blob_mut(bitmap.bits))?; |
| return Ok(()); |
| } |
| |
| pub fn write_blockmap(&mut self, bitmap: &mut BitVector) -> Result<(), BFSErr> { |
| self.bd |
| .write_range(self.blockmap_base(), slice_as_unsafe_blob(bitmap.bits))?; |
| return Ok(()); |
| } |
| |
| pub fn read_node_header(&self, index: usize, node: &mut NodeHeader) -> Result<(), BFSErr> { |
| let base = self.nodemap_base(); |
| let offset = index * mem::size_of::<Inode>() * index; |
| let blob = as_unsafe_blob_mut(node); |
| self.bd.read_range(base + offset, blob)?; |
| return Ok(()); |
| } |
| |
| pub fn read_inode(&self, index: usize, inode: &mut Inode) -> Result<(), BFSErr> { |
| let base = self.nodemap_base(); |
| let offset = index * mem::size_of::<Inode>(); |
| let blob = as_unsafe_blob_mut(inode); |
| self.bd.read_range(base + offset, blob)?; |
| return Ok(()); |
| } |
| |
| pub fn write_inode(&mut self, index: usize, inode: &Inode) -> Result<(), BFSErr> { |
| dcheck!( |
| (inode.header.flags & NodeHeader::FLAG_INODE) != 0, |
| BFSErr::InvalidArg |
| ); |
| let base = self.nodemap_base(); |
| let offset = index * mem::size_of::<Inode>(); |
| let blob = as_unsafe_blob(inode); |
| self.bd.write_range(base + offset, blob)?; |
| return Ok(()); |
| } |
| |
| /// Invalidate an inode by zeroing out its header. The inode cannot be reused |
| /// until the block containing it is erased. |
| pub fn invalidate_inode(&mut self, index: usize) -> Result<(), BFSErr> { |
| let base = self.nodemap_base(); |
| let offset = index * mem::size_of::<Inode>(); |
| let blob = [0; mem::size_of::<NodeHeader>()]; |
| self.bd.overwrite_range(base + offset, &blob)?; |
| return Ok(()); |
| } |
| |
| pub fn blob_size_in_blocks(&self, blob: &[u8]) -> u16 { |
| let block_size = self.bd.geom().block_size; |
| let result = (blob.len() + block_size + 1) / block_size; |
| assert!(result < u16::MAX as usize); |
| return result as u16; |
| } |
| |
| pub fn read_blob(&self, extent: Extent, blob_out: &mut [u8]) -> Result<(), BFSErr> { |
| let block_size = self.bd.geom().block_size; |
| let mut cursor = extent.offset(); |
| for chunk in blob_out.chunks_mut(block_size) { |
| self.bd.read_block(cursor, chunk)?; |
| cursor = cursor + 1; |
| } |
| return Ok(()); |
| } |
| |
| pub fn write_blob(&mut self, extent: Extent, blob_in: &[u8]) -> Result<(), BFSErr> { |
| let block_size = self.bd.geom().block_size; |
| let mut cursor = extent.offset(); |
| for chunk in blob_in.chunks(block_size) { |
| self.bd.write_block(cursor, &chunk)?; |
| cursor = cursor + 1; |
| } |
| return Ok(()); |
| } |
| |
| pub fn delete_blob(&mut self, extent: Extent) -> Result<(), BFSErr> { |
| let offset = extent.offset(); |
| for i in 0..extent.size as usize { |
| self.bd.erase_block(offset + i)?; |
| } |
| return Ok(()); |
| } |
| } |