]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs/ntfs3: handle delayed allocation overlap in run lookup
authorKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Fri, 22 May 2026 12:52:37 +0000 (14:52 +0200)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Tue, 2 Jun 2026 15:02:32 +0000 (17:02 +0200)
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@paragon-software.com>
fs/ntfs3/attrib.c
fs/ntfs3/ntfs_fs.h
fs/ntfs3/run.c

index e61c5bf7e27e4403dbb5a7a97837f704a9a33c18..0caf2f8a8c1e83c3cc9826e09b9e1c3238c84296 100644 (file)
@@ -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 @@ again:
        }
 
        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. */
 
index 9939556dcdc1ef3ecb2c0a5a2e14ad1bf1021f8c..d98d7e474476cf99b36a4ccf4ee583eff388067a 100644 (file)
@@ -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);
index ad7db67514ef7c5cc280e672334fe8d7ed47966b..3ebf0154eda396b89a2b1e2836fd30be5ce5bd43 100644 (file)
@@ -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;