[PATCH v2 3/5] ntfs: report inode metadata errors to fsnotify
From: Baolin Liu
Date: Tue Sep 15 2026 - 22:25:25 EST
From: Baolin Liu <liubaolin@xxxxxxxxxx>
Report inode initialization, writeback, and LogFile failures against
the affected VFS inode while retaining existing allocation and signal
handling. Consume attribute reporting state so callers do not emit a
second FAN_FS_ERROR event for the same failure.
Signed-off-by: Baolin Liu <liubaolin@xxxxxxxxxx>
---
fs/ntfs/inode.c | 52 ++++++++++++++++++++++++++++++++++-------------
fs/ntfs/logfile.c | 2 +-
2 files changed, 39 insertions(+), 15 deletions(-)
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index d5e526d3612a..13f353905fb3 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -689,7 +689,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
unsigned int name_len = 4, flags = 0;
int extend_sys = 0;
dev_t dev = 0;
- bool has_lxmod = false;
+ bool error_reported = false, has_lxmod = false;
bool vol_err = true;
ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no);
@@ -761,6 +761,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
err = ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0, 0, 0, NULL, 0,
ctx);
if (unlikely(err)) {
+ error_reported = ctx->error_reported;
if (err == -ENOENT)
ntfs_error(vi->i_sb, "$STANDARD_INFORMATION attribute is missing.");
goto unm_err_out;
@@ -801,6 +802,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
err = ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx);
if (err) {
if (unlikely(err != -ENOENT)) {
+ error_reported = ctx->error_reported;
ntfs_error(vi->i_sb, "Failed to lookup attribute list attribute.");
goto unm_err_out;
}
@@ -923,6 +925,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
err = ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE,
0, NULL, 0, ctx);
if (unlikely(err)) {
+ error_reported = ctx->error_reported;
if (err == -ENOENT)
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is missing.");
goto unm_err_out;
@@ -1056,6 +1059,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
vi->i_size = ni->initialized_size =
ni->allocated_size = 0;
if (err != -ENOENT) {
+ error_reported = ctx->error_reported;
ntfs_error(vi->i_sb, "Failed to lookup $DATA attribute.");
goto unm_err_out;
}
@@ -1091,6 +1095,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
}
err = extend_sys;
+ error_reported = ctx->error_reported;
ntfs_error(vi->i_sb, "$DATA attribute is missing, err : %d", err);
goto unm_err_out;
}
@@ -1247,12 +1252,12 @@ static int ntfs_read_locked_inode(struct inode *vi)
if (m)
unmap_mft_record(ni);
err_out:
- if (err != -EOPNOTSUPP && err != -ENOMEM &&
+ if (!error_reported && err != -EOPNOTSUPP && err != -ENOMEM &&
err != -EINTR && err != -ERESTARTSYS && vol_err == true) {
ntfs_error(vol->sb,
"Failed with error code %i. Marking corrupt inode 0x%llx as bad. Run chkdsk.",
err, ni->mft_no);
- NVolSetErrors(vol);
+ ntfs_report_file_metadata_error(vi, err);
}
return err;
}
@@ -1286,6 +1291,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
struct attr_record *a;
struct ntfs_attr_search_ctx *ctx;
int err = 0;
+ bool error_reported = false;
ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no);
@@ -1316,8 +1322,10 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
/* Find the attribute. */
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx);
- if (unlikely(err))
+ if (unlikely(err)) {
+ error_reported = ctx->error_reported;
goto unm_err_out;
+ }
a = ctx->attr;
if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) {
if (a->flags & ATTR_COMPRESSION_MASK) {
@@ -1479,9 +1487,9 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
"Failed with error code %i while reading attribute inode (mft_no 0x%llx, type 0x%x, name_len %i). Marking corrupt inode and base inode 0x%llx as bad. Run chkdsk.",
err, ni->mft_no, ni->type, ni->name_len,
base_ni->mft_no);
- if (err != -ENOENT && err != -ENOMEM &&
+ if (!error_reported && err != -ENOENT && err != -ENOMEM &&
err != -EINTR && err != -ERESTARTSYS)
- NVolSetErrors(vol);
+ ntfs_report_file_metadata_error(base_vi, err);
return err;
}
@@ -1528,6 +1536,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
struct ntfs_attr_search_ctx *ctx;
struct index_root *ir;
int err = 0;
+ bool error_reported = false;
ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no);
lockdep_assert_held(&base_ni->mrec_lock);
@@ -1558,6 +1567,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
err = ntfs_attr_lookup(AT_INDEX_ROOT, ni->name, ni->name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx);
if (unlikely(err)) {
+ error_reported = ctx->error_reported;
if (err == -ENOENT)
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is missing.");
goto unm_err_out;
@@ -1630,8 +1640,10 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
m = NULL;
ctx = NULL;
goto skip_large_index_stuff;
- } else
+ } else {
+ error_reported = ctx->error_reported;
ntfs_error(vi->i_sb, "Failed to lookup $INDEX_ALLOCATION attribute.");
+ }
goto unm_err_out;
}
NInoSetIndexAllocPresent(ni);
@@ -1685,6 +1697,9 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
bvi = ntfs_attr_iget(base_vi, AT_BITMAP, ni->name, ni->name_len);
if (IS_ERR(bvi)) {
err = PTR_ERR(bvi);
+ error_reported = err != -ENOENT && err != -ENOMEM &&
+ err != -EOPNOTSUPP && err != -EINTR &&
+ err != -ERESTARTSYS;
if (err != -EINTR && err != -ERESTARTSYS)
ntfs_error(vi->i_sb, "Failed to get bitmap attribute.");
goto unm_err_out;
@@ -1734,9 +1749,9 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
ntfs_error(vi->i_sb,
"Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i.",
err, ni->mft_no, ni->name_len);
- if (err != -EOPNOTSUPP && err != -ENOMEM &&
+ if (!error_reported && err != -EOPNOTSUPP && err != -ENOMEM &&
err != -EINTR && err != -ERESTARTSYS)
- NVolSetErrors(vol);
+ ntfs_report_file_metadata_error(base_vi, err);
return err;
}
@@ -2479,7 +2494,9 @@ int ntfs_truncate_vfs(struct inode *vi, loff_t new_size, loff_t i_size)
*
* Return 0 on success or -errno on error.
*/
-static int ntfs_inode_sync_standard_information(struct inode *vi, struct mft_record *m)
+static int ntfs_inode_sync_standard_information(struct inode *vi,
+ struct mft_record *m,
+ bool *error_reported)
{
struct ntfs_inode *ni = NTFS_I(vi);
struct ntfs_attr_search_ctx *ctx;
@@ -2495,6 +2512,8 @@ static int ntfs_inode_sync_standard_information(struct inode *vi, struct mft_rec
err = ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0,
CASE_SENSITIVE, 0, NULL, 0, ctx);
if (unlikely(err)) {
+ if (ctx->error_reported)
+ *error_reported = true;
ntfs_attr_put_search_ctx(ctx);
return err;
}
@@ -2749,7 +2768,7 @@ int __ntfs_write_inode(struct inode *vi, int sync)
struct ntfs_inode *mft_ni = NTFS_I(ni->vol->mft_ino);
struct mft_record *m;
int err = 0;
- bool need_iput = false;
+ bool error_reported = false, need_iput = false;
ntfs_debug("Entering for %sinode 0x%llx.", NInoAttr(ni) ? "attr " : "",
ni->mft_no);
@@ -2783,13 +2802,14 @@ int __ntfs_write_inode(struct inode *vi, int sync)
if (NInoNonResident(ni) && NInoRunlistDirty(ni)) {
down_write(&ni->runlist.lock);
- err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
+ err = ntfs_attr_update_mapping_pairs_locked_reported(ni, 0, ni,
+ &error_reported);
if (!err)
NInoClearRunlistDirty(ni);
up_write(&ni->runlist.lock);
}
- err = ntfs_inode_sync_standard_information(vi, m);
+ err = ntfs_inode_sync_standard_information(vi, m, &error_reported);
if (err)
goto unm_err_out;
@@ -2889,7 +2909,11 @@ int __ntfs_write_inode(struct inode *vi, int sync)
mark_inode_dirty(vi);
else {
ntfs_error(vi->i_sb, "Failed (error %i): Run chkdsk.", -err);
- NVolSetErrors(ni->vol);
+ if (!error_reported && err != -EINTR &&
+ err != -ERESTARTSYS)
+ ntfs_report_file_metadata_error(vi, err);
+ else
+ NVolSetErrors(ni->vol);
}
if (need_iput)
iput(vi);
diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c
index 024ddee42dc8..9be0506fd5c0 100644
--- a/fs/ntfs/logfile.c
+++ b/fs/ntfs/logfile.c
@@ -768,7 +768,7 @@ bool ntfs_empty_logfile(struct inode *log_vi)
rl_err:
ntfs_error(sb, "Runlist is corrupt. Unmount and run chkdsk.");
dirty_err:
- NVolSetErrors(vol);
+ ntfs_report_file_metadata_error(log_vi, -EIO);
err = -EIO;
err:
kvfree(empty_buf);
--
2.51.0