Commit 559e608c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ntfs3 updates from Konstantin Komarov:
 "New code:
   - support timestamps prior to epoch
   - do not overwrite uptodate pages
   - disable readahead for compressed files
   - setting of dummy blocksize to read boot_block when mounting
   - the run_lock initialization when loading $Extend
   - initialization of allocated memory before use
   - support for the NTFS3_IOC_SHUTDOWN ioctl
   - check for minimum alignment when performing direct I/O reads
   - check for shutdown in fsync

  Fixes:
   - mount failure for sparse runs in run_unpack()
   - use-after-free of sbi->options in cmp_fnames
   - KMSAN uninit bug after failed mi_read in mi_format_new
   - uninit error after buffer allocation by __getname()
   - KMSAN uninit-value in ni_create_attr_list
   - double free of sbi->options->nls and ownership of fc->fs_private
   - incorrect vcn adjustments in attr_collapse_range()
   - mode update when ACL can be reduced to mode
   - memory leaks in add sub record

  Changes:
   - refactor code, updated terminology, spelling
   - do not kmap pages in (de)compression code
   - after ntfs_look_free_mft(), code that fails must put mft_inode
   - default mount options for "acl" and "prealloc"

  Replaced:
   - use unsafe_memcpy() to avoid memcpy size warning
   - ntfs_bio_pages with page cache for compressed files"

* tag 'ntfs3_for_6.19' of https://github.com/Paragon-Software-Group/linux-ntfs3: (26 commits)
  fs/ntfs3: check for shutdown in fsync
  fs/ntfs3: change the default mount options for "acl" and "prealloc"
  fs/ntfs3: Prevent memory leaks in add sub record
  fs/ntfs3: out1 also needs to put mi
  fs/ntfs3: Fix spelling mistake "recommened" -> "recommended"
  fs/ntfs3: update mode in xattr when ACL can be reduced to mode
  fs/ntfs3: check minimum alignment for direct I/O
  fs/ntfs3: implement NTFS3_IOC_SHUTDOWN ioctl
  fs/ntfs3: correct attr_collapse_range when file is too fragmented
  ntfs3: fix double free of sbi->options->nls and clarify ownership of fc->fs_private
  fs/ntfs3: Initialize allocated memory before use
  fs/ntfs3: remove ntfs_bio_pages and use page cache for compressed I/O
  ntfs3: avoid memcpy size warning
  fs/ntfs3: fix KMSAN uninit-value in ni_create_attr_list
  ntfs3: init run lock for extend inode
  ntfs: set dummy blocksize to read boot_block when mounting
  fs/ntfs3: disable readahead for compressed files
  ntfs3: Fix uninit buffer allocated by __getname()
  ntfs3: fix uninit memory after failed mi_read in mi_format_new
  ntfs3: fix use-after-free of sbi->options in cmp_fnames
  ...
parents fbeea4db 1b2ae190
Loading
Loading
Loading
Loading
+42 −46
Original line number Diff line number Diff line
@@ -1457,7 +1457,6 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
		pgoff_t index = vbo[i] >> PAGE_SHIFT;

		if (index != folio->index) {
			struct page *page = &folio->page;
			u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1);
			u64 to = min(from + PAGE_SIZE, wof_size);

