[PATCH 3/6] fs/ntfs3: handle delayed allocation overlap in run lookup
From: Konstantin Komarov
Date: Tue May 26 2026 - 09:54:40 EST
Introduce run_lookup_entry_da() to look up data runs while taking
delayed allocation into account.
ntfs3 may have both committed extents and delayed allocation extents for
the same VCN range. The new helper checks delayed allocation first and
falls back to the real run, then corrects the returned range when a real
run overlaps with a delayed allocation run.
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
---
fs/ntfs3/attrib.c | 17 +++++--------
fs/ntfs3/ntfs_fs.h | 3 +++
fs/ntfs3/run.c | 61 +++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 69 insertions(+), 12 deletions(-)
diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
index e61c5bf7e27e..0caf2f8a8c1e 100644
--- a/fs/ntfs3/attrib.c
+++ b/fs/ntfs3/attrib.c
@@ -962,11 +962,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
/* Try to find in cache. */
down_read(&ni->file.run_lock);
- if (!no_da && run_lookup_entry(&ni->file.run_da, vcn, lcn, len, NULL)) {
- /* The requested vcn is delay allocated. */
- *lcn = DELALLOC_LCN;
- } else if (run_lookup_entry(&ni->file.run, vcn, lcn, len, NULL)) {
- /* The requested vcn is known in current run. */
+ if (run_lookup_entry_da(&ni->file.run, !no_da ? &ni->file.run_da : NULL,
+ vcn, lcn, len)) {
} else {
*len = 0;
}
@@ -1011,11 +1008,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
int step;
again:
- if (da && run_lookup_entry(run_da, vcn, lcn, len, NULL)) {
- /* The requested vcn is delay allocated. */
- *lcn = DELALLOC_LCN;
- } else if (run_lookup_entry(run, vcn, lcn, len, NULL)) {
- /* The requested vcn is known in current run. */
+ if (run_lookup_entry_da(run, da ? &ni->file.run_da : NULL, vcn, lcn,
+ len)) {
} else {
*len = 0;
}
@@ -1100,7 +1094,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
}
if (!*len) {
- if (run_lookup_entry(run, vcn, lcn, len, NULL)) {
+ if (run_lookup_entry_da(run, da ? run_da : NULL, vcn, lcn,
+ len)) {
if (*lcn != SPARSE_LCN || !new)
goto ok; /* Slow normal way without allocation. */
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 9939556dcdc1..d98d7e474476 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -858,6 +858,9 @@ static inline void mi_get_ref(const struct mft_inode *mi, struct MFT_REF *ref)
/* Globals from run.c */
bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn,
CLST *len, size_t *index);
+bool run_lookup_entry_da(const struct runs_tree *run,
+ const struct runs_tree *run_da, CLST vcn, CLST *lcn,
+ CLST *len);
void run_truncate(struct runs_tree *run, CLST vcn);
void run_truncate_head(struct runs_tree *run, CLST vcn);
void run_truncate_around(struct runs_tree *run, CLST vcn);
diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c
index ad7db67514ef..3ebf0154eda3 100644
--- a/fs/ntfs3/run.c
+++ b/fs/ntfs3/run.c
@@ -223,6 +223,66 @@ bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn,
return true;
}
+/*
+ * run_overlaps
+ *
+ * true if run overlaps with range [svcn, svcn + len)
+ */
+static bool run_overlaps(const struct runs_tree *run, CLST svcn, CLST len,
+ CLST *vcn, CLST *clen)
+{
+ size_t i;
+ const struct ntfs_run *r = run->runs;
+ CLST end = svcn + len;
+
+ for (i = 0; i < run->count; i++, r++) {
+ /* Check if [r->vcn, r->vcn+r->len) overlaps [svcn, end). */
+ if (r->vcn < end && svcn < r->vcn + r->len) {
+ if (vcn)
+ *vcn = r->vcn;
+ if (clen)
+ *clen = r->len;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * run_lookup_entry_da
+ *
+ * - lookup vcn in delalloc run
+ * - lookup vcn in real run
+ * - correct result if real run overlaps with delalloc
+ */
+bool run_lookup_entry_da(const struct runs_tree *run,
+ const struct runs_tree *run_da, CLST vcn, CLST *lcn,
+ CLST *len)
+{
+ CLST vcn1, len1;
+
+ if (run_da && run_lookup_entry(run_da, vcn, lcn, len, NULL)) {
+ *lcn = DELALLOC_LCN;
+ return true;
+ }
+
+ if (!run_lookup_entry(run, vcn, lcn, len, NULL))
+ return false;
+
+ if (run_da && run_overlaps(run_da, vcn, *len, &vcn1, &len1)) {
+ /* Correct return value. */
+ if (vcn1 > vcn) {
+ *len = vcn1 - vcn;
+ } else {
+ *lcn = DELALLOC_LCN;
+ *len = len1;
+ }
+ }
+
+ return true;
+}
+
/*
* run_truncate_head - Decommit the range before vcn.
*/
@@ -1286,7 +1346,6 @@ bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done)
return true;
}
-
e = run->runs + run->count;
r = run->runs + index;
end = vcn + len;
--
2.43.0