summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/ntfs3/attrib.c17
-rw-r--r--fs/ntfs3/ntfs_fs.h3
-rw-r--r--fs/ntfs3/run.c61
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 @@ 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. */
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
@@ -224,6 +224,66 @@ bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn,
}
/*
+ * 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.
*/
void run_truncate_head(struct runs_tree *run, CLST 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;