فهرست منبع

fix & refactor: ext4 file ops

liujingx 11 ماه پیش
والد
کامیت
6ae63839f2
12فایلهای تغییر یافته به همراه238 افزوده شده و 558 حذف شده
  1. 3 81
      src/constants.rs
  2. 3 2
      src/ext4/alloc.rs
  3. 25 59
      src/ext4/dir.rs
  4. 135 236
      src/ext4/file.rs
  5. 9 27
      src/ext4/link.rs
  6. 0 1
      src/ext4/mod.rs
  7. 0 101
      src/ext4/utils.rs
  8. 0 11
      src/ext4_defs/dir_entry.rs
  9. 39 27
      src/ext4_defs/file.rs
  10. 9 1
      src/ext4_defs/inode.rs
  11. 12 12
      src/ext4_error.rs
  12. 3 0
      src/prelude.rs

+ 3 - 81
src/constants.rs

@@ -1,12 +1,8 @@
 #![allow(unused)]
 
+use crate::prelude::*;
 use bitflags::bitflags;
 
-pub const EOK: usize = 0;
-
-pub type LBlockId = u32;
-pub type PBlockId = u64;
-
 pub const EXT4_INODE_FLAG_EXTENTS: usize = 0x00080000; /* Inode uses extents */
 pub const EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE: u16 = 32;
 pub const EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE: u16 = 64;
@@ -42,52 +38,7 @@ pub const SYMLINKS_MAX: usize = 40;
 /// The inode number of root inode
 pub const EXT4_ROOT_INO: u32 = 2;
 
-#[allow(non_camel_case_types)]
-#[derive(Debug, PartialEq)]
-pub enum LibcOpenFlags {
-    O_ACCMODE,
-    O_RDONLY,
-    O_WRONLY,
-    O_RDWR,
-    O_CREAT,
-    O_EXCL,
-    O_NOCTTY,
-    O_TRUNC,
-    O_APPEND,
-    O_NONBLOCK,
-    O_SYNC,
-    O_ASYNC,
-    O_LARGEFILE,
-    O_DIRECTORY,
-    O_NOFOLLOW,
-    O_CLOEXEC,
-    O_DIRECT,
-    O_NOATIME,
-    O_PATH,
-    O_DSYNC,
-}
-
-pub const O_ACCMODE: u32 = 0o0003;
-pub const O_RDONLY: u32 = 0o00;
-pub const O_WRONLY: u32 = 0o01;
-pub const O_RDWR: u32 = 0o02;
-pub const O_CREAT: u32 = 0o0100;
-pub const O_EXCL: u32 = 0o0200;
-pub const O_NOCTTY: u32 = 0o0400;
-pub const O_TRUNC: u32 = 0o01000;
-pub const O_APPEND: u32 = 0o02000;
-pub const O_NONBLOCK: u32 = 0o04000;
-pub const O_SYNC: u32 = 0o4010000;
-pub const O_ASYNC: u32 = 0o020000;
-pub const O_LARGEFILE: u32 = 0o0100000;
-pub const O_DIRECTORY: u32 = 0o0200000;
-pub const O_NOFOLLOW: u32 = 0o0400000;
-pub const O_CLOEXEC: u32 = 0o2000000;
-pub const O_DIRECT: u32 = 0o040000;
-pub const O_NOATIME: u32 = 0o1000000;
-pub const O_PATH: u32 = 0o10000000;
-pub const O_DSYNC: u32 = 0o010000;
-
+pub const EOK: usize = 0;
 pub const EPERM: usize = 1; /* Operation not permitted */
 pub const ENOENT: usize = 2; /* No such file or directory */
 pub const ESRCH: usize = 3; /* No such process */
@@ -123,35 +74,6 @@ pub const EPIPE: usize = 32; /* Broken pipe */
 pub const EDOM: usize = 33; /* Math argument out of domain of func */
 pub const ERANGE: usize = 34; /* Math result not representable */
 
