mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Baolin Liu <liubaolin12138@163.com>
To: linkinjeon@kernel.org, hyc.lee@gmail.com
Cc: ntfs@lists.linux.dev, linux-kernel@vger.kernel.org,
	Baolin Liu <liubaolin@kylinos.cn>
Subject: [PATCH v2 2/5] ntfs: report attribute errors to fsnotify
Date: Wed, 16 Sep 2026 10:23:42 +0800	[thread overview]
Message-ID: <20260916022345.1151836-3-liubaolin12138@163.com> (raw)
In-Reply-To: <20260916022345.1151836-1-liubaolin12138@163.com>

From: Baolin Liu <liubaolin@kylinos.cn>

Add a file-level helper and report attribute validation and lookup
failures against the affected inode. Cover bitmap, cluster, and
ATTRIBUTE_LIST rollback errors while preserving the original errno.
Track reports in search contexts and through mapping-pairs updates.

Signed-off-by: Baolin Liu <liubaolin@kylinos.cn>
---
 fs/ntfs/attrib.c   | 71 ++++++++++++++++++++++++++++++++++------------
 fs/ntfs/attrib.h   |  6 ++++
 fs/ntfs/attrlist.c | 16 +++++++----
 fs/ntfs/bitmap.c   |  2 +-
 fs/ntfs/super.c    |  7 +++++
 fs/ntfs/volume.h   |  1 +
 6 files changed, 78 insertions(+), 25 deletions(-)

diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index b01cbc9eea4a..136bda6433bb 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -205,6 +205,8 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, struct ntfs_attr_sea
 			ntfs_attr_put_search_ctx(ctx);
 		unmap_mft_record(base_ni);
 	} else if (ctx_needs_reset) {
+		if (ctx->error_reported)
+			old_ctx.error_reported = true;
 		/*
 		 * If there is no attribute list, restoring the search context
 		 * is accomplished simply by copying the saved context back over
@@ -838,6 +840,7 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
 		const u8 *val, const u32 val_len, struct ntfs_attr_search_ctx *ctx)
 {
 	struct attr_record *a;
+	struct ntfs_inode *base_ni;
 	struct ntfs_volume *vol = ctx->ntfs_ino->vol;
 	__le16 *upcase = vol->upcase;
 	u32 upcase_len = vol->upcase_len;
@@ -964,7 +967,12 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
 	}
 	ntfs_error(vol->sb, "mft %#llx, type %#x is corrupt. Run chkdsk.",
 		   (long long)ctx->ntfs_ino->mft_no, le32_to_cpu(type));
-	NVolSetErrors(vol);
+	if (ctx->ntfs_ino->nr_extents >= 0)
+		base_ni = ctx->ntfs_ino;
+	else
+		base_ni = ctx->ntfs_ino->ext.base_ntfs_ino;
+	ntfs_report_file_metadata_error(VFS_I(base_ni), -EIO);
+	ctx->error_reported = true;
 	return -EIO;
 }
 
@@ -1501,8 +1509,14 @@ static int ntfs_external_attr_find(const __le32 type,
 		err = -EIO;
 	}
 
-	if (err != -ENOMEM)
-		NVolSetErrors(vol);
+	if (err != -ENOMEM) {
+		if (err != -EINTR && err != -ERESTARTSYS) {
+			ntfs_report_file_metadata_error(VFS_I(base_ni), err);
+			ctx->error_reported = true;
+		} else {
+			NVolSetErrors(vol);
+		}
+	}
 	return err;
 not_found:
 	/*
@@ -1597,6 +1611,7 @@ int ntfs_attr_lookup(const __le32 type, const __le16 *name,
 	struct ntfs_inode *base_ni;
 
 	ntfs_debug("Entering.");
+	ctx->error_reported = false;
 	if (ctx->base_ntfs_ino)
 		base_ni = ctx->base_ntfs_ino;
 	else
@@ -1633,6 +1648,7 @@ static bool ntfs_attr_init_search_ctx(struct ntfs_attr_search_ctx *ctx,
 	/* Sanity checks are performed elsewhere. */
 	ctx->attr = (struct attr_record *)((u8 *)mrec + le16_to_cpu(mrec->attrs_offset));
 	ctx->is_first = true;
+	ctx->error_reported = false;
 	ctx->ntfs_ino = ni;
 	ctx->al_entry = NULL;
 	ctx->base_ntfs_ino = NULL;
@@ -2233,10 +2249,11 @@ int ntfs_attr_make_non_resident(struct ntfs_inode *ni, const u32 data_size)
 rl_err_out:
 	up_write(&ni->runlist.lock);
 	if (rl) {
-		if (ntfs_cluster_free_from_rl(vol, rl) < 0) {
+		err2 = ntfs_cluster_free_from_rl(vol, rl);
+		if (err2 < 0) {
 			ntfs_error(vol->sb,
 				"Failed to release allocated cluster(s) in error code path.  Run chkdsk to recover the lost cluster(s).");
-			NVolSetErrors(vol);
+			ntfs_report_file_metadata_error(vi, err2);
 		}
 		kvfree(rl);
 folio_err_out:
@@ -3720,7 +3737,8 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
 static int __ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni,
 					    s64 from_vcn,
 					    struct ntfs_inode *locked_ni,
-					    bool defer_attrlist)
+					    bool defer_attrlist,
+					    bool *error_reported)
 {
 	struct ntfs_attr_search_ctx *ctx;
 	struct ntfs_inode *base_ni;
@@ -4086,8 +4104,11 @@ static int __ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni,
 	return 0;
 
 put_err_out:
-	if (ctx)
+	if (ctx) {
+		if (error_reported && ctx->error_reported)
+			*error_reported = true;
 		ntfs_attr_put_search_ctx(ctx);
+	}
 	return err;
 }
 
@@ -4096,7 +4117,16 @@ int ntfs_attr_update_mapping_pairs_locked(struct ntfs_inode *ni,
 					 struct ntfs_inode *locked_ni)
 {
 	return __ntfs_attr_update_mapping_pairs(ni, from_vcn, locked_ni,
-						false);
+						false, NULL);
+}
+
+int ntfs_attr_update_mapping_pairs_locked_reported(struct ntfs_inode *ni,
+						   s64 from_vcn,
+						   struct ntfs_inode *locked_ni,
+						   bool *error_reported)
+{
+	return __ntfs_attr_update_mapping_pairs(ni, from_vcn, locked_ni,
+						false, error_reported);
 }
 
 int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
@@ -4704,7 +4734,7 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
 		/* Restore mapping pairs. */
 		if (ni != locked_ni)
 			down_read(&ni->runlist.lock);
-		if (__ntfs_attr_update_mapping_pairs(ni, 0, locked_ni, true))
+		if (__ntfs_attr_update_mapping_pairs(ni, 0, locked_ni, true, NULL))
 			ntfs_error(sb, "Failed to restore old mapping pairs");
 		if (ni != locked_ni)
 			up_read(&ni->runlist.lock);
@@ -5140,7 +5170,8 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
 	struct runlist_element *old_rl = NULL;
 	s64 vcn = vcn_start, lcn, clu_count;
 	s64 lcn_seek_from = -1;
-	int err = 0;
+	int err = 0, err2;
+	bool error_reported = false;
 	size_t new_rl_count, old_rl_count;
 
 	err = ntfs_attr_map_whole_runlist(ni);
