|
@@ -0,0 +1,1405 @@
|
|
|
+extern crate alloc;
|
|
|
+extern crate log;
|
|
|
+
|
|
|
+use crate::ext4_defs::*;
|
|
|
+use crate::constants::*;
|
|
|
+use crate::prelude::*;
|
|
|
+use crate::return_errno_with_message;
|
|
|
+use crate::utils::*;
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct Ext4 {
|
|
|
+ pub block_device: Arc<dyn BlockDevice>,
|
|
|
+ pub super_block: Ext4Superblock,
|
|
|
+ pub block_groups: Vec<Ext4BlockGroup>,
|
|
|
+ pub inodes_per_group: u32,
|
|
|
+ pub blocks_per_group: u32,
|
|
|
+ pub inode_size: usize,
|
|
|
+ pub last_inode_bg_id: u32,
|
|
|
+ pub self_ref: Weak<Self>,
|
|
|
+ pub mount_point: Ext4MountPoint,
|
|
|
+}
|
|
|
+
|
|
|
+impl Ext4 {
|
|
|
+ /// Opens and loads an Ext4 from the `block_device`.
|
|
|
+ pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Self> {
|
|
|
+ // Load the superblock
|
|
|
+ // TODO: if the main superblock is corrupted, should we load the backup?
|
|
|
+ let raw_data = block_device.read_offset(BASE_OFFSET);
|
|
|
+ let super_block = Ext4Superblock::try_from(raw_data).unwrap();
|
|
|
+
|
|
|
+ // log::info!("super_block: {:x?}", super_block);
|
|
|
+ let inodes_per_group = super_block.inodes_per_group();
|
|
|
+ let blocks_per_group = super_block.blocks_per_group();
|
|
|
+ let inode_size = super_block.inode_size();
|
|
|
+
|
|
|
+ // Load the block groups information
|
|
|
+ let load_block_groups =
|
|
|
+ |_fs: Weak<Ext4>, block_device: Arc<dyn BlockDevice>| -> Result<Vec<Ext4BlockGroup>> {
|
|
|
+ let block_groups_count = super_block.block_groups_count() as usize;
|
|
|
+ let mut block_groups = Vec::with_capacity(block_groups_count);
|
|
|
+ for idx in 0..block_groups_count {
|
|
|
+ let block_group =
|
|
|
+ Ext4BlockGroup::load(block_device.clone(), &super_block, idx).unwrap();
|
|
|
+ block_groups.push(block_group);
|
|
|
+ }
|
|
|
+ Ok(block_groups)
|
|
|
+ };
|
|
|
+
|
|
|
+ let mount_point = Ext4MountPoint::new("/");
|
|
|
+
|
|
|
+ let ext4: Arc<Ext4> = Arc::new_cyclic(|weak_ref| Self {
|
|
|
+ super_block,
|
|
|
+ inodes_per_group,
|
|
|
+ blocks_per_group,
|
|
|
+ inode_size: inode_size as usize,
|
|
|
+ block_groups: load_block_groups(weak_ref.clone(), block_device.clone()).unwrap(),
|
|
|
+ block_device,
|
|
|
+ self_ref: weak_ref.clone(),
|
|
|
+ mount_point: mount_point,
|
|
|
+ last_inode_bg_id: 0,
|
|
|
+ });
|
|
|
+
|
|
|
+ ext4
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用libc库定义的常量
|
|
|
+ fn ext4_parse_flags(&self, flags: &str) -> Result<u32> {
|
|
|
+ match flags {
|
|
|
+ "r" | "rb" => Ok(O_RDONLY),
|
|
|
+ "w" | "wb" => Ok(O_WRONLY | O_CREAT | O_TRUNC),
|
|
|
+ "a" | "ab" => Ok(O_WRONLY | O_CREAT | O_APPEND),
|
|
|
+ "r+" | "rb+" | "r+b" => Ok(O_RDWR),
|
|
|
+ "w+" | "wb+" | "w+b" => Ok(O_RDWR | O_CREAT | O_TRUNC),
|
|
|
+ "a+" | "ab+" | "a+b" => Ok(O_RDWR | O_CREAT | O_APPEND),
|
|
|
+ _ => Err(Ext4Error::new(Errnum::EINVAL)),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // start transaction
|
|
|
+ pub fn ext4_trans_start(&self) {}
|
|
|
+
|
|
|
+ // stop transaction
|
|
|
+ pub fn ext4_trans_abort(&self) {}
|
|
|
+
|
|
|
+ pub fn update_super_block(&mut self) {
|
|
|
+ let raw_data = self.block_device.read_offset(BASE_OFFSET);
|
|
|
+ let super_block = Ext4Superblock::try_from(raw_data).unwrap();
|
|
|
+ self.super_block = super_block;
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn ext4_open(
|
|
|
+ &self,
|
|
|
+ file: &mut Ext4File,
|
|
|
+ path: &str,
|
|
|
+ flags: &str,
|
|
|
+ file_expect: bool,
|
|
|
+ ) -> Result<usize> {
|
|
|
+ // get mount point
|
|
|
+ let mut ptr = Box::new(self.mount_point.clone());
|
|
|
+ file.mp = Box::as_mut(&mut ptr) as *mut Ext4MountPoint;
|
|
|
+
|
|
|
+ // get open flags
|
|
|
+ let iflags = self.ext4_parse_flags(flags).unwrap();
|
|
|
+
|
|
|
+ // file for dir
|
|
|
+ let filetype = if file_expect {
|
|
|
+ DirEntryType::EXT4_DE_REG_FILE
|
|
|
+ } else {
|
|
|
+ DirEntryType::EXT4_DE_DIR
|
|
|
+ };
|
|
|
+
|
|
|
+ if iflags & O_CREAT != 0 {
|
|
|
+ self.ext4_trans_start();
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
|
|
|
+
|
|
|
+ let r = self.ext4_generic_open(file, path, iflags, filetype.bits(), &mut root_inode_ref);
|
|
|
+
|
|
|
+ r
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn ext4_dir_mk(&self, path: &str) -> Result<usize> {
|
|
|
+ let mut file = Ext4File::new();
|
|
|
+ let flags = "w";
|
|
|
+
|
|
|
+ let filetype = DirEntryType::EXT4_DE_DIR;
|
|
|
+
|
|
|
+ // get mount point
|
|
|
+ let mut ptr = Box::new(self.mount_point.clone());
|
|
|
+ file.mp = Box::as_mut(&mut ptr) as *mut Ext4MountPoint;
|
|
|
+
|
|
|
+ // get open flags
|
|
|
+ let iflags = self.ext4_parse_flags(flags).unwrap();
|
|
|
+
|
|
|
+ if iflags & O_CREAT != 0 {
|
|
|
+ self.ext4_trans_start();
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
|
|
|
+
|
|
|
+ let r = self.ext4_generic_open(
|
|
|
+ &mut file,
|
|
|
+ path,
|
|
|
+ iflags,
|
|
|
+ filetype.bits(),
|
|
|
+ &mut root_inode_ref,
|
|
|
+ );
|
|
|
+ r
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn ext4_generic_open(
|
|
|
+ &self,
|
|
|
+ file: &mut Ext4File,
|
|
|
+ path: &str,
|
|
|
+ iflags: u32,
|
|
|
+ ftype: u8,
|
|
|
+ parent_inode: &mut Ext4InodeRef,
|
|
|
+ ) -> Result<usize> {
|
|
|
+ let mut is_goal = false;
|
|
|
+
|
|
|
+ let mut data: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
|
|
|
+ let ext4_blk = Ext4Block {
|
|
|
+ logical_block_id: 0,
|
|
|
+ disk_block_id: 0,
|
|
|
+ block_data: &mut data,
|
|
|
+ dirty: true,
|
|
|
+ };
|
|
|
+ let de = Ext4DirEntry::default();
|
|
|
+ let mut dir_search_result = Ext4DirSearchResult::new(ext4_blk, de);
|
|
|
+
|
|
|
+ file.flags = iflags;
|
|
|
+
|
|
|
+ // load root inode
|
|
|
+ let root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
|
|
|
+
|
|
|
+ // if !parent_inode.is_none() {
|
|
|
+ // parent_inode.unwrap().inode_num = root_inode_ref.inode_num;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // search dir
|
|
|
+ let mut search_parent = root_inode_ref;
|
|
|
+ let mut search_path = ext4_path_skip(&path, ".");
|
|
|
+ let mut len;
|
|
|
+ loop {
|
|
|
+ search_path = ext4_path_skip(search_path, "/");
|
|
|
+ len = ext4_path_check(search_path, &mut is_goal);
|
|
|
+
|
|
|
+ let r = ext4_dir_find_entry(
|
|
|
+ &mut search_parent,
|
|
|
+ &search_path[..len as usize],
|
|
|
+ len as u32,
|
|
|
+ &mut dir_search_result,
|
|
|
+ );
|
|
|
+
|
|
|
+ // log::info!("dir_search_result.dentry {:?} r {:?}", dir_search_result.dentry, r);
|
|
|
+ if r != EOK {
|
|
|
+ // ext4_dir_destroy_result(&mut root_inode_ref, &mut dir_search_result);
|
|
|
+
|
|
|
+ if r != ENOENT {
|
|
|
+ // dir search failed with error other than ENOENT
|
|
|
+ return_errno_with_message!(Errnum::ENOTSUP, "dir search failed");
|
|
|
+ }
|
|
|
+
|
|
|
+ if !((iflags & O_CREAT) != 0) {
|
|
|
+ return_errno_with_message!(Errnum::ENOENT, "file not found");
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut child_inode_ref = Ext4InodeRef::new(self.self_ref.clone());
|
|
|
+
|
|
|
+ let r = if is_goal {
|
|
|
+ ext4_fs_alloc_inode(&mut child_inode_ref, ftype)
|
|
|
+ } else {
|
|
|
+ ext4_fs_alloc_inode(&mut child_inode_ref, DirEntryType::EXT4_DE_DIR.bits())
|
|
|
+ };
|
|
|
+
|
|
|
+ if r != EOK {
|
|
|
+ return_errno_with_message!(Errnum::EALLOCFIAL, "alloc inode fail");
|
|
|
+ // break;
|
|
|
+ }
|
|
|
+
|
|
|
+ ext4_fs_inode_blocks_init(&mut child_inode_ref);
|
|
|
+
|
|
|
+ let r = ext4_link(
|
|
|
+ &mut search_parent,
|
|
|
+ &mut child_inode_ref,
|
|
|
+ &search_path[..len as usize],
|
|
|
+ len as u32,
|
|
|
+ );
|
|
|
+
|
|
|
+ if r != EOK {
|
|
|
+ /*Fail. Free new inode.*/
|
|
|
+ return_errno_with_message!(Errnum::ELINKFIAL, "link fail");
|
|
|
+ }
|
|
|
+
|
|
|
+ ext4_fs_put_inode_ref_csum(&mut search_parent);
|
|
|
+ ext4_fs_put_inode_ref_csum(&mut child_inode_ref);
|
|
|
+ ext4_fs_put_inode_ref_csum(parent_inode);
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let _name = get_name(
|
|
|
+ dir_search_result.dentry.name,
|
|
|
+ dir_search_result.dentry.name_len as usize,
|
|
|
+ )
|
|
|
+ .unwrap();
|
|
|
+ // log::info!("find de name{:?} de inode {:x?}", name, dir_search_result.dentry.inode);
|
|
|
+
|
|
|
+ if is_goal {
|
|
|
+ file.inode = dir_search_result.dentry.inode;
|
|
|
+ return Ok(EOK);
|
|
|
+ } else {
|
|
|
+ search_parent = Ext4InodeRef::get_inode_ref(
|
|
|
+ self.self_ref.clone(),
|
|
|
+ dir_search_result.dentry.inode,
|
|
|
+ );
|
|
|
+ search_path = &search_path[len..];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn ext4_file_read(&self, ext4_file: &mut Ext4File) -> Vec<u8> {
|
|
|
+ // 创建一个空的向量,用于存储文件的内容
|
|
|
+ let mut file_data: Vec<u8> = Vec::new();
|
|
|
+
|
|
|
+ // 创建一个空的向量,用于存储文件的所有extent信息
|
|
|
+ let mut extents: Vec<Ext4Extent> = Vec::new();
|
|
|
+
|
|
|
+ let inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), ext4_file.inode);
|
|
|
+
|
|
|
+ ext4_find_all_extent(&inode_ref, &mut extents);
|
|
|
+
|
|
|
+ // 遍历extents向量,对每个extent,计算它的物理块号,然后调用read_block函数来读取数据块,并将结果追加到file_data向量中
|
|
|
+ for extent in extents {
|
|
|
+ // 获取extent的起始块号、块数和逻辑块号
|
|
|
+ let start_block = extent.start_lo as u64 | ((extent.start_hi as u64) << 32);
|
|
|
+ let block_count = extent.block_count as u64;
|
|
|
+ let logical_block = extent.first_block as u64;
|
|
|
+ // 计算extent的物理块号
|
|
|
+ let physical_block = start_block + logical_block;
|
|
|
+ // 从file中读取extent的所有数据块,并将结果追加到file_data向量中
|
|
|
+ for i in 0..block_count {
|
|
|
+ let block_num = physical_block + i;
|
|
|
+ let block_data = inode_ref
|
|
|
+ .fs()
|
|
|
+ .block_device
|
|
|
+ .read_offset(block_num as usize * BLOCK_SIZE);
|
|
|
+ file_data.extend(block_data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ file_data
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn ext4_file_write(&self, ext4_file: &mut Ext4File, data: &[u8], size: usize) {
|
|
|
+ let super_block_data = self.block_device.read_offset(BASE_OFFSET);
|
|
|
+ let super_block = Ext4Superblock::try_from(super_block_data).unwrap();
|
|
|
+ let mut inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), ext4_file.inode);
|
|
|
+ let block_size = super_block.block_size() as usize;
|
|
|
+ let iblock_last = ext4_file.fpos as usize + size / block_size;
|
|
|
+ let mut iblk_idx = ext4_file.fpos as usize / block_size;
|
|
|
+ let ifile_blocks = ext4_file.fsize as usize + block_size - 1 / block_size;
|
|
|
+
|
|
|
+ let mut fblk = 0;
|
|
|
+ let mut fblock_start = 0;
|
|
|
+ let mut fblock_count = 0;
|
|
|
+
|
|
|
+ let mut size = size;
|
|
|
+ while size >= block_size {
|
|
|
+ while iblk_idx < iblock_last {
|
|
|
+ if iblk_idx < ifile_blocks {
|
|
|
+ ext4_fs_append_inode_dblk(&mut inode_ref, &mut (iblk_idx as u32), &mut fblk);
|
|
|
+ }
|
|
|
+
|
|
|
+ iblk_idx += 1;
|
|
|
+
|
|
|
+ if fblock_start == 0 {
|
|
|
+ fblock_start = fblk;
|
|
|
+ }
|
|
|
+ fblock_count += 1;
|
|
|
+ }
|
|
|
+ size -= block_size;
|
|
|
+ }
|
|
|
+
|
|
|
+ for i in 0..fblock_count {
|
|
|
+ let idx = i * BLOCK_SIZE as usize;
|
|
|
+ let offset = (fblock_start as usize + i as usize) * BLOCK_SIZE;
|
|
|
+ self.block_device
|
|
|
+ .write_offset(offset, &data[idx..(idx + BLOCK_SIZE as usize)]);
|
|
|
+ }
|
|
|
+ // inode_ref.inner.inode.size = fblock_count as u32 * BLOCK_SIZE as u32;
|
|
|
+ inode_ref.write_back_inode();
|
|
|
+ // let mut inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), ext4_file.inode);
|
|
|
+ let mut root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
|
|
|
+ root_inode_ref.write_back_inode();
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn ext4_file_remove(&self, _path: &str) -> Result<usize> {
|
|
|
+ return_errno_with_message!(Errnum::ENOTSUP, "not support");
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn ext4_dir_remove(&self, _path: &str) -> Result<usize> {
|
|
|
+ return_errno_with_message!(Errnum::ENOTSUP, "not support");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_fs_put_inode_ref_csum(inode_ref: &mut Ext4InodeRef) {
|
|
|
+ inode_ref.write_back_inode();
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_fs_put_inode_ref(inode_ref: &mut Ext4InodeRef) {
|
|
|
+ inode_ref.write_back_inode_without_csum();
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_link(
|
|
|
+ parent: &mut Ext4InodeRef,
|
|
|
+ child: &mut Ext4InodeRef,
|
|
|
+ name: &str,
|
|
|
+ name_len: u32,
|
|
|
+) -> usize {
|
|
|
+ // log::info!("link parent inode {:x?} child inode {:x?} name {:?}", parent.inode_num, child.inode_num, name);
|
|
|
+ /* Add entry to parent directory */
|
|
|
+ let _r = ext4_dir_add_entry(parent, child, name, name_len);
|
|
|
+
|
|
|
+ /* Fill new dir -> add '.' and '..' entries.
|
|
|
+ * Also newly allocated inode should have 0 link count.
|
|
|
+ */
|
|
|
+ let mut is_dir = false;
|
|
|
+ if child.inner.inode.mode & EXT4_INODE_MODE_TYPE_MASK as u16 == EXT4_INODE_MODE_DIRECTORY as u16
|
|
|
+ {
|
|
|
+ is_dir = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if is_dir {
|
|
|
+ // add '.' and '..' entries
|
|
|
+ let fs = child.fs().self_ref.clone();
|
|
|
+ let mut child_inode_ref = Ext4InodeRef::new(fs);
|
|
|
+ child_inode_ref.inode_num = child.inode_num;
|
|
|
+ child_inode_ref.inner.inode = child.inner.inode.clone();
|
|
|
+
|
|
|
+ let _r = ext4_dir_add_entry(&mut child_inode_ref, child, ".", 1);
|
|
|
+ child.inner.inode.size = child_inode_ref.inner.inode.size;
|
|
|
+ child.inner.inode.block = child_inode_ref.inner.inode.block;
|
|
|
+ let _r = ext4_dir_add_entry(&mut child_inode_ref, parent, "..", 2);
|
|
|
+
|
|
|
+ child.inner.inode.links_count = 2;
|
|
|
+ parent.inner.inode.links_count += 1;
|
|
|
+
|
|
|
+ return EOK;
|
|
|
+ }
|
|
|
+
|
|
|
+ child.inner.inode.links_count += 1;
|
|
|
+ EOK
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_dir_add_entry(
|
|
|
+ parent: &mut Ext4InodeRef,
|
|
|
+ child: &mut Ext4InodeRef,
|
|
|
+ path: &str,
|
|
|
+ len: u32,
|
|
|
+) -> usize {
|
|
|
+ let mut iblock = 0;
|
|
|
+ let block_size = parent.fs().super_block.block_size();
|
|
|
+ let inode_size = parent.inner.inode.ext4_inode_get_size();
|
|
|
+ // let inode_size = parent.fs().super_block.inode_size_file(&parent.inner.inode);
|
|
|
+ let total_blocks = inode_size as u32 / block_size;
|
|
|
+
|
|
|
+ let mut fblock: ext4_fsblk_t = 0;
|
|
|
+
|
|
|
+ // log::info!("ext4_dir_add_entry parent inode {:x?} inode_size {:x?}", parent.inode_num, inode_size);
|
|
|
+ while iblock < total_blocks {
|
|
|
+ ext4_fs_get_inode_dblk_idx(parent, &mut iblock, &mut fblock, false);
|
|
|
+
|
|
|
+ // load_block
|
|
|
+ let mut data = parent
|
|
|
+ .fs()
|
|
|
+ .block_device
|
|
|
+ .read_offset(fblock as usize * BLOCK_SIZE);
|
|
|
+ let mut ext4_block = Ext4Block {
|
|
|
+ logical_block_id: iblock,
|
|
|
+ disk_block_id: fblock,
|
|
|
+ block_data: &mut data,
|
|
|
+ dirty: false,
|
|
|
+ };
|
|
|
+
|
|
|
+ let r = ext4_dir_try_insert_entry(parent, &mut ext4_block, child, path, len);
|
|
|
+
|
|
|
+ if r == EOK {
|
|
|
+ return EOK;
|
|
|
+ // break;
|
|
|
+ }
|
|
|
+ let mut data: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
|
|
|
+ let ext4_blk = Ext4Block {
|
|
|
+ logical_block_id: 0,
|
|
|
+ disk_block_id: 0,
|
|
|
+ block_data: &mut data,
|
|
|
+ dirty: true,
|
|
|
+ };
|
|
|
+ let de = Ext4DirEntry::default();
|
|
|
+ let mut dir_search_result = Ext4DirSearchResult::new(ext4_blk, de);
|
|
|
+
|
|
|
+ let r = ext4_dir_find_in_block(&mut ext4_block, path, len, &mut dir_search_result);
|
|
|
+
|
|
|
+ if r {
|
|
|
+ return EOK;
|
|
|
+ }
|
|
|
+
|
|
|
+ iblock += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* No free block found - needed to allocate next data block */
|
|
|
+ iblock = 0;
|
|
|
+ fblock = 0;
|
|
|
+
|
|
|
+ ext4_fs_append_inode_dblk(parent, &mut (iblock as u32), &mut fblock);
|
|
|
+
|
|
|
+ /* Load new block */
|
|
|
+ let block_device = parent.fs().block_device.clone();
|
|
|
+ let mut data = block_device.read_offset(fblock as usize * BLOCK_SIZE);
|
|
|
+ let mut ext4_block = Ext4Block {
|
|
|
+ logical_block_id: iblock,
|
|
|
+ disk_block_id: fblock,
|
|
|
+ block_data: &mut data,
|
|
|
+ dirty: false,
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut new_entry = Ext4DirEntry::default();
|
|
|
+ let el = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
|
|
|
+ ext4_dir_write_entry(&mut new_entry, el as u16, &child, path, len);
|
|
|
+
|
|
|
+ copy_dir_entry_to_array(&new_entry, &mut ext4_block.block_data, 0);
|
|
|
+
|
|
|
+ // init tail
|
|
|
+ let ptr = ext4_block.block_data.as_mut_ptr();
|
|
|
+ let mut tail = unsafe {
|
|
|
+ *(ptr.add(BLOCK_SIZE - core::mem::size_of::<Ext4DirEntryTail>()) as *mut Ext4DirEntryTail)
|
|
|
+ };
|
|
|
+ tail.rec_len = size_of::<Ext4DirEntryTail>() as u16;
|
|
|
+ tail.reserved_ft = 0xDE;
|
|
|
+ tail.reserved_zero1 = 0;
|
|
|
+ tail.reserved_zero2 = 0;
|
|
|
+
|
|
|
+ tail.ext4_dir_set_csum(
|
|
|
+ &parent.fs().super_block,
|
|
|
+ &new_entry,
|
|
|
+ &ext4_block.block_data[..],
|
|
|
+ );
|
|
|
+
|
|
|
+ let tail_offset = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
|
|
|
+ copy_diren_tail_to_array(&tail, &mut ext4_block.block_data, tail_offset);
|
|
|
+
|
|
|
+ tail.ext4_dir_set_csum(
|
|
|
+ &parent.fs().super_block,
|
|
|
+ &new_entry,
|
|
|
+ &ext4_block.block_data[..],
|
|
|
+ );
|
|
|
+
|
|
|
+ ext4_block.sync_blk_to_disk(block_device.clone());
|
|
|
+
|
|
|
+ // struct ext4_block b;
|
|
|
+
|
|
|
+ EOK
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_dir_try_insert_entry(
|
|
|
+ parent: &Ext4InodeRef,
|
|
|
+ dst_blk: &mut Ext4Block,
|
|
|
+ child: &mut Ext4InodeRef,
|
|
|
+ name: &str,
|
|
|
+ name_len: u32,
|
|
|
+) -> usize {
|
|
|
+ let mut required_len = core::mem::size_of::<Ext4DirEntry>() + name_len as usize;
|
|
|
+
|
|
|
+ if required_len % 4 != 0 {
|
|
|
+ required_len += 4 - required_len % 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut offset = 0;
|
|
|
+
|
|
|
+ while offset < dst_blk.block_data.len() {
|
|
|
+ let mut de = Ext4DirEntry::try_from(&dst_blk.block_data[offset..]).unwrap();
|
|
|
+ if de.inode == 0 {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ let inode = de.inode;
|
|
|
+ let rec_len = de.entry_len;
|
|
|
+
|
|
|
+ // 如果是有效的目录项,尝试分割它
|
|
|
+ if inode != 0 {
|
|
|
+ let used_len = de.name_len as usize;
|
|
|
+ let mut sz = core::mem::size_of::<Ext4FakeDirEntry>() + used_len as usize;
|
|
|
+
|
|
|
+ if used_len % 4 != 0 {
|
|
|
+ sz += 4 - used_len % 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ let free_space = rec_len as usize - sz;
|
|
|
+
|
|
|
+ // 如果有足够的空闲空间
|
|
|
+ if free_space >= required_len {
|
|
|
+ let mut new_entry = Ext4DirEntry::default();
|
|
|
+
|
|
|
+ de.entry_len = sz as u16;
|
|
|
+ ext4_dir_write_entry(&mut new_entry, free_space as u16, &child, name, name_len);
|
|
|
+
|
|
|
+ // update parent new_de to blk_data
|
|
|
+ copy_dir_entry_to_array(&de, &mut dst_blk.block_data, offset);
|
|
|
+ copy_dir_entry_to_array(&new_entry, &mut dst_blk.block_data, offset + sz);
|
|
|
+
|
|
|
+ // set tail csum
|
|
|
+ let mut tail = Ext4DirEntryTail::from(&mut dst_blk.block_data, BLOCK_SIZE).unwrap();
|
|
|
+ let block_device = parent.fs().block_device.clone();
|
|
|
+ tail.ext4_dir_set_csum(
|
|
|
+ &parent.fs().super_block,
|
|
|
+ &de,
|
|
|
+ &dst_blk.block_data[offset..],
|
|
|
+ );
|
|
|
+
|
|
|
+ let parent_de = Ext4DirEntry::try_from(&dst_blk.block_data[..]).unwrap();
|
|
|
+ tail.ext4_dir_set_csum(
|
|
|
+ &parent.fs().super_block,
|
|
|
+ &parent_de,
|
|
|
+ &dst_blk.block_data[..],
|
|
|
+ );
|
|
|
+
|
|
|
+ let tail_offset = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
|
|
|
+ copy_diren_tail_to_array(&tail, &mut dst_blk.block_data, tail_offset);
|
|
|
+
|
|
|
+ // sync to disk
|
|
|
+ dst_blk.sync_blk_to_disk(block_device.clone());
|
|
|
+
|
|
|
+ return EOK;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ offset = offset + de.entry_len as usize;
|
|
|
+ }
|
|
|
+
|
|
|
+ ENOSPC
|
|
|
+}
|
|
|
+
|
|
|
+// 写入一个ext4目录项
|
|
|
+pub fn ext4_dir_write_entry(
|
|
|
+ en: &mut Ext4DirEntry,
|
|
|
+ entry_len: u16,
|
|
|
+ child: &Ext4InodeRef,
|
|
|
+ name: &str,
|
|
|
+ name_len: u32,
|
|
|
+) {
|
|
|
+ let file_type = (child.inner.inode.mode & EXT4_INODE_MODE_TYPE_MASK) as usize;
|
|
|
+
|
|
|
+ // 设置目录项的类型
|
|
|
+ match file_type {
|
|
|
+ EXT4_INODE_MODE_FILE => en.inner.inode_type = DirEntryType::EXT4_DE_REG_FILE.bits(),
|
|
|
+ EXT4_INODE_MODE_DIRECTORY => en.inner.inode_type = DirEntryType::EXT4_DE_DIR.bits(),
|
|
|
+ EXT4_INODE_MODE_CHARDEV => en.inner.inode_type = DirEntryType::EXT4_DE_CHRDEV.bits(),
|
|
|
+ EXT4_INODE_MODE_BLOCKDEV => en.inner.inode_type = DirEntryType::EXT4_DE_BLKDEV.bits(),
|
|
|
+ EXT4_INODE_MODE_FIFO => en.inner.inode_type = DirEntryType::EXT4_DE_FIFO.bits(),
|
|
|
+ EXT4_INODE_MODE_SOCKET => en.inner.inode_type = DirEntryType::EXT4_DE_SOCK.bits(),
|
|
|
+ EXT4_INODE_MODE_SOFTLINK => en.inner.inode_type = DirEntryType::EXT4_DE_SYMLINK.bits(),
|
|
|
+ _ => log::info!("{}: unknown type", file_type),
|
|
|
+ }
|
|
|
+
|
|
|
+ en.inode = child.inode_num;
|
|
|
+ en.entry_len = entry_len;
|
|
|
+ en.name_len = name_len as u8;
|
|
|
+
|
|
|
+ let en_name_ptr = en.name.as_mut_ptr();
|
|
|
+ unsafe {
|
|
|
+ en_name_ptr.copy_from_nonoverlapping(name.as_ptr(), name_len as usize);
|
|
|
+ }
|
|
|
+ let _name = get_name(en.name, en.name_len as usize).unwrap();
|
|
|
+ // log::info!("ext4_dir_write_entry name {:?}", name);
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_fs_append_inode_dblk(
|
|
|
+ inode_ref: &mut Ext4InodeRef,
|
|
|
+ iblock: &mut ext4_lblk_t,
|
|
|
+ fblock: &mut ext4_fsblk_t,
|
|
|
+) {
|
|
|
+ let inode_size = inode_ref.inner.inode.ext4_inode_get_size();
|
|
|
+ let block_size = BLOCK_SIZE as u64;
|
|
|
+
|
|
|
+ *iblock = ((inode_size + block_size - 1) / block_size) as u32;
|
|
|
+
|
|
|
+ let mut current_fsblk: ext4_fsblk_t = 0;
|
|
|
+ ext4_extent_get_blocks(inode_ref, *iblock, 1, &mut current_fsblk, true, &mut 0);
|
|
|
+
|
|
|
+ let current_block = current_fsblk;
|
|
|
+ *fblock = current_block;
|
|
|
+
|
|
|
+ inode_ref
|
|
|
+ .inner
|
|
|
+ .inode
|
|
|
+ .ext4_inode_set_size(inode_size + BLOCK_SIZE as u64);
|
|
|
+
|
|
|
+ inode_ref.write_back_inode();
|
|
|
+
|
|
|
+ // let mut inode_ref = Ext4InodeRef::get_inode_ref(inode_ref.fs().self_ref.clone(), inode_ref.inode_num);
|
|
|
+
|
|
|
+ // log::info!("ext4_fs_append_inode_dblk inode {:x?} inode_size {:x?}", inode_ref.inode_num, inode_ref.inner.inode.size);
|
|
|
+ // log::info!("fblock {:x?}", fblock);
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_fs_inode_blocks_init(inode_ref: &mut Ext4InodeRef) {
|
|
|
+ // log::info!(
|
|
|
+ // "ext4_fs_inode_blocks_init mode {:x?}",
|
|
|
+ // inode_ref.inner.inode.mode
|
|
|
+ // );
|
|
|
+
|
|
|
+ let inode = &mut inode_ref.inner.inode;
|
|
|
+
|
|
|
+ let mode = inode.mode;
|
|
|
+
|
|
|
+ let inode_type = InodeMode::from_bits(mode & EXT4_INODE_MODE_TYPE_MASK as u16).unwrap();
|
|
|
+
|
|
|
+ match inode_type {
|
|
|
+ InodeMode::S_IFDIR => {}
|
|
|
+ InodeMode::S_IFREG => {}
|
|
|
+ /* Reset blocks array. For inode which is not directory or file, just
|
|
|
+ * fill in blocks with 0 */
|
|
|
+ _ => {
|
|
|
+ log::info!("inode_type {:?}", inode_type);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialize extents */
|
|
|
+ inode.ext4_inode_set_flags(EXT4_INODE_FLAG_EXTENTS as u32);
|
|
|
+
|
|
|
+ /* Initialize extent root header */
|
|
|
+ inode.ext4_extent_tree_init();
|
|
|
+ // log::info!("inode iblock {:x?}", inode.block);
|
|
|
+
|
|
|
+ // inode_ref.dirty = true;
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_fs_alloc_inode(child_inode_ref: &mut Ext4InodeRef, filetype: u8) -> usize {
|
|
|
+ let mut is_dir = false;
|
|
|
+
|
|
|
+ let inode_size = child_inode_ref.fs().super_block.inode_size();
|
|
|
+ let extra_size = child_inode_ref.fs().super_block.extra_size();
|
|
|
+
|
|
|
+ if filetype == DirEntryType::EXT4_DE_DIR.bits() {
|
|
|
+ is_dir = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut index = 0;
|
|
|
+ let _rc = ext4_ialloc_alloc_inode(child_inode_ref.fs(), &mut index, is_dir);
|
|
|
+
|
|
|
+ child_inode_ref.inode_num = index;
|
|
|
+
|
|
|
+ let inode = &mut child_inode_ref.inner.inode;
|
|
|
+
|
|
|
+ /* Initialize i-node */
|
|
|
+
|
|
|
+ let mode = if is_dir {
|
|
|
+ 0o777 | EXT4_INODE_MODE_DIRECTORY as u16
|
|
|
+ } else if filetype == 0x7 {
|
|
|
+ 0o777 | EXT4_INODE_MODE_SOFTLINK as u16
|
|
|
+ } else {
|
|
|
+ let t = ext4_fs_correspond_inode_mode(filetype);
|
|
|
+ // log::info!("ext4_fs_correspond_inode_mode {:x?}", ext4_fs_correspond_inode_mode(filetype));
|
|
|
+ 0o666 | t as u16
|
|
|
+ };
|
|
|
+
|
|
|
+ inode.ext4_inode_set_mode(mode);
|
|
|
+ inode.ext4_inode_set_links_cnt(0);
|
|
|
+ inode.ext4_inode_set_uid(0);
|
|
|
+ inode.ext4_inode_set_gid(0);
|
|
|
+ inode.ext4_inode_set_size(0);
|
|
|
+ inode.ext4_inode_set_access_time(0);
|
|
|
+ inode.ext4_inode_set_change_inode_time(0);
|
|
|
+ inode.ext4_inode_set_modif_time(0);
|
|
|
+ inode.ext4_inode_set_del_time(0);
|
|
|
+ inode.ext4_inode_set_flags(0);
|
|
|
+ inode.ext4_inode_set_generation(0);
|
|
|
+
|
|
|
+ if inode_size > EXT4_GOOD_OLD_INODE_SIZE {
|
|
|
+ let extra_size = extra_size;
|
|
|
+ inode.ext4_inode_set_extra_isize(extra_size);
|
|
|
+ }
|
|
|
+
|
|
|
+ EOK
|
|
|
+}
|
|
|
+pub fn ext4_dir_destroy_result(_inode_ref: &mut Ext4InodeRef, result: &mut Ext4DirSearchResult) {
|
|
|
+ result.block.logical_block_id = 0;
|
|
|
+ result.block.disk_block_id = 0;
|
|
|
+ result.dentry = Ext4DirEntry::default();
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_dir_find_entry(
|
|
|
+ parent: &mut Ext4InodeRef,
|
|
|
+ name: &str,
|
|
|
+ name_len: u32,
|
|
|
+ result: &mut Ext4DirSearchResult,
|
|
|
+) -> usize {
|
|
|
+ // log::info!("ext4_dir_find_entry parent {:x?} {:?}",parent.inode_num, name);
|
|
|
+ let mut iblock = 0;
|
|
|
+ let mut fblock: ext4_fsblk_t = 0;
|
|
|
+
|
|
|
+ let inode_size: u32 = parent.inner.inode.size;
|
|
|
+ let total_blocks: u32 = inode_size / BLOCK_SIZE as u32;
|
|
|
+
|
|
|
+ while iblock < total_blocks {
|
|
|
+ ext4_fs_get_inode_dblk_idx(parent, &mut iblock, &mut fblock, false);
|
|
|
+
|
|
|
+ // load_block
|
|
|
+ let mut data = parent
|
|
|
+ .fs()
|
|
|
+ .block_device
|
|
|
+ .read_offset(fblock as usize * BLOCK_SIZE);
|
|
|
+ let mut ext4_block = Ext4Block {
|
|
|
+ logical_block_id: iblock,
|
|
|
+ disk_block_id: fblock,
|
|
|
+ block_data: &mut data,
|
|
|
+ dirty: false,
|
|
|
+ };
|
|
|
+
|
|
|
+ let r = ext4_dir_find_in_block(&mut ext4_block, name, name_len, result);
|
|
|
+ if r {
|
|
|
+ return EOK;
|
|
|
+ }
|
|
|
+
|
|
|
+ iblock += 1
|
|
|
+ }
|
|
|
+
|
|
|
+ ENOENT
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_extent_get_blocks(
|
|
|
+ inode_ref: &mut Ext4InodeRef,
|
|
|
+ iblock: ext4_lblk_t,
|
|
|
+ max_blocks: u32,
|
|
|
+ result: &mut ext4_fsblk_t,
|
|
|
+ create: bool,
|
|
|
+ blocks_count: &mut u32,
|
|
|
+) {
|
|
|
+ *result = 0;
|
|
|
+ *blocks_count = 0;
|
|
|
+
|
|
|
+ let mut path: Option<Vec<Ext4ExtentPath>> = None;
|
|
|
+ let err = ext4_find_extent(inode_ref, iblock, &mut path, 0);
|
|
|
+
|
|
|
+ let inode = &inode_ref.inner.inode;
|
|
|
+ // 确认ext4_find_extent成功执行
|
|
|
+ if err != EOK {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let depth = unsafe { *ext4_inode_hdr(inode) }.depth as usize;
|
|
|
+ let mut path = path.unwrap();
|
|
|
+
|
|
|
+ if !path[depth].extent.is_null() {
|
|
|
+ let ex = unsafe { *path[depth].extent };
|
|
|
+ let ee_block = ex.first_block;
|
|
|
+ let ee_start = ext4_ext_pblock(&ex);
|
|
|
+ let ee_len = ext4_ext_get_actual_len(&ex);
|
|
|
+
|
|
|
+ if iblock >= ee_block && iblock < ee_block + ee_len as u32 {
|
|
|
+ let allocated = ee_len - (iblock - ee_block) as u16;
|
|
|
+ *blocks_count = allocated as u32;
|
|
|
+
|
|
|
+ if !create || ext4_ext_is_unwritten(&ex) {
|
|
|
+ *result = (iblock - ee_block + ee_start) as u64;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有找到对应的extent,并且create为true,则需要分配和插入新的extent
|
|
|
+ if create {
|
|
|
+ let next = EXT_MAX_BLOCKS;
|
|
|
+
|
|
|
+ let mut allocated = next - iblock;
|
|
|
+ if allocated > max_blocks {
|
|
|
+ allocated = max_blocks;
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut newex: Ext4Extent = Ext4Extent::default();
|
|
|
+
|
|
|
+ let goal = 0;
|
|
|
+
|
|
|
+ let mut alloc_block = 0;
|
|
|
+ ext4_balloc_alloc_block(inode_ref, goal as u64, &mut alloc_block);
|
|
|
+
|
|
|
+ *result = alloc_block;
|
|
|
+
|
|
|
+ // 创建并插入新的extent
|
|
|
+ newex.first_block = iblock;
|
|
|
+ newex.start_lo = alloc_block as u32 & 0xffffffff;
|
|
|
+ newex.start_hi = (((alloc_block as u32) << 31) << 1) as u16;
|
|
|
+ newex.block_count = allocated as u16;
|
|
|
+
|
|
|
+ ext4_ext_insert_extent(inode_ref, &mut path[0], &newex, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext_inode_hdr(inode: &Ext4Inode) -> *const Ext4ExtentHeader {
|
|
|
+ let eh = &inode.block as *const [u32; 15] as *const Ext4ExtentHeader;
|
|
|
+ eh
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext_depth(inode: &Ext4Inode) -> u16 {
|
|
|
+ let header = ext_inode_hdr(inode);
|
|
|
+ unsafe { (*header).depth }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext_last_extent(eh: *const Ext4ExtentHeader) -> *mut Ext4Extent {
|
|
|
+ // 如果头部为空,返回空指针
|
|
|
+ if eh.is_null() {
|
|
|
+ return ptr::null_mut();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取头部的extent数
|
|
|
+ let count = unsafe { (*eh).entries_count };
|
|
|
+
|
|
|
+ // 如果extent数为0,返回空指针
|
|
|
+ if count == 0 {
|
|
|
+ return ptr::null_mut();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取头部中第一个extent的指针
|
|
|
+ let first = ext_first_extent(eh);
|
|
|
+
|
|
|
+ // 返回头部中最后一个extent的指针,即第一个extent的指针加上extent数减一
|
|
|
+ return unsafe { first.add((count - 1) as usize) };
|
|
|
+}
|
|
|
+
|
|
|
+// ext_first_extent函数
|
|
|
+pub fn ext_first_extent(eh: *const Ext4ExtentHeader) -> *mut Ext4Extent {
|
|
|
+ // 如果头部为空,返回空指针
|
|
|
+ if eh.is_null() {
|
|
|
+ return ptr::null_mut();
|
|
|
+ }
|
|
|
+
|
|
|
+ // // 获取头部的extent数
|
|
|
+ // let count = unsafe { (*eh).entries_count };
|
|
|
+
|
|
|
+ // // 如果extent数为0,返回空指针
|
|
|
+ // if count == 0 {
|
|
|
+ // return ptr::null_mut();
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 返回头部中第一个extent的指针,即头部的指针加上头部的大小
|
|
|
+ return unsafe { (eh as *mut u8).add(mem::size_of::<Ext4ExtentHeader>()) as *mut Ext4Extent };
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_pblock(ex: &Ext4Extent) -> u32 {
|
|
|
+ let mut block = ex.start_lo;
|
|
|
+ block |= ((ex.start_hi as u32) << 31) << 1;
|
|
|
+ block
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_is_unwritten(ext: &Ext4Extent) -> bool {
|
|
|
+ // 返回extent是否是未写入的
|
|
|
+ ext.block_count > EXT_INIT_MAX_LEN
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_get_actual_len(ext: &Ext4Extent) -> u16 {
|
|
|
+ // 返回extent的实际长度
|
|
|
+ if ext.block_count <= EXT_INIT_MAX_LEN {
|
|
|
+ ext.block_count
|
|
|
+ } else {
|
|
|
+ ext.block_count - EXT_INIT_MAX_LEN
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_mark_unwritten(ext: *mut Ext4Extent) {
|
|
|
+ unsafe {
|
|
|
+ (*ext).block_count |= EXT_INIT_MAX_LEN;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_can_append(ex1: &Ext4Extent, ex2: &Ext4Extent) -> bool {
|
|
|
+ // 检查是否可以将ex2合并到ex1的后面
|
|
|
+ // log::info!(
|
|
|
+ // "\n\npblock1={:x?} pblock2={:x?}",
|
|
|
+ // ext4_ext_pblock(ex1),
|
|
|
+ // ext4_ext_pblock(ex2)
|
|
|
+ // );
|
|
|
+ // log::info!(
|
|
|
+ // "len1={:x?} len2={:x?}",
|
|
|
+ // ext4_ext_get_actual_len(ex1),
|
|
|
+ // ext4_ext_get_actual_len(ex2)
|
|
|
+ // );
|
|
|
+ // log::info!(
|
|
|
+ // "first_block1={:x?} first_block2={:x?}",
|
|
|
+ // ex1.first_block, ex2.first_block
|
|
|
+ // );
|
|
|
+ if ext4_ext_pblock(ex1) + ext4_ext_get_actual_len(ex1) as u32 != ext4_ext_pblock(ex2) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ext4_ext_is_unwritten(ex1) {
|
|
|
+ if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_UNWRITTEN_MAX_LEN {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_INIT_MAX_LEN {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查逻辑块号是否连续
|
|
|
+ if ex1.first_block + ext4_ext_get_actual_len(ex1) as u32 != ex2.first_block {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_can_prepend(ex1: &Ext4Extent, ex2: &Ext4Extent) -> bool {
|
|
|
+ // 检查是否可以将ex2合并到ex1的前面
|
|
|
+ if ext4_ext_pblock(ex2) + ext4_ext_get_actual_len(ex2) as u32 != ext4_ext_pblock(ex1) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if ext4_ext_is_unwritten(ex1) {
|
|
|
+ if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_UNWRITTEN_MAX_LEN {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_INIT_MAX_LEN {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查逻辑块号是否连续
|
|
|
+ if ex2.first_block + ext4_ext_get_actual_len(ex2) as u32 != ex1.first_block {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果以上条件都满足,返回true
|
|
|
+ true
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_insert_extent(
|
|
|
+ inode_ref: &mut Ext4InodeRef,
|
|
|
+ path: &mut Ext4ExtentPath,
|
|
|
+ newext: &Ext4Extent,
|
|
|
+ flags: i32,
|
|
|
+) {
|
|
|
+ let depth = ext_depth(&inode_ref.inner.inode);
|
|
|
+ let mut need_split = false;
|
|
|
+
|
|
|
+ ext4_ext_insert_leaf(inode_ref, path, depth, newext, flags, &mut need_split);
|
|
|
+
|
|
|
+ inode_ref.write_back_inode_without_csum();
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ext_insert_leaf(
|
|
|
+ _inode_ref: &mut Ext4InodeRef,
|
|
|
+ path: &mut Ext4ExtentPath,
|
|
|
+ _depth: u16,
|
|
|
+ newext: &Ext4Extent,
|
|
|
+ _flags: i32,
|
|
|
+ need_split: &mut bool,
|
|
|
+) -> usize {
|
|
|
+ let eh = path.header;
|
|
|
+ let ex = path.extent;
|
|
|
+ let _last_ex = ext_last_extent(eh);
|
|
|
+
|
|
|
+ let mut diskblock = newext.start_lo;
|
|
|
+ diskblock |= ((newext.start_hi as u32) << 31) << 1;
|
|
|
+
|
|
|
+ unsafe {
|
|
|
+ if !ex.is_null() && ext4_ext_can_append(&*(path.extent), newext) {
|
|
|
+ if ext4_ext_is_unwritten(&*(path.extent)) {
|
|
|
+ ext4_ext_mark_unwritten((*path).extent);
|
|
|
+ }
|
|
|
+ (*(path.extent)).block_count =
|
|
|
+ ext4_ext_get_actual_len(&*(path.extent)) + ext4_ext_get_actual_len(&newext);
|
|
|
+ (*path).p_block = diskblock as u64;
|
|
|
+ return EOK;
|
|
|
+ }
|
|
|
+
|
|
|
+ if !ex.is_null() && ext4_ext_can_prepend(&*(path.extent), newext) {
|
|
|
+ (*(path.extent)).block_count =
|
|
|
+ ext4_ext_get_actual_len(&*(path.extent)) + ext4_ext_get_actual_len(&newext);
|
|
|
+ (*path).p_block = diskblock as u64;
|
|
|
+
|
|
|
+ if ext4_ext_is_unwritten(&*(path.extent)) {
|
|
|
+ ext4_ext_mark_unwritten((*path).extent);
|
|
|
+ }
|
|
|
+ return EOK;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ex.is_null() {
|
|
|
+ let first_extent = ext_first_extent(eh);
|
|
|
+ path.extent = first_extent;
|
|
|
+ // log::info!("first_extent {:x?}", unsafe{*first_extent});
|
|
|
+ unsafe {
|
|
|
+ if (*eh).entries_count == (*eh).max_entries_count {
|
|
|
+ *need_split = true;
|
|
|
+ return EIO;
|
|
|
+ } else {
|
|
|
+ *(path.extent) = *newext;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ unsafe {
|
|
|
+ if (*eh).entries_count == (*eh).max_entries_count {
|
|
|
+ *need_split = true;
|
|
|
+ *(path.extent) = *newext;
|
|
|
+
|
|
|
+ (*path).p_block = diskblock as u64;
|
|
|
+ return EIO;
|
|
|
+ } else {
|
|
|
+ if ex.is_null() {
|
|
|
+ let first_extent = ext_first_extent(eh);
|
|
|
+ path.extent = first_extent;
|
|
|
+ *(path.extent) = *newext;
|
|
|
+ } else if newext.first_block > (*(path.extent)).first_block {
|
|
|
+ // insert after
|
|
|
+ let next_extent = ex.add(1);
|
|
|
+ path.extent = next_extent;
|
|
|
+ } else {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ *(path.extent) = *newext;
|
|
|
+ (*eh).entries_count += 1;
|
|
|
+ }
|
|
|
+ unsafe {
|
|
|
+ *(path.extent) = *newext;
|
|
|
+ }
|
|
|
+
|
|
|
+ return EOK;
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_find_all_extent(inode_ref: &Ext4InodeRef, extents: &mut Vec<Ext4Extent>) {
|
|
|
+ let extent_header = Ext4ExtentHeader::try_from(&inode_ref.inner.inode.block[..2]).unwrap();
|
|
|
+ // log::info!("extent_header {:x?}", extent_header);
|
|
|
+ let data = &inode_ref.inner.inode.block;
|
|
|
+ let depth = extent_header.depth;
|
|
|
+
|
|
|
+ ext4_add_extent(inode_ref, depth, data, extents, true);
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_add_extent(
|
|
|
+ inode_ref: &Ext4InodeRef,
|
|
|
+ depth: u16,
|
|
|
+ data: &[u32],
|
|
|
+ extents: &mut Vec<Ext4Extent>,
|
|
|
+ _first_level: bool,
|
|
|
+) {
|
|
|
+ let extent_header = Ext4ExtentHeader::try_from(data).unwrap();
|
|
|
+ let extent_entries = extent_header.entries_count;
|
|
|
+ // log::info!("extent_entries {:x?}", extent_entries);
|
|
|
+ if depth == 0 {
|
|
|
+ for en in 0..extent_entries {
|
|
|
+ let idx = (3 + en * 3) as usize;
|
|
|
+ let extent = Ext4Extent::try_from(&data[idx..]).unwrap();
|
|
|
+
|
|
|
+ extents.push(extent)
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for en in 0..extent_entries {
|
|
|
+ let idx = (3 + en * 3) as usize;
|
|
|
+ if idx == 12 {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ let extent_index = Ext4ExtentIndex::try_from(&data[idx..]).unwrap();
|
|
|
+ let ei_leaf_lo = extent_index.leaf_lo;
|
|
|
+ let ei_leaf_hi = extent_index.leaf_hi;
|
|
|
+ let mut block = ei_leaf_lo;
|
|
|
+ block |= ((ei_leaf_hi as u32) << 31) << 1;
|
|
|
+ let data = inode_ref
|
|
|
+ .fs()
|
|
|
+ .block_device
|
|
|
+ .read_offset(block as usize * BLOCK_SIZE);
|
|
|
+ let data: Vec<u32> = unsafe { core::mem::transmute(data) };
|
|
|
+ ext4_add_extent(inode_ref, depth - 1, &data, extents, false);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_idx_pblock(idx: *mut Ext4ExtentIndex) -> u64 {
|
|
|
+ // 如果索引为空,返回0
|
|
|
+ if idx.is_null() {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ // 获取索引的低32位物理块号
|
|
|
+ let mut pblock = unsafe { (*idx).leaf_lo } as u64;
|
|
|
+
|
|
|
+ // 如果支持64位物理块号,获取索引的高16位物理块号
|
|
|
+ let pblock_hi = unsafe { (*idx).leaf_hi };
|
|
|
+ pblock |= ((pblock_hi as ext4_fsblk_t) << 32) as u64;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 返回索引的物理块号
|
|
|
+ return pblock;
|
|
|
+}
|
|
|
+
|
|
|
+fn ext4_find_extent(
|
|
|
+ inode_ref: &mut Ext4InodeRef,
|
|
|
+ block: ext4_lblk_t,
|
|
|
+ orig_path: &mut Option<Vec<Ext4ExtentPath>>,
|
|
|
+ _flags: u32,
|
|
|
+) -> usize {
|
|
|
+ let inode = &inode_ref.inner.inode;
|
|
|
+ let mut _eh: &Ext4ExtentHeader;
|
|
|
+ let mut path = orig_path.take(); // Take the path out of the Option, which may replace it with None
|
|
|
+ let depth = unsafe { *ext4_inode_hdr(inode) }.depth;
|
|
|
+
|
|
|
+ let mut ppos = 0;
|
|
|
+ let mut i: u16;
|
|
|
+
|
|
|
+ let eh = &inode.block as *const [u32; 15] as *mut Ext4ExtentHeader;
|
|
|
+
|
|
|
+ if let Some(ref mut p) = path {
|
|
|
+ if depth > p[0].maxdepth {
|
|
|
+ p.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if path.is_none() {
|
|
|
+ let path_depth = depth + 1;
|
|
|
+ path = Some(vec![Ext4ExtentPath::default(); path_depth as usize + 1]);
|
|
|
+ path.as_mut().unwrap()[0].maxdepth = path_depth;
|
|
|
+ }
|
|
|
+
|
|
|
+ let path = path.as_mut().unwrap();
|
|
|
+ path[0].header = eh;
|
|
|
+
|
|
|
+ i = depth;
|
|
|
+ while i > 0 {
|
|
|
+ ext4_ext_binsearch_idx(&mut path[ppos], block);
|
|
|
+ path[ppos].p_block = ext4_idx_pblock(path[ppos].index);
|
|
|
+ path[ppos].depth = i;
|
|
|
+ path[ppos].extent = core::ptr::null_mut();
|
|
|
+
|
|
|
+ i -= 1;
|
|
|
+ ppos += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ path[ppos].depth = i;
|
|
|
+ path[ppos].extent = core::ptr::null_mut();
|
|
|
+ path[ppos].index = core::ptr::null_mut();
|
|
|
+
|
|
|
+ ext4_ext_binsearch(&mut path[ppos], block);
|
|
|
+ if !path[ppos].extent.is_null() {
|
|
|
+ path[ppos].p_block = ext4_ext_pblock(&unsafe { *(path[ppos].extent) }) as u64;
|
|
|
+ }
|
|
|
+
|
|
|
+ *orig_path = Some(path.clone());
|
|
|
+
|
|
|
+ EOK
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_fs_get_inode_dblk_idx(
|
|
|
+ inode_ref: &mut Ext4InodeRef,
|
|
|
+ iblock: &mut ext4_lblk_t,
|
|
|
+ fblock: &mut ext4_fsblk_t,
|
|
|
+ _extent_create: bool,
|
|
|
+) -> usize {
|
|
|
+ let current_block: ext4_fsblk_t;
|
|
|
+ let mut current_fsblk: ext4_fsblk_t = 0;
|
|
|
+
|
|
|
+ let mut blocks_count = 0;
|
|
|
+ ext4_extent_get_blocks(
|
|
|
+ inode_ref,
|
|
|
+ *iblock,
|
|
|
+ 1,
|
|
|
+ &mut current_fsblk,
|
|
|
+ false,
|
|
|
+ &mut blocks_count,
|
|
|
+ );
|
|
|
+
|
|
|
+ current_block = current_fsblk;
|
|
|
+ *fblock = current_block;
|
|
|
+
|
|
|
+ EOK
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_fs_get_inode_dblk_idx_internal(
|
|
|
+ inode_ref: &mut Ext4InodeRef,
|
|
|
+ iblock: &mut ext4_lblk_t,
|
|
|
+ _fblock: &mut ext4_fsblk_t,
|
|
|
+ extent_create: bool,
|
|
|
+ _support_unwritten: bool,
|
|
|
+) {
|
|
|
+ let mut current_fsblk: ext4_fsblk_t = 0;
|
|
|
+
|
|
|
+ let mut blocks_count = 0;
|
|
|
+ ext4_extent_get_blocks(
|
|
|
+ inode_ref,
|
|
|
+ *iblock,
|
|
|
+ 1,
|
|
|
+ &mut current_fsblk,
|
|
|
+ extent_create,
|
|
|
+ &mut blocks_count,
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_dir_find_in_block(
|
|
|
+ block: &Ext4Block,
|
|
|
+ name: &str,
|
|
|
+ name_len: u32,
|
|
|
+ result: &mut Ext4DirSearchResult,
|
|
|
+) -> bool {
|
|
|
+ let mut offset = 0;
|
|
|
+
|
|
|
+ while offset < block.block_data.len() {
|
|
|
+ let de = Ext4DirEntry::try_from(&block.block_data[offset..]).unwrap();
|
|
|
+
|
|
|
+ offset = offset + de.entry_len as usize;
|
|
|
+ if de.inode == 0 {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let s = get_name(de.name, de.name_len as usize);
|
|
|
+
|
|
|
+ if let Ok(s) = s {
|
|
|
+ if name_len == de.name_len as u32 {
|
|
|
+ if name.to_string() == s {
|
|
|
+ result.dentry = de;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ false
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_ialloc_alloc_inode(fs: Arc<Ext4>, index: &mut u32, is_dir: bool) {
|
|
|
+ let mut bgid = fs.last_inode_bg_id;
|
|
|
+ let bg_count = fs.super_block.block_groups_count();
|
|
|
+
|
|
|
+ while bgid <= bg_count {
|
|
|
+ if bgid == bg_count {
|
|
|
+ bgid = 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let block_device = fs.block_device.clone();
|
|
|
+
|
|
|
+ let raw_data = fs.block_device.read_offset(BASE_OFFSET);
|
|
|
+ let mut super_block = Ext4Superblock::try_from(raw_data).unwrap();
|
|
|
+
|
|
|
+ let mut bg =
|
|
|
+ Ext4BlockGroup::load(block_device.clone(), &super_block, bgid as usize).unwrap();
|
|
|
+
|
|
|
+ let mut free_inodes = bg.get_free_inodes_count();
|
|
|
+ let mut used_dirs = bg.get_used_dirs_count(&super_block);
|
|
|
+
|
|
|
+ if free_inodes > 0 {
|
|
|
+ let inode_bitmap_block = bg.get_inode_bitmap_block(&super_block);
|
|
|
+
|
|
|
+ let mut raw_data = fs
|
|
|
+ .block_device
|
|
|
+ .read_offset(inode_bitmap_block as usize * BLOCK_SIZE);
|
|
|
+
|
|
|
+ let inodes_in_bg = super_block.get_inodes_in_group_cnt(bgid);
|
|
|
+
|
|
|
+ let bitmap_size: u32 = inodes_in_bg / 0x8;
|
|
|
+
|
|
|
+ let mut bitmap_data = &mut raw_data[..bitmap_size as usize];
|
|
|
+
|
|
|
+ let mut idx_in_bg = 0 as u32;
|
|
|
+
|
|
|
+ ext4_bmap_bit_find_clr(bitmap_data, 0, inodes_in_bg, &mut idx_in_bg);
|
|
|
+ ext4_bmap_bit_set(&mut bitmap_data, idx_in_bg);
|
|
|
+
|
|
|
+ // update bitmap in disk
|
|
|
+ fs.block_device
|
|
|
+ .write_offset(inode_bitmap_block as usize * BLOCK_SIZE, &bitmap_data);
|
|
|
+
|
|
|
+ bg.set_block_group_ialloc_bitmap_csum(&super_block, &bitmap_data);
|
|
|
+
|
|
|
+ /* Modify filesystem counters */
|
|
|
+ free_inodes -= 1;
|
|
|
+ bg.set_free_inodes_count(&super_block, free_inodes);
|
|
|
+
|
|
|
+ /* Increment used directories counter */
|
|
|
+ if is_dir {
|
|
|
+ used_dirs += 1;
|
|
|
+ bg.set_used_dirs_count(&super_block, used_dirs);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Decrease unused inodes count */
|
|
|
+ let mut unused = bg.get_itable_unused(&super_block);
|
|
|
+ let free = inodes_in_bg - unused as u32;
|
|
|
+ if idx_in_bg >= free {
|
|
|
+ unused = inodes_in_bg - (idx_in_bg + 1);
|
|
|
+ bg.set_itable_unused(&super_block, unused);
|
|
|
+ }
|
|
|
+
|
|
|
+ bg.sync_to_disk_with_csum(block_device.clone(), bgid as usize, &super_block);
|
|
|
+ // bg.sync_block_group_to_disk(block_device.clone(), bgid as usize, &super_block);
|
|
|
+
|
|
|
+ /* Update superblock */
|
|
|
+ super_block.decrease_free_inodes_count();
|
|
|
+ // super_block.sync_super_block_to_disk(block_device.clone());
|
|
|
+
|
|
|
+ /* Compute the absolute i-nodex number */
|
|
|
+ let inodes_per_group = super_block.inodes_per_group();
|
|
|
+ let inode_num = bgid * inodes_per_group + (idx_in_bg + 1);
|
|
|
+ *index = inode_num;
|
|
|
+
|
|
|
+ // log::info!("alloc inode {:x?}", inode_num);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bgid += 1;
|
|
|
+ }
|
|
|
+ log::info!("no free inode");
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ext4_balloc_alloc_block(
|
|
|
+ inode_ref: &mut Ext4InodeRef,
|
|
|
+ goal: ext4_fsblk_t,
|
|
|
+ fblock: &mut ext4_fsblk_t,
|
|
|
+) {
|
|
|
+ // let mut alloc: ext4_fsblk_t = 0;
|
|
|
+ // let mut bmp_blk_adr: ext4_fsblk_t;
|
|
|
+ // let mut rel_blk_idx: u32 = 0;
|
|
|
+ // let mut free_blocks: u64;
|
|
|
+ // let mut r: i32;
|
|
|
+
|
|
|
+ let fs = inode_ref.fs();
|
|
|
+
|
|
|
+ let block_device = fs.block_device.clone();
|
|
|
+
|
|
|
+ let super_block_data = block_device.read_offset(BASE_OFFSET);
|
|
|
+ let mut super_block = Ext4Superblock::try_from(super_block_data).unwrap();
|
|
|
+
|
|
|
+ // let inodes_per_group = super_block.inodes_per_group();
|
|
|
+ let blocks_per_group = super_block.blocks_per_group();
|
|
|
+
|
|
|
+ let bgid = goal / blocks_per_group as u64;
|
|
|
+ let idx_in_bg = goal % blocks_per_group as u64;
|
|
|
+
|
|
|
+ let mut bg = Ext4BlockGroup::load(block_device.clone(), &super_block, bgid as usize).unwrap();
|
|
|
+
|
|
|
+ let block_bitmap_block = bg.get_block_bitmap_block(&super_block);
|
|
|
+ let mut raw_data = block_device.read_offset(block_bitmap_block as usize * BLOCK_SIZE);
|
|
|
+ let mut data: &mut Vec<u8> = &mut raw_data;
|
|
|
+ let mut rel_blk_idx = 0 as u32;
|
|
|
+
|
|
|
+ ext4_bmap_bit_find_clr(data, idx_in_bg as u32, 0x8000, &mut rel_blk_idx);
|
|
|
+ *fblock = rel_blk_idx as u64;
|
|
|
+ ext4_bmap_bit_set(&mut data, rel_blk_idx);
|
|
|
+
|
|
|
+ bg.set_block_group_balloc_bitmap_csum(&super_block, &data);
|
|
|
+ block_device.write_offset(block_bitmap_block as usize * BLOCK_SIZE, &data);
|
|
|
+
|
|
|
+ /* Update superblock free blocks count */
|
|
|
+ let super_blk_free_blocks = super_block.free_blocks_count();
|
|
|
+ // super_blk_free_blocks -= 1;
|
|
|
+ super_block.set_free_blocks_count(super_blk_free_blocks);
|
|
|
+ super_block.sync_to_disk(block_device.clone());
|
|
|
+
|
|
|
+ /* Update inode blocks (different block size!) count */
|
|
|
+ let mut inode_blocks = inode_ref.inner.inode.ext4_inode_get_blocks_count();
|
|
|
+ inode_blocks += 8;
|
|
|
+ inode_ref
|
|
|
+ .inner
|
|
|
+ .inode
|
|
|
+ .ext4_inode_set_blocks_count(inode_blocks as u32);
|
|
|
+ inode_ref.write_back_inode();
|
|
|
+
|
|
|
+ /* Update block group free blocks count */
|
|
|
+ let mut fb_cnt = bg.get_free_blocks_count();
|
|
|
+ fb_cnt -= 1;
|
|
|
+ bg.set_free_blocks_count(fb_cnt);
|
|
|
+ bg.sync_to_disk_with_csum(block_device, bgid as usize, &super_block);
|
|
|
+}
|