Unverified Commit 10d7c95a authored by Konstantin Komarov's avatar Konstantin Komarov
Browse files

fs/ntfs3: add delayed-allocation (delalloc) support



This patch implements delayed allocation (delalloc) in ntfs3 driver.

It introduces an in-memory delayed-runlist (run_da) and the helpers to
track, reserve and later convert those delayed reservations into real
clusters at writeback time. The change keeps on-disk formats untouched and
focuses on pagecache integration, correctness and safe interaction with
fallocate, truncate, and dio/iomap paths.

Key points:

- add run_da (delay-allocated run tree) and bookkeeping for delayed clusters.

- mark ranges as delalloc (DELALLOC_LCN) instead of immediately allocating.
  Actual allocation performed later (writeback / attr_set_size_ex / explicit
  flush paths).

- direct i/o / iomap paths updated to avoid dio collisions with
  delalloc: dio falls back or forces allocation of delayed blocks before
  proceeding.

- punch/collapse/truncate/fallocate check and cancel delay-alloc reservations.
  Sparse/compressed files handled specially.

- free-space checks updated (ntfs_check_free_space) to account for reserved
  delalloc clusters and MFT record budgeting.

- delayed allocations are committed on last writer (file release) and on
  explicit allocation flush paths.

Tested-by: default avatar <syzbot@syzkaller.appspotmail.com>
Reported-by: default avatar <syzbot+2bd8e813c7f767aa9bb1@syzkaller.appspotmail.com>
Signed-off-by: default avatarKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
parent c5226b96
Loading
Loading
Loading
Loading
+243 −90
Original line number Diff line number Diff line
@@ -91,7 +91,8 @@ static int attr_load_runs(struct ATTRIB *attr, struct ntfs_inode *ni,
 * run_deallocate_ex - Deallocate clusters.
 */
static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run,
			     CLST vcn, CLST len, CLST *done, bool trim)
			     CLST vcn, CLST len, CLST *done, bool trim,
			     struct runs_tree *run_da)
{
	int err = 0;
	CLST vcn_next, vcn0 = vcn, lcn, clen, dn = 0;
@@ -120,6 +121,16 @@ static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run,
			if (sbi) {
				/* mark bitmap range [lcn + clen) as free and trim clusters. */
				mark_as_free_ex(sbi, lcn, clen, trim);

				if (run_da) {
					CLST da_len;
					if (!run_remove_range(run_da, vcn, clen,
							      &da_len)) {
						err = -ENOMEM;
						goto failed;
					}
					ntfs_sub_da(sbi, da_len);
				}
			}
			dn += clen;
		}
@@ -147,9 +158,10 @@ static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run,
 * attr_allocate_clusters - Find free space, mark it as used and store in @run.
 */
int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
			   CLST vcn, CLST lcn, CLST len, CLST *pre_alloc,
			   enum ALLOCATE_OPT opt, CLST *alen, const size_t fr,
			   CLST *new_lcn, CLST *new_len)
			   struct runs_tree *run_da, CLST vcn, CLST lcn,
			   CLST len, CLST *pre_alloc, enum ALLOCATE_OPT opt,
			   CLST *alen, const size_t fr, CLST *new_lcn,
			   CLST *new_len)
{
	int err;
	CLST flen, vcn0 = vcn, pre = pre_alloc ? *pre_alloc : 0;
@@ -185,12 +197,21 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,

		/* Add new fragment into run storage. */
		if (!run_add_entry(run, vcn, lcn, flen, opt & ALLOCATE_MFT)) {
undo_alloc:
			/* Undo last 'ntfs_look_for_free_space' */
			mark_as_free_ex(sbi, lcn, len, false);
			err = -ENOMEM;
			goto out;
		}

		if (run_da) {
			CLST da_len;
			if (!run_remove_range(run_da, vcn, flen, &da_len)) {
				goto undo_alloc;
			}
			ntfs_sub_da(sbi, da_len);
		}

		if (opt & ALLOCATE_ZERO) {
			u8 shift = sbi->cluster_bits - SECTOR_SHIFT;

@@ -205,7 +226,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
		vcn += flen;

		if (flen >= len || (opt & ALLOCATE_MFT) ||
		    (fr && run->count - cnt >= fr)) {
		    (opt & ALLOCATE_ONE_FR) || (fr && run->count - cnt >= fr)) {
			*alen = vcn - vcn0;
			return 0;
		}
@@ -216,7 +237,8 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
out:
	/* Undo 'ntfs_look_for_free_space' */
	if (vcn - vcn0) {
		run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false);
		run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false,
				  run_da);
		run_truncate(run, vcn0);
	}

