Commit 5ea6d724 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ntfs3 updates from Konstantin Komarov:
 "New code:
   - simple fileattr support

  Fixes:
   - transform resident to nonresident for compressed files
   - the format of the "nocase" mount option
   - getting file type
   - many other internal bugs

  Refactoring:
   - remove unused functions and macros
   - partial transition from page to folio (suggested by Matthew Wilcox)
   - legacy ntfs support"

* tag 'ntfs3_for_6.11' of https://github.com/Paragon-Software-Group/linux-ntfs3: (42 commits)
  fs/ntfs3: Fix formatting, change comments, renaming
  fs/ntfs3: Update log->page_{mask,bits} if log->page_size changed
  fs/ntfs3: Implement simple fileattr
  fs/ntfs3: Redesign legacy ntfs support
  fs/ntfs3: Use function file_inode to get inode from file
  fs/ntfs3: Minor ntfs_list_ea refactoring
  fs/ntfs3: Check more cases when directory is corrupted
  fs/ntfs3: Do copy_to_user out of run_lock
  fs/ntfs3: Keep runs for $MFT::$ATTR_DATA and $MFT::$ATTR_BITMAP
  fs/ntfs3: Missed error return
  fs/ntfs3: Fix the format of the "nocase" mount option
  fs/ntfs3: Fix field-spanning write in INDEX_HDR
  ntfs3: Convert attr_wof_frame_info() to use a folio
  ntfs3: Convert ni_readpage_cmpr() to take a folio
  ntfs3: Convert ntfs_get_frame_pages() to use a folio
  ntfs3: Remove calls to set/clear the error flag
  ntfs3: Convert attr_make_nonresident to use a folio
  ntfs3: Convert attr_data_write_resident to use a folio
  ntfs3: Convert ntfs_write_end() to work on a folio
  ntfs3: Convert attr_data_read_resident() to take a folio
  ...
parents 93306970 911daf69
Loading
Loading
Loading
Loading
+71 −61
Original line number Diff line number Diff line
@@ -231,7 +231,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
	struct ntfs_sb_info *sbi;
	struct ATTRIB *attr_s;
	struct MFT_REC *rec;
	u32 used, asize, rsize, aoff, align;
	u32 used, asize, rsize, aoff;
	bool is_data;
	CLST len, alen;
	char *next;
@@ -252,10 +252,13 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
	rsize = le32_to_cpu(attr->res.data_size);
	is_data = attr->type == ATTR_DATA && !attr->name_len;

	align = sbi->cluster_size;
	if (is_attr_compressed(attr))
		align <<= COMPRESSION_UNIT;
	len = (rsize + align - 1) >> sbi->cluster_bits;
	/* len - how many clusters required to store 'rsize' bytes */
	if (is_attr_compressed(attr)) {
		u8 shift = sbi->cluster_bits + NTFS_LZNT_CUNIT;
		len = ((rsize + (1u << shift) - 1) >> shift) << NTFS_LZNT_CUNIT;
	} else {
		len = bytes_to_cluster(sbi, rsize);
	}

	run_init(run);

@@ -285,22 +288,21 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
			if (err)
				goto out2;
		} else if (!page) {
			char *kaddr;

			page = grab_cache_page(ni->vfs_inode.i_mapping, 0);
			if (!page) {
				err = -ENOMEM;
			struct address_space *mapping = ni->vfs_inode.i_mapping;
			struct folio *folio;

			folio = __filemap_get_folio(
				mapping, 0, FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
				mapping_gfp_mask(mapping));
			if (IS_ERR(folio)) {
				err = PTR_ERR(folio);
				goto out2;
			}
			kaddr = kmap_atomic(page);
			memcpy(kaddr, data, rsize);
			memset(kaddr + rsize, 0, PAGE_SIZE - rsize);
			kunmap_atomic(kaddr);
			flush_dcache_page(page);
			SetPageUptodate(page);
			set_page_dirty(page);
			unlock_page(page);
			put_page(page);
			folio_fill_tail(folio, 0, data, rsize);
			folio_mark_uptodate(folio);
			folio_mark_dirty(folio);
			folio_unlock(folio);
			folio_put(folio);
		}
	}

