Commit fbc04bf0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-5.17-fixes-1' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
 "I was auditing operations in XFS that clear file privileges, and
  realized that XFS' fallocate implementation drops suid/sgid but
  doesn't clear file capabilities the same way that file writes and
  reflink do.

  There are VFS helpers that do it correctly, so refactor XFS to use
  them. I also noticed that we weren't flushing the log at the correct
  point in the fallocate operation, so that's fixed too.

  Summary:

   - Fix fallocate so that it drops all file privileges when files are
     modified instead of open-coding that incompletely.

   - Fix fallocate to flush the log if the caller wanted synchronous
     file updates"

* tag 'xfs-5.17-fixes-1' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: ensure log flush at the end of a synchronous fallocate call
  xfs: move xfs_update_prealloc_flags() to xfs_pnfs.c
  xfs: set prealloc flag in xfs_alloc_file_space()
  xfs: fallocate() should call file_modified()
  xfs: remove XFS_PREALLOC_SYNC
  xfs: reject crazy array sizes being fed to XFS_IOC_GETBMAP*
parents ea7b3e6d cea267c2
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -850,9 +850,6 @@ xfs_alloc_file_space(
			rblocks = 0;
		}

		/*
		 * Allocate and setup the transaction.
		 */
		error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_write,
				dblocks, rblocks, false, &tp);
		if (error)
@@ -869,9 +866,9 @@ xfs_alloc_file_space(
		if (error)
			goto error;

		/*
		 * Complete the transaction
		 */
		ip->i_diflags |= XFS_DIFLAG_PREALLOC;
		xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);

		error = xfs_trans_commit(tp);
		xfs_iunlock(ip, XFS_ILOCK_EXCL);
		if (error)
+26 −60
Original line number Diff line number Diff line
@@ -66,40 +66,6 @@ xfs_is_falloc_aligned(
	return !((pos | len) & mask);
}

int
xfs_update_prealloc_flags(
	struct xfs_inode	*ip,
	enum xfs_prealloc_flags	flags)
{
	struct xfs_trans	*tp;
	int			error;

	error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_writeid,
			0, 0, 0, &tp);
	if (error)
		return error;

	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);

	if (!(flags & XFS_PREALLOC_INVISIBLE)) {
		VFS_I(ip)->i_mode &= ~S_ISUID;
		if (VFS_I(ip)->i_mode & S_IXGRP)
			VFS_I(ip)->i_mode &= ~S_ISGID;
		xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
	}

	if (flags & XFS_PREALLOC_SET)
		ip->i_diflags |= XFS_DIFLAG_PREALLOC;
	if (flags & XFS_PREALLOC_CLEAR)
		ip->i_diflags &= ~XFS_DIFLAG_PREALLOC;

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
	if (flags & XFS_PREALLOC_SYNC)
		xfs_trans_set_sync(tp);
	return xfs_trans_commit(tp);
}

/*
 * Fsync operations on directories are much simpler than on regular files,
 * as there is no file data to flush, and thus also no need for explicit
@@ -895,6 +861,21 @@ xfs_break_layouts(
	return error;
}

/* Does this file, inode, or mount want synchronous writes? */
static inline bool xfs_file_sync_writes(struct file *filp)
{
	struct xfs_inode	*ip = XFS_I(file_inode(filp));

	if (xfs_has_wsync(ip->i_mount))
		return true;
	if (filp->f_flags & (__O_SYNC | O_DSYNC))
		return true;
	if (IS_SYNC(file_inode(filp)))
		return true;

	return false;
}