@@ -281,7 +303,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
	} else {
		const char *data = resident_data(attr);

		err = attr_allocate_clusters(sbi, run, 0, 0, len, NULL,
		err = attr_allocate_clusters(sbi, run, NULL, 0, 0, len, NULL,
					     ALLOCATE_DEF, &alen, 0, NULL,
					     NULL);
		if (err)
@@ -397,7 +419,7 @@ static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr,
}

/*
 * attr_set_size - Change the size of attribute.
 * attr_set_size_ex - Change the size of attribute.
 *
 * Extend:
 *   - Sparse/compressed: No allocated clusters.
@@ -405,24 +427,28 @@ static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr,
 * Shrink:
 *   - No deallocate if @keep_prealloc is set.
 */
int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
int attr_set_size_ex(struct ntfs_inode *ni, enum ATTR_TYPE type,
		     const __le16 *name, u8 name_len, struct runs_tree *run,
		     u64 new_size, const u64 *new_valid, bool keep_prealloc,
		  struct ATTRIB **ret)
		     struct ATTRIB **ret, bool no_da)
{
	int err = 0;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	u8 cluster_bits = sbi->cluster_bits;
	bool is_mft = ni->mi.rno == MFT_REC_MFT && type == ATTR_DATA &&
		      !name_len;
	u64 old_valid, old_size, old_alloc, new_alloc, new_alloc_tmp;
	u64 old_valid, old_size, old_alloc, new_alloc_tmp;
	u64 new_alloc = 0;
	struct ATTRIB *attr = NULL, *attr_b;
	struct ATTR_LIST_ENTRY *le, *le_b;
	struct mft_inode *mi, *mi_b;
	CLST alen, vcn, lcn, new_alen, old_alen, svcn, evcn;
	CLST next_svcn, pre_alloc = -1, done = 0;
	bool is_ext, is_bad = false;
	bool is_ext = false, is_bad = false;
	bool dirty = false;
	struct runs_tree *run_da = run == &ni->file.run ? &ni->file.run_da :
							  NULL;
	bool da = !is_mft && sbi->options->delalloc && run_da && !no_da;
	u32 align;
	struct MFT_REC *rec;

@@ -457,6 +483,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
	if (is_ext) {
		align <<= attr_b->nres.c_unit;
		keep_prealloc = false;
		da = false;
	}

	old_valid = le64_to_cpu(attr_b->nres.valid_size);
@@ -475,6 +502,37 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
		goto ok;
	}

	if (da &&
	    (vcn = old_alen + run_len(&ni->file.run_da), new_alen > vcn)) {
		/* Resize up normal file. Delay new clusters allocation. */
		alen = new_alen - vcn;

		if (ntfs_check_free_space(sbi, alen, 0, true)) {
			if (!run_add_entry(&ni->file.run_da, vcn, SPARSE_LCN,
					   alen, false)) {
				err = -ENOMEM;
				goto out;
			}

			ntfs_add_da(sbi, alen);
			goto ok1;
		}
	}

	if (!keep_prealloc && run_da && run_da->count &&
	    (vcn = run_get_max_vcn(run_da), new_alen < vcn)) {
		/* Shrink delayed clusters. */

		/* Try to remove fragment from delay allocated run. */
		if (!run_remove_range(run_da, new_alen, vcn - new_alen,
				      &alen)) {
			err = -ENOMEM;
			goto out;
		}

		ntfs_sub_da(sbi, alen);
	}

	vcn = old_alen - 1;

	svcn = le64_to_cpu(attr_b->nres.svcn);
