Unverified Commit 099ef9ab authored by Konstantin Komarov's avatar Konstantin Komarov
Browse files

fs/ntfs3: implement iomap-based file operations



This patch modifies the ntfs3 driver by replacing the buffer_head-based
operations with the iomap ones.

Implementation details:
- Implements core iomap operations (ntfs_iomap_begin/end) for block mapping:
    Proper handling of resident attributes via IOMAP_INLINE.
    Support for sparse files through IOMAP_HOLE semantics.
    Correct unwritten extent handling for zeroing operations.
- Replaces custom implementations with standardized iomap helpers:
    Converts buffered reads to use iomap_read_folio and iomap_readahead.
    Implements iomap_file_buffered_write for write operations.
    Uses iomap_dio_rw for direct I/O paths.
    Migrates zero range operations to iomap_zero_range.
- Preserves special handling paths for compressed files
- Implements proper EOF/valid data size management during writes

Signed-off-by: default avatarKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
parent e37a75bb
Loading
Loading
Loading
Loading
+33 −45
Original line number Diff line number Diff line
@@ -166,6 +166,12 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
			continue;
		}

		if (err == -ENOSPC && new_len && vcn - vcn0) {
			/* Keep already allocated clusters. */
			*alen = vcn - vcn0;
			return 0;
		}

		if (err)
			goto out;

@@ -886,7 +892,7 @@ 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)
			CLST *len, bool *new, bool zero, void **res)
{
	int err = 0;
	struct runs_tree *run = &ni->file.run;
@@ -903,6 +909,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,

	if (new)
		*new = false;
	if (res)
		*res = NULL;

	/* Try to find in cache. */
	down_read(&ni->file.run_lock);
@@ -939,8 +947,15 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
	}

	if (!attr_b->non_res) {
		u32 data_size = le32_to_cpu(attr_b->res.data_size);
		*lcn = RESIDENT_LCN;
		*len = le32_to_cpu(attr_b->res.data_size);
		*len = data_size;
		if (res && data_size) {
			*res = kmemdup(resident_data(attr_b), data_size,
				       GFP_KERNEL);
			if (!*res)
				err = -ENOMEM;
		}
		goto out;
	}

@@ -1028,6 +1043,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
		to_alloc = ((vcn0 + clen + clst_per_frame - 1) & cmask) - vcn;
		if (fr < clst_per_frame)
			fr = clst_per_frame;
		if (vcn != vcn0)
			zero = true;

		/* Check if 'vcn' and 'vcn0' in different attribute segments. */
@@ -1244,33 +1260,6 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
	goto out;
}

int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio)
{
	u64 vbo;
	struct ATTRIB *attr;
	u32 data_size;
	size_t len;

	attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, NULL);
	if (!attr)
		return -EINVAL;

	if (attr->non_res)
		return E_NTFS_NONRESIDENT;

	vbo = folio->index << PAGE_SHIFT;
	data_size = le32_to_cpu(attr->res.data_size);
	if (vbo > data_size)
		len = 0;
	else
		len = min(data_size - vbo, folio_size(folio));

	folio_fill_tail(folio, 0, resident_data(attr) + vbo, len);
	folio_mark_uptodate(folio);

	return 0;
}

