mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-21 20:27:50 -04:00
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:
@@ -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. */
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user