@@ -670,7 +672,8 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
			goto undo_2;
		}

		if (!is_mft)
		/* keep runs for $MFT::$ATTR_DATA and $MFT::$ATTR_BITMAP. */
		if (ni->mi.rno != MFT_REC_MFT)
			run_truncate_head(run, evcn + 1);

		svcn = le64_to_cpu(attr->nres.svcn);
@@ -972,6 +975,19 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
	if (err)
		goto out;

	/* Check for compressed frame. */
	err = attr_is_frame_compressed(ni, attr, vcn >> NTFS_LZNT_CUNIT, &hint);
	if (err)
		goto out;

	if (hint) {
		/* if frame is compressed - don't touch it. */
		*lcn = COMPRESSED_LCN;
		*len = hint;
		err = -EOPNOTSUPP;
		goto out;
	}

	if (!*len) {
		if (run_lookup_entry(run, vcn, lcn, len, NULL)) {
			if (*lcn != SPARSE_LCN || !new)
@@ -1223,11 +1239,12 @@ 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 page *page)
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)
@@ -1236,30 +1253,20 @@ int attr_data_read_resident(struct ntfs_inode *ni, struct page *page)
	if (attr->non_res)
		return E_NTFS_NONRESIDENT;

	vbo = page->index << PAGE_SHIFT;
	vbo = folio->index << PAGE_SHIFT;
	data_size = le32_to_cpu(attr->res.data_size);
	if (vbo < data_size) {
		const char *data = resident_data(attr);
		char *kaddr = kmap_atomic(page);
		u32 use = data_size - vbo;

		if (use > PAGE_SIZE)
			use = PAGE_SIZE;
	if (vbo > data_size)
		len = 0;
	else
		len = min(data_size - vbo, folio_size(folio));

		memcpy(kaddr, data + vbo, use);
		memset(kaddr + use, 0, PAGE_SIZE - use);
		kunmap_atomic(kaddr);
		flush_dcache_page(page);
		SetPageUptodate(page);
	} else if (!PageUptodate(page)) {
		zero_user_segment(page, 0, PAGE_SIZE);
		SetPageUptodate(page);
	}
	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 page *page)
int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
{
	u64 vbo;
	struct mft_inode *mi;
@@ -1275,17 +1282,13 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct page *page)
		return E_NTFS_NONRESIDENT;
	}

	vbo = page->index << PAGE_SHIFT;
	vbo = folio->index << PAGE_SHIFT;
	data_size = le32_to_cpu(attr->res.data_size);
	if (vbo < data_size) {
		char *data = resident_data(attr);
		char *kaddr = kmap_atomic(page);
		u32 use = data_size - vbo;
		size_t len = min(data_size - vbo, folio_size(folio));

		if (use > PAGE_SIZE)
			use = PAGE_SIZE;
		memcpy(data + vbo, kaddr, use);
		kunmap_atomic(kaddr);
		memcpy_from_folio(data + vbo, folio, 0, len);
		mi->dirty = true;
	}
	ni->i_valid = data_size;
@@ -1378,7 +1381,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
	u32 voff;
	u8 bytes_per_off;
	char *addr;
	struct page *page;
	struct folio *folio;
	int i, err;
	__le32 *off32;
	__le64 *off64;