int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
{
	u64 vbo;
@@ -1287,7 +1276,7 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
		return E_NTFS_NONRESIDENT;
	}

	vbo = folio->index << PAGE_SHIFT;
	vbo = folio_pos(folio);
	data_size = le32_to_cpu(attr->res.data_size);
	if (vbo < data_size) {
		char *data = resident_data(attr);
@@ -1360,22 +1349,21 @@ int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type,
	int retry = 0;

	for (vcn = from >> cluster_bits; vcn <= vcn_last; vcn += clen) {
		if (!run_lookup_entry(run, vcn, &lcn, &clen, NULL)) {
			if (retry != 0) { /* Next run_lookup_entry(vcn) also failed. */
		if (run_lookup_entry(run, vcn, &lcn, &clen, NULL)) {
			retry = 0;
			continue;
		}
		if (retry) {
			err = -EINVAL;
			break;
		}
			err = attr_load_runs_vcn(ni, type, name, name_len, run,
						 vcn);
		err = attr_load_runs_vcn(ni, type, name, name_len, run, vcn);
		if (err)
			break;

		clen = 0; /* Next run_lookup_entry(vcn) must be success. */
		retry++;
	}
		else
			retry = 0;
	}

	return err;
}
+192 −176
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/falloc.h>
#include <linux/fiemap.h>
#include <linux/fileattr.h>
#include <linux/iomap.h>

#include "debug.h"
#include "ntfs.h"
@@ -189,9 +190,6 @@ static int ntfs_extend_initialized_size(struct file *file,
					const loff_t new_valid)
{
	struct inode *inode = &ni->vfs_inode;
	struct address_space *mapping = inode->i_mapping;
	struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
	loff_t pos = valid;
	int err;

	if (valid >= new_valid)
@@ -204,140 +202,41 @@ static int ntfs_extend_initialized_size(struct file *file,

	WARN_ON(is_compressed(ni));

	for (;;) {
		u32 zerofrom, len;
		struct folio *folio;
		u8 bits;
		CLST vcn, lcn, clen;

		if (is_sparsed(ni)) {
			bits = sbi->cluster_bits;
			vcn = pos >> bits;

			err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL,
						  false);
			if (err)
				goto out;

			if (lcn == SPARSE_LCN) {
				pos = ((loff_t)clen + vcn) << bits;
				ni->i_valid = pos;
				goto next;
			}
		}

		zerofrom = pos & (PAGE_SIZE - 1);
		len = PAGE_SIZE - zerofrom;

		if (pos + len > new_valid)
			len = new_valid - pos;

		err = ntfs_write_begin(NULL, mapping, pos, len, &folio, NULL);
		if (err)
			goto out;

		folio_zero_range(folio, zerofrom, folio_size(folio) - zerofrom);

		err = ntfs_write_end(NULL, mapping, pos, len, len, folio, NULL);
		if (err < 0)
			goto out;
		pos += len;

next:
		if (pos >= new_valid)
			break;

		balance_dirty_pages_ratelimited(mapping);
		cond_resched();
	}

	return 0;

out:
	err = iomap_zero_range(inode, valid, new_valid - valid, NULL,
			       &ntfs_iomap_ops, &ntfs_iomap_folio_ops, NULL);
	if (err) {
		ni->i_valid = valid;
	ntfs_inode_warn(inode, "failed to extend initialized size to %llx.",
		ntfs_inode_warn(inode,
				"failed to extend initialized size to %llx.",
				new_valid);
		return err;
	}

/*
 * ntfs_zero_range - Helper function for punch_hole.
 *
 * It zeroes a range [vbo, vbo_to).
 */
static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
{
	int err = 0;
	struct address_space *mapping = inode->i_mapping;
	u32 blocksize = i_blocksize(inode);
	pgoff_t idx = vbo >> PAGE_SHIFT;
	u32 from = vbo & (PAGE_SIZE - 1);
	pgoff_t idx_end = (vbo_to + PAGE_SIZE - 1) >> PAGE_SHIFT;
	loff_t page_off;
	struct buffer_head *head, *bh;
	u32 bh_next, bh_off, to;
	sector_t iblock;
	struct folio *folio;
	bool dirty = false;

	for (; idx < idx_end; idx += 1, from = 0) {
		page_off = (loff_t)idx << PAGE_SHIFT;
		to = (page_off + PAGE_SIZE) > vbo_to ? (vbo_to - page_off) :
						       PAGE_SIZE;
		iblock = page_off >> inode->i_blkbits;

		folio = __filemap_get_folio(
			mapping, idx, FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
			mapping_gfp_constraint(mapping, ~__GFP_FS));
		if (IS_ERR(folio))
			return PTR_ERR(folio);

		head = folio_buffers(folio);
		if (!head)
			head = create_empty_buffers(folio, blocksize, 0);

		bh = head;
		bh_off = 0;
		do {
			bh_next = bh_off + blocksize;

			if (bh_next <= from || bh_off >= to)
				continue;

			if (!buffer_mapped(bh)) {
				ntfs_get_block(inode, iblock, bh, 0);
				/* Unmapped? It's a hole - nothing to do. */
				if (!buffer_mapped(bh))
					continue;
			}

			/* Ok, it's mapped. Make sure it's up-to-date. */
			if (folio_test_uptodate(folio))
				set_buffer_uptodate(bh);
			else if (bh_read(bh, 0) < 0) {
				err = -EIO;
				folio_unlock(folio);
				folio_put(folio);
				goto out;
	return 0;
}

			mark_buffer_dirty(bh);
		} while (bh_off = bh_next, iblock += 1,
			 head != (bh = bh->b_this_page));

		folio_zero_segment(folio, from, to);
		dirty = true;
static void ntfs_filemap_close(struct vm_area_struct *vma)
{
	struct inode *inode = file_inode(vma->vm_file);
	struct ntfs_inode *ni = ntfs_i(inode);
	u64 from = (u64)vma->vm_pgoff << PAGE_SHIFT;
	u64 to = min_t(u64, i_size_read(inode),
		       from + vma->vm_end - vma->vm_start);

		folio_unlock(folio);
		folio_put(folio);
		cond_resched();
	}
out:
	if (dirty)
	if (ni->i_valid < to) {
		ni->i_valid = to;
		mark_inode_dirty(inode);
	return err;
	}
}

/* Copy of generic_file_vm_ops. */
static const struct vm_operations_struct ntfs_file_vm_ops = {
	.close = ntfs_filemap_close,
	.fault = filemap_fault,
	.map_pages = filemap_map_pages,
	.page_mkwrite = filemap_page_mkwrite,
};

/*
 * ntfs_file_mmap_prepare - file_operations::mmap_prepare
 */
@@ -346,7 +245,6 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
	struct file *file = desc->file;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);
	u64 from = ((u64)desc->pgoff << PAGE_SHIFT);
	bool rw = desc->vm_flags & VM_WRITE;
	int err;

@@ -378,7 +276,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
	}

	if (rw) {
		u64 to = min_t(loff_t, i_size_read(inode),
		u64 from = (u64)desc->pgoff << PAGE_SHIFT;
		u64 to = min_t(u64, i_size_read(inode),
			       from + vma_desc_size(desc));

		if (is_sparsed(ni)) {
@@ -391,7 +290,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)

			for (; vcn < end; vcn += len) {
				err = attr_data_get_block(ni, vcn, 1, &lcn,
							  &len, &new, true);
							  &len, &new, true,
							  NULL);
				if (err)
					goto out;
			}
@@ -411,6 +311,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
	}

	err = generic_file_mmap_prepare(desc);
	if (!err && rw)
		desc->vm_ops = &ntfs_file_vm_ops;
out:
	return err;
}
@@ -465,7 +367,7 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
		 */
		for (; vcn < cend_v; vcn += clen) {
			err = attr_data_get_block(ni, vcn, cend_v - vcn, &lcn,
						  &clen, &new, true);
						  &clen, &new, true, NULL);
			if (err)
				goto out;
		}
