Subject: linux-next: manual merge of the ntfs3 tree with the nfsd, vfs-brauner trees

From: Mark Brown <[email-masked]>
Date: Fri, 29 May 2026 13:26:10 +0100

Hi all,

Today's linux-next merge of the ntfs3 tree got conflicts in:

  fs/ntfs3/file.c
  fs/ntfs3/namei.c
  fs/ntfs3/ntfs_fs.h

between commit:

  eeb7b37b9700f ("ntfs3: Implement fileattr_get for case sensitivity")

from the nfsd, vfs-brauner trees and commit:

  245bbdd2b9d65 ("fs/ntfs3: add fileattr support")

from the ntfs3 tree.

I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging.  You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.

diff --combined fs/ntfs3/file.c
index ad9350d7fc3fd,1b52447bd2289..0000000000000
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@@ -89,6 -89,80 +89,80 @@@ static int ntfs_ioctl_fitrim(struct ntf
  	return 0;
  }
  
+ /*
+  * ntfs_fileattr_get - inode_operations::fileattr_get
+  */
+ int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
+ {
+ 	struct inode *inode = d_inode(dentry);
+ 	struct ntfs_inode *ni = ntfs_i(inode);
+ 	u32 flags = 0;
+ 
+ 	/* Avoid any operation if inode is bad. */
+ 	if (unlikely(is_bad_ni(ni)))
+ 		return -EINVAL;
+ 
+ 	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;
+ 
+ 	if (ni->nodump)
+ 		flags |= FS_NODUMP_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 file_kattr *fa)
+ {
+ 	struct inode *inode = d_inode(dentry);
+ 	struct ntfs_inode *ni = ntfs_i(inode);
+ 	u32 flags = fa->flags;
+ 	unsigned int new_fl = 0;
+ 
+ 	/* Avoid any operation if inode is bad. */
+ 	if (unlikely(is_bad_ni(ni)))
+ 		return -EINVAL;
+ 
+ 	if (fileattr_has_fsx(fa))
+ 		return -EOPNOTSUPP;
+ 
+ 	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_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);
+ 
+ 	/* Save nodump flag to return in ntfs_getattr. */
+ 	if (flags & FS_NODUMP_FL)
+ 		ni->nodump = 1;
+ 	else
+ 		ni->nodump = 0;
+ 
+ 	inode_set_ctime_current(inode);
+ 	mark_inode_dirty(inode);
+ 
+ 	return 0;
+ }
+ 
  static int ntfs_ioctl_get_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
  {
  	if (copy_to_user(buf, sbi->volume.label, FSLABEL_MAX))
@@@ -180,34 -254,6 +254,34 @@@ long ntfs_compat_ioctl(struct file *fil
  }
  #endif
  
 +/*
 + * ntfs_fileattr_get - inode_operations::fileattr_get
 + */
 +int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
 +{
 +	struct inode *inode = d_inode(dentry);
 +	struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
 +
 +	/* Avoid any operation if inode is bad. */
 +	if (unlikely(is_bad_ni(ntfs_i(inode))))
 +		return -EINVAL;
 +
 +	/*
 +	 * NTFS preserves case (the default). Case sensitivity depends on
 +	 * mount options: with "nocase", NTFS is case-insensitive;
 +	 * otherwise it is case-sensitive.
 +	 */
 +	if (sbi->options->nocase) {
 +		fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
 +		fa->flags |= FS_CASEFOLD_FL;
 +	}
 +	if (inode->i_flags & S_IMMUTABLE) {
 +		fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
 +		fa->flags |= FS_IMMUTABLE_FL;
 +	}
 +	return 0;
 +}
 +
  /*
   * ntfs_getattr - inode_operations::getattr
   */
@@@ -231,6 -277,9 +305,9 @@@ int ntfs_getattr(struct mnt_idmap *idma
  	if (inode->i_flags & S_APPEND)
  		stat->attributes |= STATX_ATTR_APPEND;
  
+ 	if (ni->nodump)
+ 		stat->attributes |= STATX_ATTR_NODUMP;
+ 
  	if (is_compressed(ni))
  		stat->attributes |= STATX_ATTR_COMPRESSED;
  
@@@ -274,18 -323,44 +351,44 @@@ static int ntfs_extend_initialized_size
  	return 0;
  }
  
+ /* Zero pagecache after 'from'. */
+ static void ntfs_zero_tail(struct address_space *mapping, loff_t from)
+ {
+ 	struct folio_batch fbatch;
+ 	pgoff_t index = from >> PAGE_SHIFT;
+ 	unsigned nr, i;
+ 
+ 	folio_batch_init(&fbatch);
+ 
+ 	nr = filemap_get_folios(mapping, &index, -1, &fbatch);
+ 
+ 	for (i = 0; i < nr; i++) {
+ 		struct folio *folio = fbatch.folios[i];
+ 		u32 st = folio_pos(folio) < from ?
+ 				 offset_in_folio(folio, from) :
+ 				 0;
+ 
+ 		folio_lock(folio);
+ 		folio_zero_segment(folio, st, folio_size(folio));
+ 
+ 		folio_unlock(folio);
+ 	}
+ 	folio_batch_release(&fbatch);
+ }
+ 
  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 i_size = i_size_read(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);
+ 	u64 to = min(i_size, from + vma->vm_end - vma->vm_start);
  
  	if (ni->i_valid < to) {
  		ni->i_valid = to;
  		mark_inode_dirty(inode);
  	}
+ 	ntfs_zero_tail(inode->i_mapping, ni->i_valid);
  }
  
  /* Copy of generic_file_vm_ops. */