@@ -5239,10 +5270,11 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
 			 old_rl_count * sizeof(*old_rl), GFP_NOFS);
 	if (!old_rl) {
 		err = -ENOMEM;
-		if (ntfs_cluster_free_from_rl(vol, rlc)) {
+		err2 = ntfs_cluster_free_from_rl(vol, rlc);
+		if (err2) {
 			ntfs_error(vol->sb,
 				   "Failed to free cluster allocation after runlist backup failure.");
-			NVolSetErrors(vol);
+			ntfs_report_file_metadata_error(VFS_I(ni), err2);
 		}
 		kvfree(rlc);
 		goto out;
@@ -5272,13 +5304,13 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
 		ntfs_attr_reinit_search_ctx(ctx);
 		err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
 		if (err) {
-			int err2;
-
 			err2 = ntfs_cluster_free(ni, vcn, clu_count, ctx);
 			if (err2 < 0 || err2 != clu_count) {
 				ntfs_error(vol->sb,
 					   "Failed to free cluster allocation. Leaving inconsistent metadata.\n");
-				NVolSetErrors(vol);
+				if (err2 >= 0)
+					err2 = -EIO;
+				ntfs_report_file_metadata_error(VFS_I(ni), err2);
 				goto out;
 			}
 
@@ -5290,11 +5322,14 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
 			ni->runlist.rl = old_rl;
 			ni->runlist.count = old_rl_count;
 			old_rl = NULL;
-			if (ntfs_attr_update_mapping_pairs_locked(
-					ni, 0, ni)) {
+			err2 = ntfs_attr_update_mapping_pairs_locked_reported(ni, 0,
+									      ni,
+									      &error_reported);
+			if (err2) {
 				ntfs_error(vol->sb,
 					   "Failed to restore mapping pairs after allocation rollback.\n");
-				NVolSetErrors(vol);
+				if (!error_reported)
+					ntfs_report_file_metadata_error(VFS_I(ni), err2);
 			}
 		}
 	} else {
diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h
index 6b4fa9f57640..56cec2c8b914 100644
--- a/fs/ntfs/attrib.h
+++ b/fs/ntfs/attrib.h
@@ -21,6 +21,7 @@ extern __le16 AT_UNNAMED[];
  * @mapped_mrec: true if @mrec was mapped by the search functions
  * @attr: attribute record in @mrec where to begin/continue search
  * @is_first: if true ntfs_attr_lookup() begins search with @attr, else after
+ * @error_reported: current lookup already reported its metadata error
  * @ntfs_ino: Inode owning this attribute search
  * @al_entry: Current attribute list entry
  * @base_ntfs_ino: Base inode
@@ -44,6 +45,7 @@ struct ntfs_attr_search_ctx {
 	bool mapped_mrec;
 	struct attr_record *attr;
 	bool is_first;
+	bool error_reported;
 	struct ntfs_inode *ntfs_ino;
 	struct attr_list_entry *al_entry;
 	struct ntfs_inode *base_ntfs_ino;
@@ -142,6 +144,10 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn);
 int ntfs_attr_update_mapping_pairs_locked(struct ntfs_inode *ni,
 					  s64 from_vcn,
 					 struct ntfs_inode *locked_ni);
+int ntfs_attr_update_mapping_pairs_locked_reported(struct ntfs_inode *ni,
+						   s64 from_vcn,
+						   struct ntfs_inode *locked_ni,
+						   bool *error_reported);
 struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64 *lcn);
 
 /*
diff --git a/fs/ntfs/attrlist.c b/fs/ntfs/attrlist.c
index 7c09d02fd941..69365bf94d91 100644
--- a/fs/ntfs/attrlist.c
+++ b/fs/ntfs/attrlist.c
@@ -78,7 +78,8 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
 	s64 old_alloc_size;
 	size_t old_rl_count, new_rl_count;
 	unsigned long flags;
-	int err, restore_err;
+	int err, free_err, restore_err;
+	bool error_reported = false;
 	if (attr_ni->mft_no != FILE_MFT || !NInoNonResident(attr_ni) ||
 		min_alloc_size < 0)
 		return -EINVAL;
@@ -159,11 +160,12 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
 		goto restore_old_runlist;
 
 	/* The new mapping is now authoritative; release the old data runs. */
-	if (ntfs_cluster_free_from_rl(vol, old_rl)) {
+	free_err = ntfs_cluster_free_from_rl(vol, old_rl);
+	if (free_err) {
 		ntfs_error(vol->sb,
 			   "Failed to free old ATTRIBUTE_LIST extent: inode %#llx",
 			   (long long)attr_ni->mft_no);
-		NVolSetErrors(vol);
+		ntfs_report_file_metadata_error(attr_vi, free_err);
 	}
 	kvfree(old_rl);
 	kvfree(data);
@@ -179,12 +181,14 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
 	attr_ni->allocated_size = old_alloc_size;
 	write_unlock_irqrestore(&attr_ni->size_lock, flags);
 
-	restore_err = ntfs_attr_update_mapping_pairs_locked(
-			attr_ni, 0, locked_ni);
+	restore_err = ntfs_attr_update_mapping_pairs_locked_reported(attr_ni, 0,
+								     locked_ni,
+								     &error_reported);
 	if (restore_err) {
 		ntfs_error(vol->sb, "Failed to restore ATTRIBUTE_LIST mapping pairs (%d)",
 			   restore_err);
-		NVolSetErrors(vol);
+		if (!error_reported)
+			ntfs_report_file_metadata_error(attr_vi, restore_err);
 	}
 
 	ntfs_cluster_free_from_rl(vol, new_rl);
diff --git a/fs/ntfs/bitmap.c b/fs/ntfs/bitmap.c
index 1840b7d84c62..c3faad256e2e 100644
--- a/fs/ntfs/bitmap.c
+++ b/fs/ntfs/bitmap.c
@@ -286,7 +286,7 @@ int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
 		ntfs_error(vi->i_sb,
 			"Failed to map subsequent page (error %i) and rollback failed (error %i). Aborting and leaving inconsistent metadata. Unmount and run chkdsk.",
 			err, pos);
-		NVolSetErrors(NTFS_SB(vi->i_sb));
+		ntfs_report_file_metadata_error(vi, pos);
 	}
 	return err;
 }
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 340aad497b88..7f6812d27fd1 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -345,6 +345,13 @@ void ntfs_report_metadata_error(struct ntfs_volume *vol, int error)
 		fserror_report_metadata(vol->sb, error, GFP_ATOMIC);
 }
 