@@ -474,7 +376,7 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
		 */
		for (; vcn < cend; vcn += clen) {
			err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
						  &clen, &new, false);
						  &clen, &new, false, NULL);
			if (err)
				goto out;
		}
@@ -503,25 +405,10 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,

static int ntfs_truncate(struct inode *inode, loff_t new_size)
{
	struct super_block *sb = inode->i_sb;
	struct ntfs_inode *ni = ntfs_i(inode);
	u64 new_valid;
	int err;
	struct ntfs_inode *ni = ntfs_i(inode);
	u64 new_valid = min_t(u64, ni->i_valid, new_size);

	if (!S_ISREG(inode->i_mode))
		return 0;

	if (is_compressed(ni)) {
		if (ni->i_valid > new_size)
			ni->i_valid = new_size;
	} else {
		err = block_truncate_page(inode->i_mapping, new_size,
					  ntfs_get_block);
		if (err)
			return err;
	}

	new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size));
	truncate_setsize(inode, new_size);

	ni_lock(ni);
@@ -531,11 +418,11 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
			    &new_valid, ni->mi.sbi->options->prealloc, NULL);
	up_write(&ni->file.run_lock);

	if (new_valid < ni->i_valid)
	ni->i_valid = new_valid;

	ni_unlock(ni);
	if (unlikely(err))

	if (err)
		return err;

	ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
