[PATCH v4 03/31] ntfs: add filesystem metadata error reporting helpers

From: Baolin Liu

Date: Tue Oct 20 2026 - 04:49:06 EST


From: Baolin Liu <liubaolin@xxxxxxxxxx>

Add file and volume helpers to report metadata errors through the generic
deferred fsnotify infrastructure while retaining the volume error bit.
Pass the caller's errno unchanged.

Move ntfs_inode_base() to inode.h and use it to identify the base file for
attribute and extent inodes. Fall back to a volume report for I_FREEING
or I_WILL_FREE, when retaining the inode for deferred reporting is unsafe.
When SB_ACTIVE is clear, record only the volume error state.

Signed-off-by: Baolin Liu <liubaolin@xxxxxxxxxx>
---
fs/ntfs/inode.c | 7 -------
fs/ntfs/inode.h | 8 ++++++++
fs/ntfs/super.c | 29 +++++++++++++++++++++++++++++
fs/ntfs/volume.h | 3 +++
4 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 52e22022b4e7..c8560dd47d44 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -3532,13 +3532,6 @@ void ntfs_destroy_ext_inode(struct ntfs_inode *ni)
kmem_cache_free(ntfs_inode_cache, ni);
}

-static struct ntfs_inode *ntfs_inode_base(struct ntfs_inode *ni)
-{
- if (ni->nr_extents == -1)
- return ni->ext.base_ntfs_ino;
- return ni;
-}
-
static int ntfs_attr_position(__le32 type, struct ntfs_attr_search_ctx *ctx)
{
int err;
diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h
index cf447fb51725..952398753ed1 100644
--- a/fs/ntfs/inode.h
+++ b/fs/ntfs/inode.h
@@ -298,6 +298,14 @@ static inline bool ntfs_inode_is_named_stream(struct ntfs_inode *ni)
return NInoAttr(ni) && ni->type == AT_DATA && ni->name_len;
}

+/* Extent and attached attribute inodes refer to their base MFT inode. */
+static inline struct ntfs_inode *ntfs_inode_base(struct ntfs_inode *ni)
+{
+ if (ni->nr_extents == -1)
+ return ni->ext.base_ntfs_ino;
+ return ni;
+}
+
/* Return the base MFT inode for an attribute inode, or @ni itself. */
static inline struct ntfs_inode *ntfs_base_inode(struct ntfs_inode *ni)
{
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 71f0aea46bf8..df09c277ee54 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -14,6 +14,7 @@
#include <linux/sched/mm.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
+#include <linux/fserror.h>

#include "sysctl.h"
#include "logfile.h"
@@ -426,6 +427,34 @@ const struct option_t on_errors_arr[] = {
{ 0, NULL }
};

+/*
+ * Report confirmed metadata corruption as -EFSCORRUPTED, matching the errno
+ * returned to the caller. Preserve the errno of I/O and recovery failures
+ * so listeners can distinguish their causes.
+ */
+void ntfs_report_metadata_error(struct ntfs_volume *vol, int error)
+{
+ NVolSetErrors(vol);
+ if (vol->sb->s_flags & SB_ACTIVE)
+ fserror_report_metadata(vol->sb, error, GFP_ATOMIC);
+}
+
+void ntfs_report_file_metadata_error(struct inode *inode, int error)
+{
+ struct ntfs_inode *ni = NTFS_I(inode);
+
+ /* Attribute inodes share the file handle of their base inode. */
+ inode = VFS_I(ntfs_inode_base(ni));
+ /* Eviction cannot retain an inode reference for the deferred report. */
+ if (inode_state_read_once(inode) & (I_FREEING | I_WILL_FREE)) {
+ ntfs_report_metadata_error(NTFS_SB(inode->i_sb), error);
+ return;
+ }
+ NVolSetErrors(NTFS_SB(inode->i_sb));
+ if (inode->i_sb->s_flags & SB_ACTIVE)
+ fserror_report_file_metadata(inode, error, GFP_ATOMIC);
+}
+
void ntfs_handle_error(struct super_block *sb)
{
struct ntfs_volume *vol = NTFS_SB(sb);
diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h
index ed5c0ccb9cd9..27f58beb4b2d 100644
--- a/fs/ntfs/volume.h
+++ b/fs/ntfs/volume.h
@@ -262,6 +262,9 @@ DEFINE_NVOL_BIT_OPS(SymlinkNative)
DEFINE_NVOL_BIT_OPS(MftBootstrap)
DEFINE_NVOL_BIT_OPS(StreamsWindows)

+void ntfs_report_metadata_error(struct ntfs_volume *vol, int error);
+void ntfs_report_file_metadata_error(struct inode *inode, int error);
+
static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr)
{
if (!NVolFreeClusterKnown(vol))
--
2.51.0