[PATCH] f2fs: avoid false corruption report for stale file handles
From: Jiahao Luo
Date: Sat Oct 24 2026 - 07:44:37 EST
File handles do not pin inodes. Once an unlinked inode loses its last
reference, F2FS may free its nid and later allocate that nid as a
non-inode node belonging to another file.
f2fs_nfs_get_inode() currently uses f2fs_iget(), which requests an inode
node before it can compare i_generation. If a stale handle's nid now
identifies a non-inode node, footer validation reports an inconsistent
node, sets SBI_NEED_FSCK, and records a filesystem error even though the
filesystem is consistent.
Add a handle-specific iget path that reads the nid as NODE_TYPE_REGULAR.
This preserves the generic node footer checks, but allows a valid
non-inode node to be recognized and returned as -ESTALE without parsing
it as an inode. Keep the existing strict inode lookup for all internal
callers. A nid reused as another inode remains subject to the existing
generation check in f2fs_nfs_get_inode().
Fixes: e34438c903b6 ("f2fs: fix to do sanity check with node footer and iblocks")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Jiahao Luo <luojiahao.fs@xxxxxxxxxxxxx>
---
fs/f2fs/f2fs.h | 1 +
fs/f2fs/inode.c | 27 +++++++++++++++++++++++----
fs/f2fs/super.c | 7 +++----
3 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index aedba8b79baa..019fb2383165 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3999,6 +3999,7 @@ void f2fs_set_inode_flags(struct inode *inode);
bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct f2fs_cached_block *entry);
void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct f2fs_cached_block *entry);
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino);
+struct inode *f2fs_iget_handle(struct super_block *sb, unsigned long ino);
struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino);
int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink);
void f2fs_update_inode(struct inode *inode, struct f2fs_cached_block *entry);
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 9164a2b5d9f0..4260e8264555 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -422,7 +422,7 @@ static void init_idisk_time(struct inode *inode)
fi->i_disk_time[2] = inode_get_mtime(inode);
}
-static int do_read_inode(struct inode *inode)
+static int do_read_inode(struct inode *inode, bool from_handle)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -434,9 +434,17 @@ static int do_read_inode(struct inode *inode)
if (f2fs_check_nid_range(sbi, inode->i_ino))
return -EINVAL;
- node_entry = f2fs_get_inode_cache(sbi, inode->i_ino);
+ if (from_handle)
+ node_entry = f2fs_get_node_cache(sbi, inode->i_ino,
+ NODE_TYPE_REGULAR);
+ else
+ node_entry = f2fs_get_inode_cache(sbi, inode->i_ino);
if (IS_ERR(node_entry))
return PTR_ERR(node_entry);
+ if (from_handle && !IS_INODE(sbi, node_entry)) {
+ f2fs_put_cache(node_entry, true);
+ return -ESTALE;
+ }
ri = F2FS_INODE(node_entry);
@@ -574,7 +582,8 @@ static int do_read_inode(struct inode *inode)
return 0;
}
-struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
+static struct inode *__f2fs_iget(struct super_block *sb, unsigned long ino,
+ bool from_handle)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode;
@@ -589,7 +598,7 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
return inode;
}
- ret = do_read_inode(inode);
+ ret = do_read_inode(inode, from_handle);
if (ret)
goto bad_inode;
@@ -637,6 +646,16 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
return ERR_PTR(ret);
}
+struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
+{
+ return __f2fs_iget(sb, ino, false);
+}
+
+struct inode *f2fs_iget_handle(struct super_block *sb, unsigned long ino)
+{
+ return __f2fs_iget(sb, ino, true);
+}
+
struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino)
{
struct inode *inode;
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 857cc2b29946..4d68737da65e 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -3902,11 +3902,10 @@ static struct inode *f2fs_nfs_get_inode(struct super_block *sb,
return ERR_PTR(-ESTALE);
/*
- * f2fs_iget isn't quite right if the inode is currently unallocated!
- * However f2fs_iget currently does appropriate checks to handle stale
- * inodes so everything is OK.
+ * A deleted inode's nid can be reused for a non-inode node. Treat such
+ * reuse as a stale file handle instead of filesystem corruption.
*/
- inode = f2fs_iget(sb, ino);
+ inode = f2fs_iget_handle(sb, ino);
if (IS_ERR(inode))
return ERR_CAST(inode);
if (unlikely(generation && inode->i_generation != generation)) {
base-commit: fd106ada4dee4b8adefec791ee9b9681b7b77501
--
2.20.1