@@@ -375,93 -450,6 +478,6 @@@ out
  	return err;
  }
  
- static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
- 		       struct file *file)
- {
- 	struct ntfs_inode *ni = ntfs_i(inode);
- 	struct address_space *mapping = inode->i_mapping;
- 	loff_t end = pos + count;
- 	bool extend_init = file && pos > ni->i_valid;
- 	int err;
- 
- 	if (end <= inode->i_size && !extend_init)
- 		return 0;
- 
- 	/* Mark rw ntfs as dirty. It will be cleared at umount. */
- 	ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY);
- 
- 	if (end > inode->i_size) {
- 		/*
- 		 * Normal files: increase file size, allocate space.
- 		 * Sparse/Compressed: increase file size. No space allocated.
- 		 */
- 		err = ntfs_set_size(inode, end);
- 		if (err)
- 			goto out;
- 	}
- 
- 	if (extend_init && !is_compressed(ni)) {
- 		err = ntfs_extend_initialized_size(file, ni, pos);
- 		if (err)
- 			goto out;
- 	} else {
- 		err = 0;
- 	}
- 
- 	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
- 	mark_inode_dirty(inode);
- 
- 	if (IS_SYNC(inode)) {
- 		int err2;
- 
- 		err = filemap_fdatawrite_range(mapping, pos, end - 1);
- 		err2 = write_inode_now(inode, 1);
- 		if (!err)
- 			err = err2;
- 		if (!err)
- 			err = filemap_fdatawait_range(mapping, pos, end - 1);
- 	}
- 
- out:
- 	return err;
- }
- 
- static int ntfs_truncate(struct inode *inode, loff_t new_size)
- {
- 	int err;
- 	struct ntfs_inode *ni = ntfs_i(inode);
- 	u64 new_valid = min_t(u64, ni->i_valid, new_size);
- 
- 	truncate_setsize(inode, new_size);
- 
- 	ni_lock(ni);
- 
- 	down_write(&ni->file.run_lock);
- 	err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
- 			       &new_valid, ni->mi.sbi->options->prealloc, NULL,
- 			       false);
- 	up_write(&ni->file.run_lock);
- 
- 	ni->i_valid = new_valid;
- 
- 	ni_unlock(ni);
- 
- 	if (err)
- 		return err;
- 
- 	ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
- 	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
- 	if (!IS_DIRSYNC(inode)) {
- 		mark_inode_dirty(inode);
- 	} else {
- 		err = ntfs_sync_inode(inode);
- 		if (err)
- 			return err;
- 	}
- 
- 	return 0;
- }
- 
  /*
   * ntfs_fallocate - file_operations::ntfs_fallocate
   *
@@@ -668,57 -656,25 +684,25 @@@ static long ntfs_fallocate(struct file 
  		if (is_supported_holes) {
  			CLST vcn = vbo >> cluster_bits;
  			CLST cend = bytes_to_cluster(sbi, end);
- 			CLST cend_v = bytes_to_cluster(sbi, ni->i_valid);
  			CLST lcn, clen;
  			bool new;
  
- 			if (cend_v > cend)
- 				cend_v = cend;
- 
  			/*
  			 * Allocate and zero new clusters.
- 			 * Zeroing these clusters may be too long.
- 			 */
- 			for (; vcn < cend_v; vcn += clen) {
- 				err = attr_data_get_block(ni, vcn, cend_v - vcn,
- 							  &lcn, &clen, &new,
- 							  true, NULL, false);
- 				if (err)
- 					goto out;
- 			}
- 
- 			/*
- 			 * Moving up 'valid size'.
- 			 */
- 			err = ntfs_extend_initialized_size(
- 				file, ni, (u64)cend_v << cluster_bits);
- 			if (err)
- 				goto out;
- 
- 			/*
- 			 * Allocate but not zero new clusters.
  			 */
  			for (; vcn < cend; vcn += clen) {
  				err = attr_data_get_block(ni, vcn, cend - vcn,
  							  &lcn, &clen, &new,
- 							  false, NULL, false);
+ 							  true, NULL, false);
  				if (err)
  					goto out;
  			}
  		}
  
  		if (mode & FALLOC_FL_KEEP_SIZE) {
- 			ni_lock(ni);
- 			/* True - Keep preallocated. */
- 			err = attr_set_size(ni, ATTR_DATA, NULL, 0,
- 					    &ni->file.run, i_size, &ni->i_valid,
- 					    true);
- 			ni_unlock(ni);
+ 			err = ntfs_set_size(inode, i_size);
  			if (err)
  				goto out;
- 			i_size_write(inode, i_size);
- 		} else if (new_size > i_size) {
- 			i_size_write(inode, new_size);
  		}
  	}
  
