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*
This commit is contained in:
@ -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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
}
|
}
|
||||||
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(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
|||||||
* 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(
|
|||||||
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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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(
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
* 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);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user