@@ -1423,18 +1426,18 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,

	wof_size = le64_to_cpu(attr->nres.data_size);
	down_write(&ni->file.run_lock);
	page = ni->file.offs_page;
	if (!page) {
		page = alloc_page(GFP_KERNEL);
		if (!page) {
	folio = ni->file.offs_folio;
	if (!folio) {
		folio = folio_alloc(GFP_KERNEL, 0);
		if (!folio) {
			err = -ENOMEM;
			goto out;
		}
		page->index = -1;
		ni->file.offs_page = page;
		folio->index = -1;
		ni->file.offs_folio = folio;
	}
	lock_page(page);
	addr = page_address(page);
	folio_lock(folio);
	addr = folio_address(folio);

	if (vbo[1]) {
		voff = vbo[1] & (PAGE_SIZE - 1);
@@ -1450,7 +1453,8 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
	do {
		pgoff_t index = vbo[i] >> PAGE_SHIFT;

		if (index != page->index) {
		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);

@@ -1463,10 +1467,10 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
			err = ntfs_bio_pages(sbi, run, &page, 1, from,
					     to - from, REQ_OP_READ);
			if (err) {
				page->index = -1;
				folio->index = -1;
				goto out1;
			}
			page->index = index;
			folio->index = index;
		}

		if (i) {
@@ -1504,7 +1508,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
	*ondisk_size = off[1] - off[0];

out1:
	unlock_page(page);
	folio_unlock(folio);
out:
	up_write(&ni->file.run_lock);
	return err;
@@ -1722,6 +1726,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,

	attr_b->nres.total_size = cpu_to_le64(total_size);
	inode_set_bytes(&ni->vfs_inode, total_size);
	ni->ni_flags |= NI_FLAG_UPDATE_PARENT;

	mi_b->dirty = true;
	mark_inode_dirty(&ni->vfs_inode);
@@ -2356,8 +2361,13 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
		mask = (sbi->cluster_size << attr_b->nres.c_unit) - 1;
	}

	if (vbo > data_size) {
		/* Insert range after the file size is not allowed. */
	if (vbo >= data_size) {
		/*
		 * Insert range after the file size is not allowed.
		 * If the offset is equal to or greater than the end of
		 * file, an error is returned.  For such operations (i.e., inserting
		 * a hole at the end of file), ftruncate(2) should be used.
		 */
		return -EINVAL;
	}

+1 −1
Original line number Diff line number Diff line
@@ -1382,7 +1382,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits)

		err = ntfs_vbo_to_lbo(sbi, &wnd->run, vbo, &lbo, &bytes);
		if (err)
			break;
			return err;

		bh = ntfs_bread(sb, lbo >> sb->s_blocksize_bits);
		if (!bh)
+36 −21
Original line number Diff line number Diff line
@@ -272,9 +272,12 @@ struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni,
	return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode;
}

static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
			       const struct NTFS_DE *e, u8 *name,
			       struct dir_context *ctx)
/*
 * returns false if 'ctx' if full
 */
static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi,
				 struct ntfs_inode *ni, const struct NTFS_DE *e,
				 u8 *name, struct dir_context *ctx)
{
	const struct ATTR_FILE_NAME *fname;
	unsigned long ino;
@@ -284,29 +287,29 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
	fname = Add2Ptr(e, sizeof(struct NTFS_DE));

	if (fname->type == FILE_NAME_DOS)
		return 0;
		return true;

	if (!mi_is_ref(&ni->mi, &fname->home))
		return 0;
		return true;

	ino = ino_get(&e->ref);

	if (ino == MFT_REC_ROOT)
		return 0;
		return true;

	/* Skip meta files. Unless option to show metafiles is set. */
	if (!sbi->options->showmeta && ntfs_is_meta_file(sbi, ino))
		return 0;
		return true;

	if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
		return 0;
		return true;

	name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name,
				     PATH_MAX);
	if (name_len <= 0) {
		ntfs_warn(sbi->sb, "failed to convert name for inode %lx.",
			  ino);
		return 0;
		return true;
	}

	/*
@@ -326,7 +329,8 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
	 * 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.fa & FILE_ATTRIBUTE_REPARSE_POINT) &&
	if (((fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT) ||
	     fname->dup.ea_size) &&
	    ino != ni->mi.rno) {
		struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL);
		if (!IS_ERR_OR_NULL(inode)) {
@@ -335,17 +339,20 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
		}
	}

	return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
	return dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
}

/*
 * ntfs_read_hdr - Helper function for ntfs_readdir().
 *
 * returns 0 if ok.
 * returns -EINVAL if directory is corrupted.
 * returns +1 if 'ctx' is full.
 */