@@@ -775,16 -731,20 +759,20 @@@ int ntfs_setattr(struct mnt_idmap *idma
  		oldsize = i_size_read(inode);
  		newsize = attr->ia_size;
  
- 		if (newsize <= oldsize)
- 			err = ntfs_truncate(inode, newsize);
- 		else
- 			err = ntfs_extend(inode, newsize, 0, NULL);
+ 		if (newsize != oldsize) {
+ 			truncate_setsize(inode, newsize);
  
- 		if (err)
- 			goto out;
+ 			err = ntfs_set_size(inode, newsize);
+ 			if (err) {
+ 				i_size_write(inode, oldsize);
+ 				goto out;
+ 			}
  
- 		ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
- 		i_size_write(inode, newsize);
+ 			ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
+ 			ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
+ 			inode_set_mtime_to_ts(inode,
+ 					      inode_set_ctime_current(inode));
+ 		}
  	}
  
  	setattr_copy(idmap, inode, attr);
@@@ -1048,7 -1008,7 +1036,7 @@@ static ssize_t ntfs_compress_write(stru
  		CLST lcn, clen;
  
  		frame = valid >> frame_bits;
- 		frame_vbo = valid & ~(frame_size - 1);
+ 		frame_vbo = valid & ~(u64)(frame_size - 1);
  		off = valid & (frame_size - 1);
  
  		err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 1, &lcn,
@@@ -1117,7 -1077,7 +1105,7 @@@
  		if (bytes > count)
  			bytes = count;
  
- 		frame_vbo = pos & ~(frame_size - 1);
+ 		frame_vbo = pos & ~(u64)(frame_size - 1);
  		index = frame_vbo >> PAGE_SHIFT;
  
  		if (unlikely(fault_in_iov_iter_readable(from, bytes))) {
@@@ -1258,6 -1218,7 +1246,7 @@@ static ssize_t ntfs_file_write_iter(str
  	struct file *file = iocb->ki_filp;
  	struct inode *inode = file_inode(file);
  	struct ntfs_inode *ni = ntfs_i(inode);
+ 	loff_t vbo, endbyte;
  	ssize_t ret, err;
  
  	if (!inode_trylock(inode)) {
@@@ -1292,15 -1253,30 +1281,30 @@@
  		goto out;
  	}
  
- 	ret = ntfs_extend(inode, iocb->ki_pos, ret, file);
- 	if (ret)
- 		goto out;
+ 	vbo = iocb->ki_pos;
+ 	endbyte = vbo + ret;
+ 
+ 	if (endbyte > inode->i_size) {
+ 		/*
+ 		 * Normal files: increase file size, allocate space.
+ 		 * Sparse/Compressed: increase file size. No space allocated.
+ 		 */
+ 		ret = ntfs_set_size(inode, endbyte);
+ 		if (ret)
+ 			goto out;
+ 	}
  
  	if (is_compressed(ni)) {
  		ret = ntfs_compress_write(iocb, from);
  		goto out;
  	}
  
+ 	if (vbo > ni->i_valid) {
+ 		ret = ntfs_extend_initialized_size(file, ni, vbo);
+ 		if (ret)
+ 			goto out;
+ 	}
+ 
  	/* Fallback to buffered I/O if the inode does not support direct I/O. */
  	if (!(iocb->ki_flags & IOCB_DIRECT) ||
  	    !ntfs_should_use_dio(iocb, from)) {
@@@ -1323,7 -1299,8 +1327,8 @@@
  			goto out;
  	}
  
- 	ret = iomap_dio_rw(iocb, from, &ntfs_iomap_ops, NULL, 0, NULL, 0);
+ 	ret = iomap_dio_rw(iocb, from, &ntfs_iomap_ops, NULL,
+ 			   IOMAP_DIO_FORCE_WAIT, NULL, 0);
  
  	if (ret == -ENOTBLK) {
  		/* Returns -ENOTBLK in case of a page invalidation failure for writes.*/
@@@ -1332,7 -1309,7 +1337,7 @@@
  	}
  
  	if (ret >= 0 && iov_iter_count(from)) {
- 		loff_t offset = iocb->ki_pos, endbyte;
+ 		vbo = iocb->ki_pos;
  
  		iocb->ki_flags &= ~IOCB_DIRECT;
  		err = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
@@@ -1350,15 -1327,15 +1355,15 @@@
  		* to complete off the I/O request.
  		*/
  		ret += err;
- 		endbyte = offset + err - 1;
- 		err = filemap_write_and_wait_range(inode->i_mapping, offset,
+ 		endbyte = vbo + err - 1;
+ 		err = filemap_write_and_wait_range(inode->i_mapping, vbo,
  						   endbyte);
  		if (err) {
  			ret = err;
  			goto out;
  		}
  
- 		invalidate_mapping_pages(inode->i_mapping, offset >> PAGE_SHIFT,
+ 		invalidate_mapping_pages(inode->i_mapping, vbo >> PAGE_SHIFT,
  					 endbyte >> PAGE_SHIFT);
  	}
  
@@@ -1439,8 -1416,9 +1444,9 @@@ static int ntfs_file_release(struct ino
  		down_write(&ni->file.run_lock);
  
  		/* Deallocate preallocated. */
- 		err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run,
- 				    inode->i_size, &ni->i_valid, false);
+ 		err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
+ 				       inode->i_size, &ni->i_valid, false, NULL,
+ 				       true);
  
  		up_write(&ni->file.run_lock);
  		ni_unlock(ni);
@@@ -1552,7 -1530,12 +1558,12 @@@ static loff_t ntfs_llseek(struct file *
  	loff_t maxbytes = ntfs_get_maxbytes(ni);
  	loff_t ret;
  
- 	if (whence == SEEK_DATA || whence == SEEK_HOLE) {
+ 	if (whence != SEEK_DATA && whence != SEEK_HOLE) {
+ 		ret = generic_file_llseek_size(file, offset, whence, maxbytes,
+ 					       i_size_read(inode));
+ 	} else if ((unsigned long long)offset >= i_size_read(inode)) {
+ 		ret = -ENXIO;
+ 	} else {
  		inode_lock_shared(inode);
  		/* Scan file for hole or data. */
  		ret = ni_seek_data_or_hole(ni, offset, whence == SEEK_DATA);
@@@ -1560,9 -1543,6 +1571,6 @@@
  
  		if (ret >= 0)
  			ret = vfs_setpos(file, ret, maxbytes);
- 	} else {
- 		ret = generic_file_llseek_size(file, offset, whence, maxbytes,
- 					       i_size_read(inode));
  	}
  	return ret;
  }
@@@ -1576,6 -1556,7 +1584,7 @@@ const struct inode_operations ntfs_file
  	.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 = {
diff --combined fs/ntfs3/namei.c
index e159ba66a34a4,c59de5f2fa977..0000000000000
--- a/fs/ntfs3/namei.c
+++ b/fs/ntfs3/namei.c
@@@ -340,7 -340,7 +340,7 @@@ static int ntfs_rename(struct mnt_idma
  			ntfs_sync_inode(dir);
  
  		if (IS_DIRSYNC(new_dir))
- 			ntfs_sync_inode(inode);
+ 			ntfs_sync_inode(new_dir);
  	}
  
  	if (dir_ni != new_dir_ni)
@@@ -519,6 -519,7 +519,7 @@@ const struct inode_operations ntfs_dir_
  	.listxattr	= ntfs_listxattr,
  	.fiemap		= ntfs_fiemap,
  	.fileattr_get	= ntfs_fileattr_get,
+ 	.fileattr_set	= ntfs_fileattr_set,
  };
  
  const struct inode_operations ntfs_special_inode_operations = {
@@@ -527,6 -528,8 +528,8 @@@
  	.listxattr	= ntfs_listxattr,
  	.get_acl	= ntfs_get_acl,
  	.set_acl	= ntfs_set_acl,
+ 	.fileattr_get	= ntfs_fileattr_get,
+ 	.fileattr_set	= ntfs_fileattr_set,
  };
  
  const struct dentry_operations ntfs_dentry_ops = {
diff --combined fs/ntfs3/ntfs_fs.h
index 41db22d652c47,d98d7e474476c..0000000000000
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@@ -392,6 -392,9 +392,9 @@@ struct ntfs_inode 
  	 */
  	u8 ni_bad;
  
+ 	/* Keep track of FS_NODUMP_FL. */
+ 	u8 nodump;
+ 
  	union {
  		struct ntfs_index dir;
  		struct {
@@@ -530,6 -533,8 +533,8 @@@ extern const struct file_operations ntf
  
  /* Globals from file.c */
  int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
+ int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
+ 		      struct file_kattr *fa);
  int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path,
  		 struct kstat *stat, u32 request_mask, u32 flags);
  int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
@@@ -853,6 -858,9 +858,9 @@@ static inline void mi_get_ref(const str
  /* Globals from run.c */
  bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn,
  		      CLST *len, size_t *index);
+ bool run_lookup_entry_da(const struct runs_tree *run,
+ 			 const struct runs_tree *run_da, CLST vcn, CLST *lcn,
+ 			 CLST *len);
  void run_truncate(struct runs_tree *run, CLST vcn);
  void run_truncate_head(struct runs_tree *run, CLST vcn);
  void run_truncate_around(struct runs_tree *run, CLST vcn);
@@@ -878,7 -886,8 +886,8 @@@ int run_unpack_ex(struct runs_tree *run
  #else
  #define run_unpack_ex run_unpack
  #endif
- int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn);
+ int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, 
+ 		       u64 *highest_vcn);
  int run_clone(const struct runs_tree *run, struct runs_tree *new_run);
  bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done);
  CLST run_len(const struct runs_tree *run);