@@ -1467,8 +1466,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
			if (err)
				goto out1;

			err = ntfs_bio_pages(sbi, run, &page, 1, from,
					     to - from, REQ_OP_READ);
			err = ntfs_read_run(sbi, run, addr, from, to - from);
			if (err) {
				folio->index = -1;
				goto out1;
@@ -1862,7 +1860,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
	struct ATTRIB *attr = NULL, *attr_b;
	struct ATTR_LIST_ENTRY *le, *le_b;
	struct mft_inode *mi, *mi_b;
	CLST svcn, evcn1, len, dealloc, alen;
	CLST svcn, evcn1, len, dealloc, alen, done;
	CLST vcn, end;
	u64 valid_size, data_size, alloc_size, total_size;
	u32 mask;
@@ -1925,6 +1923,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
	len = bytes >> sbi->cluster_bits;
	end = vcn + len;
	dealloc = 0;
	done = 0;

	svcn = le64_to_cpu(attr_b->nres.svcn);
	evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1;
@@ -1933,23 +1932,28 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
		attr = attr_b;
		le = le_b;
		mi = mi_b;
	} else if (!le_b) {
		goto check_seg;
	}

	if (!le_b) {
		err = -EINVAL;
		goto out;
	} else {
	}

	le = le_b;
		attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn,
				    &mi);
	attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, &mi);
	if (!attr) {
		err = -EINVAL;
		goto out;
	}

	for (;;) {
		CLST vcn1, eat, next_svcn;

		svcn = le64_to_cpu(attr->nres.svcn);
		evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
	}

	for (;;) {
check_seg:
		if (svcn >= end) {
			/* Shift VCN- */
			attr->nres.svcn = cpu_to_le64(svcn - len);
@@ -1959,22 +1963,25 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
				ni->attr_list.dirty = true;
			}
			mi->dirty = true;
		} else if (svcn < vcn || end < evcn1) {
			CLST vcn1, eat, next_svcn;
			goto next_attr;
		}

			/* Collapse a part of this attribute segment. */
		run_truncate(run, 0);
		err = attr_load_runs(attr, ni, run, &svcn);
		if (err)
			goto out;
			vcn1 = max(vcn, svcn);

		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);
		if (err)
			goto out;

			if (!run_collapse_range(run, vcn1, eat)) {
		if (svcn + eat < evcn1) {
			/* Collapse a part of this attribute segment. */

			if (!run_collapse_range(run, vcn1, eat, done)) {
				err = -ENOMEM;
				goto out;
			}
@@ -1982,7 +1989,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
			if (svcn >= vcn) {
				/* Shift VCN */
				attr->nres.svcn = cpu_to_le64(vcn);
				if (le) {
				if (le && attr->nres.svcn != le->vcn) {
					le->vcn = attr->nres.svcn;
					ni->attr_list.dirty = true;
				}
@@ -1993,7 +2000,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
				goto out;

			next_svcn = le64_to_cpu(attr->nres.evcn) + 1;
			if (next_svcn + eat < evcn1) {
			if (next_svcn + eat + done < evcn1) {
				err = ni_insert_nonresident(
					ni, ATTR_DATA, NULL, 0, run, next_svcn,
					evcn1 - eat - next_svcn, a_flags, &attr,
@@ -2007,18 +2014,9 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)

			/* Free all allocated memory. */
			run_truncate(run, 0);
			done += eat;
		} else {
			u16 le_sz;
			u16 roff = le16_to_cpu(attr->nres.run_off);

			if (roff > le32_to_cpu(attr->size)) {
				err = -EINVAL;
				goto out;
			}

			run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn,
				      evcn1 - 1, svcn, Add2Ptr(attr, roff),
				      le32_to_cpu(attr->size) - roff);

			/* Delete this attribute segment. */
			mi_remove_attr(NULL, mi, attr);
@@ -2031,6 +2029,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
				goto out;
			}

			done += evcn1 - svcn;
			if (evcn1 >= alen)
				break;

@@ -2048,11 +2047,12 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
					err = -EINVAL;
					goto out;
				}
				goto next_attr;
				continue;
			}
			le = (struct ATTR_LIST_ENTRY *)((u8 *)le - le_sz);
		}

next_attr:
		if (evcn1 >= alen)
			break;

@@ -2061,10 +2061,6 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
			err = -EINVAL;
			goto out;
		}