@@ -646,13 +533,17 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)

		tmp = min(vbo_a, end);
		if (tmp > vbo) {
			err = ntfs_zero_range(inode, vbo, tmp);
			err = iomap_zero_range(inode, vbo, tmp - vbo, NULL,
					       &ntfs_iomap_ops,
					       &ntfs_iomap_folio_ops, NULL);
			if (err)
				goto out;
		}

		if (vbo < end_a && end_a < end) {
			err = ntfs_zero_range(inode, end_a, end);
			err = iomap_zero_range(inode, end_a, end - end_a, NULL,
					       &ntfs_iomap_ops,
					       &ntfs_iomap_folio_ops, NULL);
			if (err)
				goto out;
		}
@@ -762,7 +653,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
			for (; vcn < cend_v; vcn += clen) {
				err = attr_data_get_block(ni, vcn, cend_v - vcn,
							  &lcn, &clen, &new,
							  true);
							  true, NULL);
				if (err)
					goto out;
			}
@@ -772,7 +663,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
			for (; vcn < cend; vcn += clen) {
				err = attr_data_get_block(ni, vcn, cend - vcn,
							  &lcn, &clen, &new,
							  false);
							  false, NULL);
				if (err)
					goto out;
			}
@@ -787,6 +678,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
			ni_unlock(ni);
			if (err)
				goto out;
			i_size_write(inode, i_size);
		} else if (new_size > i_size) {
			i_size_write(inode, new_size);
		}
