blob: de6e439351d0ed6453e99496ad4a256eec489328 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// 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(());
}
}