-bitflags! {
-    pub struct OpenFlag: u32 {
-        // 以下是open/fcntl的一些常量,和C语言的值相同
-        const O_ACCMODE = 0o0003;
-        const O_RDONLY = 0o00;
-        const O_WRONLY = 0o01;
-        const O_RDWR = 0o02;
-        const O_CREAT = 0o0100;
-        const O_EXCL = 0o0200;
-        const O_NOCTTY = 0o0400;
-        const O_TRUNC = 0o01000;
-        const O_APPEND = 0o02000;
-        const O_NONBLOCK = 0o04000;
-        const O_NDELAY = Self::O_NONBLOCK.bits();
-        const O_SYNC = 0o4010000;
-        const O_FSYNC = Self::O_SYNC.bits();
-        const O_ASYNC = 0o020000;
-        const O_LARGEFILE = 0o0100000;
-        const O_DIRECTORY = 0o0200000;
-        const O_NOFOLLOW = 0o0400000;
-        const O_CLOEXEC = 0o2000000;
-        const O_DIRECT = 0o040000;
-        const O_NOATIME = 0o1000000;
-        const O_PATH = 0o10000000;
-        const O_DSYNC = 0o010000;
-        const O_TMPFILE = 0o20000000 | Self::O_DIRECTORY.bits();
-    }
-}
-
 bitflags! {
     #[derive(Debug, PartialEq, Eq)]
     pub struct InodeMode: u16 {
@@ -166,4 +88,4 @@ bitflags! {
 }
 
 pub const BASE_OFFSET: usize = 1024;
-pub const BLOCK_SIZE: usize = 4096;
+pub const BLOCK_SIZE: usize = 4096;

+ 3 - 2
src/ext4/alloc.rs

@@ -1,6 +1,7 @@
 use super::Ext4;
 use crate::constants::*;
 use crate::ext4_defs::*;
+use crate::prelude::*;
 
 impl Ext4 {
     /// Allocate a new data block for an inode, return the physical block number
@@ -108,7 +109,7 @@ impl Ext4 {
 
         // Sync the inode to disk
         self.write_back_inode_with_csum(&mut inode_ref);
-        
+
         inode_ref
     }
 
@@ -146,7 +147,7 @@ impl Ext4 {
                 .find_and_set_first_clear_bit(0, inode_count as usize)
                 .unwrap() as u32;
 
-            // update bitmap in disk
+            // Update bitmap in disk
             self.block_device
                 .write_offset(inode_bitmap_block as usize * BLOCK_SIZE, &bitmap.as_raw());
             bg.set_inode_bitmap_csum(&self.super_block, &bitmap);

+ 25 - 59
src/ext4/dir.rs

@@ -1,4 +1,3 @@
-use super::utils::*;
 use super::Ext4;
 use crate::constants::*;
 use crate::ext4_defs::*;
@@ -6,14 +5,7 @@ use crate::prelude::*;
 
 impl Ext4 {
     /// Find a directory entry that matches a given name under a parent directory
-    ///
-    /// Save the result in `Ext4DirSearchResult`
-    pub fn dir_find_entry(
-        &self,
-        parent: &mut Ext4InodeRef,
-        name: &str,
-        result: &mut Ext4DirSearchResult,
-    ) -> usize {
+    pub fn dir_find_entry(&self, parent: &mut Ext4InodeRef, name: &str) -> Result<Ext4DirEntry> {
         let inode_size: u32 = parent.inode.size;
         let total_blocks: u32 = inode_size / BLOCK_SIZE as u32;
         let mut iblock: LBlockId = 0;
@@ -22,34 +14,24 @@ impl Ext4 {
             // Get the fs block id
             let fblock = self.extent_get_pblock(parent, iblock);
             // Load block from disk
-            let mut data = self.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 block_data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
             // Find the entry in block
-            let r = Self::find_entry_in_block(&mut ext4_block, name, result);
-            if r {
-                return EOK;
+            let res = Self::find_entry_in_block(&mut block_data, name);
+            if let Ok(r) = res {
+                return Ok(r);
             }
             iblock += 1
         }
-        ENOENT
+        Err(Ext4Error::new(ErrCode::ENOENT))
     }
 
     /// Find a directory entry that matches a given name in a given block
     ///
     /// Save the result in `Ext4DirSearchResult`
-    fn find_entry_in_block(
-        block: &Ext4Block,
-        name: &str,
-        result: &mut Ext4DirSearchResult,
-    ) -> bool {
+    fn find_entry_in_block(block_data: &[u8], name: &str) -> Result<Ext4DirEntry> {
         let mut offset = 0;
-        while offset < block.block_data.len() {
-            let de = Ext4DirEntry::try_from(&block.block_data[offset..]).unwrap();
+        while offset < block_data.len() {
+            let de = Ext4DirEntry::try_from(&block_data[offset..]).unwrap();
             offset += de.rec_len() as usize;
             // Unused dir entry
             if de.unused() {
@@ -57,24 +39,23 @@ impl Ext4 {
             }
             // Compare name
             if de.compare_name(name) {
-                result.dentry = de;
-                return true;
+                return Ok(de);
             }
         }
-        false
+        Err(Ext4Error::new(ErrCode::ENOENT))
     }
 
     /// Add an entry to a directory
     pub fn dir_add_entry(
         &mut self,
         parent: &mut Ext4InodeRef,
-        child: &mut Ext4InodeRef,
+        child: &Ext4InodeRef,
         path: &str,
     ) -> usize {
         let block_size = self.super_block.block_size();
         let inode_size = parent.inode.size();
         let total_blocks = inode_size as u32 / block_size;
-        
+
         // Try finding a block with enough space
         let mut iblock: LBlockId = 0;
         while iblock < total_blocks {
@@ -117,12 +98,7 @@ impl Ext4 {
 
     /// Insert a directory entry of a child inode into a new parent block.
     /// A new block must have enough space
-    fn insert_entry_to_new_block(
-        &self,
-        dst_blk: &mut Ext4Block,
-        child: &mut Ext4InodeRef,
-        name: &str,
-    ) {
+    fn insert_entry_to_new_block(&self, dst_blk: &mut Ext4Block, child: &Ext4InodeRef, name: &str) {
         // Set the entry
         let mut new_entry = Ext4DirEntry::default();
         let rec_len = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
@@ -152,7 +128,7 @@ impl Ext4 {
     fn insert_entry_to_old_block(
         &self,
         dst_blk: &mut Ext4Block,
-        child: &mut Ext4InodeRef,
+        child: &Ext4InodeRef,
         name: &str,
     ) -> usize {
         let required_size = Ext4DirEntry::required_size(name.len());
@@ -212,26 +188,16 @@ impl Ext4 {
         en.set_name(name);
     }
 
-    pub fn ext4_dir_mk(&self, path: &str) -> Result<usize> {
-        let mut file = Ext4File::new();
-        let flags = "w";
-
-        let filetype = FileType::Directory;
-
-        // get mount point
-        let mut ptr = Box::new(self.mount_point.clone());
-        file.mp = Box::as_mut(&mut ptr) as *mut Ext4MountPoint;
-
+    /// Create a new directory. `path` is the absolute path of the new directory.
+    pub fn ext4_dir_mk(&mut self, path: &str) -> Result<()> {
         // get open flags
-        let iflags = ext4_parse_flags(flags).unwrap();
-
-        if iflags & O_CREAT != 0 {
-            self.ext4_trans_start();
-        }
-
-        let mut root_inode_ref = self.get_root_inode_ref();
-
-        let r = self.ext4_generic_open(&mut file, path, iflags, filetype, &mut root_inode_ref);
-        r
+        let iflags = OpenFlags::from_str("w").unwrap();
+        self.ext4_generic_open(
+            path,
+            iflags,
+            FileType::Directory,
+            &self.get_root_inode_ref(),
+        )
+        .map(|_| ())
     }
 }

+ 135 - 236
src/ext4/file.rs

@@ -1,295 +1,194 @@
-use super::utils::*;
 use super::Ext4;
 use crate::constants::*;
 use crate::ext4_defs::*;
 use crate::prelude::*;
 use crate::return_errno_with_message;
+use core::cmp::min;
 
 impl Ext4 {
+    fn split_path(path: &str) -> Vec<String> {
+        let _ = path.trim_start_matches("/");
+        path.split("/").map(|s| s.to_string()).collect()
+    }
+
     pub fn ext4_generic_open(
-        &self,
-        file: &mut Ext4File,
+        &mut self,
         path: &str,
-        iflags: u32,
+        flag: OpenFlags,
         ftype: FileType,
-        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 = self.get_root_inode_ref();
-
-        // 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 = self.dir_find_entry(
-                &mut search_parent,
-                &search_path[..len as usize],
-                &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::default();
-
-                let r = if is_goal {
-                    self.ext4_fs_alloc_inode(&mut child_inode_ref, ftype)
-                } else {
-                    self.ext4_fs_alloc_inode(&mut child_inode_ref, FileType::Directory)
-                };
-
-                if r != EOK {
-                    return_errno_with_message!(Errnum::EALLOCFIAL, "alloc inode fail");
-                    // break;
+        parent_inode: &Ext4InodeRef,
+    ) -> Result<Ext4File> {
+        // Search from the given parent inode
+        let mut parent = parent_inode.clone();
+        let search_path = Self::split_path(path);
+
+        for (i, path) in search_path.iter().enumerate() {
+            let res = self.dir_find_entry(&mut parent, path);
+            match res {
+                Ok(entry) => {
+                    parent = self.get_inode_ref(entry.inode());
                 }
-
-                Self::ext4_fs_inode_blocks_init(&mut child_inode_ref);
-
-                let r = self.ext4_link(
-                    &mut search_parent,
-                    &mut child_inode_ref,
-                    &search_path[..len as usize],
-                );
-
-                if r != EOK {
-                    /*Fail. Free new inode.*/
-                    return_errno_with_message!(Errnum::ELINKFIAL, "link fail");
+                Err(e) => {
+                    if e.code() != ErrCode::ENOENT {
+                        // dir search failed with error other than ENOENT
+                        return_errno_with_message!(ErrCode::ENOTSUP, "dir search failed");
+                    }
+                    if !flag.contains(OpenFlags::O_CREAT) {
+                        return_errno_with_message!(ErrCode::ENOENT, "file not found");
+                    }
+                    // Create file/directory
+                    let mut child = if i == search_path.len() - 1 {
+                        self.alloc_inode(ftype)
+                    } else {
+                        self.alloc_inode(FileType::Directory)
+                    };
+                    Self::ext4_fs_inode_blocks_init(&mut child);
+                    // Link the new inode
+                    let r = self.ext4_link(&mut parent, &mut child, path);
+                    if r != EOK {
+                        // Fail. Free new inode
+                        return_errno_with_message!(ErrCode::ELINKFIAL, "link fail");
+                    }
+                    // Write back parent and child
+                    self.write_back_inode_with_csum(&mut parent);
+                    self.write_back_inode_with_csum(&mut child);
                 }
-
-                self.write_back_inode_with_csum(&mut search_parent);
-                self.write_back_inode_with_csum(&mut child_inode_ref);
-                self.write_back_inode_with_csum(parent_inode);
-
-                continue;
-            }
-            // 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 = self.get_inode_ref(dir_search_result.dentry.inode());
-                search_path = &search_path[len..];
             }
         }
-    }
 
-    pub fn ext4_open(
-        &self,
-        file: &mut Ext4File,
-        path: &str,
-        flags: &str,
-        file_expect: bool,
-    ) -> Result<usize> {
-        // get open flags
-        let iflags = ext4_parse_flags(flags).unwrap();
-
-        // get mount point
-        let mut ptr = Box::new(self.mount_point.clone());
-        file.mp = Box::as_mut(&mut ptr) as *mut Ext4MountPoint;
+        // Reach the target
+        let mut file = Ext4File::default();
+        file.inode = parent.inode_id;
+        Ok(file)
+    }
 
-        // file for dir
-        let filetype = if file_expect {
+    pub fn ext4_open(&mut self, path: &str, flags: &str, file_expect: bool) -> Result<Ext4File> {
+        // open flags
+        let iflags = OpenFlags::from_str(flags).unwrap();
+        // file type
+        let file_type = if file_expect {
             FileType::RegularFile
         } else {
             FileType::Directory
         };
-
-        if iflags & O_CREAT != 0 {
+        // TODO:journal
+        if iflags.contains(OpenFlags::O_CREAT) {
             self.ext4_trans_start();
         }
-
-        let mut root_inode_ref = self.get_root_inode_ref();
-
-        let r = self.ext4_generic_open(file, path, iflags, filetype, &mut root_inode_ref);
-
-        r
+        // open file
+        let res = self.ext4_generic_open(path, iflags, file_type, &self.get_root_inode_ref());
+        res.map(|mut file| {
+            // set mount point
+            let mut ptr = Box::new(self.mount_point.clone());
+            file.mp = Box::as_mut(&mut ptr) as *mut Ext4MountPoint;
+            file
+        })
     }
 
     #[allow(unused)]
     pub fn ext4_file_read(
         &self,
-        ext4_file: &mut Ext4File,
+        file: &mut Ext4File,
         read_buf: &mut [u8],
-        size: usize,
-        read_cnt: &mut usize,
+        read_size: usize,
     ) -> Result<usize> {
-        if size == 0 {
-            return Ok(EOK);
+        // Read no bytes
+        if read_size == 0 {
+            return Ok(0);
         }
-
-        let mut inode_ref = self.get_inode_ref(ext4_file.inode);
-
+        // Get the inode of the file
+        let mut inode_ref = self.get_inode_ref(file.inode);
         // sync file size
-        ext4_file.fsize = inode_ref.inode.size();
-
-        let is_softlink =
-            inode_ref.inode.inode_type(&self.super_block) == EXT4_INODE_MODE_SOFTLINK as u32;
+        file.fsize = inode_ref.inode.size();
 
-        if is_softlink {
+        // Check if the file is a softlink
+        if inode_ref.inode.is_softlink(&self.super_block) {
+            // TODO: read softlink
             log::debug!("ext4_read unsupported softlink");
         }
 
-        let block_size = BLOCK_SIZE;
-
-        // 计算读取大小
-        let size_to_read = if size > (ext4_file.fsize as usize - ext4_file.fpos) {
-            ext4_file.fsize as usize - ext4_file.fpos
-        } else {
-            size
-        };
-
-        let mut iblock_idx = (ext4_file.fpos / block_size) as u32;
-        let iblock_last = ((ext4_file.fpos + size_to_read) / block_size) as u32;
-        let mut unalg = (ext4_file.fpos % block_size) as u32;
-
-        let mut offset = 0;
-        let mut total_bytes_read = 0;
-
-        if unalg > 0 {
-            let first_block_read_len = core::cmp::min(block_size - unalg as usize, size_to_read);
-            let mut fblock = 0;
-
-            self.ext4_fs_get_inode_dblk_idx(&mut inode_ref, &mut iblock_idx, &mut fblock, false);
-
-            // if r != EOK {
-            //     return Err(Ext4Error::new(r));
-            // }
-
+        // Calc the actual size to read
+        let size_to_read = min(read_size, file.fsize as usize - file.fpos);
+        // Calc the start block of reading
+        let start_iblock = (file.fpos / BLOCK_SIZE) as LBlockId;
+        // Calc the length that is not aligned to the block size
+        let mut misaligned = file.fpos % BLOCK_SIZE;
+
+        let mut cursor = 0;
+        let mut iblock = start_iblock;
+        // Read first block
+        if misaligned > 0 {
+            let first_block_read_len = min(BLOCK_SIZE - misaligned, size_to_read);
+            let fblock = self.extent_get_pblock(&mut inode_ref, start_iblock);
             if fblock != 0 {
-                let block_offset = fblock * block_size as u64 + unalg as u64;
-                let block_data = self.block_device.read_offset(block_offset as usize);
-
+                let block_offset = fblock as usize * BLOCK_SIZE + misaligned;
+                let block_data = self.block_device.read_offset(block_offset);
                 // Copy data from block to the user buffer
-                read_buf[offset..offset + first_block_read_len]
+                read_buf[cursor..cursor + first_block_read_len]
                     .copy_from_slice(&block_data[0..first_block_read_len]);
             } else {
                 // Handle the unwritten block by zeroing out the respective part of the buffer
-                for x in &mut read_buf[offset..offset + first_block_read_len] {
-                    *x = 0;
-                }
+                read_buf[cursor..cursor + first_block_read_len].fill(0);
             }
-
-            offset += first_block_read_len;
-            total_bytes_read += first_block_read_len;
-            ext4_file.fpos += first_block_read_len;
-            *read_cnt += first_block_read_len;
-            iblock_idx += 1;
+            cursor += first_block_read_len;
+            file.fpos += first_block_read_len;
+            iblock += 1;
         }
-
         // Continue with full block reads
-        while total_bytes_read < size_to_read {
-            let read_length = core::cmp::min(block_size, size_to_read - total_bytes_read);
-            let mut fblock = 0;
-
-            self.ext4_fs_get_inode_dblk_idx(&mut inode_ref, &mut iblock_idx, &mut fblock, false);
-
-            // if r != EOK {
-            //     return Err(Ext4Error::new(r));
-            // }
-
+        while cursor < size_to_read {
+            let read_length = min(BLOCK_SIZE, size_to_read - cursor);
+            let fblock = self.extent_get_pblock(&mut inode_ref, iblock);
             if fblock != 0 {
-                let block_data = self
-                    .block_device
-                    .read_offset((fblock * block_size as u64) as usize);
-                read_buf[offset..offset + read_length].copy_from_slice(&block_data[0..read_length]);
+                let block_data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
+                // Copy data from block to the user buffer
+                read_buf[cursor..cursor + read_length].copy_from_slice(&block_data[0..read_length]);
             } else {
                 // Handle the unwritten block by zeroing out the respective part of the buffer
-                for x in &mut read_buf[offset..offset + read_length] {
-                    *x = 0;
-                }
+                read_buf[cursor..cursor + read_length].fill(0);
             }
-
-            offset += read_length;
-            total_bytes_read += read_length;
-            ext4_file.fpos += read_length;
-            *read_cnt += read_length;
-            iblock_idx += 1;
+            cursor += read_length;
+            file.fpos += read_length;
+            iblock += 1;
         }
 
-        return Ok(EOK);
+        Ok(cursor)
     }
 
-    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 = self.get_inode_ref(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 {
-                    self.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;
+    pub fn ext4_file_write(&mut self, file: &mut Ext4File, data: &[u8]) {
+        let size = data.len();
+        let mut inode_ref = self.get_inode_ref(file.inode);
+        // Sync ext file
+        file.fsize = inode_ref.inode.size();
+
+        // Calc the start and end block of reading
+        let start_iblock = (file.fpos / BLOCK_SIZE) as LBlockId;
+        let end_iblock = ((file.fpos + size) / BLOCK_SIZE) as LBlockId;
+        // Calc the block count of the file
+        let block_count = (file.fsize as usize + BLOCK_SIZE - 1) / BLOCK_SIZE;
+        // Append enough block for writing
+        let append_block_count = end_iblock + 1 - block_count as LBlockId;
+        for _ in 0..append_block_count {
+            self.inode_append_block(&mut inode_ref);
         }
 
-        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)]);
+        // Write data
+        let mut cursor = 0;
+        let mut iblock = start_iblock;
+        while cursor < size {
+            let write_len = min(BLOCK_SIZE, size - cursor);
+            let fblock = self.extent_get_pblock(&mut inode_ref, iblock);
+            if fblock != 0 {
+                self.block_device.write_offset(cursor, &data[cursor..cursor + write_len]);
+            } else {
+                panic!("Write to unallocated block");
+            }
+            cursor += write_len;
+            file.fpos += write_len;
+            iblock += 1;
         }
-        // inode_ref.inner.inode.size = fblock_count as u32 * BLOCK_SIZE as u32;
-        self.write_back_inode_with_csum(&mut inode_ref);
-        // let mut inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), ext4_file.inode);
-        let mut root_inode_ref = self.get_root_inode_ref();
-        self.write_back_inode_with_csum(&mut root_inode_ref);
     }
 
     pub fn ext4_file_remove(&self, _path: &str) -> Result<usize> {
-        return_errno_with_message!(Errnum::ENOTSUP, "not support");
+        return_errno_with_message!(ErrCode::ENOTSUP, "not support");
     }
 }

+ 9 - 27
src/ext4/link.rs

@@ -9,37 +9,19 @@ impl Ext4 {
         child: &mut Ext4InodeRef,
         name: &str,
     ) -> usize {
-        // log::info!("link parent inode {:x?} child inode {:x?} name {:?}", parent.inode_num, child.inode_num, name);
-        /* Add entry to parent directory */
+        // Add entry to parent directory
         let _r = self.dir_add_entry(parent, child, name);
-    
-        /* Fill new dir -> add '.' and '..' entries.
-         * Also newly allocated inode should have 0 link count.
-            */
-        let mut is_dir = false;
-        if child.inode.mode & EXT4_INODE_MODE_TYPE_MASK as u16 == EXT4_INODE_MODE_DIRECTORY as u16
-        {
-            is_dir = true;
-        }
-    
-        if is_dir {
+        child.inode.links_count += 1;
+
+        if child.inode.is_dir(&self.super_block) {
             // add '.' and '..' entries
-            let mut child_inode_ref = Ext4InodeRef::default();
-            child_inode_ref.inode_id = child.inode_id;
-            child_inode_ref.inode = child.inode.clone();
-    
-            let _r = self.dir_add_entry(&mut child_inode_ref, child, ".");
-            child.inode.size = child_inode_ref.inode.size;
-            child.inode.block = child_inode_ref.inode.block;
-            let _r = self.dir_add_entry(&mut child_inode_ref, parent, "..");
-    
-            child.inode.links_count = 2;
+            let child_self = child.clone();
+            self.dir_add_entry(child, &child_self, ".");
+            child.inode.links_count += 1;
+            self.dir_add_entry(child, parent, "..");
             parent.inode.links_count += 1;
-    
-            return EOK;
         }
-    
-        child.inode.links_count += 1;
+
         EOK
     }
 }

+ 0 - 1
src/ext4/mod.rs

@@ -8,7 +8,6 @@ mod extent;
 mod file;
 mod journal;
 mod link;
-mod utils;
 
 #[derive(Debug)]
 pub struct Ext4 {

+ 0 - 101
src/ext4/utils.rs

@@ -1,101 +0,0 @@
-use crate::prelude::*;
-use crate::constants::*;
-
-/// 检查位图中的某一位是否被设置
-/// 参数 bmap 位图缓冲区
-/// 参数 bit 要检查的位
-pub fn ext4_bmap_is_bit_set(bmap: &[u8], bit: u32) -> bool {
-    bmap[(bit >> 3) as usize] & (1 << (bit & 7)) != 0
-}
-
-/// 检查位图中的某一位是否被清除
-/// 参数 bmap 位图缓冲区
-/// 参数 bit 要检查的位
-pub fn ext4_bmap_is_bit_clr(bmap: &[u8], bit: u32) -> bool {
-    !ext4_bmap_is_bit_set(bmap, bit)
-}
-
-/// 设置位图中的某一位
-/// 参数 bmap 位图
-/// 参数 bit 要设置的位
-pub fn ext4_bmap_bit_set(bmap: &mut [u8], bit: u32) {
-    bmap[(bit >> 3) as usize] |= 1 << (bit & 7);
-}
-
-/// 查找位图中第一个为0的位
-pub fn ext4_bmap_bit_find_clr(bmap: &[u8], sbit: u32, ebit: u32, bit_id: &mut u32) -> bool {
-    let mut i: u32;
-    let mut bcnt = ebit - sbit;
-
-    i = sbit;
-
-    while i & 7 != 0 {
-        if bcnt == 0 {
-            return false;
-        }
-
-        if ext4_bmap_is_bit_clr(bmap, i) {
-            *bit_id = sbit;
-            return true;
-        }
-
-        i += 1;
-        bcnt -= 1;
-    }
-
-    let mut sbit = i;
-    let mut bmap = &bmap[(sbit >> 3) as usize..];
-    while bcnt >= 8 {
-        if bmap[0] != 0xFF {
-            for i in 0..8 {
-                if ext4_bmap_is_bit_clr(bmap, i) {
-                    *bit_id = sbit + i;
-                    return true;
-                }
-            }
-        }
-
-        bmap = &bmap[1..];
-        bcnt -= 8;
-        sbit += 8;
-    }
-
-    for i in 0..bcnt {
-        if ext4_bmap_is_bit_clr(bmap, i) {
-            *bit_id = sbit + i;
-            return true;
-        }
-    }
-
-    false
-}
-
-pub fn ext4_path_skip<'a>(path: &'a str, skip: &str) -> &'a str {
-    let path = &path.trim_start_matches(skip);
-    path
-}
-
-pub fn ext4_path_check(path: &str, is_goal: &mut bool) -> usize {
-    for (i, c) in path.chars().enumerate() {
-        if c == '/' {
-            *is_goal = false;
-            return i;
-        }
-    }
-    let path = path.to_string();
-    *is_goal = true;
-    return path.len();
-}
-
-// 使用libc库定义的常量
-pub fn ext4_parse_flags(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)),
-    }
-}

+ 0 - 11
src/ext4_defs/dir_entry.rs

@@ -202,17 +202,6 @@ impl Ext4DirEntryTail {
     }
 }
 
-pub struct Ext4DirSearchResult<'a> {
-    pub block: Ext4Block<'a>,
-    pub dentry: Ext4DirEntry,
-}
-
-impl<'a> Ext4DirSearchResult<'a> {
-    pub fn new(block: Ext4Block<'a>, dentry: Ext4DirEntry) -> Self {
-        Self { block, dentry }
-    }
-}
-
 /// Fake dir entry. A normal entry without `name` field`
 #[repr(C)]
 pub struct Ext4FakeDirEntry {

+ 39 - 27
src/ext4_defs/file.rs

@@ -15,10 +15,10 @@ pub struct Ext4File {
     pub fpos: usize,
 }
 
-impl Ext4File {
-    pub fn new() -> Self {
+impl Default for Ext4File {
+    fn default() -> Self {
         Self {
-            mp: core::ptr::null_mut(),
+            mp: ptr::null_mut(),
             inode: 0,
             flags: 0,
             fsize: 0,
@@ -27,31 +27,44 @@ impl Ext4File {
     }
 }
 
-#[derive(Debug, PartialEq)]
-pub enum Ext4OpenFlags {
-    ReadOnly,
-    WriteOnly,
-    WriteCreateTrunc,
-    WriteCreateAppend,
-    ReadWrite,
-    ReadWriteCreateTrunc,
-    ReadWriteCreateAppend,
+bitflags! {
+    pub struct OpenFlags: u32 {
+        const O_ACCMODE = 0o0003;
+        const O_RDONLY = 0o00;
+        const O_WRONLY = 0o01;
+        const O_RDWR = 0o02;
+        const O_CREAT = 0o0100;
+        const O_EXCL = 0o0200;
+        const O_NOCTTY = 0o0400;
+        const O_TRUNC = 0o01000;
+        const O_APPEND = 0o02000;
+        const O_NONBLOCK = 0o04000;
+        const O_NDELAY = Self::O_NONBLOCK.bits();
+        const O_SYNC = 0o4010000;
+        const O_FSYNC = Self::O_SYNC.bits();
+        const O_ASYNC = 0o020000;
+        const O_LARGEFILE = 0o0100000;
+        const O_DIRECTORY = 0o0200000;
+        const O_NOFOLLOW = 0o0400000;
+        const O_CLOEXEC = 0o2000000;
+        const O_DIRECT = 0o040000;
+        const O_NOATIME = 0o1000000;
+        const O_PATH = 0o10000000;
+        const O_DSYNC = 0o010000;
+        const O_TMPFILE = 0o20000000 | Self::O_DIRECTORY.bits();
+    }
 }
 
-// 实现一个从字符串转换为标志的函数
-// 使用core::str::FromStr特性[^1^][1]
-impl core::str::FromStr for Ext4OpenFlags {
-    type Err = String;
-
-    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
-        match s {
-            "r" | "rb" => Ok(Ext4OpenFlags::ReadOnly),
-            "w" | "wb" => Ok(Ext4OpenFlags::WriteOnly),
-            "a" | "ab" => Ok(Ext4OpenFlags::WriteCreateAppend),
-            "r+" | "rb+" | "r+b" => Ok(Ext4OpenFlags::ReadWrite),
-            "w+" | "wb+" | "w+b" => Ok(Ext4OpenFlags::ReadWriteCreateTrunc),
-            "a+" | "ab+" | "a+b" => Ok(Ext4OpenFlags::ReadWriteCreateAppend),
-            _ => Err(alloc::format!("Unknown open mode: {}", s)),
+impl OpenFlags {
+    pub fn from_str(flags: &str) -> Result<Self> {
+        match flags {
+            "r" | "rb" => Ok(Self::O_RDONLY),
+            "w" | "wb" => Ok(Self::O_WRONLY | Self::O_CREAT | Self::O_TRUNC),
+            "a" | "ab" => Ok(Self::O_WRONLY | Self::O_CREAT | Self::O_APPEND),
+            "r+" | "rb+" | "r+b" => Ok(Self::O_RDWR),
+            "w+" | "wb+" | "w+b" => Ok(Self::O_RDWR | Self::O_CREAT | Self::O_TRUNC),
+            "a+" | "ab+" | "a+b" => Ok(Self::O_RDWR | Self::O_CREAT | Self::O_APPEND),
+            _ => Err(Ext4Error::new(ErrCode::EINVAL)),
         }
     }
 }
@@ -63,4 +76,3 @@ pub enum SeekFrom {
     End(isize),
     Current(isize),
 }
-

+ 9 - 1
src/ext4_defs/inode.rs

@@ -93,6 +93,14 @@ impl Ext4Inode {
         (v & EXT4_INODE_MODE_TYPE_MASK) as u32
     }
 
+    pub fn is_dir(&self, super_block: &Ext4Superblock) -> bool {
+        self.inode_type(super_block) == EXT4_INODE_MODE_DIRECTORY as u32
+    }
+
+    pub fn is_softlink(&self, super_block: &Ext4Superblock) -> bool {
+        self.inode_type(super_block) == EXT4_INODE_MODE_SOFTLINK as u32
+    }
+
     pub fn links_cnt(&self) -> u16 {
         self.links_count
     }
@@ -313,7 +321,7 @@ impl Ext4Inode {
 }
 
 /// A combination of an `Ext4Inode` and its id
-#[derive(Default)]
+#[derive(Default, Clone)]
 pub struct Ext4InodeRef {
     pub inode_id: u32,
     pub inode: Ext4Inode,

+ 12 - 12
src/ext4_error.rs

@@ -3,7 +3,7 @@
 /// Ext4Error number.
 #[repr(i32)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Errnum {
+pub enum ErrCode {
     EPERM = 1,       /* Operation not permitted */
     ENOENT = 2,      /* No such file or directory */
     EIO = 5,         /* I/O error */
@@ -32,61 +32,61 @@ pub enum Errnum {
 /// error used in this crate
 #[derive(Debug, Clone, Copy)]
 pub struct Ext4Error {
-    errno: Errnum,
+    errno: ErrCode,
     #[allow(unused)]
     msg: Option<&'static str>,
 }
 
 impl Ext4Error {
-    pub const fn new(errno: Errnum) -> Self {
+    pub const fn new(errno: ErrCode) -> Self {
         Ext4Error { errno, msg: None }
     }
 
-    pub const fn with_message(errno: Errnum, msg: &'static str) -> Self {
+    pub const fn with_message(errno: ErrCode, msg: &'static str) -> Self {
         Ext4Error {
             errno,
             msg: Some(msg),
         }
     }
 
-    pub const fn error(&self) -> Errnum {
+    pub const fn code(&self) -> ErrCode {
         self.errno
     }
 }
 
-impl From<Errnum> for Ext4Error {
-    fn from(errno: Errnum) -> Self {
+impl From<ErrCode> for Ext4Error {
+    fn from(errno: ErrCode) -> Self {
         Ext4Error::new(errno)
     }
 }
 
 impl From<core::str::Utf8Error> for Ext4Error {
     fn from(_: core::str::Utf8Error) -> Self {
-        Ext4Error::with_message(Errnum::EINVAL, "Invalid utf-8 string")
+        Ext4Error::with_message(ErrCode::EINVAL, "Invalid utf-8 string")
     }
 }
 
 impl From<alloc::string::FromUtf8Error> for Ext4Error {
     fn from(_: alloc::string::FromUtf8Error) -> Self {
-        Ext4Error::with_message(Errnum::EINVAL, "Invalid utf-8 string")
+        Ext4Error::with_message(ErrCode::EINVAL, "Invalid utf-8 string")
     }
 }
 
 impl From<core::ffi::FromBytesUntilNulError> for Ext4Error {
     fn from(_: core::ffi::FromBytesUntilNulError) -> Self {
-        Ext4Error::with_message(Errnum::E2BIG, "Cannot find null in cstring")
+        Ext4Error::with_message(ErrCode::E2BIG, "Cannot find null in cstring")
     }
 }
 
 impl From<core::ffi::FromBytesWithNulError> for Ext4Error {
     fn from(_: core::ffi::FromBytesWithNulError) -> Self {
-        Ext4Error::with_message(Errnum::E2BIG, "Cannot find null in cstring")
+        Ext4Error::with_message(ErrCode::E2BIG, "Cannot find null in cstring")
     }
 }
 
 impl From<alloc::ffi::NulError> for Ext4Error {
     fn from(_: alloc::ffi::NulError) -> Self {
-        Ext4Error::with_message(Errnum::E2BIG, "Cannot find null in cstring")
+        Ext4Error::with_message(ErrCode::E2BIG, "Cannot find null in cstring")
     }
 }
 

+ 3 - 0
src/prelude.rs

@@ -27,3 +27,6 @@ pub(crate) use log::{debug, info, trace, warn};
 
 pub(crate) use crate::ext4_error::*;
 pub(crate) type Result<T> = core::result::Result<T, Ext4Error>;
+
+pub(crate) type LBlockId = u32;
+pub(crate) type PBlockId = u64;