@@ -923,12 +815,16 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);
	size_t bytes = iov_iter_count(iter);
	ssize_t err;

	err = check_read_restriction(inode);
	if (err)
		return err;

	if (!bytes)
		return 0; /* skip atime */

	if (is_compressed(ni)) {
		if (iocb->ki_flags & IOCB_DIRECT) {
			ntfs_inode_warn(
@@ -940,13 +836,58 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
	}

	/* Check minimum alignment for dio. */
	if (iocb->ki_flags & IOCB_DIRECT) {
		struct super_block *sb = inode->i_sb;
		struct ntfs_sb_info *sbi = sb->s_fs_info;
		if ((iocb->ki_pos | iov_iter_alignment(iter)) &
		    sbi->bdev_blocksize_mask) {
	if ((iocb->ki_flags & IOCB_DIRECT) &&
	    (is_resident(ni) || ((iocb->ki_pos | iov_iter_alignment(iter)) &
				 ni->mi.sbi->bdev_blocksize_mask))) {
		/* Fallback to buffered I/O */
		iocb->ki_flags &= ~IOCB_DIRECT;
	}

	if (iocb->ki_flags & IOCB_DIRECT) {
		loff_t valid, i_size;
		loff_t vbo = iocb->ki_pos;
		loff_t end = vbo + bytes;
		unsigned int dio_flags = IOMAP_DIO_PARTIAL;

		if (iocb->ki_flags & IOCB_NOWAIT) {
			if (!inode_trylock_shared(inode))
				return -EAGAIN;
		} else {
			inode_lock_shared(inode);
		}

		valid = ni->i_valid;
		i_size = inode->i_size;

		if (vbo < valid) {
			if (valid < end) {
				/* read cross 'valid' size. */
				dio_flags |= IOMAP_DIO_FORCE_WAIT;
			}

			err = iomap_dio_rw(iocb, iter, &ntfs_iomap_ops, NULL,
					   dio_flags, NULL, 0);

			if (err > 0) {
				end = vbo + err;
				if (valid < end) {
					size_t to_zero = end - valid;
					/* Fix iter. */
					iov_iter_revert(iter, to_zero);
					iov_iter_zero(to_zero, iter);
				}
			}
		} else if (vbo < i_size) {
			if (end > i_size)
				bytes = i_size - vbo;
			iov_iter_zero(bytes, iter);
			iocb->ki_pos += bytes;
			err = bytes;
		}

		inode_unlock_shared(inode);
		file_accessed(iocb->ki_filp);
		return err;
	}

	return generic_file_read_iter(iocb, iter);
@@ -1070,7 +1011,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
		off = valid & (frame_size - 1);

		err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 1, &lcn,
					  &clen, NULL, false);
					  &clen, NULL, false, NULL);
		if (err)
			goto out;

@@ -1273,8 +1214,9 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);
	ssize_t ret;
	int err;
	struct super_block *sb = inode->i_sb;
	struct ntfs_sb_info *sbi = sb->s_fs_info;
	ssize_t ret, err;

	if (!inode_trylock(inode)) {
		if (iocb->ki_flags & IOCB_NOWAIT)
@@ -1312,18 +1254,76 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
	if (ret)
		goto out;

	ret = is_compressed(ni) ? ntfs_compress_write(iocb, from) :
				  __generic_file_write_iter(iocb, from);
	if (is_compressed(ni)) {
		ret = ntfs_compress_write(iocb, from);
		goto out;
	}

out:
	/* Check minimum alignment for dio. */
	if ((iocb->ki_flags & IOCB_DIRECT) &&
	    (is_resident(ni) || ((iocb->ki_pos | iov_iter_alignment(from)) &
				 sbi->bdev_blocksize_mask))) {
		/* Fallback to buffered I/O */
		iocb->ki_flags &= ~IOCB_DIRECT;
	}

	if (!(iocb->ki_flags & IOCB_DIRECT)) {
		ret = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
						&ntfs_iomap_folio_ops, NULL);
		inode_unlock(inode);

	if (ret > 0)
		if (likely(ret > 0))
			ret = generic_write_sync(iocb, ret);

		return ret;
	}

	ret = iomap_dio_rw(iocb, from, &ntfs_iomap_ops, NULL, IOMAP_DIO_PARTIAL,
			   NULL, 0);

	if (ret == -ENOTBLK) {
		/* Returns -ENOTBLK in case of a page invalidation failure for writes.*/
		/* The callers needs to fall back to buffered I/O in this case. */
		ret = 0;
	}

	if (ret >= 0 && iov_iter_count(from)) {
		loff_t offset = iocb->ki_pos, endbyte;

		iocb->ki_flags &= ~IOCB_DIRECT;
		err = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
						&ntfs_iomap_folio_ops, NULL);
		if (err < 0) {
			ret = err;
			goto out;
		}

		/*
		* We need to ensure that the pages within the page cache for
		* the range covered by this I/O are written to disk and
		* invalidated. This is in attempt to preserve the expected
		* direct I/O semantics in the case we fallback to buffered I/O
		* to complete off the I/O request.
		*/
		ret += err;
		endbyte = offset + err - 1;
		err = filemap_write_and_wait_range(inode->i_mapping, offset,
						   endbyte);
		if (err) {
			ret = err;
			goto out;
		}

		invalidate_mapping_pages(inode->i_mapping, offset >> PAGE_SHIFT,
					 endbyte >> PAGE_SHIFT);
	}

out:
	inode_unlock(inode);

	return ret;
}

