Commit fbc04bf0 authored by Linus Torvalds's avatar Linus Torvalds

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
...@@ -850,9 +850,6 @@ xfs_alloc_file_space( ...@@ -850,9 +850,6 @@ xfs_alloc_file_space(
rblocks = 0; rblocks = 0;
} }
/*
* Allocate and setup the transaction.
*/
error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_write, error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_write,
dblocks, rblocks, false, &tp); dblocks, rblocks, false, &tp);
if (error) if (error)
...@@ -869,9 +866,9 @@ xfs_alloc_file_space( ...@@ -869,9 +866,9 @@ xfs_alloc_file_space(
if (error) if (error)
goto error; goto error;
/* ip->i_diflags |= XFS_DIFLAG_PREALLOC;
* Complete the transaction xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
*/
error = xfs_trans_commit(tp); error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL); xfs_iunlock(ip, XFS_ILOCK_EXCL);
if (error) if (error)
......
...@@ -66,40 +66,6 @@ xfs_is_falloc_aligned( ...@@ -66,40 +66,6 @@ xfs_is_falloc_aligned(
return !((pos | len) & mask); 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, * 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 * as there is no file data to flush, and thus also no need for explicit
...@@ -895,6 +861,21 @@ xfs_break_layouts( ...@@ -895,6 +861,21 @@ xfs_break_layouts(
return error; 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 \ #define XFS_FALLOC_FL_SUPPORTED \
(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \ (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \
FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | \ FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | \
...@@ -910,7 +891,6 @@ xfs_file_fallocate( ...@@ -910,7 +891,6 @@ xfs_file_fallocate(
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct xfs_inode *ip = XFS_I(inode); struct xfs_inode *ip = XFS_I(inode);
long error; long error;
enum xfs_prealloc_flags flags = 0;
uint iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL; uint iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
loff_t new_size = 0; loff_t new_size = 0;
bool do_file_insert = false; bool do_file_insert = false;
...@@ -955,6 +935,10 @@ xfs_file_fallocate( ...@@ -955,6 +935,10 @@ xfs_file_fallocate(
goto out_unlock; goto out_unlock;
} }
error = file_modified(file);
if (error)
goto out_unlock;
if (mode & FALLOC_FL_PUNCH_HOLE) { if (mode & FALLOC_FL_PUNCH_HOLE) {
error = xfs_free_file_space(ip, offset, len); error = xfs_free_file_space(ip, offset, len);
if (error) if (error)
...@@ -1004,8 +988,6 @@ xfs_file_fallocate( ...@@ -1004,8 +988,6 @@ xfs_file_fallocate(
} }
do_file_insert = true; do_file_insert = true;
} else { } else {
flags |= XFS_PREALLOC_SET;
if (!(mode & FALLOC_FL_KEEP_SIZE) && if (!(mode & FALLOC_FL_KEEP_SIZE) &&
offset + len > i_size_read(inode)) { offset + len > i_size_read(inode)) {
new_size = offset + len; new_size = offset + len;
...@@ -1057,13 +1039,6 @@ xfs_file_fallocate( ...@@ -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 */ /* Change file size if needed */
if (new_size) { if (new_size) {
struct iattr iattr; struct iattr iattr;
...@@ -1082,8 +1057,14 @@ xfs_file_fallocate( ...@@ -1082,8 +1057,14 @@ xfs_file_fallocate(
* leave shifted extents past EOF and hence losing access to * leave shifted extents past EOF and hence losing access to
* the data that is contained within them. * the data that is contained within them.
*/ */
if (do_file_insert) if (do_file_insert) {
error = xfs_insert_file_space(ip, offset, len); 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: out_unlock:
xfs_iunlock(ip, iolock); xfs_iunlock(ip, iolock);
...@@ -1115,21 +1096,6 @@ xfs_file_fadvise( ...@@ -1115,21 +1096,6 @@ xfs_file_fadvise(
return ret; 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 STATIC loff_t
xfs_file_remap_range( xfs_file_remap_range(
struct file *file_in, struct file *file_in,
......
...@@ -462,15 +462,6 @@ xfs_itruncate_extents( ...@@ -462,15 +462,6 @@ xfs_itruncate_extents(
} }
/* from xfs_file.c */ /* 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, int xfs_break_layouts(struct inode *inode, uint *iolock,
enum layout_break_reason reason); enum layout_break_reason reason);
......
...@@ -1464,7 +1464,7 @@ xfs_ioc_getbmap( ...@@ -1464,7 +1464,7 @@ xfs_ioc_getbmap(
if (bmx.bmv_count < 2) if (bmx.bmv_count < 2)
return -EINVAL; return -EINVAL;
if (bmx.bmv_count > ULONG_MAX / recsize) if (bmx.bmv_count >= INT_MAX / recsize)
return -ENOMEM; return -ENOMEM;
buf = kvcalloc(bmx.bmv_count, sizeof(*buf), GFP_KERNEL); buf = kvcalloc(bmx.bmv_count, sizeof(*buf), GFP_KERNEL);
......
...@@ -70,6 +70,40 @@ xfs_fs_get_uuid( ...@@ -70,6 +70,40 @@ xfs_fs_get_uuid(
return 0; 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. * Get a layout for the pNFS client.
*/ */
...@@ -164,10 +198,12 @@ xfs_fs_map_blocks( ...@@ -164,10 +198,12 @@ xfs_fs_map_blocks(
* that the blocks allocated and handed out to the client are * that the blocks allocated and handed out to the client are
* guaranteed to be present even after a server crash. * guaranteed to be present even after a server crash.
*/ */
error = xfs_update_prealloc_flags(ip, error = xfs_fs_map_update_inode(ip);
XFS_PREALLOC_SET | XFS_PREALLOC_SYNC); if (!error)
error = xfs_log_force_inode(ip);
if (error) if (error)
goto out_unlock; goto out_unlock;
} else { } else {
xfs_iunlock(ip, lock_flags); xfs_iunlock(ip, lock_flags);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment