[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251031062905.4135909-3-yi.zhang@huaweicloud.com>
Date: Fri, 31 Oct 2025 14:29:03 +0800
From: Zhang Yi <yi.zhang@...weicloud.com>
To: linux-ext4@...r.kernel.org
Cc: linux-fsdevel@...r.kernel.org,
linux-kernel@...r.kernel.org,
tytso@....edu,
adilger.kernel@...ger.ca,
jack@...e.cz,
yi.zhang@...wei.com,
yi.zhang@...weicloud.com,
libaokun1@...wei.com,
yangerkun@...wei.com
Subject: [PATCH 2/4] ext4: check for conflicts when caching extents
From: Zhang Yi <yi.zhang@...wei.com>
Since ext4_es_cache_extent() can only be used to load on-disk extents
and does not permit modifying extents, it is not possible to overwrite
an extent of a different type. To prevent misuse of the interface, the
current implementation checks only the first existing extent but does
not verify all extents within the range to be inserted, as doing so
would be time-consuming in highly fragmented scenarios. Furthermore,
adding such checks to __es_remove_extent() would complicate its logic.
Therefore, a full check can be performed in debug mode to ensure that
the function does not overwrite any valuable extents.
Signed-off-by: Zhang Yi <yi.zhang@...wei.com>
---
fs/ext4/extents_status.c | 50 +++++++++++++++++++++++++++++++++++++---
1 file changed, 47 insertions(+), 3 deletions(-)
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index f9546ecf7340..55103c331b6b 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -985,6 +985,48 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
return;
}
+#ifdef CONFIG_EXT4_DEBUG
+/*
+ * If we find an extent that already exists during caching extents, its
+ * status must match the one to be cached. Otherwise, the extent status
+ * tree may have been corrupted.
+ */
+static void ext4_es_cache_extent_check(struct inode *inode,
+ struct extent_status *es, struct extent_status *newes)
+{
+ unsigned int status = ext4_es_type(newes);
+ struct rb_node *node;
+
+ if (ext4_es_type(es) != status)
+ goto conflict;
+
+ while ((node = rb_next(&es->rb_node)) != NULL) {
+ es = rb_entry(node, struct extent_status, rb_node);
+
+ if (es->es_lblk >= newes->es_lblk + newes->es_len)
+ break;
+ if (ext4_es_type(es) != status)
+ goto conflict;
+ }
+ return;
+
+conflict:
+ ext4_warning_inode(inode,
+ "ES cache extent failed: add [%d,%d,%llu,0x%x] conflict with existing [%d,%d,%llu,0x%x]\n",
+ newes->es_lblk, newes->es_len, ext4_es_pblock(newes),
+ ext4_es_status(newes), es->es_lblk, es->es_len,
+ ext4_es_pblock(es), ext4_es_status(es));
+
+ WARN_ON_ONCE(1);
+}
+#else
+static void ext4_es_cache_extent_check(struct inode __maybe_unused *inode,
+ struct extent_status *es, struct extent_status *newes)
+{
+ WARN_ON_ONCE(ext4_es_type(es) != ext4_es_type(newes));
+}
+#endif
+
/*
* ext4_es_cache_extent() inserts extent information into the extent status
* tree. If 'overwrite' is not set, it inserts extent only if there isn't
@@ -1022,9 +1064,11 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
if (es && es->es_lblk <= end) {
if (!overwrite)
goto unlock;
-
- /* Only extents of the same type can be overwritten. */
- WARN_ON_ONCE(ext4_es_type(es) != status);
+ /*
+ * Check whether the overwrites are safe. Only extents
+ * of the same type can be overwritten.
+ */
+ ext4_es_cache_extent_check(inode, es, &newes);
if (__es_remove_extent(inode, lblk, end, NULL, NULL))
goto unlock;
}
--
2.46.1
Powered by blists - more mailing lists