/*
 * ntfs_file_open - file_operations::open
 */
@@ -1359,6 +1359,8 @@ int ntfs_file_open(struct inode *inode, struct file *file)
#endif
	}

	file->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;

	return generic_file_open(inode, file);
}

@@ -1408,16 +1410,30 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
	if (unlikely(is_bad_ni(ni)))
		return -EINVAL;

	err = fiemap_prep(inode, fieinfo, start, &len, ~FIEMAP_FLAG_XATTR);
	if (err)
		return err;
	if (is_compressed(ni)) {
		/* Unfortunately cp -r incorrectly treats compressed clusters. */
		ntfs_inode_warn(inode,
				"fiemap is not supported for compressed file");
		return -EOPNOTSUPP;
	}

	ni_lock(ni);
	if (S_ISDIR(inode->i_mode)) {
		/* TODO: add support for dirs (ATTR_ALLOC). */
		ntfs_inode_warn(inode,
				"fiemap is not supported for directories");
		return -EOPNOTSUPP;
	}

	err = ni_fiemap(ni, fieinfo, start, len);
	if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
		ntfs_inode_warn(inode, "fiemap(xattr) is not supported");
		return -EOPNOTSUPP;
	}

	ni_unlock(ni);
	inode_lock_shared(inode);

	err = iomap_fiemap(inode, fieinfo, start, len, &ntfs_iomap_ops);

	inode_unlock_shared(inode);
	return err;
}

@@ -1463,7 +1479,7 @@ int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)

	if (!ret) {
		ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
		ntfs_update_mftmirr(sbi, false);
		ntfs_update_mftmirr(sbi);
	}

	err = sync_blockdev(sb->s_bdev);
+6 −177
Original line number Diff line number Diff line
@@ -1850,183 +1850,11 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
	return REPARSE_LINK;
}

/*
 * ni_fiemap - Helper for file_fiemap().
 *
 * Assumed ni_lock.
 * TODO: Less aggressive locks.
 */
