diff --git a/kernel/driver/fs/ext2/src/data.rs b/kernel/driver/fs/ext2/src/data.rs
index b0b0d6dc..885925b8 100644
--- a/kernel/driver/fs/ext2/src/data.rs
+++ b/kernel/driver/fs/ext2/src/data.rs
@@ -241,6 +241,10 @@ impl Inode {
         unsafe { GroupId::from_raw(self.uid as _) }
     }
 
+    pub fn is_deleted(&self) -> bool {
+        self.hard_links == 0
+    }
+
     pub fn metadata(&self, fs: &Ext2Fs, ino: u32) -> Metadata {
         Metadata {
             uid: self.user_id(),
diff --git a/kernel/driver/fs/ext2/src/dir/mod.rs b/kernel/driver/fs/ext2/src/dir/mod.rs
index 108627d2..910eee86 100644
--- a/kernel/driver/fs/ext2/src/dir/mod.rs
+++ b/kernel/driver/fs/ext2/src/dir/mod.rs
@@ -170,19 +170,9 @@ impl DirectoryNode {
 
         let child = InodeAccess::new(self.inode.cache().clone(), ino);
 
+        child.link().await?;
         let ty = child
-            .amap_mut(async |child_inode| {
-                // Get child inode type indicator which may or may not be used in the dirent
-                let ty = child_inode
-                    .mode
-                    .dirent_indicator()
-                    .ok_or(Error::InvalidArgument)?;
-
-                child_inode.inc_hard_count();
-                child_inode.dtime = 0;
-
-                Ok(ty)
-            })
+            .map(|inode| inode.mode.dirent_indicator().ok_or(Error::InvalidArgument))
             .await?;
 
         // Insert the entry
@@ -195,7 +185,9 @@ impl DirectoryNode {
                 // Otherwise, allocate a new block and extend the directory
                 extend(inode, &self.fs, name, ino, ty).await
             })
-            .await
+            .await?;
+
+        Ok(())
     }
 
     async fn prepare_for_removal(&self) -> Result<(), Error> {
diff --git a/kernel/driver/fs/ext2/src/inode/cache.rs b/kernel/driver/fs/ext2/src/inode/cache.rs
index 548558c0..f8a6d814 100644
--- a/kernel/driver/fs/ext2/src/inode/cache.rs
+++ b/kernel/driver/fs/ext2/src/inode/cache.rs
@@ -46,7 +46,7 @@ impl InodeAccess {
         &self,
         mapper: F,
     ) -> Result<T, Error> {
-        let inode = self.inode_cache.entry(self.ino).await?;
+        let inode = self.inode_cache.get(self.ino).await?;
         let lock = inode.read();
         mapper(&lock.inode)
     }
@@ -55,7 +55,7 @@ impl InodeAccess {
         &self,
         mapper: F,
     ) -> Result<T, Error> {
-        let inode = self.inode_cache.entry(self.ino).await?;
+        let inode = self.inode_cache.get(self.ino).await?;
         let mut lock = inode.write();
         let result = mapper(&mut lock.inode);
         self.inode_cache.put(self.ino, &mut lock).await?;
@@ -66,7 +66,7 @@ impl InodeAccess {
         &self,
         mapper: F,
     ) -> Result<T, Error> {
-        let inode = self.inode_cache.entry(self.ino).await?;
+        let inode = self.inode_cache.get(self.ino).await?;
         let lock = inode.read();
         mapper(&lock.inode).await
     }
@@ -75,7 +75,7 @@ impl InodeAccess {
         &self,
         mapper: F,
     ) -> Result<T, Error> {
-        let inode = self.inode_cache.entry(self.ino).await?;
+        let inode = self.inode_cache.get(self.ino).await?;
         let mut lock = inode.write();
         let result = mapper(&mut lock.inode).await;
         self.inode_cache.put(self.ino, &mut lock).await?;
@@ -176,6 +176,7 @@ impl InodeAccess {
     ) -> Result<NodeRef, Error> {
         log::info!("ext2: allocated inode #{ino}");
 
+        let cache = fs.inode_cache.get().clone();
         let now = real_time().seconds as u32;
 
         let mut imode = InodeMode::default_for_type(ty);
@@ -193,7 +194,7 @@ impl InodeAccess {
         )
         .await?;
 
-        let this = InodeAccess::new(fs.inode_cache.get().clone(), ino);
+        let this = InodeAccess::new(cache, ino);
         let fs = fs.clone();
         let node = match ty {
             FileType::Directory => DirectoryNode::create(fs, this, parent_ino).await?,
@@ -205,6 +206,15 @@ impl InodeAccess {
         Ok(node)
     }
 
+    pub async fn link(&self) -> Result<(), Error> {
+        let inode = self.inode_cache.entry(self.ino, true).await?;
+        let mut inode = inode.write();
+        inode.hard_links += 1;
+        inode.dtime = 0;
+        self.inode_cache.put(self.ino, &mut inode).await?;
+        Ok(())
+    }
+
     pub async fn allocate(
         fs: &Arc<Ext2Fs>,
         ty: FileType,
@@ -256,22 +266,35 @@ impl InodeCache {
         Ok(())
     }
 
-    async fn fetch_inode(&self, ino: u32) -> Result<Arc<IrqSafeRwLock<InodeHolder>>, Error> {
+    async fn fetch_inode(
+        &self,
+        ino: u32,
+        allow_deleted: bool,
+    ) -> Result<Arc<IrqSafeRwLock<InodeHolder>>, Error> {
         let inode = self.fs.read_inode(ino).await?;
+
+        if inode.is_deleted() && !allow_deleted {
+            return Err(Error::DoesNotExist);
+        }
+
         Ok(Arc::new(IrqSafeRwLock::new(InodeHolder {
             inode,
             dirty: false,
         })))
     }
 
-    async fn entry(&self, ino: u32) -> Result<Arc<IrqSafeRwLock<InodeHolder>>, Error> {
+    async fn entry(
+        &self,
+        ino: u32,
+        allow_deleted: bool,
+    ) -> Result<Arc<IrqSafeRwLock<InodeHolder>>, Error> {
         if ino < 1 || ino > self.fs.total_inodes {
             return Err(Error::InvalidFile);
         }
 
         let mut lock = self.cache.lock().await;
         let (value, evicted) = lock
-            .try_get_or_insert_with_async(ino, || self.fetch_inode(ino))
+            .try_get_or_insert_with_async(ino, || self.fetch_inode(ino, allow_deleted))
             .await?;
         let value = value.clone();
 
@@ -284,6 +307,10 @@ impl InodeCache {
         Ok(value)
     }
 
+    async fn get(&self, ino: u32) -> Result<Arc<IrqSafeRwLock<InodeHolder>>, Error> {
+        self.entry(ino, false).await
+    }
+
     async fn put(&self, ino: u32, holder: &mut InodeHolder) -> Result<(), Error> {
         if self.synchronous {
             // Immediately write-back
diff --git a/kernel/driver/fs/ext2/src/lib.rs b/kernel/driver/fs/ext2/src/lib.rs
index ed50efcb..c077d7c5 100644
--- a/kernel/driver/fs/ext2/src/lib.rs
+++ b/kernel/driver/fs/ext2/src/lib.rs
@@ -272,10 +272,6 @@ impl Ext2Fs {
             inode_cache: OneTimeInit::new(),
 
             sb_position: data::SUPERBLOCK_OFFSET,
-            // state: IrqSafeRwLock::new(State {
-            //     superblock,
-            //     dirty: false,
-            // }),
             bgdt,
 
             required_features,