@@ -580,7 +638,8 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
		} else {
			/* ~3 bytes per fragment. */
			err = attr_allocate_clusters(
				sbi, run, vcn, lcn, to_allocate, &pre_alloc,
				sbi, run, run_da, vcn, lcn, to_allocate,
				&pre_alloc,
				is_mft ? ALLOCATE_MFT : ALLOCATE_DEF, &alen,
				is_mft ? 0 :
					 (sbi->record_size -
@@ -759,14 +818,14 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
		mi_b->dirty = dirty = true;

		err = run_deallocate_ex(sbi, run, vcn, evcn - vcn + 1, &dlen,
					true);
					true, run_da);
		if (err)
			goto out;

		if (is_ext) {
			/* dlen - really deallocated clusters. */
			le64_sub_cpu(&attr_b->nres.total_size,
				     ((u64)dlen << cluster_bits));
				     (u64)dlen << cluster_bits);
		}

		run_truncate(run, vcn);
@@ -821,13 +880,13 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
	if (((type == ATTR_DATA && !name_len) ||
	     (type == ATTR_ALLOC && name == I30_NAME))) {
		/* Update inode_set_bytes. */
		if (attr_b->non_res) {
			new_alloc = le64_to_cpu(attr_b->nres.alloc_size);
			if (inode_get_bytes(&ni->vfs_inode) != new_alloc) {
		if (attr_b->non_res &&
		    inode_get_bytes(&ni->vfs_inode) != new_alloc) {
			inode_set_bytes(&ni->vfs_inode, new_alloc);
			dirty = true;
		}
		}

		i_size_write(&ni->vfs_inode, new_size);

		/* Don't forget to update duplicate information in parent. */
		if (dirty) {
@@ -869,7 +928,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
		is_bad = true;

undo_1:
	run_deallocate_ex(sbi, run, vcn, alen, NULL, false);
	run_deallocate_ex(sbi, run, vcn, alen, NULL, false, run_da);

	run_truncate(run, vcn);
out:
@@ -892,20 +951,9 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
 *  - new allocated clusters are zeroed via blkdev_issue_zeroout.
 */
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
			CLST *len, bool *new, bool zero, void **res)
			CLST *len, bool *new, bool zero, void **res, bool no_da)
{
	int err = 0;
	struct runs_tree *run = &ni->file.run;
	struct ntfs_sb_info *sbi;
	u8 cluster_bits;
	struct ATTRIB *attr, *attr_b;
	struct ATTR_LIST_ENTRY *le, *le_b;
	struct mft_inode *mi, *mi_b;
	CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0, alen;
	CLST alloc, evcn;
	unsigned fr;
	u64 total_size, total_size0;
	int step = 0;
	int err;

	if (new)
		*new = false;
@@ -914,23 +962,63 @@ 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 (!run_lookup_entry(run, vcn, lcn, len, NULL))
	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. */
	} else {
		*len = 0;
	}
	up_read(&ni->file.run_lock);

	if (*len && (*lcn != SPARSE_LCN || !new))
		return 0; /* Fast normal way without allocation. */

	/* No cluster in cache or we need to allocate cluster in hole. */
	sbi = ni->mi.sbi;
	cluster_bits = sbi->cluster_bits;

	ni_lock(ni);
	down_write(&ni->file.run_lock);

	/* Repeat the code above (under write lock). */
	if (!run_lookup_entry(run, vcn, lcn, len, NULL))
	err = attr_data_get_block_locked(ni, vcn, clen, lcn, len, new, zero,
					 res, no_da);

	up_write(&ni->file.run_lock);
	ni_unlock(ni);

	return err;
}

/*
 * attr_data_get_block_locked - Helper for attr_data_get_block.
 */
int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
			       CLST *lcn, CLST *len, bool *new, bool zero,
			       void **res, bool no_da)
{
	int err = 0;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	struct runs_tree *run = &ni->file.run;
	struct runs_tree *run_da = &ni->file.run_da;
	bool da = sbi->options->delalloc && !no_da;
	u8 cluster_bits;
	struct ATTRIB *attr, *attr_b;
	struct ATTR_LIST_ENTRY *le, *le_b;
	struct mft_inode *mi, *mi_b;
	CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0;
	CLST alloc, evcn;
	unsigned fr;
	u64 total_size, total_size0;
	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. */
	} else {
		*len = 0;
	}

	if (*len) {
		if (*lcn != SPARSE_LCN || !new)
@@ -939,6 +1027,9 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
			clen = *len;
	}

	cluster_bits = sbi->cluster_bits;
	step = 0;

	le_b = NULL;
	attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b);
	if (!attr_b) {
@@ -1061,11 +1152,38 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
			if (err)
				goto out;
		}
		da = false; /* no delalloc for compressed file. */
	}

	if (vcn + to_alloc > asize)
		to_alloc = asize - vcn;

	if (da) {
		CLST rlen1, rlen2;
		if (!ntfs_check_free_space(sbi, to_alloc, 0, true)) {
			err = ni_allocate_da_blocks_locked(ni);
			if (err)
				goto out;
			/* Layout of records may be changed. Start again without 'da'. */
			da = false;
			goto again;
		}

		/* run_add_entry consolidates existed ranges. */
		rlen1 = run_len(run_da);
		if (!run_add_entry(run_da, vcn, SPARSE_LCN, to_alloc, false)) {
			err = -ENOMEM;
			goto out;
		}
		rlen2 = run_len(run_da);

		/* new added delay clusters = rlen2 - rlen1. */
		ntfs_add_da(sbi, rlen2 - rlen1);
		*len = to_alloc;
		*lcn = DELALLOC_LCN;
		goto ok;
	}

	/* Get the last LCN to allocate from. */
	hint = 0;

