fs/ntfs3: handle delayed allocation overlap in run lookup

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>
This commit is contained in:
Konstantin Komarov
2026-05-22 14:52:37 +02:00
parent 4f165b1ff9
commit 5569b3c525
3 changed files with 69 additions and 12 deletions

View 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. */

View 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);

View 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;