int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
	      __u64 vbo, __u64 len)
{
	int err = 0;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	u8 cluster_bits = sbi->cluster_bits;
	struct runs_tree run;
	struct ATTRIB *attr;
	CLST vcn = vbo >> cluster_bits;
	CLST lcn, clen;
	u64 valid = ni->i_valid;
	u64 lbo, bytes;
	u64 end, alloc_size;
	size_t idx = -1;
	u32 flags;
	bool ok;

	run_init(&run);
	if (S_ISDIR(ni->vfs_inode.i_mode)) {
		attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME,
				    ARRAY_SIZE(I30_NAME), NULL, NULL);
	} else {
		attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL,
				    NULL);
		if (!attr) {
			err = -EINVAL;
			goto out;
		}
		if (is_attr_compressed(attr)) {
			/* Unfortunately cp -r incorrectly treats compressed clusters. */
			err = -EOPNOTSUPP;
			ntfs_inode_warn(
				&ni->vfs_inode,
				"fiemap is not supported for compressed file (cp -r)");
			goto out;
		}
	}

	if (!attr || !attr->non_res) {
		err = fiemap_fill_next_extent(
			fieinfo, 0, 0,
			attr ? le32_to_cpu(attr->res.data_size) : 0,
			FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_LAST |
				FIEMAP_EXTENT_MERGED);
		goto out;
	}

	end = vbo + len;
	alloc_size = le64_to_cpu(attr->nres.alloc_size);
	if (end > alloc_size)
		end = alloc_size;

	while (vbo < end) {
		if (idx == -1) {
			ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);
		} else {
			CLST vcn_next = vcn;

			ok = run_get_entry(&run, ++idx, &vcn, &lcn, &clen) &&
			     vcn == vcn_next;
			if (!ok)
				vcn = vcn_next;
		}

		if (!ok) {
			err = attr_load_runs_vcn(ni, attr->type,
						 attr_name(attr),
						 attr->name_len, &run, vcn);

			if (err)
				break;

			ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);

			if (!ok) {
				err = -EINVAL;
				break;
			}
		}

		if (!clen) {
			err = -EINVAL; // ?
			break;
		}

		if (lcn == SPARSE_LCN) {
			vcn += clen;
			vbo = (u64)vcn << cluster_bits;
			continue;
		}

		flags = FIEMAP_EXTENT_MERGED;
		if (S_ISDIR(ni->vfs_inode.i_mode)) {
			;
		} else if (is_attr_compressed(attr)) {
			CLST clst_data;

			err = attr_is_frame_compressed(ni, attr,
						       vcn >> attr->nres.c_unit,
						       &clst_data, &run);
			if (err)
				break;
			if (clst_data < NTFS_LZNT_CLUSTERS)
				flags |= FIEMAP_EXTENT_ENCODED;
		} else if (is_attr_encrypted(attr)) {
			flags |= FIEMAP_EXTENT_DATA_ENCRYPTED;
		}

		vbo = (u64)vcn << cluster_bits;
		bytes = (u64)clen << cluster_bits;
		lbo = (u64)lcn << cluster_bits;

		vcn += clen;

		if (vbo + bytes >= end)
			bytes = end - vbo;

		if (vbo + bytes <= valid) {
			;
		} else if (vbo >= valid) {
			flags |= FIEMAP_EXTENT_UNWRITTEN;
		} else {
			/* vbo < valid && valid < vbo + bytes */
			u64 dlen = valid - vbo;

			if (vbo + dlen >= end)
				flags |= FIEMAP_EXTENT_LAST;

			err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen,
						      flags);

			if (err < 0)
				break;
			if (err == 1) {
				err = 0;
				break;
			}

			vbo = valid;
			bytes -= dlen;
			if (!bytes)
				continue;

			lbo += dlen;
			flags |= FIEMAP_EXTENT_UNWRITTEN;
		}

		if (vbo + bytes >= end)
			flags |= FIEMAP_EXTENT_LAST;

		err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags);
		if (err < 0)
			break;
		if (err == 1) {
			err = 0;
			break;
		}

		vbo += bytes;
	}

out:
	run_close(&run);
	return err;
}

static struct page *ntfs_lock_new_page(struct address_space *mapping,
				       pgoff_t index, gfp_t gfp)
{
	struct folio *folio = __filemap_get_folio(mapping, index,
			FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
	struct folio *folio = __filemap_get_folio(
		mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
	struct page *page;

	if (IS_ERR(folio))
@@ -2186,7 +2014,7 @@ 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);
						  &clen, &new, false, NULL);
			if (err)
				goto out;
		}
@@ -3017,7 +2845,8 @@ 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);
		err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL, false,
					  NULL);
		if (err) {
			return err;
		}
+1 −1
Original line number Diff line number Diff line
@@ -5130,7 +5130,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)

undo_action_done:

	ntfs_update_mftmirr(sbi, 0);
	ntfs_update_mftmirr(sbi);

	sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY;

+1 −9
Original line number Diff line number Diff line
@@ -843,9 +843,8 @@ int ntfs_refresh_zone(struct ntfs_sb_info *sbi)
/*
 * ntfs_update_mftmirr - Update $MFTMirr data.
 */
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi)
{
	int err;
	struct super_block *sb = sbi->sb;
	u32 blocksize, bytes;
	sector_t block1, block2;
@@ -884,12 +883,7 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)

		put_bh(bh1);
		bh1 = NULL;

		err = wait ? sync_dirty_buffer(bh2) : 0;

		put_bh(bh2);
		if (err)
			return;
	}

	sbi->flags &= ~NTFS_FLAGS_MFTMIRR;
@@ -1357,9 +1351,7 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
					err = -ENOMEM;
					goto out;
				}

				wait_on_buffer(bh);

				lock_buffer(bh);
				if (!buffer_uptodate(bh)) {
					memset(bh->b_data, 0, blocksize);
Loading