#define	XFS_FALLOC_FL_SUPPORTED						\
		(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |		\
		 FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |	\
@@ -910,7 +891,6 @@ xfs_file_fallocate(
	struct inode		*inode = file_inode(file);
	struct xfs_inode	*ip = XFS_I(inode);
	long			error;
	enum xfs_prealloc_flags	flags = 0;
	uint			iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
	loff_t			new_size = 0;
	bool			do_file_insert = false;
@@ -955,6 +935,10 @@ xfs_file_fallocate(
			goto out_unlock;
	}

	error = file_modified(file);
	if (error)
		goto out_unlock;

	if (mode & FALLOC_FL_PUNCH_HOLE) {
		error = xfs_free_file_space(ip, offset, len);
		if (error)
@@ -1004,8 +988,6 @@ xfs_file_fallocate(
		}
		do_file_insert = true;
	} else {
		flags |= XFS_PREALLOC_SET;

		if (!(mode & FALLOC_FL_KEEP_SIZE) &&
		    offset + len > i_size_read(inode)) {
			new_size = offset + len;
@@ -1057,13 +1039,6 @@ xfs_file_fallocate(
		}
	}

	if (file->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;

	error = xfs_update_prealloc_flags(ip, flags);
	if (error)
		goto out_unlock;

	/* Change file size if needed */
	if (new_size) {
		struct iattr iattr;
@@ -1082,8 +1057,14 @@ xfs_file_fallocate(
	 * leave shifted extents past EOF and hence losing access to
	 * the data that is contained within them.
	 */
	if (do_file_insert)
	if (do_file_insert) {
		error = xfs_insert_file_space(ip, offset, len);
		if (error)
			goto out_unlock;
	}

	if (xfs_file_sync_writes(file))
		error = xfs_log_force_inode(ip);

out_unlock:
	xfs_iunlock(ip, iolock);
@@ -1115,21 +1096,6 @@ xfs_file_fadvise(
	return ret;
}

/* Does this file, inode, or mount want synchronous writes? */
static inline bool xfs_file_sync_writes(struct file *filp)
{
	struct xfs_inode	*ip = XFS_I(file_inode(filp));

	if (xfs_has_wsync(ip->i_mount))
		return true;
	if (filp->f_flags & (__O_SYNC | O_DSYNC))
		return true;
	if (IS_SYNC(file_inode(filp)))
		return true;

	return false;
}

STATIC loff_t
xfs_file_remap_range(
	struct file		*file_in,
+0 −9
Original line number Diff line number Diff line
@@ -462,15 +462,6 @@ xfs_itruncate_extents(
}

/* from xfs_file.c */
enum xfs_prealloc_flags {
	XFS_PREALLOC_SET	= (1 << 1),
	XFS_PREALLOC_CLEAR	= (1 << 2),
	XFS_PREALLOC_SYNC	= (1 << 3),
	XFS_PREALLOC_INVISIBLE	= (1 << 4),
};

int	xfs_update_prealloc_flags(struct xfs_inode *ip,
				  enum xfs_prealloc_flags flags);
int	xfs_break_layouts(struct inode *inode, uint *iolock,
		enum layout_break_reason reason);

+1 −1
Original line number Diff line number Diff line
@@ -1464,7 +1464,7 @@ xfs_ioc_getbmap(

	if (bmx.bmv_count < 2)
		return -EINVAL;
	if (bmx.bmv_count > ULONG_MAX / recsize)
	if (bmx.bmv_count >= INT_MAX / recsize)
		return -ENOMEM;

	buf = kvcalloc(bmx.bmv_count, sizeof(*buf), GFP_KERNEL);
+39 −3
Original line number Diff line number Diff line
@@ -70,6 +70,40 @@ xfs_fs_get_uuid(
	return 0;
}

/*
 * We cannot use file based VFS helpers such as file_modified() to update
 * inode state as we modify the data/metadata in the inode here. Hence we have
 * to open code the timestamp updates and SUID/SGID stripping. We also need
 * to set the inode prealloc flag to ensure that the extents we allocate are not
 * removed if the inode is reclaimed from memory before xfs_fs_block_commit()
 * is from the client to indicate that data has been written and the file size
 * can be extended.
 */
static int
xfs_fs_map_update_inode(
	struct xfs_inode	*ip)
{
	struct xfs_trans	*tp;
	int			error;

	error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_writeid,
			0, 0, 0, &tp);
	if (error)
		return error;

	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);

	VFS_I(ip)->i_mode &= ~S_ISUID;
	if (VFS_I(ip)->i_mode & S_IXGRP)
		VFS_I(ip)->i_mode &= ~S_ISGID;
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
	ip->i_diflags |= XFS_DIFLAG_PREALLOC;

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
	return xfs_trans_commit(tp);
}

/*
 * Get a layout for the pNFS client.
 */
@@ -164,10 +198,12 @@ xfs_fs_map_blocks(
		 * that the blocks allocated and handed out to the client are
		 * guaranteed to be present even after a server crash.
		 */
		error = xfs_update_prealloc_flags(ip,
				XFS_PREALLOC_SET | XFS_PREALLOC_SYNC);
		error = xfs_fs_map_update_inode(ip);
		if (!error)
			error = xfs_log_force_inode(ip);
		if (error)
			goto out_unlock;

	} else {
		xfs_iunlock(ip, lock_flags);
	}