From 807a5d3dac6a4a145ad2a058ae1fb7fefc3f6c79 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Fri, 29 Mar 2019 00:55:10 +0800 Subject: [PATCH] Implement double indirect blocks --- rcore-fs-sfs/src/lib.rs | 108 ++++++++++++++++++++++++++++++++---- rcore-fs-sfs/src/structs.rs | 9 ++- rcore-fs-sfs/src/tests.rs | 39 ++++++++++++- 3 files changed, 144 insertions(+), 12 deletions(-) diff --git a/rcore-fs-sfs/src/lib.rs b/rcore-fs-sfs/src/lib.rs index c3ae8d2..f18400f 100644 --- a/rcore-fs-sfs/src/lib.rs +++ b/rcore-fs-sfs/src/lib.rs @@ -80,8 +80,8 @@ impl INodeImpl { let disk_inode = self.disk_inode.read(); match file_block_id { id if id >= disk_inode.blocks as BlockId => Err(FsError::InvalidParam), - id if id < NDIRECT => Ok(disk_inode.direct[id] as BlockId), - id if id < NDIRECT + BLK_NENTRY => { + id if id < MAX_NBLOCK_DIRECT => Ok(disk_inode.direct[id] as BlockId), + id if id < MAX_NBLOCK_INDIRECT => { let mut disk_block_id: u32 = 0; self.fs.device.read_block( disk_inode.indirect as usize, @@ -90,17 +90,36 @@ impl INodeImpl { )?; Ok(disk_block_id as BlockId) } - _ => unimplemented!("double indirect blocks is not supported"), + id if id < MAX_NBLOCK_DOUBLE_INDIRECT => { + // double indirect + let indirect_id = id - MAX_NBLOCK_INDIRECT; + let mut indirect_block_id: u32 = 0; + self.fs.device.read_block( + disk_inode.db_indirect as usize, + ENTRY_SIZE * (indirect_id / BLK_NENTRY), + indirect_block_id.as_buf_mut(), + )?; + assert!(indirect_block_id > 0); + let mut disk_block_id: u32 = 0; + self.fs.device.read_block( + indirect_block_id as usize, + ENTRY_SIZE * (indirect_id as usize % BLK_NENTRY), + disk_block_id.as_buf_mut(), + )?; + assert!(disk_block_id > 0); + Ok(disk_block_id as BlockId) + } + _ => unimplemented!("triple indirect blocks is not supported"), } } fn set_disk_block_id(&self, file_block_id: BlockId, disk_block_id: BlockId) -> vfs::Result<()> { match file_block_id { id if id >= self.disk_inode.read().blocks as BlockId => Err(FsError::InvalidParam), - id if id < NDIRECT => { + id if id < MAX_NBLOCK_DIRECT => { self.disk_inode.write().direct[id] = disk_block_id as u32; Ok(()) } - id if id < NDIRECT + BLK_NENTRY => { + id if id < MAX_NBLOCK_INDIRECT => { let disk_block_id = disk_block_id as u32; self.fs.device.write_block( self.disk_inode.read().indirect as usize, @@ -109,7 +128,25 @@ impl INodeImpl { )?; Ok(()) } - _ => unimplemented!("double indirect blocks is not supported"), + id if id < MAX_NBLOCK_DOUBLE_INDIRECT => { + // double indirect + let indirect_id = id - MAX_NBLOCK_INDIRECT; + let mut indirect_block_id: u32 = 0; + self.fs.device.read_block( + self.disk_inode.read().db_indirect as usize, + ENTRY_SIZE * (indirect_id / BLK_NENTRY), + indirect_block_id.as_buf_mut(), + )?; + assert!(indirect_block_id > 0); + let disk_block_id = disk_block_id as u32; + self.fs.device.write_block( + indirect_block_id as usize, + ENTRY_SIZE * (indirect_id as usize % BLK_NENTRY), + disk_block_id.as_buf(), + )?; + Ok(()) + } + _ => unimplemented!("triple indirect blocks is not supported"), } } /// Only for Dir @@ -172,6 +209,9 @@ impl INodeImpl { return Err(FsError::InvalidParam); } let blocks = ((len + BLKSIZE - 1) / BLKSIZE) as u32; + if blocks > MAX_NBLOCK_DOUBLE_INDIRECT as u32 { + return Err(FsError::InvalidParam); + } use core::cmp::{Ord, Ordering}; let old_blocks = self.disk_inode.read().blocks; match blocks.cmp(&old_blocks) { @@ -180,10 +220,32 @@ impl INodeImpl { { let mut disk_inode = self.disk_inode.write(); disk_inode.blocks = blocks; - // allocate indirect block if need - if old_blocks < NDIRECT as u32 && blocks >= NDIRECT as u32 { + // allocate indirect block if needed + if old_blocks < MAX_NBLOCK_DIRECT as u32 && blocks >= MAX_NBLOCK_DIRECT as u32 { disk_inode.indirect = self.fs.alloc_block().expect("no space") as u32; } + // allocate double indirect block if needed + if blocks >= MAX_NBLOCK_INDIRECT as u32 { + if disk_inode.db_indirect == 0 { + disk_inode.db_indirect = self.fs.alloc_block().expect("no space") as u32; + } + let indirect_begin = { + if (old_blocks as usize) < MAX_NBLOCK_INDIRECT { + 0 + } else { + (old_blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1 + } + }; + let indirect_end = (blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1; + for i in indirect_begin..indirect_end { + let indirect = self.fs.alloc_block().expect("no space") as u32; + self.fs.device.write_block( + disk_inode.db_indirect as usize, + ENTRY_SIZE * i, + indirect.as_buf(), + )?; + } + } } // allocate extra blocks for i in old_blocks..blocks { @@ -202,11 +264,37 @@ impl INodeImpl { self.fs.free_block(disk_block_id); } let mut disk_inode = self.disk_inode.write(); - // free indirect block if need - if blocks < NDIRECT as u32 && disk_inode.blocks >= NDIRECT as u32 { + // free indirect block if needed + if blocks < MAX_NBLOCK_DIRECT as u32 && disk_inode.blocks >= MAX_NBLOCK_DIRECT as u32 { self.fs.free_block(disk_inode.indirect as usize); disk_inode.indirect = 0; } + // free double indirect block if needed + if disk_inode.blocks >= MAX_NBLOCK_INDIRECT as u32 { + let indirect_begin = { + if (blocks as usize) < MAX_NBLOCK_INDIRECT { + 0 + } else { + (blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1 + } + }; + let indirect_end = (disk_inode.blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1; + for i in indirect_begin..indirect_end { + let mut indirect: u32 = 0; + self.fs.device.read_block( + disk_inode.db_indirect as usize, + ENTRY_SIZE * i, + indirect.as_buf_mut(), + )?; + assert!(indirect > 0); + self.fs.free_block(indirect as usize); + } + if blocks < MAX_NBLOCK_INDIRECT as u32 { + assert!(disk_inode.db_indirect > 0); + self.fs.free_block(disk_inode.db_indirect as usize); + disk_inode.db_indirect = 0; + } + } disk_inode.blocks = blocks; } } diff --git a/rcore-fs-sfs/src/structs.rs b/rcore-fs-sfs/src/structs.rs index 7a17cb3..a192db8 100644 --- a/rcore-fs-sfs/src/structs.rs +++ b/rcore-fs-sfs/src/structs.rs @@ -186,7 +186,8 @@ pub const DEFAULT_INFO: &str = "simple file system"; pub const MAX_INFO_LEN: usize = 31; /// max length of filename pub const MAX_FNAME_LEN: usize = 255; -/// max file size (128M) +/// max file size in theory (48KB + 4MB + 4GB), but freemap is not large enough +/// max file size (128M), 128M = 8 * 4096 * 4096 pub const MAX_FILE_SIZE: usize = 1024 * 1024 * 128; /// block the superblock lives in pub const BLKN_SUPER: BlockId = 0; @@ -202,6 +203,12 @@ pub const ENTRY_SIZE: usize = 4; pub const BLK_NENTRY: usize = BLKSIZE / ENTRY_SIZE; /// size of a dirent used in the size field pub const DIRENT_SIZE: usize = MAX_FNAME_LEN + 1; +/// max number of blocks with direct blocks +pub const MAX_NBLOCK_DIRECT: usize = NDIRECT; +/// max number of blocks with indirect blocks +pub const MAX_NBLOCK_INDIRECT: usize = NDIRECT + BLK_NENTRY; +/// max number of blocks with double indirect blocks +pub const MAX_NBLOCK_DOUBLE_INDIRECT: usize = NDIRECT + BLK_NENTRY + BLK_NENTRY * BLK_NENTRY; /// file types #[repr(u16)] diff --git a/rcore-fs-sfs/src/tests.rs b/rcore-fs-sfs/src/tests.rs index 8328ffd..fdbf54c 100644 --- a/rcore-fs-sfs/src/tests.rs +++ b/rcore-fs-sfs/src/tests.rs @@ -19,7 +19,7 @@ fn _open_sample_file() -> Arc { fn _create_new_sfs() -> Arc { let file = tempfile::tempfile().expect("failed to create file"); - SimpleFileSystem::create(Arc::new(Mutex::new(file)), 32 * 4096) + SimpleFileSystem::create(Arc::new(Mutex::new(file)), 32 * 4096 * 4096) } #[test] @@ -241,6 +241,43 @@ fn test_symlinks() -> Result<()> { Ok(()) } +#[test] +fn test_double_indirect_blocks() -> Result<()> { + let sfs = _create_new_sfs(); + let root = sfs.root_inode(); + + let file1 = root + .create("file1", FileType::File, 0o777) + .expect("failed to create file1"); + assert!( + Arc::ptr_eq(&root.lookup("file1")?, &file1), + "failed to find file1" + ); + + // resize to direct maximum size + file1.resize(MAX_NBLOCK_DIRECT * BLKSIZE).unwrap(); + // force usage of indirect block + file1.resize((MAX_NBLOCK_DIRECT + 1) * BLKSIZE).unwrap(); + file1.resize(MAX_NBLOCK_INDIRECT * BLKSIZE).unwrap(); + // force usage of double indirect block + file1.resize((MAX_NBLOCK_INDIRECT + 1) * BLKSIZE).unwrap(); + file1.resize(MAX_FILE_SIZE / 8).unwrap(); + + // resize up and down + file1.resize(0).unwrap(); + file1.resize(MAX_NBLOCK_DIRECT * BLKSIZE).unwrap(); + file1.resize((MAX_NBLOCK_DIRECT + 1) * BLKSIZE).unwrap(); + file1.resize(MAX_NBLOCK_DIRECT * BLKSIZE).unwrap(); + file1.resize(0).unwrap(); + file1.resize((MAX_NBLOCK_INDIRECT + 1) * BLKSIZE).unwrap(); + file1.resize(MAX_NBLOCK_DIRECT * BLKSIZE).unwrap(); + file1.resize((MAX_NBLOCK_INDIRECT + 1) * BLKSIZE).unwrap(); + file1.resize(0).unwrap(); + + sfs.sync()?; + Ok(()) +} + #[test] fn arc_layout() { // [usize, usize, T]