next_attr:
		svcn = le64_to_cpu(attr->nres.svcn);
		evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
	}

	if (!attr_b) {
@@ -2554,7 +2550,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
	if (attr_load_runs(attr, ni, run, NULL))
		goto bad_inode;

	if (!run_collapse_range(run, vcn, len))
	if (!run_collapse_range(run, vcn, len, 0))
		goto bad_inode;

	if (mi_pack_runs(mi, attr, run, evcn1 + len - svcn))
+1 −2
Original line number Diff line number Diff line
@@ -332,8 +332,7 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi,
	 * It does additional locks/reads just to get the type of name.
	 * Should we use additional mount option to enable branch below?
	 */
	if (fname->dup.extend_data &&
	    ino != ni->mi.rno) {
	if (fname->dup.extend_data && ino != ni->mi.rno) {
		struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL);
		if (!IS_ERR_OR_NULL(inode)) {
			dt_type = fs_umode_to_dtype(inode->i_mode);
+93 −16
Original line number Diff line number Diff line
@@ -19,6 +19,12 @@
#include "ntfs.h"
#include "ntfs_fs.h"

/*
 * cifx, btrfs, exfat, ext4, f2fs use this constant.
 * Hope this value will become common to all fs.
 */
#define NTFS3_IOC_SHUTDOWN _IOR('X', 125, __u32)

static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
{
	struct fstrim_range __user *user_range;
@@ -73,13 +79,47 @@ static int ntfs_ioctl_set_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
	return ntfs_set_label(sbi, user, len);
}

/*
 * ntfs_force_shutdown - helper function. Called from ioctl
 */
static int ntfs_force_shutdown(struct super_block *sb, u32 flags)
{
	int err;
	struct ntfs_sb_info *sbi = sb->s_fs_info;

	if (unlikely(ntfs3_forced_shutdown(sb)))
		return 0;

	/* No additional options yet (flags). */
	err = bdev_freeze(sb->s_bdev);
	if (err)
		return err;
	set_bit(NTFS_FLAGS_SHUTDOWN_BIT, &sbi->flags);
	bdev_thaw(sb->s_bdev);
	return 0;
}

static int ntfs_ioctl_shutdown(struct super_block *sb, unsigned long arg)
{
	u32 flags;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (get_user(flags, (__u32 __user *)arg))
		return -EFAULT;

	return ntfs_force_shutdown(sb, flags);
}

/*
 * ntfs_ioctl - file_operations::unlocked_ioctl
 */
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{
	struct inode *inode = file_inode(filp);
	struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
	struct super_block *sb = inode->i_sb;
	struct ntfs_sb_info *sbi = sb->s_fs_info;

	/* Avoid any operation if inode is bad. */
	if (unlikely(is_bad_ni(ntfs_i(inode))))
@@ -92,6 +132,8 @@ long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
		return ntfs_ioctl_get_volume_label(sbi, (u8 __user *)arg);
	case FS_IOC_SETFSLABEL:
		return ntfs_ioctl_set_volume_label(sbi, (u8 __user *)arg);
	case NTFS3_IOC_SHUTDOWN:
		return ntfs_ioctl_shutdown(sb, arg);
	}
	return -ENOTTY; /* Inappropriate ioctl for device. */
}
@@ -325,10 +367,15 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
		return -EOPNOTSUPP;
	}

	if (is_compressed(ni) && rw) {
		ntfs_inode_warn(inode, "mmap(write) compressed not supported");
	if (is_compressed(ni)) {
		if (rw) {
			ntfs_inode_warn(inode,
					"mmap(write) compressed not supported");
			return -EOPNOTSUPP;
		}
		/* Turn off readahead for compressed files. */
		file->f_ra.ra_pages = 0;
	}

	if (rw) {
		u64 to = min_t(loff_t, i_size_read(inode),
@@ -503,8 +550,6 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
	if (dirty)
		mark_inode_dirty(inode);

	/*ntfs_flush_inodes(inode->i_sb, inode, NULL);*/

	return 0;
}

@@ -886,10 +931,25 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
	if (err)
		return err;

	if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) {
		ntfs_inode_warn(inode, "direct i/o + compressed not supported");
	if (is_compressed(ni)) {
		if (iocb->ki_flags & IOCB_DIRECT) {
			ntfs_inode_warn(
				inode, "direct i/o + compressed not supported");
			return -EOPNOTSUPP;
		}
		/* Turn off readahead for compressed files. */
		file->f_ra.ra_pages = 0;
	}

	/* 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) {
			iocb->ki_flags &= ~IOCB_DIRECT;
		}
	}

	return generic_file_read_iter(iocb, iter);
}
@@ -908,6 +968,11 @@ static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
	if (err)
		return err;

	if (is_compressed(ntfs_i(inode))) {
		/* Turn off readahead for compressed files. */
		in->f_ra.ra_pages = 0;
	}

	return filemap_splice_read(in, ppos, pipe, len, flags);
}

@@ -1026,7 +1091,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)

		if (!frame_uptodate && off) {
			err = ni_read_frame(ni, frame_vbo, pages,
					    pages_per_frame);
					    pages_per_frame, 0);
			if (err) {
				for (ip = 0; ip < pages_per_frame; ip++) {
					folio = page_folio(pages[ip]);
@@ -1091,7 +1156,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)

			if (off || (to < i_size && (to & (frame_size - 1)))) {
				err = ni_read_frame(ni, frame_vbo, pages,
						    pages_per_frame);
						    pages_per_frame, 0);
				if (err) {
					for (ip = 0; ip < pages_per_frame;
					     ip++) {
@@ -1114,8 +1179,8 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
			size_t cp, tail = PAGE_SIZE - off;

			folio = page_folio(pages[ip]);
			cp = copy_folio_from_iter_atomic(folio, off,
							min(tail, bytes), from);
			cp = copy_folio_from_iter_atomic(
				folio, off, min(tail, bytes), from);
			flush_dcache_folio(folio);

			copied += cp;
@@ -1375,6 +1440,18 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
	return iter_file_splice_write(pipe, file, ppos, len, flags);
}

/*
 * ntfs_file_fsync - file_operations::fsync
 */
static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
	struct inode *inode = file_inode(file);
	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
		return -EIO;

	return generic_file_fsync(file, start, end, datasync);
}

// clang-format off
const struct inode_operations ntfs_file_inode_operations = {
	.getattr	= ntfs_getattr,
@@ -1397,7 +1474,7 @@ const struct file_operations ntfs_file_operations = {
	.splice_write	= ntfs_file_splice_write,
	.mmap_prepare	= ntfs_file_mmap_prepare,
	.open		= ntfs_file_open,
	.fsync		= generic_file_fsync,
	.fsync		= ntfs_file_fsync,
	.fallocate	= ntfs_fallocate,
	.release	= ntfs_file_release,
};
+78 −141
Original line number Diff line number Diff line
@@ -325,8 +325,10 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi)

	mi_get_ref(&ni->mi, &m->mrec->parent_ref);

	ni_add_mi(ni, m);
	*mi = m;
	*mi = ni_ins_mi(ni, &ni->mi_tree, m->rno, &m->node);
	if (*mi != m)
		mi_put(m);

	return true;
}