static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
			 const struct INDEX_HDR *hdr, u64 vbo, u64 pos,
			 u8 *name, struct dir_context *ctx)
{
	int err;
	const struct NTFS_DE *e;
	u32 e_size;
	u32 end = le32_to_cpu(hdr->used);
@@ -353,12 +360,12 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,

	for (;; off += e_size) {
		if (off + sizeof(struct NTFS_DE) > end)
			return -1;
			return -EINVAL;

		e = Add2Ptr(hdr, off);
		e_size = le16_to_cpu(e->size);
		if (e_size < sizeof(struct NTFS_DE) || off + e_size > end)
			return -1;
			return -EINVAL;

		if (de_is_last(e))
			return 0;
@@ -368,14 +375,15 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
			continue;

		if (le16_to_cpu(e->key_size) < SIZEOF_ATTRIBUTE_FILENAME)
			return -1;
			return -EINVAL;

		ctx->pos = vbo + off;

		/* Submit the name to the filldir callback. */
		err = ntfs_filldir(sbi, ni, e, name, ctx);
		if (err)
			return err;
		if (!ntfs_dir_emit(sbi, ni, e, name, ctx)) {
			/* ctx is full. */
			return +1;
		}
	}
}

@@ -474,8 +482,6 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)

		vbo = (u64)bit << index_bits;
		if (vbo >= i_size) {
			ntfs_inode_err(dir, "Looks like your dir is corrupt");
			ctx->pos = eod;
			err = -EINVAL;
			goto out;
		}
@@ -498,9 +504,16 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
	__putname(name);
	put_indx_node(node);

	if (err == -ENOENT) {
	if (err == 1) {
		/* 'ctx' is full. */
		err = 0;
	} else if (err == -ENOENT) {
		err = 0;
		ctx->pos = pos;
	} else if (err < 0) {
		if (err == -EINVAL)
			ntfs_inode_err(dir, "directory corrupted");
		ctx->pos = eod;
	}

	return err;
@@ -618,10 +631,12 @@ const struct file_operations ntfs_dir_operations = {
#endif
};

#if IS_ENABLED(CONFIG_NTFS_FS)
const struct file_operations ntfs_legacy_dir_operations = {
	.llseek		= generic_file_llseek,
	.read		= generic_read_dir,
	.iterate_shared	= ntfs_readdir,
	.open		= ntfs_file_open,
};
#endif
// clang-format on
+96 −28
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/compat.h>
#include <linux/falloc.h>
#include <linux/fiemap.h>
#include <linux/fileattr.h>

#include "debug.h"
#include "ntfs.h"
@@ -48,6 +49,65 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
	return 0;
}

/*
 * ntfs_fileattr_get - inode_operations::fileattr_get
 */
int ntfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
{
	struct inode *inode = d_inode(dentry);
	struct ntfs_inode *ni = ntfs_i(inode);
	u32 flags = 0;

	if (inode->i_flags & S_IMMUTABLE)
		flags |= FS_IMMUTABLE_FL;

	if (inode->i_flags & S_APPEND)
		flags |= FS_APPEND_FL;

	if (is_compressed(ni))
		flags |= FS_COMPR_FL;

	if (is_encrypted(ni))
		flags |= FS_ENCRYPT_FL;

	fileattr_fill_flags(fa, flags);

	return 0;
}

/*
 * ntfs_fileattr_set - inode_operations::fileattr_set
 */
int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
		      struct fileattr *fa)
{
	struct inode *inode = d_inode(dentry);
	u32 flags = fa->flags;
	unsigned int new_fl = 0;

	if (fileattr_has_fsx(fa))
		return -EOPNOTSUPP;

	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL))
		return -EOPNOTSUPP;

	if (flags & FS_IMMUTABLE_FL)
		new_fl |= S_IMMUTABLE;

	if (flags & FS_APPEND_FL)
		new_fl |= S_APPEND;

	inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND);

	inode_set_ctime_current(inode);
	mark_inode_dirty(inode);

	return 0;
}