+void ntfs_report_file_metadata_error(struct inode *inode, int error)
+{
+	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 55d08319820e..7daac7048a4c 100644
--- a/fs/ntfs/volume.h
+++ b/fs/ntfs/volume.h
@@ -251,6 +251,7 @@ DEFINE_NVOL_BIT_OPS(NativeSymlinkRel)
 DEFINE_NVOL_BIT_OPS(SymlinkNative)
 
 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)
 {
-- 
2.51.0


  parent reply	other threads:[~2026-09-16  2:24 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-16  2:23 [PATCH v2 0/5] ntfs: report filesystem errors through fsnotify Baolin Liu
2026-09-16  2:23 ` [PATCH v2 1/5] ntfs: report allocation metadata errors to fsnotify Baolin Liu
2026-09-16  2:23 ` Baolin Liu [this message]
2026-09-16  7:38   ` [PATCH v2 2/5] ntfs: report attribute " Hyunchul Lee
2026-09-17  4:21     ` liubaolin
2026-09-16  2:23 ` [PATCH v2 3/5] ntfs: report inode metadata " Baolin Liu
2026-09-16  2:23 ` [PATCH v2 4/5] ntfs: report MFT " Baolin Liu
2026-09-16  2:23 ` [PATCH v2 5/5] ntfs: report shutdown " Baolin Liu
2026-09-16  2:40 ` [PATCH v2 0/5] ntfs: report filesystem errors through fsnotify liubaolin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260916022345.1151836-3-liubaolin12138@163.com \
    --to=liubaolin12138@163.com \
    --cc=hyc.lee@gmail.com \
    --cc=linkinjeon@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liubaolin@kylinos.cn \
    --cc=ntfs@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®