@@ -1080,18 +1198,19 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
	}

	/* Allocate and zeroout new clusters. */
	err = attr_allocate_clusters(sbi, run, vcn, hint + 1, to_alloc, NULL,
				     zero ? ALLOCATE_ZERO : ALLOCATE_DEF, &alen,
				     fr, lcn, len);
	err = attr_allocate_clusters(sbi, run, run_da, vcn, hint + 1, to_alloc,
				     NULL,
				     zero ? ALLOCATE_ZERO : ALLOCATE_ONE_FR,
				     len, fr, lcn, len);
	if (err)
		goto out;
	*new = true;
	step = 1;

	end = vcn + alen;
	end = vcn + *len;
	/* Save 'total_size0' to restore if error. */
	total_size0 = le64_to_cpu(attr_b->nres.total_size);
	total_size = total_size0 + ((u64)alen << cluster_bits);
	total_size = total_size0 + ((u64)*len << cluster_bits);

	if (vcn != vcn0) {
		if (!run_lookup_entry(run, vcn0, lcn, len, NULL)) {
@@ -1157,7 +1276,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
	 * in 'ni_insert_nonresident'.
	 * Return in advance -ENOSPC here if there are no free cluster and no free MFT.
	 */
	if (!ntfs_check_for_free_space(sbi, 1, 1)) {
	if (!ntfs_check_free_space(sbi, 1, 1, false)) {
		/* Undo step 1. */
		err = -ENOSPC;
		goto undo1;
@@ -1242,8 +1361,6 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
		/* Too complex to restore. */
		_ntfs_bad_inode(&ni->vfs_inode);
	}
	up_write(&ni->file.run_lock);
	ni_unlock(ni);

	return err;

@@ -1252,8 +1369,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
	attr_b->nres.total_size = cpu_to_le64(total_size0);
	inode_set_bytes(&ni->vfs_inode, total_size0);

	if (run_deallocate_ex(sbi, run, vcn, alen, NULL, false) ||
	    !run_add_entry(run, vcn, SPARSE_LCN, alen, false) ||
	if (run_deallocate_ex(sbi, run, vcn, *len, NULL, false, run_da) ||
	    !run_add_entry(run, vcn, SPARSE_LCN, *len, false) ||
	    mi_pack_runs(mi, attr, run, max(end, evcn1) - svcn)) {
		_ntfs_bad_inode(&ni->vfs_inode);
	}
@@ -1688,7 +1805,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,

	if (len < clst_data) {
		err = run_deallocate_ex(sbi, run, vcn + len, clst_data - len,
					NULL, true);
					NULL, true, NULL);
		if (err)
			goto out;

@@ -1708,7 +1825,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
			hint = -1;
		}

		err = attr_allocate_clusters(sbi, run, vcn + clst_data,
		err = attr_allocate_clusters(sbi, run, NULL, vcn + clst_data,
					     hint + 1, len - clst_data, NULL,
					     ALLOCATE_DEF, &alen, 0, NULL,
					     NULL);
@@ -1863,6 +1980,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
	CLST vcn, end;
	u64 valid_size, data_size, alloc_size, total_size;
	u32 mask;
	u64 i_size;
	__le16 a_flags;

	if (!bytes)
@@ -1878,52 +1996,79 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
		return 0;
	}

	data_size = le64_to_cpu(attr_b->nres.data_size);
	alloc_size = le64_to_cpu(attr_b->nres.alloc_size);
	a_flags = attr_b->flags;

	if (is_attr_ext(attr_b)) {
		total_size = le64_to_cpu(attr_b->nres.total_size);
		mask = (sbi->cluster_size << attr_b->nres.c_unit) - 1;
	} else {
		total_size = alloc_size;
		mask = sbi->cluster_mask;
	}

	if ((vbo & mask) || (bytes & mask)) {
	mask = is_attr_ext(attr_b) ?
		       ((sbi->cluster_size << attr_b->nres.c_unit) - 1) :
		       sbi->cluster_mask;
	if ((vbo | bytes) & mask) {
		/* Allow to collapse only cluster aligned ranges. */
		return -EINVAL;
	}

	if (vbo > data_size)
	/* i_size - size of file with delay allocated clusters. */
	i_size = ni->vfs_inode.i_size;

	if (vbo > i_size)
		return -EINVAL;

	down_write(&ni->file.run_lock);

	if (vbo + bytes >= data_size) {
		u64 new_valid = min(ni->i_valid, vbo);
	if (vbo + bytes >= i_size) {
		valid_size = min(ni->i_valid, vbo);

		/* Simple truncate file at 'vbo'. */
		truncate_setsize(&ni->vfs_inode, vbo);
		err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, vbo,
				    &new_valid, true, NULL);
				    &valid_size, true);

		if (!err && new_valid < ni->i_valid)
			ni->i_valid = new_valid;
		if (!err && valid_size < ni->i_valid)
			ni->i_valid = valid_size;

		goto out;
	}

	/*
	 * Enumerate all attribute segments and collapse.
	 */
	alen = alloc_size >> sbi->cluster_bits;
	vcn = vbo >> sbi->cluster_bits;
	len = bytes >> sbi->cluster_bits;
	end = vcn + len;
	dealloc = 0;
	done = 0;

	/*
	 * Check delayed clusters.
	 */
	if (ni->file.run_da.count) {
		struct runs_tree *run_da = &ni->file.run_da;
		if (run_is_mapped_full(run_da, vcn, end - 1)) {
			/*
			 * The requested range is full in delayed clusters.
			 */
			err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, run,
					       i_size - bytes, NULL, false,
					       NULL, true);
			goto out;
		}

		/* Collapse request crosses real and delayed clusters. */
		err = ni_allocate_da_blocks_locked(ni);
		if (err)
			goto out;

		/* Layout of records maybe changed. */
		le_b = NULL;
		attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL,
				      &mi_b);
		if (!attr_b || !attr_b->non_res) {
			err = -ENOENT;
			goto out;
		}
	}

	data_size = le64_to_cpu(attr_b->nres.data_size);
	alloc_size = le64_to_cpu(attr_b->nres.alloc_size);
	total_size = is_attr_ext(attr_b) ?
			     le64_to_cpu(attr_b->nres.total_size) :
			     alloc_size;
	alen = alloc_size >> sbi->cluster_bits;
	a_flags = attr_b->flags;
	svcn = le64_to_cpu(attr_b->nres.svcn);
	evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1;

@@ -1946,6 +2091,9 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
		goto out;
	}

	/*
	 * Enumerate all attribute segments and collapse.
	 */
	for (;;) {
		CLST vcn1, eat, next_svcn;

@@ -1973,13 +2121,13 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
		vcn1 = vcn + done; /* original vcn in attr/run. */
		eat = min(end, evcn1) - vcn1;

		err = run_deallocate_ex(sbi, run, vcn1, eat, &dealloc, true);
		err = run_deallocate_ex(sbi, run, vcn1, eat, &dealloc, true,
					NULL);
		if (err)
			goto out;

		if (svcn + eat < evcn1) {
			/* Collapse a part of this attribute segment. */

			if (!run_collapse_range(run, vcn1, eat, done)) {
				err = -ENOMEM;
				goto out;
@@ -2160,9 +2308,9 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size)
		bytes = alloc_size;
	bytes -= vbo;

	if ((vbo & mask) || (bytes & mask)) {
	if ((vbo | bytes) & mask) {
		/* We have to zero a range(s). */
		if (frame_size == NULL) {
		if (!frame_size) {
			/* Caller insists range is aligned. */
			return -EINVAL;
		}
@@ -2221,7 +2369,8 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size)
		 * Calculate how many clusters there are.
		 * Don't do any destructive actions.
		 */
		err = run_deallocate_ex(NULL, run, vcn1, zero, &hole2, false);
		err = run_deallocate_ex(NULL, run, vcn1, zero, &hole2, false,
					NULL);
		if (err)
			goto done;

@@ -2259,7 +2408,8 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size)
		}

		/* Real deallocate. Should not fail. */
		run_deallocate_ex(sbi, &run2, vcn1, zero, &hole, true);
		run_deallocate_ex(sbi, &run2, vcn1, zero, &hole, true,
				  &ni->file.run_da);

next_attr:
		/* Free all allocated memory. */
@@ -2371,7 +2521,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
		return -EINVAL;
	}

	if ((vbo & mask) || (bytes & mask)) {
	if ((vbo | bytes) & mask) {
		/* Allow to insert only frame aligned ranges. */
		return -EINVAL;
	}
@@ -2390,7 +2540,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)

	if (!attr_b->non_res) {
		err = attr_set_size(ni, ATTR_DATA, NULL, 0, run,
				    data_size + bytes, NULL, false, NULL);
				    data_size + bytes, NULL, false);

		le_b = NULL;
		attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL,
@@ -2413,7 +2563,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
			goto done;
		}

		/* Resident files becomes nonresident. */
		/* Resident file becomes nonresident. */
		data_size = le64_to_cpu(attr_b->nres.data_size);
		alloc_size = le64_to_cpu(attr_b->nres.alloc_size);
	}