/*
 * ntfs_ioctl - file_operations::unlocked_ioctl
 */
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{
	struct inode *inode = file_inode(filp);
@@ -77,20 +137,27 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path,
	struct inode *inode = d_inode(path->dentry);
	struct ntfs_inode *ni = ntfs_i(inode);

	stat->result_mask |= STATX_BTIME;
	stat->btime = ni->i_crtime;
	stat->blksize = ni->mi.sbi->cluster_size; /* 512, 1K, ..., 2M */

	if (inode->i_flags & S_IMMUTABLE)
		stat->attributes |= STATX_ATTR_IMMUTABLE;

	if (inode->i_flags & S_APPEND)
		stat->attributes |= STATX_ATTR_APPEND;

	if (is_compressed(ni))
		stat->attributes |= STATX_ATTR_COMPRESSED;

	if (is_encrypted(ni))
		stat->attributes |= STATX_ATTR_ENCRYPTED;

	stat->attributes_mask |= STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED;
	stat->attributes_mask |= STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED |
				 STATX_ATTR_IMMUTABLE | STATX_ATTR_APPEND;

	generic_fillattr(idmap, request_mask, inode, stat);

	stat->result_mask |= STATX_BTIME;
	stat->btime = ni->i_crtime;
	stat->blksize = ni->mi.sbi->cluster_size; /* 512, 1K, ..., 2M */

	return 0;
}

@@ -196,8 +263,8 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
						       PAGE_SIZE;
		iblock = page_off >> inode->i_blkbits;

		folio = __filemap_get_folio(mapping, idx,
				FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
		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);
@@ -253,8 +320,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
 */
static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct address_space *mapping = file->f_mapping;
	struct inode *inode = mapping->host;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);
	u64 from = ((u64)vma->vm_pgoff << PAGE_SHIFT);
	bool rw = vma->vm_flags & VM_WRITE;
