From nobody Fri Sep 25 06:47:19 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C71CD37C112 for ; Wed, 16 Sep 2026 02:24:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.2 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525453; cv=none; b=ZKyT5VW5bAa6Rb/UYDD5dKVNnrZGnHAeEUVZfNS5JdNeb5OTf2OezD2MpDCKIcPrHARJs9hHPiExzNgnhGuGQagqrLG5I/LX0BO0w8V23ju3bUjfVHMB6j/2oKJF3ZRmdn7aY/qIIx/SldEorGjpUX1iAW1pk7IGucuq5rvDku0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525453; c=relaxed/simple; bh=1pq+jBO6Dp1A8O/meEM7LBdcx1VTsWePkUpzLOPE8HE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TZDxLl30a9ER6FQDq36/9azBC7XgGpyplnq+U2TO6+6e3fa4J6jjoUik5nou3iW2WE0fJH3yjYOZTkyInRCgwYb0hTRjrD1/e32kx6H37CYAuS26gTyVIbgqyGZUmDqyBxpUj58Nk6LeBY5e+9NuLnD1RxW6a0oUFqsUuWHUQR4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=PeXPHFlJ; arc=none smtp.client-ip=117.135.210.2 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="PeXPHFlJ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=eW IwSlaYHCxquBMYqYFpMo6jyTyzxF2kh5MhwtVDnUg=; b=PeXPHFlJLXMz0SZTTC bOI2Ih4IBgq2TEeIciEcwjAy4onM/3N7Y9VTT1iYqWatNJY/KanrHU+jCmIfrlNh P4VnzGQREsfQmlSNStIRiRUrIGFWX+L8f+vouFi0fFqimI+QeHrLZrtBvAoppzWG 01EypueToC37CHK2XoUQ/vKYU= Received: from liubaolin-ThinkPad-E15-Gen-2 (unknown []) by gzga-smtp-mtada-g0-3 (Coremail) with SMTP id _____wD3J2qz_alqzNjjAw--.2073S3; Wed, 16 Sep 2026 10:23:52 +0800 (CST) From: Baolin Liu To: linkinjeon@kernel.org, hyc.lee@gmail.com Cc: ntfs@lists.linux.dev, linux-kernel@vger.kernel.org, Baolin Liu Subject: [PATCH v2 1/5] ntfs: report allocation metadata errors to fsnotify Date: Wed, 16 Sep 2026 10:23:41 +0800 Message-ID: <20260916022345.1151836-2-liubaolin12138@163.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260916022345.1151836-1-liubaolin12138@163.com> References: <20260916022345.1151836-1-liubaolin12138@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3J2qz_alqzNjjAw--.2073S3 X-Coremail-Antispam: 1Uf129KBjvJXoW7AF1rAF43Aw4DGr4kJw1rCrg_yoW5JF4UpF 97ArnakrWkt3WjqwsFk3ySyw4S9a4kGw4Utry5Xw1fXrn3Kr1kXF4kKF1F9F1F9FWUZw1U Xw1jg3y3urWDZr7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07j75r7UUUUU= X-CM-SenderInfo: xolxutxrol0iasrtmqqrwthudrp/xtbC6RiyfWqp-biyjwAA3F Content-Type: text/plain; charset="utf-8" From: Baolin Liu Add a volume-level helper that records metadata failures and notifies FAN_FS_ERROR listeners after mount activation. Use it for cluster allocation and free rollbacks that leave volume metadata inconsistent. Signed-off-by: Baolin Liu --- fs/ntfs/lcnalloc.c | 4 ++-- fs/ntfs/super.c | 8 ++++++++ fs/ntfs/volume.h | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c index 0d6cd08ee2e7..347f9e152588 100644 --- a/fs/ntfs/lcnalloc.c +++ b/fs/ntfs/lcnalloc.c @@ -763,7 +763,7 @@ switch_to_data1_zone: search_zone =3D 2; ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving inconsistent metadata! Unmount= and run chkdsk.", err2); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err2); } /* Free the runlist. */ kvfree(rl); @@ -1044,7 +1044,7 @@ s64 __ntfs_cluster_free(struct ntfs_inode *ni, const = s64 start_vcn, s64 count, ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving inconsistent metadata! Unmoun= t and run chkdsk.", (int)delta); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, delta); } else { ntfs_dec_free_clusters(vol, delta); } diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index b8bb2268e609..340aad497b88 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -14,6 +14,7 @@ #include #include #include +#include =20 #include "sysctl.h" #include "logfile.h" @@ -337,6 +338,13 @@ const struct option_t on_errors_arr[] =3D { { 0, NULL } }; =20 +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_handle_error(struct super_block *sb) { struct ntfs_volume *vol =3D NTFS_SB(sb); diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index 8c17a66b4f22..55d08319820e 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -250,6 +250,8 @@ DEFINE_NVOL_BIT_OPS(DisableSparse) DEFINE_NVOL_BIT_OPS(NativeSymlinkRel) DEFINE_NVOL_BIT_OPS(SymlinkNative) =20 +void ntfs_report_metadata_error(struct ntfs_volume *vol, int error); + static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr) { if (!NVolFreeClusterKnown(vol)) --=20 2.51.0 From nobody Fri Sep 25 06:47:19 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF0EEFC0A for ; Wed, 16 Sep 2026 02:24:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.3 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525457; cv=none; b=K6ZOMpj3ZgqSQ3PUBhRJknPdwYgUxhWWDpCzQdTv5NG5Os/DcKYaRewNhu/lauai2x7jf4ARblEuziwPYFnC4LEzyYgABbr8IiDuU0LsiiNyBuRQQSezCi0ZoxIkQb8oE2oLHRDet12P9Nsu5zTfpMG9V2j030HYptqs9ysV4TI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525457; c=relaxed/simple; bh=XXTjMs4aVhyAu3NjwGU1gn0pA79ggF1DQAsPzmR5Rbk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Vj+9hK4V/S5/wz2ZKK/vO0L2b6smZmH5HB6UqHndmtfh6rbYlr73zZnsTXkVd5Yqz5LcY45JBX0r00CM0OmUGMPKVk2PXcBclXKhX8QZPON8sm2ELtekc5OjC3Cj395bnbt3STXjPVJ9mNnm8l2ARTy5BzGH4R8IV+aj26DoceQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=fSc1ME88; arc=none smtp.client-ip=117.135.210.3 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="fSc1ME88" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=+p GDLOOORkqjfws3dt7zf+KvtByCqkMZDyWZMkAAUFk=; b=fSc1ME88woRBKgxiz9 b4JiG86+f6MMgQStd8LRU2UJg8xAoWuJEt/cYRqsgLpoIKL5CeeQEsMG66DVbv2n H8UXGpfDZgCvWYWLrpwr0/69PnGXxwX9KDmJYhGwXKi3ptGfB6/YEPg0HZQx4Qx9 owx3oQoIrxa1MkRWq7FhN1dJM= Received: from liubaolin-ThinkPad-E15-Gen-2 (unknown []) by gzga-smtp-mtada-g0-3 (Coremail) with SMTP id _____wD3J2qz_alqzNjjAw--.2073S4; Wed, 16 Sep 2026 10:23:54 +0800 (CST) From: Baolin Liu To: linkinjeon@kernel.org, hyc.lee@gmail.com Cc: ntfs@lists.linux.dev, linux-kernel@vger.kernel.org, Baolin Liu Subject: [PATCH v2 2/5] ntfs: report attribute errors to fsnotify Date: Wed, 16 Sep 2026 10:23:42 +0800 Message-ID: <20260916022345.1151836-3-liubaolin12138@163.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260916022345.1151836-1-liubaolin12138@163.com> References: <20260916022345.1151836-1-liubaolin12138@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3J2qz_alqzNjjAw--.2073S4 X-Coremail-Antispam: 1Uf129KBjvJXoW3KFWrAF1UXrWUXw43ZFWrXwb_yoWktrWxpF 9rGwnxtw43try2gws2y34jv34Sk348Wryjvry5Jw13Zan8twn7tF48KFyfuF1S9rWrJwsa qr4jg3y7Cr4UXrUanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jyxRhUUUUU= X-CM-SenderInfo: xolxutxrol0iasrtmqqrwthudrp/xtbCwRqyfWqp-bq9bwAA3C Content-Type: text/plain; charset="utf-8" From: Baolin Liu 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 --- 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 =3D 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 __le= 16 *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 =3D ctx->ntfs_ino->vol; __le16 *upcase =3D vol->upcase; u32 upcase_len =3D vol->upcase_len; @@ -964,7 +967,12 @@ static int ntfs_attr_find(const __le32 type, const __l= e16 *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 >=3D 0) + base_ni =3D ctx->ntfs_ino; + else + base_ni =3D ctx->ntfs_ino->ext.base_ntfs_ino; + ntfs_report_file_metadata_error(VFS_I(base_ni), -EIO); + ctx->error_reported =3D true; return -EIO; } =20 @@ -1501,8 +1509,14 @@ static int ntfs_external_attr_find(const __le32 type, err =3D -EIO; } =20 - if (err !=3D -ENOMEM) - NVolSetErrors(vol); + if (err !=3D -ENOMEM) { + if (err !=3D -EINTR && err !=3D -ERESTARTSYS) { + ntfs_report_file_metadata_error(VFS_I(base_ni), err); + ctx->error_reported =3D 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; =20 ntfs_debug("Entering."); + ctx->error_reported =3D false; if (ctx->base_ntfs_ino) base_ni =3D ctx->base_ntfs_ino; else @@ -1633,6 +1648,7 @@ static bool ntfs_attr_init_search_ctx(struct ntfs_att= r_search_ctx *ctx, /* Sanity checks are performed elsewhere. */ ctx->attr =3D (struct attr_record *)((u8 *)mrec + le16_to_cpu(mrec->attrs= _offset)); ctx->is_first =3D true; + ctx->error_reported =3D false; ctx->ntfs_ino =3D ni; ctx->al_entry =3D NULL; ctx->base_ntfs_ino =3D 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 =3D 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 chkds= k 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 n= tfs_inode *ni, return 0; =20 put_err_out: - if (ctx) + if (ctx) { + if (error_reported && ctx->error_reported) + *error_reported =3D true; ntfs_attr_put_search_ctx(ctx); + } return err; } =20 @@ -4096,7 +4117,16 @@ int ntfs_attr_update_mapping_pairs_locked(struct ntf= s_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); } =20 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 !=3D 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 !=3D 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 =3D NULL; s64 vcn =3D vcn_start, lcn, clu_count; s64 lcn_seek_from =3D -1; - int err =3D 0; + int err =3D 0, err2; + bool error_reported =3D false; size_t new_rl_count, old_rl_count; =20 err =3D ntfs_attr_map_whole_runlist(ni); @@ -5239,10 +5270,11 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s6= 4 vcn_start, s64 *lcn_start, old_rl_count * sizeof(*old_rl), GFP_NOFS); if (!old_rl) { err =3D -ENOMEM; - if (ntfs_cluster_free_from_rl(vol, rlc)) { + err2 =3D 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, s6= 4 vcn_start, s64 *lcn_start, ntfs_attr_reinit_search_ctx(ctx); err =3D ntfs_attr_update_mapping_pairs_locked(ni, 0, ni); if (err) { - int err2; - err2 =3D ntfs_cluster_free(ni, vcn, clu_count, ctx); if (err2 < 0 || err2 !=3D clu_count) { ntfs_error(vol->sb, "Failed to free cluster allocation. Leaving inconsistent metadata.= \n"); - NVolSetErrors(vol); + if (err2 >=3D 0) + err2 =3D -EIO; + ntfs_report_file_metadata_error(VFS_I(ni), err2); goto out; } =20 @@ -5290,11 +5322,14 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s6= 4 vcn_start, s64 *lcn_start, ni->runlist.rl =3D old_rl; ni->runlist.count =3D old_rl_count; old_rl =3D NULL; - if (ntfs_attr_update_mapping_pairs_locked( - ni, 0, ni)) { + err2 =3D 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 af= ter + * @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); =20 /* 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 =3D false; if (attr_ni->mft_no !=3D 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; =20 /* The new mapping is now authoritative; release the old data runs. */ - if (ntfs_cluster_free_from_rl(vol, old_rl)) { + free_err =3D 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 =3D old_alloc_size; write_unlock_irqrestore(&attr_ni->size_lock, flags); =20 - restore_err =3D ntfs_attr_update_mapping_pairs_locked( - attr_ni, 0, locked_ni); + restore_err =3D 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); } =20 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, con= st 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 *vo= l, int error) fserror_report_metadata(vol->sb, error, GFP_ATOMIC); } =20 +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 =3D 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) =20 void ntfs_report_metadata_error(struct ntfs_volume *vol, int error); +void ntfs_report_file_metadata_error(struct inode *inode, int error); =20 static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr) { --=20 2.51.0 From nobody Fri Sep 25 06:47:19 2026 Received: from m16.mail.163.com (m16.mail.163.com [220.197.31.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E8CCE381AF0 for ; Wed, 16 Sep 2026 02:24:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.5 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525463; cv=none; b=Et9/romrwrLU6RZPY/XEqj3UVGRlbQnkKTkV1sy+dhOyCOX9C6eeeseobSl4unVyEjqDTkPUiAdmaCGhyAZrltAxCxmx9xshMAhSZ8Jc6cFxze4H/Ur8k6T07YADm8Khgg1tKcip0XdAP720HoPoXMb1OCJzdDSUT1A9CJE8XG8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525463; c=relaxed/simple; bh=MahDzO3XrpdT4JVoi+SEwYDFX96eHbBV/wSiFd1wNR8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iMXuDuUJarMLLEjd4ZO+ASRE1pTank/W0ASGumFECnHRNLbSGxaN20T/mHHbfzlKfGHXlzirIzBchbaxtKuSUl2tyI115nhwsYfmr73kKMmoPBhTxaMnFnr928lI7CpnZnAKVdd7dvmMunyqyyMKDyjVH+CIL592DXuA/a4QGOQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=SP2bNWzk; arc=none smtp.client-ip=220.197.31.5 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="SP2bNWzk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=Pv JXkmL/eDH7C2G2BbRaRWTQ6BWaIk48qbQ3GmlCIQg=; b=SP2bNWzkOFpTpiLkIl M9/Jl0wEkuEiDgHapW5rp3RUsZvileKW/u+HRf9QYctZkSYU8mwZusY/TwFVxdTD Ons/mbx7k+pDR8UTA5EVDk+VlsH8HB9ib0hiRF+/OrZIT1/DyH1XXt8CvyY0qDjG BZL25oAAks+oM5oEwDeFG/Bkg= Received: from liubaolin-ThinkPad-E15-Gen-2 (unknown []) by gzga-smtp-mtada-g0-3 (Coremail) with SMTP id _____wD3J2qz_alqzNjjAw--.2073S5; Wed, 16 Sep 2026 10:23:56 +0800 (CST) From: Baolin Liu To: linkinjeon@kernel.org, hyc.lee@gmail.com Cc: ntfs@lists.linux.dev, linux-kernel@vger.kernel.org, Baolin Liu Subject: [PATCH v2 3/5] ntfs: report inode metadata errors to fsnotify Date: Wed, 16 Sep 2026 10:23:43 +0800 Message-ID: <20260916022345.1151836-4-liubaolin12138@163.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260916022345.1151836-1-liubaolin12138@163.com> References: <20260916022345.1151836-1-liubaolin12138@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3J2qz_alqzNjjAw--.2073S5 X-Coremail-Antispam: 1Uf129KBjvJXoW3Ar4DZryfGF4DGrWfAF4kXrb_yoWfXw4DpF 9rG3sxK39FqF9FgFn2yrWavw13K348KrW2vryfWw1xWw1Yqwn5tF10qF1S9F1Fkr95W3yY qr4UCrWDCF47J3DanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jWHqcUUUUU= X-CM-SenderInfo: xolxutxrol0iasrtmqqrwthudrp/xtbCwRyzfmqp-by9jgAA3h Content-Type: text/plain; charset="utf-8" From: Baolin Liu 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 --- 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 =3D 4, flags =3D 0; int extend_sys =3D 0; dev_t dev =3D 0; - bool has_lxmod =3D false; + bool error_reported =3D false, has_lxmod =3D false; bool vol_err =3D true; =20 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 =3D ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx); if (unlikely(err)) { + error_reported =3D ctx->error_reported; if (err =3D=3D -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 =3D ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx); if (err) { if (unlikely(err !=3D -ENOENT)) { + error_reported =3D 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 =3D ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { + error_reported =3D ctx->error_reported; if (err =3D=3D -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 =3D ni->initialized_size =3D ni->allocated_size =3D 0; if (err !=3D -ENOENT) { + error_reported =3D 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) } =20 err =3D extend_sys; + error_reported =3D 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 !=3D -EOPNOTSUPP && err !=3D -ENOMEM && + if (!error_reported && err !=3D -EOPNOTSUPP && err !=3D -ENOMEM && err !=3D -EINTR && err !=3D -ERESTARTSYS && vol_err =3D=3D 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 =3D 0; + bool error_reported =3D false; =20 ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no); =20 @@ -1316,8 +1322,10 @@ static int ntfs_read_locked_attr_inode(struct inode = *base_vi, struct inode *vi) /* Find the attribute. */ err =3D ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx); - if (unlikely(err)) + if (unlikely(err)) { + error_reported =3D ctx->error_reported; goto unm_err_out; + } a =3D 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 !=3D -ENOENT && err !=3D -ENOMEM && + if (!error_reported && err !=3D -ENOENT && err !=3D -ENOMEM && err !=3D -EINTR && err !=3D -ERESTARTSYS) - NVolSetErrors(vol); + ntfs_report_file_metadata_error(base_vi, err); return err; } =20 @@ -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 =3D 0; + bool error_reported =3D false; =20 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 =3D ntfs_attr_lookup(AT_INDEX_ROOT, ni->name, ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { + error_reported =3D ctx->error_reported; if (err =3D=3D -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 =3D NULL; ctx =3D NULL; goto skip_large_index_stuff; - } else + } else { + error_reported =3D 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 =3D ntfs_attr_iget(base_vi, AT_BITMAP, ni->name, ni->name_len); if (IS_ERR(bvi)) { err =3D PTR_ERR(bvi); + error_reported =3D err !=3D -ENOENT && err !=3D -ENOMEM && + err !=3D -EOPNOTSUPP && err !=3D -EINTR && + err !=3D -ERESTARTSYS; if (err !=3D -EINTR && err !=3D -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, na= me_len %i.", err, ni->mft_no, ni->name_len); - if (err !=3D -EOPNOTSUPP && err !=3D -ENOMEM && + if (!error_reported && err !=3D -EOPNOTSUPP && err !=3D -ENOMEM && err !=3D -EINTR && err !=3D -ERESTARTSYS) - NVolSetErrors(vol); + ntfs_report_file_metadata_error(base_vi, err); return err; } =20 @@ -2479,7 +2494,9 @@ int ntfs_truncate_vfs(struct inode *vi, loff_t new_si= ze, loff_t i_size) * * Return 0 on success or -errno on error. */ -static int ntfs_inode_sync_standard_information(struct inode *vi, struct m= ft_record *m) +static int ntfs_inode_sync_standard_information(struct inode *vi, + struct mft_record *m, + bool *error_reported) { struct ntfs_inode *ni =3D NTFS_I(vi); struct ntfs_attr_search_ctx *ctx; @@ -2495,6 +2512,8 @@ static int ntfs_inode_sync_standard_information(struc= t inode *vi, struct mft_rec err =3D ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { + if (ctx->error_reported) + *error_reported =3D 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 =3D NTFS_I(ni->vol->mft_ino); struct mft_record *m; int err =3D 0; - bool need_iput =3D false; + bool error_reported =3D false, need_iput =3D false; =20 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) =20 if (NInoNonResident(ni) && NInoRunlistDirty(ni)) { down_write(&ni->runlist.lock); - err =3D ntfs_attr_update_mapping_pairs_locked(ni, 0, ni); + err =3D ntfs_attr_update_mapping_pairs_locked_reported(ni, 0, ni, + &error_reported); if (!err) NInoClearRunlistDirty(ni); up_write(&ni->runlist.lock); } =20 - err =3D ntfs_inode_sync_standard_information(vi, m); + err =3D ntfs_inode_sync_standard_information(vi, m, &error_reported); if (err) goto unm_err_out; =20 @@ -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 !=3D -EINTR && + err !=3D -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 =3D -EIO; err: kvfree(empty_buf); --=20 2.51.0 From nobody Fri Sep 25 06:47:19 2026 Received: from m16.mail.163.com (m16.mail.163.com [220.197.31.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 18B65270552 for ; Wed, 16 Sep 2026 02:24:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.5 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525471; cv=none; b=mVNAaHCmxffZIsXDW0mHhDGNr1gGvFhrTsiP8booNhkrkjoEEvhl3w5morUHQDzTzMf1w9PooXGpoV/s69w7Ks8nWKcC3CkR2s4Vw5X476nXbSe1Sq9gx2IM9MYNOVGeHuCgKApp9VdqGy1p+VRplWNHoHpP0BQQADOXM4mct2c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525471; c=relaxed/simple; bh=dnF+hnW1kSlFfktQ6P3qpFW5C4yLudBpsGLeBpSdCLw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FXtc/esLNg8Rp6lfusdG3n0zHWngWmj8KLFP448lLsVGK2iQ5rbhiYQ5V9C11gsBCHYmCqna9vfM6CTuBNE4n+db+wAZS+ULkqaN+Lk8bsXI12kvPkLZkKctzSfnU0OfMu+7bmBAlUW13GSWx1ZaRY7Q7GyIX6F+mLKVL6YyTPo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=o7IMa+gR; arc=none smtp.client-ip=220.197.31.5 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="o7IMa+gR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=u/ ajqwzhgC1yyrd7vylUCVYvwt0R2itCEqfOr5Vmpto=; b=o7IMa+gRE+No9r0f+3 vWO3JQdZjU2VymJ7cFAeO5Bqc32AAV1STPenj4/9kCE0tES0I2WFel0+/xYJoieq qdfA0kG4wArLKQvovrYZ1UAd1DPOoekfz5kzZra5sxoIbCPP0RecRB6zTUT8mu5j yVtMFCmTTe5IO3c/skSsYqEM4= Received: from liubaolin-ThinkPad-E15-Gen-2 (unknown []) by gzga-smtp-mtada-g0-3 (Coremail) with SMTP id _____wD3J2qz_alqzNjjAw--.2073S6; Wed, 16 Sep 2026 10:23:57 +0800 (CST) From: Baolin Liu To: linkinjeon@kernel.org, hyc.lee@gmail.com Cc: ntfs@lists.linux.dev, linux-kernel@vger.kernel.org, Baolin Liu Subject: [PATCH v2 4/5] ntfs: report MFT errors to fsnotify Date: Wed, 16 Sep 2026 10:23:44 +0800 Message-ID: <20260916022345.1151836-5-liubaolin12138@163.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260916022345.1151836-1-liubaolin12138@163.com> References: <20260916022345.1151836-1-liubaolin12138@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3J2qz_alqzNjjAw--.2073S6 X-Coremail-Antispam: 1Uf129KBjvAXoWfZw48uFyfXrW8uFW8Aw4kCrg_yoW5JF48Xo WavFs0yw4vk3W5Aa4jk3Z3Xas8Aws8C395trW5WrsrurZ2gw1UJ34xKw129ayxW3WrWws2 gr1ktrs5ZF13AFyfn29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxU738nUUUUU X-CM-SenderInfo: xolxutxrol0iasrtmqqrwthudrp/xtbCwR2zfmqp-b29uAAA3X Content-Type: text/plain; charset="utf-8" From: Baolin Liu Report MFT validation, mapping, allocation, rollback, and writeback failures through the appropriate volume-level or file-level helper. Add reporting-aware mapping APIs and propagate their state to callers. Report asynchronous MFT writeback errors from I/O completion. Signed-off-by: Baolin Liu --- fs/ntfs/attrib.c | 19 ++-- fs/ntfs/inode.c | 10 +- fs/ntfs/mft.c | 252 +++++++++++++++++++++++++++++++---------------- fs/ntfs/mft.h | 6 ++ 4 files changed, 191 insertions(+), 96 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 136bda6433bb..c847bffd0bca 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -94,6 +94,7 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vc= n, struct ntfs_attr_sea struct folio *put_this_folio =3D NULL; int err =3D 0; bool ctx_is_temporary =3D false, ctx_needs_reset =3D false; + bool mft_error_reported =3D false; struct ntfs_attr_search_ctx old_ctx =3D { NULL, }; size_t new_rl_count; =20 @@ -238,7 +239,10 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64= vcn, struct ntfs_attr_sea if (old_ctx.base_ntfs_ino && old_ctx.ntfs_ino !=3D old_ctx.base_ntfs_ino) { retry_map: - ctx->mrec =3D map_mft_record(old_ctx.ntfs_ino); + ctx->mrec =3D map_mft_record_reported(old_ctx.ntfs_ino, + &mft_error_reported); + if (mft_error_reported) + old_ctx.error_reported =3D true; /* * Something bad has happened. If out * of memory retry till it succeeds. @@ -1163,7 +1167,7 @@ static int ntfs_external_attr_find(const __le32 type, __le16 *al_name; u32 al_name_len; u32 attr_len, mft_free_len; - bool is_first_search =3D false; + bool error_reported =3D false, is_first_search =3D false; int err =3D 0; static const char *es =3D " Unmount and run chkdsk."; =20 @@ -1382,10 +1386,12 @@ static int ntfs_external_attr_find(const __le32 typ= e, ctx->mrec =3D ctx->base_mrec; ctx->mapped_mrec =3D ctx->mapped_base_mrec; } else { + u64 mref =3D le64_to_cpu(al_entry->mft_reference); + /* We want an extent record. */ - ctx->mrec =3D map_extent_mft_record(base_ni, - le64_to_cpu( - al_entry->mft_reference), &ni); + ctx->mrec =3D map_extent_mft_record_reported(base_ni, mref, + &ni, + &error_reported); if (IS_ERR(ctx->mrec)) { ntfs_error(vol->sb, "Failed to map extent mft record 0x%lx of base inode 0x%llx.%s", @@ -1511,7 +1517,8 @@ static int ntfs_external_attr_find(const __le32 type, =20 if (err !=3D -ENOMEM) { if (err !=3D -EINTR && err !=3D -ERESTARTSYS) { - ntfs_report_file_metadata_error(VFS_I(base_ni), err); + if (!error_reported) + ntfs_report_file_metadata_error(VFS_I(base_ni), err); ctx->error_reported =3D true; } else { NVolSetErrors(vol); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 13f353905fb3..ab5424b1593f 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -715,7 +715,7 @@ static int ntfs_read_locked_inode(struct inode *vi) if (vi->i_ino !=3D FILE_MFT) ntfs_init_big_inode(vi); =20 - m =3D map_mft_record(ni); + m =3D map_mft_record_reported(ni, &error_reported); if (IS_ERR(m)) { err =3D PTR_ERR(m); goto err_out; @@ -1309,7 +1309,7 @@ static int ntfs_read_locked_attr_inode(struct inode *= base_vi, struct inode *vi) /* Set inode type to zero but preserve permissions. */ vi->i_mode =3D base_vi->i_mode & ~S_IFMT; =20 - m =3D map_mft_record(base_ni); + m =3D map_mft_record_reported(base_ni, &error_reported); if (IS_ERR(m)) { err =3D PTR_ERR(m); goto err_out; @@ -1553,7 +1553,7 @@ static int ntfs_read_locked_index_inode(struct inode = *base_vi, struct inode *vi) /* Set inode type to zero but preserve permissions. */ vi->i_mode =3D base_vi->i_mode & ~S_IFMT; /* Map the mft record for the base inode. */ - m =3D map_mft_record(base_ni); + m =3D map_mft_record_reported(base_ni, &error_reported); if (IS_ERR(m)) { err =3D PTR_ERR(m); goto err_out; @@ -2793,7 +2793,7 @@ int __ntfs_write_inode(struct inode *vi, int sync) =20 mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); /* Map, pin, and lock the mft record belonging to the inode. */ - m =3D map_mft_record(ni); + m =3D map_mft_record_reported(ni, &error_reported); if (IS_ERR(m)) { mutex_unlock(&ni->mrec_lock); err =3D PTR_ERR(m); @@ -2873,7 +2873,7 @@ int __ntfs_write_inode(struct inode *vi, int sync) int ret; =20 mutex_lock(&tni->mrec_lock); - tm =3D map_mft_record(tni); + tm =3D map_mft_record_reported(tni, &error_reported); if (IS_ERR(tm)) { mutex_unlock(&tni->mrec_lock); if (!err || err =3D=3D -ENOMEM) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index c565bec49c7d..b7b018f4350e 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -108,9 +108,11 @@ int ntfs_mft_record_check(const struct ntfs_volume *vo= l, struct mft_record *m, * The return value needs to be checked with IS_ERR(). If it is true, * PTR_ERR() contains the negative error code. */ -static inline struct mft_record *map_mft_record_folio(struct ntfs_inode *n= i) +static inline struct mft_record * +map_mft_record_folio(struct ntfs_inode *ni, bool *error_reported) { loff_t i_size; + struct ntfs_inode *base_ni; struct ntfs_volume *vol =3D ni->vol; struct inode *mft_vi =3D vol->mft_ino; struct folio *folio; @@ -169,7 +171,13 @@ static inline struct mft_record *map_mft_record_folio(= struct ntfs_inode *ni) kfree(ni->mrec); ni->mrec =3D NULL; folio =3D ERR_PTR(-EIO); - NVolSetErrors(vol); + if (ni->nr_extents >=3D 0) + base_ni =3D ni; + else + base_ni =3D ni->ext.base_ntfs_ino; + ntfs_report_file_metadata_error(VFS_I(base_ni), -EIO); + if (error_reported) + *error_reported =3D true; } err_out: ni->folio =3D NULL; @@ -195,7 +203,8 @@ static inline struct mft_record *map_mft_record_folio(s= truct ntfs_inode *ni) * Return: A pointer to the mft record. You need to check the returned * pointer with IS_ERR(). */ -struct mft_record *map_mft_record(struct ntfs_inode *ni) +static struct mft_record *__map_mft_record(struct ntfs_inode *ni, + bool *error_reported) { struct mft_record *m; =20 @@ -210,7 +219,7 @@ struct mft_record *map_mft_record(struct ntfs_inode *ni) if (ni->folio) return (struct mft_record *)ni->mrec; =20 - m =3D map_mft_record_folio(ni); + m =3D map_mft_record_folio(ni, error_reported); if (!IS_ERR(m)) return m; =20 @@ -220,6 +229,17 @@ struct mft_record *map_mft_record(struct ntfs_inode *n= i) return m; } =20 +struct mft_record *map_mft_record(struct ntfs_inode *ni) +{ + return __map_mft_record(ni, NULL); +} + +struct mft_record *map_mft_record_reported(struct ntfs_inode *ni, + bool *error_reported) +{ + return __map_mft_record(ni, error_reported); +} + /* * unmap_mft_record - release a reference to a mapped mft record * @ni: ntfs inode whose MFT record to unmap @@ -262,8 +282,10 @@ void unmap_mft_record(struct ntfs_inode *ni) * On successful return, @ntfs_ino contains a pointer to the ntfs_inode * structure of the mapped extent inode. */ -struct mft_record *map_extent_mft_record(struct ntfs_inode *base_ni, u64 m= ref, - struct ntfs_inode **ntfs_ino) +static struct mft_record *__map_extent_mft_record(struct ntfs_inode *base_= ni, + u64 mref, + struct ntfs_inode **ntfs_ino, + bool *error_reported) { struct mft_record *m; struct ntfs_inode *ni =3D NULL; @@ -299,7 +321,7 @@ struct mft_record *map_extent_mft_record(struct ntfs_in= ode *base_ni, u64 mref, mutex_unlock(&base_ni->extent_lock); atomic_dec(&base_ni->count); /* We found the record; just have to map and return it. */ - m =3D map_mft_record(ni); + m =3D map_mft_record_reported(ni, error_reported); /* map_mft_record() has incremented this on success. */ atomic_dec(&ni->count); if (!IS_ERR(m)) { @@ -333,7 +355,7 @@ struct mft_record *map_extent_mft_record(struct ntfs_in= ode *base_ni, u64 mref, ni->nr_extents =3D -1; ni->ext.base_ntfs_ino =3D base_ni; /* Now map the record. */ - m =3D map_mft_record(ni); + m =3D map_mft_record_reported(ni, error_reported); if (IS_ERR(m)) { atomic_dec(&base_ni->count); ntfs_clear_extent_inode(ni); @@ -396,6 +418,21 @@ struct mft_record *map_extent_mft_record(struct ntfs_i= node *base_ni, u64 mref, return m; } =20 +struct mft_record *map_extent_mft_record(struct ntfs_inode *base_ni, u64 m= ref, + struct ntfs_inode **ntfs_ino) +{ + return __map_extent_mft_record(base_ni, mref, ntfs_ino, NULL); +} + +struct mft_record * +map_extent_mft_record_reported(struct ntfs_inode *base_ni, u64 mref, + struct ntfs_inode **ntfs_ino, + bool *error_reported) +{ + return __map_extent_mft_record(base_ni, mref, ntfs_ino, + error_reported); +} + /* * __mark_mft_record_dirty - mark the base vfs inode dirty * @ni: ntfs inode describing the mapped mft record @@ -475,7 +512,8 @@ static void ntfs_mft_end_io(struct bio *bio) err =3D ctx->error; if (err) { mapping_set_error(ctx->mapping, err); - NVolSetErrors(ctx->vol); + if (!done) + ntfs_report_file_metadata_error(ctx->mapping->host, err); ntfs_error(ctx->vol->sb, "I/O error while writing MFT: %d", err); } @@ -672,8 +710,8 @@ static int ntfs_prepare_mft_record_io_units(struct ntfs= _inode *ni, * * On success, clean the mft record and return 0. On ENOMEM, redirty the * record so it can be retried. Asynchronous callers return success after - * redirtying while synchronous callers receive the error. For other erro= rs, - * mark the volume with errors. + * redirtying while synchronous callers receive the error. The caller is + * responsible for reporting other errors. * * If @sync is false, PG_writeback keeps the folio stable and serializes l= ater * writers until the I/O completes. @@ -807,8 +845,7 @@ int write_mft_record_nolock(struct ntfs_inode *ni, stru= ct mft_record *m, int syn mark_mft_record_dirty(ni); if (!sync) err =3D 0; - } else - NVolSetErrors(vol); + } return err; } =20 @@ -1339,9 +1376,11 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(= struct ntfs_volume *vol) struct ntfs_attr_search_ctx *ctx =3D NULL; struct mft_record *mrec; struct attr_record *a =3D NULL; - int ret, mp_size; + int err, ret, mp_size; u32 old_alen =3D 0; + u16 mp_ofs; u8 *b, tb; + bool error_reported =3D false; struct { u8 added_cluster:1; u8 added_run:1; @@ -1425,10 +1464,11 @@ static int ntfs_mft_bitmap_extend_allocation_nolock= (struct ntfs_volume *vol) if (IS_ERR(rl)) { up_write(&mftbmp_ni->runlist.lock); ntfs_error(vol->sb, "Failed to merge runlists for mft bitmap."); - if (ntfs_cluster_free_from_rl(vol, rl2)) { + err =3D ntfs_cluster_free_from_rl(vol, rl2); + if (err) { ntfs_error(vol->sb, "Failed to deallocate allocated cluster.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); } kvfree(rl2); return PTR_ERR(rl); @@ -1548,9 +1588,11 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(= struct ntfs_volume *vol) =20 restore_undo_alloc: ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, - mftbmp_ni->name_len, CASE_SENSITIVE, rl[1].vcn, NULL, - 0, ctx)) { + err =3D ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, + mftbmp_ni->name_len, CASE_SENSITIVE, rl[1].vcn, + NULL, 0, ctx); + if (err) { + error_reported =3D ctx->error_reported; ntfs_error(vol->sb, "Failed to find last attribute extent of mft bitmap attribute.%s", es); write_lock_irqsave(&mftbmp_ni->size_lock, flags); @@ -1563,7 +1605,8 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(s= truct ntfs_volume *vol) * The only thing that is now wrong is ->allocated_size of the * base attribute extent which chkdsk should be able to fix. */ - NVolSetErrors(vol); + if (!error_reported) + ntfs_report_metadata_error(vol, err); return ret; } a =3D ctx->attr; @@ -1582,31 +1625,38 @@ static int ntfs_mft_bitmap_extend_allocation_nolock= (struct ntfs_volume *vol) } /* Deallocate the cluster. */ down_write(&vol->lcnbmp_lock); - if (ntfs_bitmap_clear_bit(vol->lcnbmp_ino, lcn)) { + err =3D ntfs_bitmap_clear_bit(vol->lcnbmp_ino, lcn); + if (err) { ntfs_error(vol->sb, "Failed to free allocated cluster.%s", es); - NVolSetErrors(vol); - } else + ntfs_report_metadata_error(vol, err); + } else { ntfs_inc_free_clusters(vol, 1); + } up_write(&vol->lcnbmp_lock); if (status.mp_rebuilt) { - if (ntfs_mapping_pairs_build(vol, (u8 *)a + le16_to_cpu( - a->data.non_resident.mapping_pairs_offset), - old_alen - le16_to_cpu( - a->data.non_resident.mapping_pairs_offset), - rl2, ll, -1, NULL, NULL, NULL)) { + mp_ofs =3D le16_to_cpu(a->data.non_resident.mapping_pairs_offset); + err =3D ntfs_mapping_pairs_build(vol, (u8 *)a + mp_ofs, + old_alen - mp_ofs, rl2, ll, -1, + NULL, NULL, NULL); + if (err) { ntfs_error(vol->sb, "Failed to restore mapping pairs array.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); } - if (ntfs_attr_record_resize(ctx->mrec, a, old_alen)) { + err =3D ntfs_attr_record_resize(ctx->mrec, a, old_alen); + if (err) { ntfs_error(vol->sb, "Failed to restore attribute record.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); } mark_mft_record_dirty(ctx->ntfs_ino); - } else if (status.mp_extended && - ntfs_attr_update_mapping_pairs_locked(mftbmp_ni, 0, - mftbmp_ni)) { - ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", es); - NVolSetErrors(vol); + } else if (status.mp_extended) { + err =3D ntfs_attr_update_mapping_pairs_locked_reported(mftbmp_ni, 0, + mftbmp_ni, + &error_reported); + if (err) { + ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", es); + if (!error_reported) + ntfs_report_metadata_error(vol, err); + } } if (ctx) ntfs_attr_put_search_ctx(ctx); @@ -1639,7 +1689,8 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(= struct ntfs_volume *vol) struct ntfs_attr_search_ctx *ctx; struct mft_record *mrec; struct attr_record *a; - int ret; + int err, ret; + bool error_reported =3D false; =20 ntfs_debug("Extending mft bitmap initialized (and data) size."); mft_ni =3D NTFS_I(vol->mft_ino); @@ -1696,23 +1747,28 @@ static int ntfs_mft_bitmap_extend_initialized_noloc= k(struct ntfs_volume *vol) } ntfs_error(vol->sb, "Failed to write to mft bitmap."); /* Try to recover from the error. */ - mrec =3D map_mft_record(mft_ni); + mrec =3D map_mft_record_reported(mft_ni, &error_reported); if (IS_ERR(mrec)) { ntfs_error(vol->sb, "Failed to map mft record.%s", es); - NVolSetErrors(vol); + err =3D PTR_ERR(mrec); + if (!error_reported) + ntfs_report_metadata_error(vol, err); return ret; } ctx =3D ntfs_attr_get_search_ctx(mft_ni, mrec); if (unlikely(!ctx)) { ntfs_error(vol->sb, "Failed to get search context.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, -ENOMEM); goto unm_err_out; } - if (ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, - mftbmp_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + err =3D ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, + mftbmp_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, + ctx); + if (err) { ntfs_error(vol->sb, "Failed to find first attribute extent of mft bitmap attribute.%s", es); - NVolSetErrors(vol); + if (!ctx->error_reported) + ntfs_report_metadata_error(vol, err); put_err_out: ntfs_attr_put_search_ctx(ctx); unm_err_out: @@ -1774,9 +1830,10 @@ static int ntfs_mft_data_extend_allocation_nolock(st= ruct ntfs_volume *vol) struct ntfs_attr_search_ctx *ctx =3D NULL; struct mft_record *mrec; struct attr_record *a =3D NULL; - int ret, mp_size; + int err, ret, mp_size; u32 old_alen =3D 0; - bool mp_rebuilt =3D false, mp_extended =3D false; + u16 mp_ofs; + bool error_reported =3D false, mp_rebuilt =3D false, mp_extended =3D fals= e; size_t new_rl_count; =20 ntfs_debug("Extending mft data allocation."); @@ -1862,10 +1919,11 @@ static int ntfs_mft_data_extend_allocation_nolock(s= truct ntfs_volume *vol) if (IS_ERR(rl)) { up_write(&mft_ni->runlist.lock); ntfs_error(vol->sb, "Failed to merge runlists for mft data attribute."); - if (ntfs_cluster_free_from_rl(vol, rl2)) { + err =3D ntfs_cluster_free_from_rl(vol, rl2); + if (err) { ntfs_error(vol->sb, "Failed to deallocate clusters from the mft data attribute.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); } kvfree(rl2); return PTR_ERR(rl); @@ -1985,8 +2043,10 @@ static int ntfs_mft_data_extend_allocation_nolock(st= ruct ntfs_volume *vol) return 0; restore_undo_alloc: ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len, - CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx)) { + err =3D ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len, + CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx); + if (err) { + error_reported =3D ctx->error_reported; ntfs_error(vol->sb, "Failed to find last attribute extent of mft data attribute.%s", es); write_lock_irqsave(&mft_ni->size_lock, flags); @@ -1999,45 +2059,55 @@ static int ntfs_mft_data_extend_allocation_nolock(s= truct ntfs_volume *vol) * The only thing that is now wrong is ->allocated_size of the * base attribute extent which chkdsk should be able to fix. */ - NVolSetErrors(vol); + if (!error_reported) + ntfs_report_metadata_error(vol, err); return ret; } ctx->attr->data.non_resident.highest_vcn =3D cpu_to_le64(old_last_vcn - 1); undo_alloc: - if (ntfs_cluster_free(mft_ni, old_last_vcn, -1, ctx) < 0) { + err =3D ntfs_cluster_free(mft_ni, old_last_vcn, -1, ctx); + if (err < 0) { ntfs_error(vol->sb, "Failed to free clusters from mft data attribute.%s"= , es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); } =20 - if (ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn)) { + err =3D ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn); + if (err) { ntfs_error(vol->sb, "Failed to truncate mft data attribute runlist.%s", = es); - NVolSetErrors(vol); - } - if (mp_extended && ntfs_attr_update_mapping_pairs(mft_ni, 0)) { - ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", - es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); + } + if (mp_extended) { + err =3D ntfs_attr_update_mapping_pairs_locked_reported(mft_ni, 0, + NULL, + &error_reported); + if (err) { + ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", + es); + if (!error_reported) + ntfs_report_metadata_error(vol, err); + } } if (ctx) { a =3D ctx->attr; if (mp_rebuilt && !IS_ERR(ctx->mrec)) { - if (ntfs_mapping_pairs_build(vol, (u8 *)a + le16_to_cpu( - a->data.non_resident.mapping_pairs_offset), - old_alen - le16_to_cpu( - a->data.non_resident.mapping_pairs_offset), - rl2, ll, -1, NULL, NULL, NULL)) { + mp_ofs =3D le16_to_cpu(a->data.non_resident.mapping_pairs_offset); + err =3D ntfs_mapping_pairs_build(vol, (u8 *)a + mp_ofs, + old_alen - mp_ofs, rl2, ll, + -1, NULL, NULL, NULL); + if (err) { ntfs_error(vol->sb, "Failed to restore mapping pairs array.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); } - if (ntfs_attr_record_resize(ctx->mrec, a, old_alen)) { + err =3D ntfs_attr_record_resize(ctx->mrec, a, old_alen); + if (err) { ntfs_error(vol->sb, "Failed to restore attribute record.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, err); } mark_mft_record_dirty(ctx->ntfs_ino); } else if (IS_ERR(ctx->mrec)) { ntfs_error(vol->sb, "Failed to restore attribute search context.%s", es= ); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, PTR_ERR(ctx->mrec)); } ntfs_attr_put_search_ctx(ctx); } @@ -2269,8 +2339,8 @@ static int ntfs_mft_record_format(const struct ntfs_v= olume *vol, const s64 mft_n * * On error, the volume will be left in a consistent state and no record w= ill * be allocated. If rolling back a partial operation fails, we may leave = some - * inconsistent metadata in which case we set NVolErrors() so the volume is - * left dirty when unmounted. + * inconsistent metadata in which case we report the error so the volume is + * left dirty when unmounted and userspace is notified. * * Note, this function cannot make use of most of the normal functions, li= ke * for example for attribute resizing, etc, because when the run list over= flows @@ -2301,7 +2371,7 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, co= nst int mode, struct attr_record *a; pgoff_t index; unsigned int ofs; - int err; + int err, rollback_err; __le16 seq_no, usn; bool record_formatted =3D false, from_reserve =3D false, tail_alloc =3D f= alse; bool reserve_created =3D false; @@ -2705,7 +2775,7 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, co= nst int mode, folio_unlock(folio); kunmap_local(m); folio_put(folio); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, -EFSCORRUPTED); goto search_free_rec; } /* @@ -2861,9 +2931,12 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, c= onst int mode, if (!base_ni || base_ni->mft_no !=3D FILE_MFT) down_write(&vol->mftbmp_lock); undo_mftbmp_alloc_nolock: - if (!forced_reserved_record && ntfs_bitmap_clear_bit(vol->mftbmp_ino, bit= )) { + rollback_err =3D 0; + if (!forced_reserved_record) + rollback_err =3D ntfs_bitmap_clear_bit(vol->mftbmp_ino, bit); + if (rollback_err) { ntfs_error(vol->sb, "Failed to clear bit in mft bitmap.%s", es); - NVolSetErrors(vol); + ntfs_report_metadata_error(vol, rollback_err); } if ((from_reserve || reserve_created) && vol->mft_record_reserve_pos =3D=3D bit + 1) @@ -2900,30 +2973,35 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, = const int mode, int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni) { u64 mft_no; - int err; + int err, rollback_err; u16 seq_no; __le16 old_seq_no; __le64 old_base_mft_record; struct mft_record *ni_mrec; unsigned int memalloc_flags; struct ntfs_inode *base_ni; - bool keep_reserved; + bool error_reported =3D false, keep_reserved; =20 if (!vol || !ni) return -EINVAL; =20 ntfs_debug("Entering for inode 0x%llx.\n", (long long)ni->mft_no); + if (likely(ni->nr_extents >=3D 0)) + base_ni =3D ni; + else + base_ni =3D ni->ext.base_ntfs_ino; =20 - ni_mrec =3D map_mft_record(ni); - if (IS_ERR(ni_mrec)) + ni_mrec =3D map_mft_record_reported(ni, &error_reported); + if (IS_ERR(ni_mrec)) { + err =3D PTR_ERR(ni_mrec); + if (!error_reported && err !=3D -ENOMEM && + err !=3D -EINTR && err !=3D -ERESTARTSYS) + ntfs_report_file_metadata_error(VFS_I(base_ni), err); return -EIO; + } =20 /* Cache the mft reference for later. */ mft_no =3D ni->mft_no; - if (likely(ni->nr_extents >=3D 0)) - base_ni =3D ni; - else - base_ni =3D ni->ext.base_ntfs_ino; keep_reserved =3D mft_no >=3D FILE_reserved12 && mft_no <=3D FILE_reserved15 && base_ni->mft_no =3D=3D FILE_MFT; @@ -2962,8 +3040,10 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, st= ruct ntfs_inode *ni) */ NInoSetDirty(ni); err =3D write_mft_record(ni, ni_mrec, 1); - if (err) + if (err) { + ntfs_report_file_metadata_error(VFS_I(base_ni), err); goto sync_rollback; + } =20 if (keep_reserved) { unmap_mft_record(ni); @@ -3006,7 +3086,9 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, str= uct ntfs_inode *ni) ni_mrec->sequence_number =3D old_seq_no; ni_mrec->base_mft_record =3D old_base_mft_record; NInoSetDirty(ni); - write_mft_record(ni, ni_mrec, 0); + rollback_err =3D write_mft_record(ni, ni_mrec, 0); + if (rollback_err) + ntfs_report_file_metadata_error(VFS_I(base_ni), rollback_err); unmap_mft_record(ni); return err; } @@ -3153,7 +3235,7 @@ static void ntfs_mft_write_error(struct ntfs_volume *= vol, struct address_space *mapping, int err) { mapping_set_error(mapping, err); - NVolSetErrors(vol); + ntfs_report_file_metadata_error(mapping->host, err); ntfs_error(vol->sb, "Error while writing MFT folio: %d", err); } =20 diff --git a/fs/ntfs/mft.h b/fs/ntfs/mft.h index d2a31205e08c..3eda89aab67a 100644 --- a/fs/ntfs/mft.h +++ b/fs/ntfs/mft.h @@ -14,9 +14,15 @@ #include "inode.h" =20 struct mft_record *map_mft_record(struct ntfs_inode *ni); +struct mft_record *map_mft_record_reported(struct ntfs_inode *ni, + bool *error_reported); void unmap_mft_record(struct ntfs_inode *ni); struct mft_record *map_extent_mft_record(struct ntfs_inode *base_ni, u64 m= ref, struct ntfs_inode **ntfs_ino); +struct mft_record * +map_extent_mft_record_reported(struct ntfs_inode *base_ni, u64 mref, + struct ntfs_inode **ntfs_ino, + bool *error_reported); =20 static inline void unmap_extent_mft_record(struct ntfs_inode *ni) { --=20 2.51.0 From nobody Fri Sep 25 06:47:19 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 81402381EB4 for ; Wed, 16 Sep 2026 02:24:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.3 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525469; cv=none; b=dflXePo3S/P3Lezqk3WUz4SRxfUx7RPdhabEM6z0lE0mgjGdrJ9EPH9+8AGx7Hi0PRYNdkf0roXb91kiPuxSv5kg+dykTkVWhFNmpMABdQjOuQI6Rb4F5L5GCXsf1mrrJmsHVLQFRPMXnWVWgC71pgwXbIj1PwTSvnBwrLkkXZw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789525469; c=relaxed/simple; bh=Gf+QfnqldKJoDQjFkBmyjkwn0AB7BeFnOle3Wo9umS8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JDVinoIjSFfgACx0xjxfW/mJcnfa7GC3EB8dHWTkR01RB63UQ849Jdext1EgbYGOcjTUJQeioVXlyVmJhxNRjbpG5h/2NOLOKHmfOd09J3AiNQKReMIpjpq+1BIhoDeFPnaWT0WSGFlq/7VlIgZwJWEysEsx23riC8gWJkYV7j4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=BaC6whjW; arc=none smtp.client-ip=117.135.210.3 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="BaC6whjW" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=GF rL/09p2yHSVNvezjjZk89Fyb0BNmPAHvLqQqpEmNw=; b=BaC6whjWMLpf3Yx7m7 l0KRN/J2t0GeqQH00UzKJ3myVDcHedEmmo8nK5iytpWj+J9velb7FDgCalvql3w4 khX7kt9yueYxwVh2w89fHxXKF3q5L10nB7qmcMThNkVfZs+WSbdgz/lqiurhn25i BStiJueSCcvrANNR8Z81TtsR0= Received: from liubaolin-ThinkPad-E15-Gen-2 (unknown []) by gzga-smtp-mtada-g0-3 (Coremail) with SMTP id _____wD3J2qz_alqzNjjAw--.2073S7; Wed, 16 Sep 2026 10:23:59 +0800 (CST) From: Baolin Liu To: linkinjeon@kernel.org, hyc.lee@gmail.com Cc: ntfs@lists.linux.dev, linux-kernel@vger.kernel.org, Baolin Liu Subject: [PATCH v2 5/5] ntfs: report shutdown errors to fsnotify Date: Wed, 16 Sep 2026 10:23:45 +0800 Message-ID: <20260916022345.1151836-6-liubaolin12138@163.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260916022345.1151836-1-liubaolin12138@163.com> References: <20260916022345.1151836-1-liubaolin12138@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3J2qz_alqzNjjAw--.2073S7 X-Coremail-Antispam: 1Uf129KBjvJXoW7ZFyDXr4DZFyxJw4kJFW7CFg_yoW8uF17pF 97Ar93twn7tF12vws2yw43A3WSv3WkWw4UK34UGw1Igw1kKr10qF4vkF109F1F9asY9w48 Xw1jg398uFW8ZrUanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jcb1bUUUUU= X-CM-SenderInfo: xolxutxrol0iasrtmqqrwthudrp/xtbCwh+zfmqp-b-E2wAA3O Content-Type: text/plain; charset="utf-8" From: Baolin Liu Notify FAN_FS_ERROR listeners when NTFS enters forced shutdown after FS_IOC_SHUTDOWN or backing-device removal. Keep the shutdown state transition and notification together in a dedicated helper. Signed-off-by: Baolin Liu --- fs/ntfs/super.c | 12 +++++++++--- fs/ntfs/volume.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 7f6812d27fd1..a331188286da 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -352,6 +352,12 @@ void ntfs_report_file_metadata_error(struct inode *ino= de, int error) fserror_report_file_metadata(inode, error, GFP_ATOMIC); } =20 +void ntfs_report_shutdown(struct ntfs_volume *vol) +{ + NVolSetShutdown(vol); + fserror_report_shutdown(vol->sb, GFP_ATOMIC); +} + void ntfs_handle_error(struct super_block *sb) { struct ntfs_volume *vol =3D NTFS_SB(sb); @@ -368,7 +374,7 @@ void ntfs_handle_error(struct super_block *sb) sb->s_id); } else if (vol->on_errors =3D=3D ON_ERRORS_CONTINUE) { if (errseq_check(&sb->s_wb_err, vol->wb_err) =3D=3D -ENODEV) { - NVolSetShutdown(vol); + ntfs_report_shutdown(vol); vol->wb_err =3D sb->s_wb_err; } } @@ -1911,10 +1917,10 @@ int ntfs_force_shutdown(struct super_block *sb, u32= flags) if (ret) return ret; bdev_thaw(sb->s_bdev); - NVolSetShutdown(vol); + ntfs_report_shutdown(vol); break; case FS_SHUTDOWN_FLAGS_NOLOGFLUSH: - NVolSetShutdown(vol); + ntfs_report_shutdown(vol); break; default: return -EINVAL; diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index 7daac7048a4c..09f92dc1117b 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -252,6 +252,7 @@ DEFINE_NVOL_BIT_OPS(SymlinkNative) =20 void ntfs_report_metadata_error(struct ntfs_volume *vol, int error); void ntfs_report_file_metadata_error(struct inode *inode, int error); +void ntfs_report_shutdown(struct ntfs_volume *vol); =20 static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr) { --=20 2.51.0