@@ -2450,10 +2600,13 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
	if (err)
		goto out;

	if (!run_insert_range(run, vcn, len)) {
		err = -ENOMEM;
	err = run_insert_range(run, vcn, len);
	if (err)
		goto out;

	err = run_insert_range_da(&ni->file.run_da, vcn, len);
	if (err)
		goto out;
	}

	/* Try to pack in current record as much as possible. */
	err = mi_pack_runs(mi, attr, run, evcn1 + len - svcn);
+4 −4
Original line number Diff line number Diff line
@@ -345,8 +345,8 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
	le->id = id;
	memcpy(le->name, name, sizeof(short) * name_len);

	err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
			    &new_size, true, &attr);
	err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
			       &new_size, true, &attr, false);
	if (err) {
		/* Undo memmove above. */
		memmove(le, Add2Ptr(le, sz), old_size - off);
@@ -404,8 +404,8 @@ int al_update(struct ntfs_inode *ni, int sync)
	 * Attribute list increased on demand in al_add_le.
	 * Attribute list decreased here.
	 */
	err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
			    false, &attr);
	err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
			       false, &attr, false);
	if (err)
		goto out;

+164 −142

File changed.

Preview size limit exceeded, changes collapsed.

+67 −5
Original line number Diff line number Diff line
@@ -123,6 +123,8 @@ void ni_clear(struct ntfs_inode *ni)
		indx_clear(&ni->dir);
	else {
		run_close(&ni->file.run);
		ntfs_sub_da(ni->mi.sbi, run_len(&ni->file.run_da));
		run_close(&ni->file.run_da);
#ifdef CONFIG_NTFS3_LZX_XPRESS
		if (ni->file.offs_folio) {
			/* On-demand allocated page for offsets. */
@@ -2014,7 +2016,8 @@ int ni_decompress_file(struct ntfs_inode *ni)

		for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) {
			err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
						  &clen, &new, false, NULL);
						  &clen, &new, false, NULL,
						  false);
			if (err)
				goto out;
		}