@@ -299,10 +365,7 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
		}

		if (ni->i_valid < to) {
			if (!inode_trylock(inode)) {
				err = -EAGAIN;
				goto out;
			}
			inode_lock(inode);
			err = ntfs_extend_initialized_size(file, ni,
							   ni->i_valid, to);
			inode_unlock(inode);
@@ -431,7 +494,7 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
 */
static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
{
	struct inode *inode = file->f_mapping->host;
	struct inode *inode = file_inode(file);
	struct address_space *mapping = inode->i_mapping;
	struct super_block *sb = inode->i_sb;
	struct ntfs_sb_info *sbi = sb->s_fs_info;
@@ -744,7 +807,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct file *file = iocb->ki_filp;
	struct inode *inode = file->f_mapping->host;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
@@ -781,7 +844,7 @@ static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
				     struct pipe_inode_info *pipe, size_t len,
				     unsigned int flags)
{
	struct inode *inode = in->f_mapping->host;
	struct inode *inode = file_inode(in);
	struct ntfs_inode *ni = ntfs_i(inode);

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
@@ -824,23 +887,25 @@ static int ntfs_get_frame_pages(struct address_space *mapping, pgoff_t index,
	*frame_uptodate = true;

	for (npages = 0; npages < pages_per_frame; npages++, index++) {
		struct page *page;
		struct folio *folio;

		page = find_or_create_page(mapping, index, gfp_mask);
		if (!page) {
		folio = __filemap_get_folio(mapping, index,
					    FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
					    gfp_mask);
		if (IS_ERR(folio)) {
			while (npages--) {
				page = pages[npages];
				unlock_page(page);
				put_page(page);
				folio = page_folio(pages[npages]);
				folio_unlock(folio);
				folio_put(folio);
			}

			return -ENOMEM;
		}

		if (!PageUptodate(page))
		if (!folio_test_uptodate(folio))
			*frame_uptodate = false;

		pages[npages] = page;
		pages[npages] = &folio->page;
	}

	return 0;
@@ -1075,8 +1140,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
	struct file *file = iocb->ki_filp;
	struct address_space *mapping = file->f_mapping;
	struct inode *inode = mapping->host;
	struct inode *inode = file_inode(file);
	ssize_t ret;
	int err;
	struct ntfs_inode *ni = ntfs_i(inode);
@@ -1198,7 +1262,7 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
}

/*
 * ntfs_fiemap - file_operations::fiemap
 * ntfs_fiemap - inode_operations::fiemap
 */
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
		__u64 start, __u64 len)
@@ -1227,6 +1291,8 @@ const struct inode_operations ntfs_file_inode_operations = {
	.get_acl	= ntfs_get_acl,
	.set_acl	= ntfs_set_acl,
	.fiemap		= ntfs_fiemap,
	.fileattr_get	= ntfs_fileattr_get,
	.fileattr_set	= ntfs_fileattr_set,
};

const struct file_operations ntfs_file_operations = {
@@ -1246,6 +1312,7 @@ const struct file_operations ntfs_file_operations = {
	.release	= ntfs_file_release,
};

#if IS_ENABLED(CONFIG_NTFS_FS)
const struct file_operations ntfs_legacy_file_operations = {
	.llseek		= generic_file_llseek,
	.read_iter	= ntfs_file_read_iter,
@@ -1253,4 +1320,5 @@ const struct file_operations ntfs_legacy_file_operations = {
	.open		= ntfs_file_open,
	.release	= ntfs_file_release,
};
#endif
// clang-format on
+89 −21
Original line number Diff line number Diff line
@@ -122,10 +122,10 @@ void ni_clear(struct ntfs_inode *ni)
	else {
		run_close(&ni->file.run);
#ifdef CONFIG_NTFS3_LZX_XPRESS
		if (ni->file.offs_page) {
		if (ni->file.offs_folio) {
			/* On-demand allocated page for offsets. */
			put_page(ni->file.offs_page);
			ni->file.offs_page = NULL;
			folio_put(ni->file.offs_folio);
			ni->file.offs_folio = NULL;
		}
#endif
	}
@@ -1501,7 +1501,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type,

	if (is_ext) {
		if (flags & ATTR_FLAG_COMPRESSED)
			attr->nres.c_unit = COMPRESSION_UNIT;
			attr->nres.c_unit = NTFS_LZNT_CUNIT;
		attr->nres.total_size = attr->nres.alloc_size;
	}

@@ -1601,8 +1601,10 @@ int ni_delete_all(struct ntfs_inode *ni)
		asize = le32_to_cpu(attr->size);
		roff = le16_to_cpu(attr->nres.run_off);

		if (roff > asize)
		if (roff > asize) {
			_ntfs_bad_inode(&ni->vfs_inode);
			return -EINVAL;
		}

		/* run==1 means unpack and deallocate. */
		run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, evcn, svcn,
@@ -1896,6 +1898,47 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
	return REPARSE_LINK;
}

/*
 * fiemap_fill_next_extent_k - a copy of fiemap_fill_next_extent
 * but it accepts kernel address for fi_extents_start
 */
static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo,
				     u64 logical, u64 phys, u64 len, u32 flags)
{
	struct fiemap_extent extent;
	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;

	/* only count the extents */
	if (fieinfo->fi_extents_max == 0) {
		fieinfo->fi_extents_mapped++;
		return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
	}

	if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
		return 1;

	if (flags & FIEMAP_EXTENT_DELALLOC)
		flags |= FIEMAP_EXTENT_UNKNOWN;
	if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED)
		flags |= FIEMAP_EXTENT_ENCODED;
	if (flags & (FIEMAP_EXTENT_DATA_TAIL | FIEMAP_EXTENT_DATA_INLINE))
		flags |= FIEMAP_EXTENT_NOT_ALIGNED;

	memset(&extent, 0, sizeof(extent));
	extent.fe_logical = logical;
	extent.fe_physical = phys;
	extent.fe_length = len;
	extent.fe_flags = flags;

	dest += fieinfo->fi_extents_mapped;
	memcpy(dest, &extent, sizeof(extent));

	fieinfo->fi_extents_mapped++;
	if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
		return 1;
	return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
}

/*
 * ni_fiemap - Helper for file_fiemap().
 *
@@ -1906,6 +1949,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
	      __u64 vbo, __u64 len)
{
	int err = 0;
	struct fiemap_extent __user *fe_u = fieinfo->fi_extents_start;
	struct fiemap_extent *fe_k = NULL;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	u8 cluster_bits = sbi->cluster_bits;
	struct runs_tree *run;
@@ -1953,6 +1998,18 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
		goto out;
	}

	/*
	 * To avoid lock problems replace pointer to user memory by pointer to kernel memory.
	 */
	fe_k = kmalloc_array(fieinfo->fi_extents_max,
			     sizeof(struct fiemap_extent),
			     GFP_NOFS | __GFP_ZERO);
	if (!fe_k) {
		err = -ENOMEM;
		goto out;
	}
	fieinfo->fi_extents_start = fe_k;

	end = vbo + len;
	alloc_size = le64_to_cpu(attr->nres.alloc_size);
	if (end > alloc_size)
@@ -2041,8 +2098,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
			if (vbo + dlen >= end)
				flags |= FIEMAP_EXTENT_LAST;

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

			if (err < 0)
				break;
			if (err == 1) {
@@ -2062,7 +2120,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
		if (vbo + bytes >= end)
			flags |= FIEMAP_EXTENT_LAST;

		err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags);
		err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, bytes,
						flags);
		if (err < 0)
			break;
		if (err == 1) {
@@ -2075,7 +2134,19 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,

	up_read(run_lock);

	/*
	 * Copy to user memory out of lock
	 */
	if (copy_to_user(fe_u, fe_k,
			 fieinfo->fi_extents_max *
				 sizeof(struct fiemap_extent))) {
		err = -EFAULT;
	}

out:
	/* Restore original pointer. */
	fieinfo->fi_extents_start = fe_u;
	kfree(fe_k);
	return err;
}

@@ -2085,12 +2156,12 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
 * When decompressing, we typically obtain more than one page per reference.
 * We inject the additional pages into the page cache.
 */
int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page)
int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
{
	int err;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	struct address_space *mapping = page->mapping;
	pgoff_t index = page->index;
	struct address_space *mapping = folio->mapping;
	pgoff_t index = folio->index;
	u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT;
	struct page **pages = NULL; /* Array of at most 16 pages. stack? */
	u8 frame_bits;
@@ -2100,7 +2171,8 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page)
	struct page *pg;

	if (vbo >= i_size_read(&ni->vfs_inode)) {
		SetPageUptodate(page);
		folio_zero_range(folio, 0, folio_size(folio));
		folio_mark_uptodate(folio);
		err = 0;
		goto out;
	}
