summaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_vnodeops.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/xfs_vnodeops.c')
-rw-r--r--fs/xfs/xfs_vnodeops.c30
1 files changed, 25 insertions, 5 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index c2ddd7a43942..de3702a57e55 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -2118,7 +2118,7 @@ xfs_change_file_space(
xfs_fsize_t fsize;
int setprealloc;
xfs_off_t startoffset;
- xfs_off_t llen;
+ xfs_off_t end;
xfs_trans_t *tp;
struct iattr iattr;
int prealloc_type;
@@ -2139,12 +2139,30 @@ xfs_change_file_space(
return XFS_ERROR(EINVAL);
}
- llen = bf->l_len > 0 ? bf->l_len - 1 : bf->l_len;
+ /*
+ * length of <= 0 for resv/unresv/zero is invalid. length for
+ * alloc/free is ignored completely and we have no idea what userspace
+ * might have set it to, so set it to zero to allow range
+ * checks to pass.
+ */
+ switch (cmd) {
+ case XFS_IOC_ZERO_RANGE:
+ case XFS_IOC_RESVSP:
+ case XFS_IOC_RESVSP64:
+ case XFS_IOC_UNRESVSP:
+ case XFS_IOC_UNRESVSP64:
+ if (bf->l_len <= 0)
+ return XFS_ERROR(EINVAL);
+ break;
+ default:
+ bf->l_len = 0;
+ break;
+ }
if (bf->l_start < 0 ||
bf->l_start > mp->m_super->s_maxbytes ||
- bf->l_start + llen < 0 ||
- bf->l_start + llen > mp->m_super->s_maxbytes)
+ bf->l_start + bf->l_len < 0 ||
+ bf->l_start + bf->l_len >= mp->m_super->s_maxbytes)
return XFS_ERROR(EINVAL);
bf->l_whence = 0;
@@ -2169,7 +2187,9 @@ xfs_change_file_space(
switch (cmd) {
case XFS_IOC_ZERO_RANGE:
prealloc_type |= XFS_BMAPI_CONVERT;
- xfs_tosspages(ip, startoffset, startoffset + bf->l_len, 0);
+ end = round_down(startoffset + bf->l_len, PAGE_SIZE) - 1;
+ if (startoffset > end)
+ truncate_pagecache_range(VFS_I(ip), startoffset, end);
/* FALLTHRU */
case XFS_IOC_RESVSP:
case XFS_IOC_RESVSP64: