We need BlockCache.

pull/3/head
Yifan Wu 4 years ago
parent af02e68d19
commit 459efec595

@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
spin = "0.7.0"

@ -4,6 +4,7 @@ extern crate alloc;
use easy_fs::{
BlockDevice,
EasyFileSystem,
Inode,
};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Seek, SeekFrom};
@ -16,6 +17,7 @@ struct BlockFile(Mutex<File>);
impl BlockDevice for BlockFile {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
println!("reading block {}", block_id);
let mut file = self.0.lock().unwrap();
file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64))
.expect("Error when seeking!");
@ -23,6 +25,7 @@ impl BlockDevice for BlockFile {
}
fn write_block(&self, block_id: usize, buf: &[u8]) {
println!("writing block {}", block_id);
let mut file = self.0.lock().unwrap();
file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64))
.expect("Error when seeking!");
@ -35,19 +38,23 @@ fn main() {
}
fn easy_fs_pack() -> std::io::Result<()> {
let block_file = BlockFile(Mutex::new(
let block_file = Arc::new(BlockFile(Mutex::new(
OpenOptions::new()
.read(true)
.write(true)
.open("target/fs.img")?
));
/*
let _efs = EasyFileSystem::create(
Arc::new(block_file),
)));
EasyFileSystem::create(
block_file.clone(),
4096,
1,
);
*/
let _efs = EasyFileSystem::open(Arc::new(block_file));
let efs = EasyFileSystem::open(block_file.clone());
let mut root_inode = EasyFileSystem::root_inode(&efs);
root_inode.create("filea");
root_inode.create("fileb");
for name in root_inode.ls() {
println!("name");
}
Ok(())
}

@ -7,6 +7,7 @@ pub struct Dirty<T> {
block_id: usize,
block_cache: [u8; BLOCK_SZ],
offset: usize,
dirty: bool,
block_device: Arc<dyn BlockDevice>,
phantom: PhantomData<T>,
}
@ -21,29 +22,37 @@ impl<T> Dirty<T> where T: Sized {
cache
},
offset,
dirty: false,
block_device,
phantom: PhantomData,
}
}
pub fn get_mut(&mut self) -> &mut T {
self.dirty = true;
let type_size = core::mem::size_of::<T>();
// assert that the struct is inside a block
assert!(self.offset + type_size <= BLOCK_SZ);
let start_addr = &self.block_cache[self.offset] as *const _ as usize;
unsafe { &mut *(start_addr as *mut T) }
}
pub fn read(&self) -> &T {
pub fn get_ref(&self) -> &T {
let type_size = core::mem::size_of::<T>();
// assert that the struct is inside a block
assert!(self.offset + type_size <= BLOCK_SZ);
let start_addr = &self.block_cache[self.offset] as *const _ as usize;
unsafe { &*(start_addr as *const T) }
}
pub fn modify(&mut self, f: impl Fn(&mut T)) {
pub fn read<V>(&self, f: impl FnOnce(&T) -> V) -> V {
f(self.get_ref())
}
pub fn modify(&mut self, f: impl FnOnce(&mut T)) {
f(self.get_mut());
}
pub fn write_back(&mut self) {
self.block_device.write_block(self.block_id as usize, &self.block_cache);
if self.dirty {
self.block_device
.write_block(self.block_id as usize, &self.block_cache);
}
}
}

@ -1,4 +1,5 @@
use alloc::sync::Arc;
use spin::Mutex;
use super::{
BlockDevice,
Bitmap,
@ -6,6 +7,7 @@ use super::{
DiskInode,
DiskInodeType,
Dirty,
Inode,
};
use crate::BLOCK_SZ;
@ -24,7 +26,7 @@ impl EasyFileSystem {
block_device: Arc<dyn BlockDevice>,
total_blocks: u32,
inode_bitmap_blocks: u32,
) -> Self {
) -> Arc<Mutex<Self>> {
// calculate block size of areas & create bitmaps
let inode_bitmap = Bitmap::new(1, inode_bitmap_blocks as usize);
let inode_num = inode_bitmap.maximum();
@ -38,7 +40,7 @@ impl EasyFileSystem {
(1 + inode_bitmap_blocks + inode_area_blocks) as usize,
data_bitmap_blocks as usize,
);
let efs = Self {
let mut efs = Self {
block_device,
inode_bitmap,
data_bitmap,
@ -65,17 +67,20 @@ impl EasyFileSystem {
});
// write back immediately
// create a inode for root node "/"
assert_eq!(efs.inode_bitmap.alloc(&efs.block_device).unwrap(), 0);
assert_eq!(efs.alloc_inode(), 0);
efs.get_disk_inode(0).modify(|disk_inode| {
disk_inode.initialize(DiskInodeType::Directory);
disk_inode.initialize(
DiskInodeType::Directory,
efs.alloc_data(),
);
});
efs
Arc::new(Mutex::new(efs))
}
pub fn open(block_device: Arc<dyn BlockDevice>) -> Self {
pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {
// read SuperBlock
let super_block_dirty: Dirty<SuperBlock> = Dirty::new(0, 0, block_device.clone());
let super_block = super_block_dirty.read();
let super_block = super_block_dirty.get_ref();
assert!(super_block.is_valid(), "Error loading EFS!");
println!("{:?}", super_block);
let inode_total_blocks =
@ -93,14 +98,22 @@ impl EasyFileSystem {
inode_area_start_block: 1 + super_block.inode_bitmap_blocks,
data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks,
};
efs
Arc::new(Mutex::new(efs))
}
pub fn root_inode(efs: &Arc<Mutex<Self>>) -> Inode {
Inode::new(
0,
efs.clone(),
efs.lock().block_device.clone(),
)
}
fn get_super_block(&self) -> Dirty<SuperBlock> {
Dirty::new(0, 0, self.block_device.clone())
}
fn get_disk_inode(&self, inode_id: u32) -> Dirty<DiskInode> {
pub fn get_disk_inode(&self, inode_id: u32) -> Dirty<DiskInode> {
let inode_size = core::mem::size_of::<DiskInode>();
let inodes_per_block = (BLOCK_SZ / inode_size) as u32;
let block_id = self.inode_area_start_block + inode_id / inodes_per_block;
@ -111,12 +124,8 @@ impl EasyFileSystem {
)
}
fn get_data_block(&self, data_block_id: u32) -> Dirty<DataBlock> {
Dirty::new(
(self.data_area_start_block + data_block_id) as usize,
0,
self.block_device.clone(),
)
pub fn get_data_block(&self, data_block_id: u32) -> Dirty<DataBlock> {
self.get_block(self.data_area_start_block + data_block_id)
}
fn get_block(&self, block_id: u32) -> Dirty<DataBlock> {
@ -127,4 +136,20 @@ impl EasyFileSystem {
)
}
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.
pub fn alloc_data(&mut self) -> u32 {
self.data_bitmap.alloc(&self.block_device).unwrap() as u32 + self.data_area_start_block
}
fn dealloc_data(&mut self, block_id: u32) {
self.data_bitmap.dealloc(
&self.block_device,
(block_id - self.data_area_start_block) as usize
)
}
}

@ -1,4 +1,11 @@
use core::fmt::{Debug, Formatter, Result};
use super::{
BLOCK_SZ,
BlockDevice,
Dirty,
};
use alloc::sync::Arc;
use alloc::vec::Vec;
const EFS_MAGIC: u32 = 0x3b800001;
const INODE_DIRECT_COUNT: usize = 12;
@ -49,32 +56,224 @@ impl SuperBlock {
}
}
#[derive(PartialEq)]
pub enum DiskInodeType {
File,
Directory,
}
type IndirectBlock = [u32; BLOCK_SZ / 4];
type DataBlock = [u8; BLOCK_SZ];
#[repr(C)]
/// Only support level-1 indirect now, **indirect2** field is always 0.
pub struct DiskInode {
size: u32,
direct: [u32; INODE_DIRECT_COUNT],
indirect1: u32,
indirect2: u32,
pub size: u32,
pub direct: [u32; INODE_DIRECT_COUNT],
pub indirect1: u32,
pub indirect2: u32,
type_: DiskInodeType,
}
impl DiskInode {
pub fn initialize(&mut self, type_: DiskInodeType) {
/// indirect1 block is allocated when the file is created.
pub fn initialize(&mut self, type_: DiskInodeType, indirect1: u32) {
self.size = 0;
self.direct.iter_mut().for_each(|v| *v = 0);
self.indirect1 = 0;
self.indirect1 = indirect1;
self.indirect2 = 0;
self.type_ = type_;
}
pub fn is_dir(&self) -> bool {
self.type_ == DiskInodeType::Directory
}
pub fn is_file(&self) -> bool {
self.type_ == DiskInodeType::File
}
pub fn blocks(&self) -> u32 {
Self::_blocks(self.size)
}
fn _blocks(size: u32) -> u32 {
(size + BLOCK_SZ as u32 - 1) / BLOCK_SZ as u32
}
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 {
self.direct[inner_id]
} else {
// only support indirect1 now
Dirty::<IndirectBlock>::new(
self.indirect1 as usize,
0,
block_device.clone()
).read(|indirect_block| {
// it will panic if file is too large
indirect_block[inner_id - INODE_DIRECT_COUNT]
})
}
}
pub fn blocks_num_needed(&self, new_size: u32) -> u32 {
assert!(new_size >= self.size);
Self::_blocks(new_size) - self.blocks()
}
pub fn increase_size(
&mut self,
new_size: u32,
new_blocks: Vec<u32>,
block_device: &Arc<dyn BlockDevice>,
) {
println!("increase_size new_size={}, new_blocks={:?}", new_size, new_blocks);
assert_eq!(new_blocks.len() as u32, self.blocks_num_needed(new_size));
let last_blocks = self.blocks();
self.size = new_size;
let current_blocks = self.blocks();
Dirty::<IndirectBlock>::new(
self.indirect1 as usize,
0,
block_device.clone()
).modify(|indirect_block| {
for i in 0..current_blocks - last_blocks {
let inner_id = (last_blocks + i) as usize;
let new_block = new_blocks[i as usize];
if inner_id < INODE_DIRECT_COUNT {
self.direct[inner_id] = new_block;
} else {
indirect_block[inner_id - INODE_DIRECT_COUNT] = new_block;
}
}
});
}
/// Clear size to zero and return blocks that should be deallocated.
pub fn clear_size(&mut self, block_device: &Arc<dyn BlockDevice>) -> Vec<u32> {
let mut v: Vec<u32> = Vec::new();
let blocks = self.blocks() as usize;
self.size = 0;
for i in 0..blocks.min(INODE_DIRECT_COUNT) {
v.push(self.direct[i]);
self.direct[i] = 0;
}
if blocks > INODE_DIRECT_COUNT {
Dirty::<IndirectBlock>::new(
self.indirect1 as usize,
0,
block_device.clone(),
).modify(|indirect_block| {
for i in 0..blocks - INODE_DIRECT_COUNT {
v.push(indirect_block[i]);
indirect_block[i] = 0;
}
});
}
v
}
pub fn read_at(
&self,
offset: usize,
buf: &mut [u8],
block_device: &Arc<dyn BlockDevice>,
) -> usize {
let mut start = offset;
let end = (offset + buf.len()).min(self.size as usize);
if start >= end {
return 0;
}
let mut start_block = start / BLOCK_SZ;
let mut read_size = 0usize;
loop {
// calculate end of current block
let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ;
end_current_block = end_current_block.min(end);
// read and update read size
let block_read_size = end_current_block - start;
let dst = &mut buf[read_size..read_size + block_read_size];
Dirty::<DataBlock>::new(
self.get_block_id(start_block as u32, block_device) as usize,
0,
block_device.clone()
).read(|data_block| {
let src = &data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_read_size];
dst.copy_from_slice(src);
});
read_size += block_read_size;
// move to next block
if end_current_block == end { break; }
start_block += 1;
start = end_current_block;
}
read_size
}
/// File size must be adjusted before.
pub fn write_at(
&mut self,
offset: usize,
buf: &[u8],
block_device: &Arc<dyn BlockDevice>,
) -> usize {
let mut start = offset;
let end = (offset + buf.len()).min(self.size as usize);
assert!(start <= end);
let mut start_block = start / BLOCK_SZ;
let mut write_size = 0usize;
loop {
// calculate end of current block
let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ;
end_current_block = end_current_block.min(end);
// write and update write size
let block_write_size = end_current_block - start;
Dirty::<DataBlock>::new(
self.get_block_id(start_block as u32, block_device) as usize,
0,
block_device.clone()
).modify(|data_block| {
let src = &buf[write_size..write_size + block_write_size];
let dst = &mut data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_write_size];
dst.copy_from_slice(src);
});
write_size += block_write_size;
// move to next block
if end_current_block == end { break; }
start_block += 1;
start = end_current_block;
}
write_size
}
}
#[repr(C)]
pub struct DirEntry {
name: [u8; NAME_LENGTH_LIMIT + 1],
inode_number: u32,
}
pub const DIRENT_SZ: usize = 32;
pub type DirentBlock = [DirEntry; BLOCK_SZ / DIRENT_SZ];
pub type DirentBytes = [u8; DIRENT_SZ];
impl DirEntry {
pub fn new(name: &str, inode_number: u32) -> Self {
let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1];
&mut bytes[..name.len()].copy_from_slice(name.as_bytes());
Self {
name: bytes,
inode_number,
}
}
pub fn into_bytes(&self) -> &DirentBytes {
unsafe {
&*(self as *const Self as usize as *const DirentBytes)
}
}
pub fn from_bytes(bytes: &DirentBytes) -> &Self {
unsafe { &*(bytes.as_ptr() as usize as *const Self) }
}
pub fn from_bytes_mut(bytes: &mut DirentBytes) -> &mut Self {
unsafe {
&mut *(bytes.as_mut_ptr() as usize as *mut Self)
}
}
pub fn name(&self) -> &str {
let len = (0usize..).find(|i| self.name[*i] == 0).unwrap();
core::str::from_utf8(&self.name[..len]).unwrap()
}
}

@ -10,6 +10,7 @@ mod vfs;
pub const BLOCK_SZ: usize = 512;
pub use block_dev::BlockDevice;
pub use efs::EasyFileSystem;
pub use vfs::Inode;
use layout::*;
use dirty::Dirty;
use bitmap::Bitmap;

@ -1,3 +1,156 @@
use super::{
BlockDevice,
Dirty,
DiskInode,
DiskInodeType,
DirEntry,
DirentBytes,
EasyFileSystem,
BLOCK_SZ,
DIRENT_SZ,
};
use alloc::sync::Arc;
use alloc::string::String;
use alloc::vec::Vec;
use crate::layout::DiskInodeType::Directory;
use spin::{Mutex, MutexGuard};
pub struct Inode {
inode_id: u32,
fs: Arc<Mutex<EasyFileSystem>>,
block_device: Arc<dyn BlockDevice>,
}
impl Inode {
pub fn new(
inode_id: u32,
fs: Arc<Mutex<EasyFileSystem>>,
block_device: Arc<dyn BlockDevice>,
) -> Self {
Self {
inode_id,
fs,
block_device,
}
}
fn get_disk_inode(&self) -> Dirty<DiskInode> {
self.fs.lock().get_disk_inode(self.inode_id)
}
fn find_inode_id(&self, name: &str) -> Option<u32> {
let inode = self.get_disk_inode();
// assert it is a directory
assert!(inode.read(|inode| inode.is_dir()));
let file_count = inode.read(|inode| {
inode.size as usize
}) / DIRENT_SZ;
let mut dirent_space: DirentBytes = Default::default();
for i in 0..file_count {
assert_eq!(
inode.read(|inode| {
inode.read_at(
DIRENT_SZ * i,
&mut dirent_space,
&self.block_device,
)
}),
DIRENT_SZ,
);
if DirEntry::from_bytes(&dirent_space).name() == name {
return Some(i as u32);
}
}
None
}
fn increase_size(&self, new_size: u32, fs: &mut MutexGuard<EasyFileSystem>) {
let mut inode = fs.get_disk_inode(self.inode_id);
let blocks_needed = inode.read(|inode| {
inode.blocks_num_needed(new_size)
});
println!("blocks_num_needed = {}", blocks_needed);
let mut v: Vec<u32> = Vec::new();
for _ in 0..blocks_needed {
v.push(fs.alloc_data());
}
inode.modify(|inode| {
inode.increase_size(new_size, v, &self.block_device);
});
}
pub fn create(&mut self, name: &str) -> Option<Arc<Inode>> {
println!("creating name {}", name);
let mut inode = self.get_disk_inode();
// assert it is a directory
assert!(inode.read(|inode| inode.is_dir()));
// has the file been created?
if let Some(_) = self.find_inode_id(name) {
return None;
}
println!("stop1");
// create a new file
// alloc a inode with an indirect block
let mut fs = self.fs.lock();
let new_inode_id = fs.alloc_inode();
let indirect1 = fs.alloc_data();
println!("creating new file, new_inode_id={}, indirect={}", new_inode_id, indirect1);
// initialize inode
fs.get_disk_inode(new_inode_id).modify(|inode| {
inode.initialize(
DiskInodeType::File,
indirect1,
)
});
// append file in the dirent
let file_count =
inode.read(|inode| inode.size as usize) / DIRENT_SZ;
let new_size = (file_count + 1) * DIRENT_SZ;
println!("expected new_size={}", new_size);
// increase size
self.increase_size(new_size as u32, &mut fs);
// write dirent
let dirent = DirEntry::new(name, new_inode_id);
inode.modify(|inode| {
inode.write_at(
file_count * DIRENT_SZ,
dirent.into_bytes(),
&self.block_device,
);
});
// return inode
Some(Arc::new(Self::new(
new_inode_id,
self.fs.clone(),
self.block_device.clone(),
)))
}
}
pub fn ls(&self) -> Vec<String> {
println!("into ls!");
let fs = self.fs.lock();
let inode = fs.get_disk_inode(self.inode_id);
let file_count = inode.read(|inode| {
(inode.size as usize) / DIRENT_SZ
});
println!("file_count = {}", file_count);
let mut v: Vec<String> = Vec::new();
for i in 0..file_count {
let mut dirent_bytes: DirentBytes = Default::default();
assert_eq!(
inode.read(|inode| {
inode.read_at(
i * DIRENT_SZ,
&mut dirent_bytes,
&self.block_device,
)
}),
DIRENT_SZ,
);
v.push(String::from(DirEntry::from_bytes(&dirent_bytes).name()));
}
v
}
}

Loading…
Cancel
Save