@@ -2124,7 +2196,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page)
		goto out;
	}

	pages[idx] = page;
	pages[idx] = &folio->page;
	index = frame_vbo >> PAGE_SHIFT;
	gfp_mask = mapping_gfp_mask(mapping);

@@ -2143,9 +2215,6 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page)
	err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame);

out1:
	if (err)
		SetPageError(page);

	for (i = 0; i < pages_per_frame; i++) {
		pg = pages[i];
		if (i == idx || !pg)
@@ -2157,7 +2226,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page)
out:
	/* At this point, err contains 0 or -EIO depending on the "critical" page. */
	kfree(pages);
	unlock_page(page);
	folio_unlock(folio);

	return err;
}
@@ -2362,9 +2431,9 @@ int ni_decompress_file(struct ntfs_inode *ni)

	/* Clear cached flag. */
	ni->ni_flags &= ~NI_FLAG_COMPRESSED_MASK;
	if (ni->file.offs_page) {
		put_page(ni->file.offs_page);
		ni->file.offs_page = NULL;
	if (ni->file.offs_folio) {
		folio_put(ni->file.offs_folio);
		ni->file.offs_folio = NULL;
	}
	mapping->a_ops = &ntfs_aops;

@@ -2718,7 +2787,6 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
	for (i = 0; i < pages_per_frame; i++) {
		pg = pages[i];
		kunmap(pg);
		ClearPageError(pg);
		SetPageUptodate(pg);
	}

Loading