diff options
Diffstat (limited to 'fs/xfs/xfs_ioctl.c')
-rw-r--r-- | fs/xfs/xfs_ioctl.c | 158 |
1 files changed, 93 insertions, 65 deletions
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index aa75389be8cf..20dc65fef6a4 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -44,6 +44,7 @@ #include "xfs_btree.h" #include <linux/fsmap.h> #include "xfs_fsmap.h" +#include "scrub/xfs_scrub.h" #include <linux/capability.h> #include <linux/cred.h> @@ -310,8 +311,8 @@ xfs_readlink_by_handle( int xfs_set_dmattrs( xfs_inode_t *ip, - u_int evmask, - u_int16_t state) + uint evmask, + uint16_t state) { xfs_mount_t *mp = ip->i_mount; xfs_trans_t *tp; @@ -1201,6 +1202,8 @@ out_unlock: * 8. for non-realtime files, the extent size hint must be limited * to half the AG size to avoid alignment extending the extent beyond the * limits of the AG. + * + * Please keep this function in sync with xfs_scrub_inode_extsize. */ static int xfs_ioctl_setattr_check_extsize( @@ -1257,6 +1260,8 @@ xfs_ioctl_setattr_check_extsize( * 5. Extent size must be a multiple of the appropriate block size. * 6. The extent size hint must be limited to half the AG size to avoid * alignment extending the extent beyond the limits of the AG. + * + * Please keep this function in sync with xfs_scrub_inode_cowextsize. */ static int xfs_ioctl_setattr_check_cowextsize( @@ -1540,17 +1545,26 @@ out_drop_write: return error; } -STATIC int -xfs_getbmap_format(void **ap, struct getbmapx *bmv) +static bool +xfs_getbmap_format( + struct kgetbmap *p, + struct getbmapx __user *u, + size_t recsize) { - struct getbmap __user *base = (struct getbmap __user *)*ap; - - /* copy only getbmap portion (not getbmapx) */ - if (copy_to_user(base, bmv, sizeof(struct getbmap))) - return -EFAULT; - - *ap += sizeof(struct getbmap); - return 0; + if (put_user(p->bmv_offset, &u->bmv_offset) || + put_user(p->bmv_block, &u->bmv_block) || + put_user(p->bmv_length, &u->bmv_length) || + put_user(0, &u->bmv_count) || + put_user(0, &u->bmv_entries)) + return false; + if (recsize < sizeof(struct getbmapx)) + return true; + if (put_user(0, &u->bmv_iflags) || + put_user(p->bmv_oflags, &u->bmv_oflags) || + put_user(0, &u->bmv_unused1) || + put_user(0, &u->bmv_unused2)) + return false; + return true; } STATIC int @@ -1560,68 +1574,57 @@ xfs_ioc_getbmap( void __user *arg) { struct getbmapx bmx = { 0 }; - int error; - - /* struct getbmap is a strict subset of struct getbmapx. */ - if (copy_from_user(&bmx, arg, offsetof(struct getbmapx, bmv_iflags))) - return -EFAULT; + struct kgetbmap *buf; + size_t recsize; + int error, i; - if (bmx.bmv_count < 2) + switch (cmd) { + case XFS_IOC_GETBMAPA: + bmx.bmv_iflags = BMV_IF_ATTRFORK; + /*FALLTHRU*/ + case XFS_IOC_GETBMAP: + if (file->f_mode & FMODE_NOCMTIME) + bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ; + /* struct getbmap is a strict subset of struct getbmapx. */ + recsize = sizeof(struct getbmap); + break; + case XFS_IOC_GETBMAPX: + recsize = sizeof(struct getbmapx); + break; + default: return -EINVAL; + } - bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0); - if (file->f_mode & FMODE_NOCMTIME) - bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ; - - error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, xfs_getbmap_format, - (__force struct getbmap *)arg+1); - if (error) - return error; - - /* copy back header - only size of getbmap */ - if (copy_to_user(arg, &bmx, sizeof(struct getbmap))) - return -EFAULT; - return 0; -} - -STATIC int -xfs_getbmapx_format(void **ap, struct getbmapx *bmv) -{ - struct getbmapx __user *base = (struct getbmapx __user *)*ap; - - if (copy_to_user(base, bmv, sizeof(struct getbmapx))) - return -EFAULT; - - *ap += sizeof(struct getbmapx); - return 0; -} - -STATIC int -xfs_ioc_getbmapx( - struct xfs_inode *ip, - void __user *arg) -{ - struct getbmapx bmx; - int error; - - if (copy_from_user(&bmx, arg, sizeof(bmx))) + if (copy_from_user(&bmx, arg, recsize)) return -EFAULT; if (bmx.bmv_count < 2) return -EINVAL; + if (bmx.bmv_count > ULONG_MAX / recsize) + return -ENOMEM; - if (bmx.bmv_iflags & (~BMV_IF_VALID)) - return -EINVAL; + buf = kmem_zalloc_large(bmx.bmv_count * sizeof(*buf), 0); + if (!buf) + return -ENOMEM; - error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format, - (__force struct getbmapx *)arg+1); + error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf); if (error) - return error; + goto out_free_buf; - /* copy back header */ - if (copy_to_user(arg, &bmx, sizeof(struct getbmapx))) - return -EFAULT; + error = -EFAULT; + if (copy_to_user(arg, &bmx, recsize)) + goto out_free_buf; + arg += recsize; + + for (i = 0; i < bmx.bmv_entries; i++) { + if (!xfs_getbmap_format(buf + i, arg, recsize)) + goto out_free_buf; + arg += recsize; + } + error = 0; +out_free_buf: + kmem_free(buf); return 0; } @@ -1703,6 +1706,30 @@ xfs_ioc_getfsmap( return 0; } +STATIC int +xfs_ioc_scrub_metadata( + struct xfs_inode *ip, + void __user *arg) +{ + struct xfs_scrub_metadata scrub; + int error; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(&scrub, arg, sizeof(scrub))) + return -EFAULT; + + error = xfs_scrub_metadata(ip, &scrub); + if (error) + return error; + + if (copy_to_user(arg, &scrub, sizeof(scrub))) + return -EFAULT; + + return 0; +} + int xfs_ioc_swapext( xfs_swapext_t *sxp) @@ -1878,14 +1905,15 @@ xfs_file_ioctl( case XFS_IOC_GETBMAP: case XFS_IOC_GETBMAPA: - return xfs_ioc_getbmap(filp, cmd, arg); - case XFS_IOC_GETBMAPX: - return xfs_ioc_getbmapx(ip, arg); + return xfs_ioc_getbmap(filp, cmd, arg); case FS_IOC_GETFSMAP: return xfs_ioc_getfsmap(ip, arg); + case XFS_IOC_SCRUB_METADATA: + return xfs_ioc_scrub_metadata(ip, arg); + case XFS_IOC_FD_TO_HANDLE: case XFS_IOC_PATH_TO_HANDLE: case XFS_IOC_PATH_TO_FSHANDLE: { |