Add comments in ch6
This commit is contained in:
parent
cea2febe35
commit
f9346edad1
35 changed files with 374 additions and 116 deletions
|
@ -1,16 +1,16 @@
|
|||
use super::{get_block_cache, BlockDevice, BLOCK_SZ};
|
||||
use alloc::sync::Arc;
|
||||
|
||||
/// A bitmap block
|
||||
type BitmapBlock = [u64; 64];
|
||||
|
||||
/// Number of bits in a block
|
||||
const BLOCK_BITS: usize = BLOCK_SZ * 8;
|
||||
|
||||
/// A bitmap
|
||||
pub struct Bitmap {
|
||||
start_block_id: usize,
|
||||
blocks: usize,
|
||||
}
|
||||
|
||||
/// Return (block_pos, bits64_pos, inner_pos)
|
||||
/// Decompose bits into (block_pos, bits64_pos, inner_pos)
|
||||
fn decomposition(mut bit: usize) -> (usize, usize, usize) {
|
||||
let block_pos = bit / BLOCK_BITS;
|
||||
bit %= BLOCK_BITS;
|
||||
|
@ -18,13 +18,14 @@ fn decomposition(mut bit: usize) -> (usize, usize, usize) {
|
|||
}
|
||||
|
||||
impl Bitmap {
|
||||
/// A new bitmap from start block id and number of blocks
|
||||
pub fn new(start_block_id: usize, blocks: usize) -> Self {
|
||||
Self {
|
||||
start_block_id,
|
||||
blocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a new block from a block device
|
||||
pub fn alloc(&self, block_device: &Arc<dyn BlockDevice>) -> Option<usize> {
|
||||
for block_id in 0..self.blocks {
|
||||
let pos = get_block_cache(
|
||||
|
@ -52,7 +53,7 @@ impl Bitmap {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Deallocate a block
|
||||
pub fn dealloc(&self, block_device: &Arc<dyn BlockDevice>, bit: usize) {
|
||||
let (block_pos, bits64_pos, inner_pos) = decomposition(bit);
|
||||
get_block_cache(block_pos + self.start_block_id, Arc::clone(block_device))
|
||||
|
@ -62,7 +63,7 @@ impl Bitmap {
|
|||
bitmap_block[bits64_pos] -= 1u64 << inner_pos;
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the max number of allocatable blocks
|
||||
pub fn maximum(&self) -> usize {
|
||||
self.blocks * BLOCK_BITS
|
||||
}
|
||||
|
|
|
@ -3,11 +3,15 @@ use alloc::collections::VecDeque;
|
|||
use alloc::sync::Arc;
|
||||
use lazy_static::*;
|
||||
use spin::Mutex;
|
||||
|
||||
/// Cached block inside memory
|
||||
pub struct BlockCache {
|
||||
/// cached block data
|
||||
cache: [u8; BLOCK_SZ],
|
||||
/// underlying block id
|
||||
block_id: usize,
|
||||
/// underlying block device
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
/// whether the block is dirty
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
|
@ -23,7 +27,7 @@ impl BlockCache {
|
|||
modified: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the address of an offset inside the cached block data
|
||||
fn addr_of_offset(&self, offset: usize) -> usize {
|
||||
&self.cache[offset] as *const _ as usize
|
||||
}
|
||||
|
@ -70,7 +74,7 @@ impl Drop for BlockCache {
|
|||
self.sync()
|
||||
}
|
||||
}
|
||||
|
||||
/// Use a block cache of 16 blocks
|
||||
const BLOCK_CACHE_SIZE: usize = 16;
|
||||
|
||||
pub struct BlockCacheManager {
|
||||
|
@ -118,10 +122,11 @@ impl BlockCacheManager {
|
|||
}
|
||||
|
||||
lazy_static! {
|
||||
/// The global block cache manager
|
||||
pub static ref BLOCK_CACHE_MANAGER: Mutex<BlockCacheManager> =
|
||||
Mutex::new(BlockCacheManager::new());
|
||||
}
|
||||
|
||||
/// Get the block cache corresponding to the given block id and block device
|
||||
pub fn get_block_cache(
|
||||
block_id: usize,
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
|
@ -130,7 +135,7 @@ pub fn get_block_cache(
|
|||
.lock()
|
||||
.get_block_cache(block_id, block_device)
|
||||
}
|
||||
|
||||
/// Sync all block cache to block device
|
||||
pub fn block_cache_sync_all() {
|
||||
let manager = BLOCK_CACHE_MANAGER.lock();
|
||||
for (_, cache) in manager.queue.iter() {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use core::any::Any;
|
||||
|
||||
/// Trait for block devices
|
||||
/// which reads and writes data in the unit of blocks
|
||||
pub trait BlockDevice: Send + Sync + Any {
|
||||
///Read data form block to buffer
|
||||
fn read_block(&self, block_id: usize, buf: &mut [u8]);
|
||||
///Write data from buffer to block
|
||||
fn write_block(&self, block_id: usize, buf: &[u8]);
|
||||
}
|
||||
|
|
|
@ -5,18 +5,22 @@ use super::{
|
|||
use crate::BLOCK_SZ;
|
||||
use alloc::sync::Arc;
|
||||
use spin::Mutex;
|
||||
|
||||
///An easy file system on block
|
||||
pub struct EasyFileSystem {
|
||||
///Real device
|
||||
pub block_device: Arc<dyn BlockDevice>,
|
||||
///Inode bitmap
|
||||
pub inode_bitmap: Bitmap,
|
||||
///Data bitmap
|
||||
pub data_bitmap: Bitmap,
|
||||
inode_area_start_block: u32,
|
||||
data_area_start_block: u32,
|
||||
}
|
||||
|
||||
type DataBlock = [u8; BLOCK_SZ];
|
||||
|
||||
/// An easy fs over a block device
|
||||
impl EasyFileSystem {
|
||||
/// A data block of block size
|
||||
pub fn create(
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
total_blocks: u32,
|
||||
|
@ -77,7 +81,7 @@ impl EasyFileSystem {
|
|||
block_cache_sync_all();
|
||||
Arc::new(Mutex::new(efs))
|
||||
}
|
||||
|
||||
/// Open a block device as a filesystem
|
||||
pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {
|
||||
// read SuperBlock
|
||||
get_block_cache(0, Arc::clone(&block_device))
|
||||
|
@ -99,7 +103,7 @@ impl EasyFileSystem {
|
|||
Arc::new(Mutex::new(efs))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the root inode of the filesystem
|
||||
pub fn root_inode(efs: &Arc<Mutex<Self>>) -> Inode {
|
||||
let block_device = Arc::clone(&efs.lock().block_device);
|
||||
// acquire efs lock temporarily
|
||||
|
@ -107,7 +111,7 @@ impl EasyFileSystem {
|
|||
// release efs lock
|
||||
Inode::new(block_id, block_offset, Arc::clone(efs), block_device)
|
||||
}
|
||||
|
||||
/// Get inode by id
|
||||
pub fn get_disk_inode_pos(&self, inode_id: u32) -> (u32, usize) {
|
||||
let inode_size = core::mem::size_of::<DiskInode>();
|
||||
let inodes_per_block = (BLOCK_SZ / inode_size) as u32;
|
||||
|
@ -117,20 +121,20 @@ impl EasyFileSystem {
|
|||
(inode_id % inodes_per_block) as usize * inode_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Get data block by id
|
||||
pub fn get_data_block_id(&self, data_block_id: u32) -> u32 {
|
||||
self.data_area_start_block + data_block_id
|
||||
}
|
||||
|
||||
/// Allocate a new inode
|
||||
pub fn alloc_inode(&mut self) -> u32 {
|
||||
self.inode_bitmap.alloc(&self.block_device).unwrap() as u32
|
||||
}
|
||||
|
||||
/// Return a block ID not ID in the data area.
|
||||
/// Allocate a data block
|
||||
pub fn alloc_data(&mut self) -> u32 {
|
||||
self.data_bitmap.alloc(&self.block_device).unwrap() as u32 + self.data_area_start_block
|
||||
}
|
||||
|
||||
/// Deallocate a data block
|
||||
pub fn dealloc_data(&mut self, block_id: u32) {
|
||||
get_block_cache(block_id as usize, Arc::clone(&self.block_device))
|
||||
.lock()
|
||||
|
|
|
@ -3,16 +3,24 @@ use alloc::sync::Arc;
|
|||
use alloc::vec::Vec;
|
||||
use core::fmt::{Debug, Formatter, Result};
|
||||
|
||||
/// Magic number for sanity check
|
||||
const EFS_MAGIC: u32 = 0x3b800001;
|
||||
/// The max number of direct inodes
|
||||
const INODE_DIRECT_COUNT: usize = 28;
|
||||
/// The max length of inode name
|
||||
const NAME_LENGTH_LIMIT: usize = 27;
|
||||
/// The max number of indirect1 inodes
|
||||
const INODE_INDIRECT1_COUNT: usize = BLOCK_SZ / 4;
|
||||
/// The max number of indirect2 inodes
|
||||
const INODE_INDIRECT2_COUNT: usize = INODE_INDIRECT1_COUNT * INODE_INDIRECT1_COUNT;
|
||||
/// The upper bound of direct inode index
|
||||
const DIRECT_BOUND: usize = INODE_DIRECT_COUNT;
|
||||
/// The upper bound of indirect1 inode index
|
||||
const INDIRECT1_BOUND: usize = DIRECT_BOUND + INODE_INDIRECT1_COUNT;
|
||||
/// The upper bound of indirect2 inode indexs
|
||||
#[allow(unused)]
|
||||
const INDIRECT2_BOUND: usize = INDIRECT1_BOUND + INODE_INDIRECT2_COUNT;
|
||||
|
||||
/// Super block of a filesystem
|
||||
#[repr(C)]
|
||||
pub struct SuperBlock {
|
||||
magic: u32,
|
||||
|
@ -36,6 +44,7 @@ impl Debug for SuperBlock {
|
|||
}
|
||||
|
||||
impl SuperBlock {
|
||||
/// Initialize a super block
|
||||
pub fn initialize(
|
||||
&mut self,
|
||||
total_blocks: u32,
|
||||
|
@ -53,20 +62,23 @@ impl SuperBlock {
|
|||
data_area_blocks,
|
||||
}
|
||||
}
|
||||
/// Check if a super block is valid using efs magic
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.magic == EFS_MAGIC
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of a disk inode
|
||||
#[derive(PartialEq)]
|
||||
pub enum DiskInodeType {
|
||||
File,
|
||||
Directory,
|
||||
}
|
||||
|
||||
/// A indirect block
|
||||
type IndirectBlock = [u32; BLOCK_SZ / 4];
|
||||
/// A data block
|
||||
type DataBlock = [u8; BLOCK_SZ];
|
||||
|
||||
/// A disk inode
|
||||
#[repr(C)]
|
||||
pub struct DiskInode {
|
||||
pub size: u32,
|
||||
|
@ -77,7 +89,8 @@ pub struct DiskInode {
|
|||
}
|
||||
|
||||
impl DiskInode {
|
||||
/// indirect1 and indirect2 block are allocated only when they are needed.
|
||||
/// Initialize a disk inode, as well as all direct inodes under it
|
||||
/// indirect1 and indirect2 block are allocated only when they are needed
|
||||
pub fn initialize(&mut self, type_: DiskInodeType) {
|
||||
self.size = 0;
|
||||
self.direct.iter_mut().for_each(|v| *v = 0);
|
||||
|
@ -85,9 +98,11 @@ impl DiskInode {
|
|||
self.indirect2 = 0;
|
||||
self.type_ = type_;
|
||||
}
|
||||
/// Whether this inode is a directory
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.type_ == DiskInodeType::Directory
|
||||
}
|
||||
/// Whether this inode is a file
|
||||
#[allow(unused)]
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.type_ == DiskInodeType::File
|
||||
|
@ -116,10 +131,12 @@ impl DiskInode {
|
|||
}
|
||||
total as u32
|
||||
}
|
||||
/// Get the number of data blocks that have to be allocated given the new size of data
|
||||
pub fn blocks_num_needed(&self, new_size: u32) -> u32 {
|
||||
assert!(new_size >= self.size);
|
||||
Self::total_blocks(new_size) - Self::total_blocks(self.size)
|
||||
}
|
||||
/// Get id of block given inner id
|
||||
pub fn get_block_id(&self, inner_id: u32, block_device: &Arc<dyn BlockDevice>) -> u32 {
|
||||
let inner_id = inner_id as usize;
|
||||
if inner_id < INODE_DIRECT_COUNT {
|
||||
|
@ -144,6 +161,7 @@ impl DiskInode {
|
|||
})
|
||||
}
|
||||
}
|
||||
/// Inncrease the size of current disk inode
|
||||
pub fn increase_size(
|
||||
&mut self,
|
||||
new_size: u32,
|
||||
|
@ -218,7 +236,6 @@ impl DiskInode {
|
|||
}
|
||||
|
||||
/// Clear size to zero and return blocks that should be deallocated.
|
||||
///
|
||||
/// We will clear the block contents to zero later.
|
||||
pub fn clear_size(&mut self, block_device: &Arc<dyn BlockDevice>) -> Vec<u32> {
|
||||
let mut v: Vec<u32> = Vec::new();
|
||||
|
@ -291,6 +308,7 @@ impl DiskInode {
|
|||
self.indirect2 = 0;
|
||||
v
|
||||
}
|
||||
/// Read data from current disk inode
|
||||
pub fn read_at(
|
||||
&self,
|
||||
offset: usize,
|
||||
|
@ -330,7 +348,8 @@ impl DiskInode {
|
|||
}
|
||||
read_size
|
||||
}
|
||||
/// File size must be adjusted before.
|
||||
/// Write data into current disk inode
|
||||
/// size must be adjusted properly beforehand
|
||||
pub fn write_at(
|
||||
&mut self,
|
||||
offset: usize,
|
||||
|
@ -369,22 +388,24 @@ impl DiskInode {
|
|||
write_size
|
||||
}
|
||||
}
|
||||
|
||||
/// A directory entry
|
||||
#[repr(C)]
|
||||
pub struct DirEntry {
|
||||
name: [u8; NAME_LENGTH_LIMIT + 1],
|
||||
inode_number: u32,
|
||||
}
|
||||
|
||||
/// Size of a directory entry
|
||||
pub const DIRENT_SZ: usize = 32;
|
||||
|
||||
impl DirEntry {
|
||||
/// Create an empty directory entry
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
name: [0u8; NAME_LENGTH_LIMIT + 1],
|
||||
inode_number: 0,
|
||||
}
|
||||
}
|
||||
/// Crate a directory entry from name and inode number
|
||||
pub fn new(name: &str, inode_number: u32) -> Self {
|
||||
let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1];
|
||||
bytes[..name.len()].copy_from_slice(name.as_bytes());
|
||||
|
@ -393,16 +414,20 @@ impl DirEntry {
|
|||
inode_number,
|
||||
}
|
||||
}
|
||||
/// Serialize into bytes
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { core::slice::from_raw_parts(self as *const _ as usize as *const u8, DIRENT_SZ) }
|
||||
}
|
||||
/// Serialize into mutable bytes
|
||||
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { core::slice::from_raw_parts_mut(self as *mut _ as usize as *mut u8, DIRENT_SZ) }
|
||||
}
|
||||
/// Get name of the entry
|
||||
pub fn name(&self) -> &str {
|
||||
let len = (0usize..).find(|i| self.name[*i] == 0).unwrap();
|
||||
core::str::from_utf8(&self.name[..len]).unwrap()
|
||||
}
|
||||
/// Get inode number of the entry
|
||||
pub fn inode_number(&self) -> u32 {
|
||||
self.inode_number
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
//!An easy file system isolated from the kernel
|
||||
#![no_std]
|
||||
|
||||
#![deny(missing_docs)]
|
||||
extern crate alloc;
|
||||
|
||||
mod bitmap;
|
||||
mod block_cache;
|
||||
mod block_dev;
|
||||
mod efs;
|
||||
mod layout;
|
||||
mod vfs;
|
||||
|
||||
/// Use a block size of 512 bytes
|
||||
pub const BLOCK_SZ: usize = 512;
|
||||
use bitmap::Bitmap;
|
||||
use block_cache::{block_cache_sync_all, get_block_cache};
|
||||
|
|
|
@ -6,7 +6,7 @@ use alloc::string::String;
|
|||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use spin::{Mutex, MutexGuard};
|
||||
|
||||
/// Virtual filesystem layer over easy-fs
|
||||
pub struct Inode {
|
||||
block_id: usize,
|
||||
block_offset: usize,
|
||||
|
@ -15,7 +15,7 @@ pub struct Inode {
|
|||
}
|
||||
|
||||
impl Inode {
|
||||
/// We should not acquire efs lock here.
|
||||
/// Create a vfs inode
|
||||
pub fn new(
|
||||
block_id: u32,
|
||||
block_offset: usize,
|
||||
|
@ -29,19 +29,19 @@ impl Inode {
|
|||
block_device,
|
||||
}
|
||||
}
|
||||
|
||||
/// Call a function over a disk inode to read it
|
||||
fn read_disk_inode<V>(&self, f: impl FnOnce(&DiskInode) -> V) -> V {
|
||||
get_block_cache(self.block_id, Arc::clone(&self.block_device))
|
||||
.lock()
|
||||
.read(self.block_offset, f)
|
||||
}
|
||||
|
||||
/// Call a function over a disk inode to modify it
|
||||
fn modify_disk_inode<V>(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V {
|
||||
get_block_cache(self.block_id, Arc::clone(&self.block_device))
|
||||
.lock()
|
||||
.modify(self.block_offset, f)
|
||||
}
|
||||
|
||||
/// Find inode under a disk inode by name
|
||||
fn find_inode_id(&self, name: &str, disk_inode: &DiskInode) -> Option<u32> {
|
||||
// assert it is a directory
|
||||
assert!(disk_inode.is_dir());
|
||||
|
@ -58,7 +58,7 @@ impl Inode {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Find inode under current inode by name
|
||||
pub fn find(&self, name: &str) -> Option<Arc<Inode>> {
|
||||
let fs = self.fs.lock();
|
||||
self.read_disk_inode(|disk_inode| {
|
||||
|
@ -73,7 +73,7 @@ impl Inode {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Increase the size of a disk inode
|
||||
fn increase_size(
|
||||
&self,
|
||||
new_size: u32,
|
||||
|
@ -90,7 +90,7 @@ impl Inode {
|
|||
}
|
||||
disk_inode.increase_size(new_size, v, &self.block_device);
|
||||
}
|
||||
|
||||
/// Create inode under current inode by name
|
||||
pub fn create(&self, name: &str) -> Option<Arc<Inode>> {
|
||||
let mut fs = self.fs.lock();
|
||||
let op = |root_inode: &DiskInode| {
|
||||
|
@ -138,7 +138,7 @@ impl Inode {
|
|||
)))
|
||||
// release efs lock automatically by compiler
|
||||
}
|
||||
|
||||
/// List inodes under current inode
|
||||
pub fn ls(&self) -> Vec<String> {
|
||||
let _fs = self.fs.lock();
|
||||
self.read_disk_inode(|disk_inode| {
|
||||
|
@ -155,12 +155,12 @@ impl Inode {
|
|||
v
|
||||
})
|
||||
}
|
||||
|
||||
/// Read data from current inode
|
||||
pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize {
|
||||
let _fs = self.fs.lock();
|
||||
self.read_disk_inode(|disk_inode| disk_inode.read_at(offset, buf, &self.block_device))
|
||||
}
|
||||
|
||||
/// Write data to current inode
|
||||
pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize {
|
||||
let mut fs = self.fs.lock();
|
||||
let size = self.modify_disk_inode(|disk_inode| {
|
||||
|
@ -170,7 +170,7 @@ impl Inode {
|
|||
block_cache_sync_all();
|
||||
size
|
||||
}
|
||||
|
||||
/// Clear the data in current inode
|
||||
pub fn clear(&self) {
|
||||
let mut fs = self.fs.lock();
|
||||
self.modify_disk_inode(|disk_inode| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue