Jelajahi Sumber

fix: rename update link count

liujingx 10 bulan lalu
induk
melakukan
6bcfd7c085
4 mengubah file dengan 67 tambahan dan 42 penghapusan
  1. 19 0
      ext4_fuse/src/fuse_fs.rs
  2. 1 1
      src/ext4/high_level.rs
  3. 17 24
      src/ext4/link.rs
  4. 30 17
      src/ext4/low_level.rs

+ 19 - 0
ext4_fuse/src/fuse_fs.rs

@@ -148,6 +148,10 @@ impl<T: 'static> Filesystem for StateExt4FuseFs<T> {
         _flags: i32,
         reply: ReplyCreate,
     ) {
+        // Check if name is already in use
+        if let Ok(_) = self.fs.lookup(parent as u32, name.to_str().unwrap()) {
+            return reply.error(ErrCode::EEXIST as i32);
+        }
         match self.fs.create(
             parent as u32,
             name.to_str().unwrap(),
@@ -238,6 +242,10 @@ impl<T: 'static> Filesystem for StateExt4FuseFs<T> {
         newname: &OsStr,
         reply: ReplyEntry,
     ) {
+        // Check if newname is already in use
+        if let Ok(_) = self.fs.lookup(newparent as u32, newname.to_str().unwrap()) {
+            return reply.error(ErrCode::EEXIST as i32);
+        }
         match self
             .fs
             .link(ino as u32, newparent as u32, newname.to_str().unwrap())
@@ -264,6 +272,13 @@ impl<T: 'static> Filesystem for StateExt4FuseFs<T> {
         _flags: u32,
         reply: ReplyEmpty,
     ) {
+        if parent == newparent && name == newname {
+            return reply.ok();
+        }
+        // Check if newname is already in use
+        if let Ok(_) = self.fs.lookup(newparent as u32, newname.to_str().unwrap()) {
+            return reply.error(ErrCode::EEXIST as i32);
+        }
         match self.fs.rename(
             parent as u32,
             name.to_str().unwrap(),
@@ -284,6 +299,10 @@ impl<T: 'static> Filesystem for StateExt4FuseFs<T> {
         _umask: u32,
         reply: ReplyEntry,
     ) {
+        // Check if name is already in use
+        if let Ok(_) = self.fs.lookup(parent as u32, name.to_str().unwrap()) {
+            return reply.error(ErrCode::EEXIST as i32);
+        }
         match self.fs.mkdir(
             parent as u32,
             name.to_str().unwrap(),

+ 1 - 1
src/ext4/high_level.rs

@@ -132,7 +132,7 @@ impl Ext4 {
             }
         }
         // Unlink the file
-        self.unlink_inode(&mut parent, &mut child, file_name)
+        self.unlink_inode(&mut parent, &mut child, file_name, true)
     }
 
     /// A helper function to split a path by '/'

+ 17 - 24
src/ext4/link.rs

@@ -14,52 +14,45 @@ impl Ext4 {
         self.dir_add_entry(parent, child, name)?;
 
         let child_link_count = child.inode.link_count();
-        if child.inode.is_dir() && child_link_count == 0 {
-            // Add '.' and '..' entries if child is a newly created directory
-            let child_self = child.clone();
-            self.dir_add_entry(child, &child_self, ".")?;
-            self.dir_add_entry(child, parent, "..")?;
+        if child.inode.is_dir() {
             // Link child/".."
+            self.dir_add_entry(child, parent, "..")?;
             parent.inode.set_link_count(parent.inode.link_count() + 1);
             self.write_inode_with_csum(parent);
-            // Link parent/child + child/"."
-            child.inode.set_link_count(child_link_count + 2);
-        } else {
-            // Link parent/child
-            child.inode.set_link_count(child_link_count + 1);
         }
+        // Link parent/child
+        child.inode.set_link_count(child_link_count + 1);
         self.write_inode_with_csum(child);
         Ok(())
     }
 
     /// Unlink a child inode from a parent directory.
-    /// Free the inode if link count is 0.
+    /// 
+    /// If `free` is true, the inode will be freed if it has no links.
     pub(super) fn unlink_inode(
         &self,
         parent: &mut InodeRef,
         child: &mut InodeRef,
         name: &str,
+        free: bool, 
     ) -> Result<()> {
         // Remove entry from parent directory
         self.dir_remove_entry(parent, name)?;
 
         let child_link_cnt = child.inode.link_count();
-        if child.inode.is_dir() && child_link_cnt <= 2 {
-            // Child is an empty directory
+        if child.inode.is_dir() {
+            // Child is a directory
             // Unlink "child/.."
+            self.dir_remove_entry(&child, "..")?;
             parent.inode.set_link_count(parent.inode.link_count() - 1);
             self.write_inode_with_csum(parent);
-            // Remove directory
-            self.free_inode(child)
-        } else if child_link_cnt <= 1 {
-            // Child is a file
-            // Remove file
-            self.free_inode(child)
-        } else {
-            // Not remove
-            child.inode.set_link_count(child_link_cnt - 1);
-            self.write_inode_with_csum(child);
-            Ok(())
         }
+        if free && ((child.inode.is_dir() && child_link_cnt <= 2) || child_link_cnt <= 1) {
+            // Remove file or directory
+            return self.free_inode(child);
+        }
+        child.inode.set_link_count(child_link_cnt - 1);
+        self.write_inode_with_csum(child);
+        Ok(())
     }
 }

+ 30 - 17
src/ext4/low_level.rs

@@ -112,8 +112,8 @@ impl Ext4 {
         Ok(())
     }
 
-    /// Create and open a file. This function will not check the existence
-    /// of the file. Call `lookup` to check beforehand.
+    /// Create a file. This function will not check the existence of
+    /// the file, call `lookup` to check beforehand.
     ///
     /// # Params
     ///
@@ -259,8 +259,8 @@ impl Ext4 {
         Ok(cursor)
     }
 
-    /// Create a hard link. This function will not check name conflict.
-    /// Call `lookup` to check beforehand.
+    /// Create a hard link. This function will not check name conflict,
+    /// call `lookup` to check beforehand.
     ///
     /// # Params
     ///
@@ -278,6 +278,10 @@ impl Ext4 {
             return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
         }
         let mut child = self.read_inode(child);
+        // Cannot link a directory
+        if child.inode.is_dir() {
+            return_error!(ErrCode::EISDIR, "Cannot link a directory");
+        }
         self.link_inode(&mut parent, &mut child, name)?;
         Ok(())
     }
@@ -293,19 +297,25 @@ impl Ext4 {
     ///
     /// * `ENOTDIR` - `parent` is not a directory
     /// * `ENOENT` - `name` does not exist in `parent`
+    /// * `EISDIR` - `parent/name` is a directory
     pub fn unlink(&self, parent: InodeId, name: &str) -> Result<()> {
         let mut parent = self.read_inode(parent);
         // Can only unlink from a directory
         if !parent.inode.is_dir() {
             return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
         }
+        // Cannot unlink directory
         let child_id = self.dir_find_entry(&parent, name)?.inode();
         let mut child = self.read_inode(child_id);
-        self.unlink_inode(&mut parent, &mut child, name)
+        if child.inode.is_dir() {
+            return_error!(ErrCode::EISDIR, "Cannot unlink a directory");
+        }
+        self.unlink_inode(&mut parent, &mut child, name, true)
     }
 
-    /// Move a file. This function will not check name conflict.
-    /// Call `lookup` to check beforehand.
+    /// Move a file. This function will not check name conflict,
+    /// call `lookup` to check beforehand. Caller should ensure
+    /// `parent/name` and `new_parent/new_name` are different.
     ///
     /// # Params
     ///
@@ -338,16 +348,15 @@ impl Ext4 {
                 new_parent.id
             );
         }
+        let child_entry = self.dir_find_entry(&parent, name)?;
+        let mut child = self.read_inode(child_entry.inode());
 
-        let child_id = self.dir_find_entry(&parent, name)?;
-        let mut child = self.read_inode(child_id.inode());
-
-        self.link_inode(&mut new_parent, &mut child, new_name)?;
-        self.unlink_inode(&mut parent, &mut child, name)
+        self.unlink_inode(&mut parent, &mut child, name, false)?;
+        self.link_inode(&mut new_parent, &mut child, new_name)
     }
 
-    /// Create a directory. This function will not check name conflict.
-    /// Call `lookup` to check beforehand.
+    /// Create a directory. This function will not check name conflict,
+    /// call `lookup` to check beforehand.
     ///
     /// # Params
     ///
@@ -372,6 +381,10 @@ impl Ext4 {
         // Create file/directory
         let mode = mode & InodeMode::PERM_MASK | InodeMode::DIRECTORY;
         let mut child = self.create_inode(mode)?;
+        // Add "." entry
+        let child_self = child.clone();
+        self.dir_add_entry(&mut child, &child_self, ".")?;
+        child.inode.set_link_count(1);
         // Link the new inode
         self.link_inode(&mut parent, &mut child, name)?;
         Ok(child.id)
@@ -452,7 +465,7 @@ impl Ext4 {
             return_error!(ErrCode::ENOTEMPTY, "Directory {} is not empty", child.id);
         }
         // Remove directory entry
-        self.unlink_inode(&mut parent, &mut child, name)
+        self.unlink_inode(&mut parent, &mut child, name, true)
     }
 
     /// Get extended attribute of a file.
@@ -486,8 +499,8 @@ impl Ext4 {
         }
     }
 
-    /// Set extended attribute of a file. This function will not check name conflict.
-    /// Call `getxattr` to check beforehand.
+    /// Set extended attribute of a file. This function will not check name conflict,
+    /// call `getxattr` to check beforehand.
     ///
     /// # Params
     ///