@@ -767,7 +769,7 @@ int ni_create_attr_list(struct ntfs_inode *ni)
	 * Skip estimating exact memory requirement.
	 * Looks like one record_size is always enough.
	 */
	le = kmalloc(al_aligned(rs), GFP_NOFS);
	le = kzalloc(al_aligned(rs), GFP_NOFS);
	if (!le)
		return -ENOMEM;

@@ -1015,9 +1017,9 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,

out2:
	ni_remove_mi(ni, mi);
	mi_put(mi);

out1:
	mi_put(mi);
	ntfs_mark_rec_free(sbi, rno, is_mft);

out:
@@ -2020,6 +2022,29 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
	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 page *page;

	if (IS_ERR(folio))
		return ERR_CAST(folio);

	if (!folio_test_uptodate(folio))
		return folio_file_page(folio, index);

	/* Use a temporary page to avoid data corruption */
	folio_unlock(folio);
	folio_put(folio);
	page = alloc_page(gfp);
	if (!page)
		return ERR_PTR(-ENOMEM);
	__SetPageLocked(page);
	return page;
}

/*
 * ni_readpage_cmpr
 *
@@ -2074,15 +2099,15 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
		if (i == idx)
			continue;

		pg = find_or_create_page(mapping, index, gfp_mask);
		if (!pg) {
			err = -ENOMEM;
		pg = ntfs_lock_new_page(mapping, index, gfp_mask);
		if (IS_ERR(pg)) {
			err = PTR_ERR(pg);
			goto out1;
		}
		pages[i] = pg;
	}

	err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame);
	err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);

out1:
	for (i = 0; i < pages_per_frame; i++) {
@@ -2152,17 +2177,9 @@ int ni_decompress_file(struct ntfs_inode *ni)
	 */
	index = 0;
	for (vbo = 0; vbo < i_size; vbo += bytes) {
		u32 nr_pages;
		bool new;

		if (vbo + frame_size > i_size) {
			bytes = i_size - vbo;
			nr_pages = (bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
		} else {
			nr_pages = pages_per_frame;
			bytes = frame_size;
		}

		bytes = vbo + frame_size > i_size ? (i_size - vbo) : frame_size;
		end = bytes_to_cluster(sbi, vbo + bytes);

		for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) {
@@ -2175,27 +2192,19 @@ int ni_decompress_file(struct ntfs_inode *ni)
		for (i = 0; i < pages_per_frame; i++, index++) {
			struct page *pg;

			pg = find_or_create_page(mapping, index, gfp_mask);
			if (!pg) {
			pg = ntfs_lock_new_page(mapping, index, gfp_mask);
			if (IS_ERR(pg)) {
				while (i--) {
					unlock_page(pages[i]);
					put_page(pages[i]);
				}
				err = -ENOMEM;
				err = PTR_ERR(pg);
				goto out;
			}
			pages[i] = pg;
		}

		err = ni_read_frame(ni, vbo, pages, pages_per_frame);

		if (!err) {
			down_read(&ni->file.run_lock);
			err = ntfs_bio_pages(sbi, &ni->file.run, pages,
					     nr_pages, vbo, bytes,
					     REQ_OP_WRITE);
			up_read(&ni->file.run_lock);
		}
		err = ni_read_frame(ni, vbo, pages, pages_per_frame, 1);

		for (i = 0; i < pages_per_frame; i++) {
			unlock_page(pages[i]);
@@ -2385,20 +2394,19 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr,
 * Pages - Array of locked pages.
 */
int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
		  u32 pages_per_frame)
		  u32 pages_per_frame, int copy)
{
	int err;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	u8 cluster_bits = sbi->cluster_bits;
	char *frame_ondisk = NULL;
	char *frame_mem = NULL;
	struct page **pages_disk = NULL;
	struct ATTR_LIST_ENTRY *le = NULL;
	struct runs_tree *run = &ni->file.run;
	u64 valid_size = ni->i_valid;
	u64 vbo_disk;
	size_t unc_size;
	u32 frame_size, i, npages_disk, ondisk_size;
	u32 frame_size, i, ondisk_size;
	struct page *pg;
	struct ATTRIB *attr;
	CLST frame, clst_data;
@@ -2407,9 +2415,6 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
	 * To simplify decompress algorithm do vmap for source
	 * and target pages.
	 */
	for (i = 0; i < pages_per_frame; i++)
		kmap(pages[i]);

	frame_size = pages_per_frame << PAGE_SHIFT;
	frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL);
	if (!frame_mem) {
@@ -2493,7 +2498,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
		err = attr_wof_frame_info(ni, attr, run, frame64, frames,
					  frame_bits, &ondisk_size, &vbo_data);
		if (err)
			goto out2;
			goto out1;

		if (frame64 == frames) {
			unc_size = 1 + ((i_size - 1) & (frame_size - 1));
@@ -2504,7 +2509,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,

		if (ondisk_size > frame_size) {
			err = -EINVAL;
			goto out2;
			goto out1;
		}

		if (!attr->non_res) {
@@ -2525,10 +2530,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
					   ARRAY_SIZE(WOF_NAME), run, vbo_disk,
					   vbo_data + ondisk_size);
		if (err)
			goto out2;
		npages_disk = (ondisk_size + (vbo_disk & (PAGE_SIZE - 1)) +
			       PAGE_SIZE - 1) >>
			      PAGE_SHIFT;
			goto out1;
#endif
	} else if (is_attr_compressed(attr)) {
		/* LZNT compression. */
@@ -2562,61 +2564,37 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
		if (clst_data >= NTFS_LZNT_CLUSTERS) {
			/* Frame is not compressed. */
			down_read(&ni->file.run_lock);
			err = ntfs_bio_pages(sbi, run, pages, pages_per_frame,
					     frame_vbo, ondisk_size,
					     REQ_OP_READ);
			err = ntfs_read_run(sbi, run, frame_mem, frame_vbo,
					    ondisk_size);
			up_read(&ni->file.run_lock);
			goto out1;
		}
		vbo_disk = frame_vbo;
		npages_disk = (ondisk_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
	} else {
		__builtin_unreachable();
		err = -EINVAL;
		goto out1;
	}

	pages_disk = kcalloc(npages_disk, sizeof(*pages_disk), GFP_NOFS);
	if (!pages_disk) {
		err = -ENOMEM;
		goto out2;
	}

	for (i = 0; i < npages_disk; i++) {
		pg = alloc_page(GFP_KERNEL);
		if (!pg) {
	/* Allocate memory to read compressed data to. */
	frame_ondisk = kvmalloc(ondisk_size, GFP_KERNEL);
	if (!frame_ondisk) {
		err = -ENOMEM;
			goto out3;
		}
		pages_disk[i] = pg;
		lock_page(pg);
		kmap(pg);
		goto out1;
	}

	/* Read 'ondisk_size' bytes from disk. */
	down_read(&ni->file.run_lock);
	err = ntfs_bio_pages(sbi, run, pages_disk, npages_disk, vbo_disk,
			     ondisk_size, REQ_OP_READ);
	err = ntfs_read_run(sbi, run, frame_ondisk, vbo_disk, ondisk_size);
	up_read(&ni->file.run_lock);
	if (err)
		goto out3;

	/*
	 * To simplify decompress algorithm do vmap for source and target pages.
	 */
	frame_ondisk = vmap(pages_disk, npages_disk, VM_MAP, PAGE_KERNEL_RO);
	if (!frame_ondisk) {
		err = -ENOMEM;
		goto out3;
	}
		goto out2;

	/* Decompress: Frame_ondisk -> frame_mem. */
#ifdef CONFIG_NTFS3_LZX_XPRESS
	if (run != &ni->file.run) {
		/* LZX or XPRESS */
		err = decompress_lzx_xpress(
			sbi, frame_ondisk + (vbo_disk & (PAGE_SIZE - 1)),
			ondisk_size, frame_mem, unc_size, frame_size);
		err = decompress_lzx_xpress(sbi, frame_ondisk, ondisk_size,
					    frame_mem, unc_size, frame_size);
	} else
#endif
	{
@@ -2634,30 +2612,25 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
		memset(frame_mem + ok, 0, frame_size - ok);
	}

	vunmap(frame_ondisk);

out3:
	for (i = 0; i < npages_disk; i++) {
		pg = pages_disk[i];
		if (pg) {
			kunmap(pg);
			unlock_page(pg);
			put_page(pg);
		}
	}
	kfree(pages_disk);

out2:
	kvfree(frame_ondisk);
out1:
#ifdef CONFIG_NTFS3_LZX_XPRESS
	if (run != &ni->file.run)
		run_free(run);
	if (!err && copy) {
		/* We are called from 'ni_decompress_file' */
		/* Copy decompressed LZX or XPRESS data into new place. */
		down_read(&ni->file.run_lock);
		err = ntfs_write_run(sbi, &ni->file.run, frame_mem, frame_vbo,
				     frame_size);
		up_read(&ni->file.run_lock);
	}
#endif
out1:
	vunmap(frame_mem);
out:
	for (i = 0; i < pages_per_frame; i++) {
		pg = pages[i];
		kunmap(pg);
		SetPageUptodate(pg);
	}

@@ -2680,13 +2653,10 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
	u64 frame_vbo = folio_pos(folio);
	CLST frame = frame_vbo >> frame_bits;
	char *frame_ondisk = NULL;
	struct page **pages_disk = NULL;
	struct ATTR_LIST_ENTRY *le = NULL;
	char *frame_mem;
	struct ATTRIB *attr;
	struct mft_inode *mi;
	u32 i;
	struct page *pg;
	size_t compr_size, ondisk_size;
	struct lznt *lznt;

@@ -2721,38 +2691,18 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
		goto out;
	}

	pages_disk = kcalloc(pages_per_frame, sizeof(struct page *), GFP_NOFS);
	if (!pages_disk) {
		err = -ENOMEM;
		goto out;
	}

	for (i = 0; i < pages_per_frame; i++) {
		pg = alloc_page(GFP_KERNEL);
		if (!pg) {
			err = -ENOMEM;
			goto out1;
		}
		pages_disk[i] = pg;
		lock_page(pg);
		kmap(pg);
	}

	/* To simplify compress algorithm do vmap for source and target pages. */
	frame_ondisk = vmap(pages_disk, pages_per_frame, VM_MAP, PAGE_KERNEL);
	/* Allocate memory to write compressed data to. */
	frame_ondisk = kvmalloc(frame_size, GFP_KERNEL);
	if (!frame_ondisk) {
		err = -ENOMEM;
		goto out1;
		goto out;
	}

	for (i = 0; i < pages_per_frame; i++)
		kmap(pages[i]);

	/* Map in-memory frame for read-only. */
	frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL_RO);
	if (!frame_mem) {
		err = -ENOMEM;
		goto out2;
		goto out1;
	}

	mutex_lock(&sbi->compress.mtx_lznt);
@@ -2768,7 +2718,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
		if (!lznt) {
			mutex_unlock(&sbi->compress.mtx_lznt);
			err = -ENOMEM;
			goto out3;
			goto out2;
		}

		sbi->compress.lznt = lznt;
@@ -2805,30 +2755,16 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
		goto out2;

	down_read(&ni->file.run_lock);
	err = ntfs_bio_pages(sbi, &ni->file.run,
			     ondisk_size < frame_size ? pages_disk : pages,
			     pages_per_frame, frame_vbo, ondisk_size,
			     REQ_OP_WRITE);
	err = ntfs_write_run(sbi, &ni->file.run,
			     ondisk_size < frame_size ? frame_ondisk :
							frame_mem,
			     frame_vbo, ondisk_size);
	up_read(&ni->file.run_lock);

out3:
	vunmap(frame_mem);

out2:
	for (i = 0; i < pages_per_frame; i++)
		kunmap(pages[i]);

	vunmap(frame_ondisk);
	vunmap(frame_mem);
out1:
	for (i = 0; i < pages_per_frame; i++) {
		pg = pages_disk[i];
		if (pg) {
			kunmap(pg);
			unlock_page(pg);
			put_page(pg);
		}
	}
	kfree(pages_disk);
	kvfree(frame_ondisk);
out:
	return err;
}
@@ -3026,8 +2962,8 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
	err = ni_add_name(new_dir_ni, ni, new_de);
	if (!err) {
		err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
		WARN_ON(err && ni_remove_name(new_dir_ni, ni, new_de, &de2,
			&undo));
		WARN_ON(err &&
			ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo));
	}

	/*
@@ -3127,7 +3063,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
		if (attr) {
			const struct REPARSE_POINT *rp;

			rp = resident_data_ex(attr, sizeof(struct REPARSE_POINT));
			rp = resident_data_ex(attr,
					      sizeof(struct REPARSE_POINT));
			/* If ATTR_REPARSE exists 'rp' can't be NULL. */
			if (rp)
				dup->extend_data = rp->ReparseTag;
+63 −69
Original line number Diff line number Diff line
@@ -1349,7 +1349,14 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
				}
				if (buffer_locked(bh))
					__wait_on_buffer(bh);

				lock_buffer(bh);
				if (!buffer_uptodate(bh))
				{
					memset(bh->b_data, 0, blocksize);
					set_buffer_uptodate(bh);
				}
				unlock_buffer(bh);
			} else {
				bh = ntfs_bread(sb, block);
				if (!bh) {
@@ -1472,99 +1479,86 @@ int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
}

/*
 * ntfs_bio_pages - Read/write pages from/to disk.
 * ntfs_read_write_run - Read/Write disk's page cache.
 */
int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run,
		   struct page **pages, u32 nr_pages, u64 vbo, u32 bytes,
		   enum req_op op)
int ntfs_read_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
			void *buf, u64 vbo, size_t bytes, int wr)
{
	int err = 0;
	struct bio *new, *bio = NULL;
	struct super_block *sb = sbi->sb;
	struct block_device *bdev = sb->s_bdev;
	struct page *page;
	struct address_space *mapping = sb->s_bdev->bd_mapping;
	u8 cluster_bits = sbi->cluster_bits;
	CLST lcn, clen, vcn, vcn_next;
	u32 add, off, page_idx;
	CLST vcn_next, vcn = vbo >> cluster_bits;
	CLST lcn, clen;
	u64 lbo, len;
	size_t run_idx;
	struct blk_plug plug;
	size_t idx;
	u32 off, op;
	struct folio *folio;
	char *kaddr;

	if (!bytes)
		return 0;

	blk_start_plug(&plug);
	if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx))
		return -ENOENT;

	/* Align vbo and bytes to be 512 bytes aligned. */
	lbo = (vbo + bytes + 511) & ~511ull;
	vbo = vbo & ~511ull;
	bytes = lbo - vbo;
	if (lcn == SPARSE_LCN)
		return -EINVAL;

	vcn = vbo >> cluster_bits;
	if (!run_lookup_entry(run, vcn, &lcn, &clen, &run_idx)) {
		err = -ENOENT;
		goto out;
	}
	off = vbo & sbi->cluster_mask;
	page_idx = 0;
	page = pages[0];

	for (;;) {
	lbo = ((u64)lcn << cluster_bits) + off;
	len = ((u64)clen << cluster_bits) - off;
new_bio:
		new = bio_alloc(bdev, nr_pages - page_idx, op, GFP_NOFS);
		if (bio) {
			bio_chain(bio, new);
			submit_bio(bio);
		}
		bio = new;
		bio->bi_iter.bi_sector = lbo >> 9;

		while (len) {
			off = vbo & (PAGE_SIZE - 1);
			add = off + len > PAGE_SIZE ? (PAGE_SIZE - off) : len;
	for (;;) {
		/* Read range [lbo, lbo+len). */
		folio = read_mapping_folio(mapping, lbo >> PAGE_SHIFT, NULL);

			if (bio_add_page(bio, page, add, off) < add)
				goto new_bio;
		if (IS_ERR(folio))
			return PTR_ERR(folio);

			if (bytes <= add)
				goto out;
			bytes -= add;
			vbo += add;
		off = offset_in_page(lbo);
		op = PAGE_SIZE - off;

			if (add + off == PAGE_SIZE) {
				page_idx += 1;
				if (WARN_ON(page_idx >= nr_pages)) {
					err = -EINVAL;
					goto out;
				}
				page = pages[page_idx];
		if (op > len)
			op = len;
		if (op > bytes)
			op = bytes;

		kaddr = kmap_local_folio(folio, 0);
		if (wr) {
			memcpy(kaddr + off, buf, op);
			folio_mark_dirty(folio);
		} else {
			memcpy(buf, kaddr + off, op);
			flush_dcache_folio(folio);
		}
		kunmap_local(kaddr);
		folio_put(folio);

			if (len <= add)
				break;
			len -= add;
			lbo += add;
		bytes -= op;
		if (!bytes)
			return 0;

		buf += op;
		len -= op;
		if (len) {
			/* next volume's page. */
			lbo += op;
			continue;
		}

		/* get next range. */
		vcn_next = vcn + clen;
		if (!run_get_entry(run, ++run_idx, &vcn, &lcn, &clen) ||
		if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) ||
		    vcn != vcn_next) {
			err = -ENOENT;
			goto out;
		}
		off = 0;
	}
out:
	if (bio) {
		if (!err)
			err = submit_bio_wait(bio);
		bio_put(bio);
			return -ENOENT;
		}
	blk_finish_plug(&plug);

	return err;
		if (lcn == SPARSE_LCN)
			return -EINVAL;

		lbo = ((u64)lcn << cluster_bits);
		len = ((u64)clen << cluster_bits);
	}
}

/*
Loading