@@ -2235,7 +2238,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
	struct runs_tree *run = &ni->file.run;
	u64 valid_size = ni->i_valid;
	u64 vbo_disk;
	size_t unc_size;
	size_t unc_size = 0;
	u32 frame_size, i, ondisk_size;
	struct page *pg;
	struct ATTRIB *attr;
@@ -2846,7 +2849,7 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data)
	/* Enumerate all fragments. */
	for (vcn = offset >> cluster_bits;; vcn += clen) {
		err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL, false,
					  NULL);
					  NULL, false);
		if (err) {
			return err;
		}
@@ -3235,3 +3238,62 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)

	return 0;
}

/*
 * Force to allocate all delay allocated clusters.
 */
int ni_allocate_da_blocks(struct ntfs_inode *ni)
{
	int err;

	ni_lock(ni);
	down_write(&ni->file.run_lock);

	err = ni_allocate_da_blocks_locked(ni);

	up_write(&ni->file.run_lock);
	ni_unlock(ni);

	return err;
}

/*
 * Force to allocate all delay allocated clusters.
 */
int ni_allocate_da_blocks_locked(struct ntfs_inode *ni)
{
	int err;

	if (!ni->file.run_da.count)
		return 0;

	if (is_sparsed(ni)) {
		CLST vcn, lcn, clen, alen;
		bool new;

		/*
		 * Sparse file allocates clusters in 'attr_data_get_block_locked'
		 */
		while (run_get_entry(&ni->file.run_da, 0, &vcn, &lcn, &clen)) {
			/* TODO: zero=true? */
			err = attr_data_get_block_locked(ni, vcn, clen, &lcn,
							 &alen, &new, true,
							 NULL, true);
			if (err)
				break;
			if (!new) {
				err = -EINVAL;
				break;
			}
		}
	} else {
		/*
		 * Normal file allocates clusters in 'attr_set_size'
		 */
		err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
				       ni->vfs_inode.i_size, &ni->i_valid,
				       false, NULL, true);
	}

	return err;
}
+38 −15
Original line number Diff line number Diff line
@@ -445,36 +445,59 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
}

/*
 * ntfs_check_for_free_space
 * ntfs_check_free_space
 *
 * Check if it is possible to allocate 'clen' clusters and 'mlen' Mft records
 */
bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen)
bool ntfs_check_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen,
			   bool da)
{
	size_t free, zlen, avail;
	struct wnd_bitmap *wnd;
	CLST da_clusters = ntfs_get_da(sbi);

	wnd = &sbi->used.bitmap;
	down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
	free = wnd_zeroes(wnd);

	if (free >= da_clusters) {
		free -= da_clusters;
	} else {
		free = 0;
	}

	zlen = min_t(size_t, NTFS_MIN_MFT_ZONE, wnd_zone_len(wnd));
	up_read(&wnd->rw_lock);

	if (free < zlen + clen)
	if (free < zlen + clen) {
		return false;
	}

	avail = free - (zlen + clen);

	/* 
	 * When delalloc is active then keep in mind some reserved space.
	 * The worst case: 1 mft record per each ~500 clusters.
	 */
	if (da) {
		/* 1 mft record per each 1024 clusters. */
		mlen += da_clusters >> 10;
	}

	if (mlen || !avail) {
		wnd = &sbi->mft.bitmap;
		down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT);
		free = wnd_zeroes(wnd);
		zlen = wnd_zone_len(wnd);
		up_read(&wnd->rw_lock);

	if (free >= zlen + mlen)
		return true;
		if (free < zlen + mlen &&
		    avail < bytes_to_cluster(sbi, mlen << sbi->record_bits)) {
			return false;
		}
	}

	return avail >= bytes_to_cluster(sbi, mlen << sbi->record_bits);
	return true;
}

/*
@@ -509,8 +532,8 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi)

	/* Step 1: Resize $MFT::DATA. */
	down_write(&ni->file.run_lock);
	err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run,
			    new_mft_bytes, NULL, false, &attr);
	err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
			       new_mft_bytes, NULL, false, &attr, false);

	if (err) {
		up_write(&ni->file.run_lock);
@@ -525,7 +548,7 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi)
	new_bitmap_bytes = ntfs3_bitmap_size(new_mft_total);

	err = attr_set_size(ni, ATTR_BITMAP, NULL, 0, &sbi->mft.bitmap.run,
			    new_bitmap_bytes, &new_bitmap_bytes, true, NULL);
			    new_bitmap_bytes, &new_bitmap_bytes, true);

	/* Refresh MFT Zone if necessary. */
	down_write_nested(&sbi->used.bitmap.rw_lock, BITMAP_MUTEX_CLUSTERS);
@@ -2191,7 +2214,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
	if (new_sds_size > ni->vfs_inode.i_size) {
		err = attr_set_size(ni, ATTR_DATA, SDS_NAME,
				    ARRAY_SIZE(SDS_NAME), &ni->file.run,
				    new_sds_size, &new_sds_size, false, NULL);
				    new_sds_size, &new_sds_size, false);
		if (err)
			goto out;
	}
Loading