filesystem freeze: remove XFS specific ioctl interfaces for freeze feature
[safe/jmp/linux-2.6] / fs / xfs / linux-2.6 / xfs_ioctl.c
index a9952e4..e5be1e0 100644 (file)
@@ -48,6 +48,8 @@
 #include "xfs_dfrag.h"
 #include "xfs_fsops.h"
 #include "xfs_vnodeops.h"
+#include "xfs_quota.h"
+#include "xfs_inode_item.h"
 
 #include <linux/capability.h>
 #include <linux/dcache.h>
  * XFS_IOC_PATH_TO_HANDLE
  *    returns full handle for a path
  */
-STATIC int
+int
 xfs_find_handle(
        unsigned int            cmd,
-       void                    __user *arg)
+       xfs_fsop_handlereq_t    *hreq)
 {
        int                     hsize;
        xfs_handle_t            handle;
-       xfs_fsop_handlereq_t    hreq;
        struct inode            *inode;
 
-       if (copy_from_user(&hreq, arg, sizeof(hreq)))
-               return -XFS_ERROR(EFAULT);
-
        memset((char *)&handle, 0, sizeof(handle));
 
        switch (cmd) {
        case XFS_IOC_PATH_TO_FSHANDLE:
        case XFS_IOC_PATH_TO_HANDLE: {
-               struct nameidata        nd;
-               int                     error;
-
-               error = user_path_walk_link((const char __user *)hreq.path, &nd);
+               struct path path;
+               int error = user_lpath((const char __user *)hreq->path, &path);
                if (error)
                        return error;
 
-               ASSERT(nd.path.dentry);
-               ASSERT(nd.path.dentry->d_inode);
-               inode = igrab(nd.path.dentry->d_inode);
-               path_put(&nd.path);
+               ASSERT(path.dentry);
+               ASSERT(path.dentry->d_inode);
+               inode = igrab(path.dentry->d_inode);
+               path_put(&path);
                break;
        }
 
        case XFS_IOC_FD_TO_HANDLE: {
                struct file     *file;
 
-               file = fget(hreq.fd);
+               file = fget(hreq->fd);
                if (!file)
                    return -EBADF;
 
@@ -158,8 +154,8 @@ xfs_find_handle(
        }
 
        /* now copy our handle into the user buffer & write out the size */
-       if (copy_to_user(hreq.ohandle, &handle, hsize) ||
-           copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) {
+       if (copy_to_user(hreq->ohandle, &handle, hsize) ||
+           copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32))) {
                iput(inode);
                return -XFS_ERROR(EFAULT);
        }
@@ -238,38 +234,36 @@ xfs_vget_fsop_handlereq(
                return error;
        if (ip == NULL)
                return XFS_ERROR(EIO);
-       if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
+       if (ip->i_d.di_gen != igen) {
                xfs_iput_new(ip, XFS_ILOCK_SHARED);
                return XFS_ERROR(ENOENT);
        }
 
        xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
-       *inode = XFS_ITOV(ip);
+       *inode = VFS_I(ip);
        return 0;
 }
 
-STATIC int
+int
 xfs_open_by_handle(
        xfs_mount_t             *mp,
-       void                    __user *arg,
+       xfs_fsop_handlereq_t    *hreq,
        struct file             *parfilp,
        struct inode            *parinode)
 {
+       const struct cred       *cred = current_cred();
        int                     error;
        int                     new_fd;
        int                     permflag;
        struct file             *filp;
        struct inode            *inode;
        struct dentry           *dentry;
-       xfs_fsop_handlereq_t    hreq;
 
        if (!capable(CAP_SYS_ADMIN))
                return -XFS_ERROR(EPERM);
-       if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
-               return -XFS_ERROR(EFAULT);
 
-       error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &inode);
+       error = xfs_vget_fsop_handlereq(mp, parinode, hreq, &inode);
        if (error)
                return -error;
 
@@ -280,10 +274,10 @@ xfs_open_by_handle(
        }
 
 #if BITS_PER_LONG != 32
-       hreq.oflags |= O_LARGEFILE;
+       hreq->oflags |= O_LARGEFILE;
 #endif
        /* Put open permission in namei format. */
-       permflag = hreq.oflags;
+       permflag = hreq->oflags;
        if ((permflag+1) & O_ACCMODE)
                permflag++;
        if (permflag & O_TRUNC)
@@ -311,26 +305,26 @@ xfs_open_by_handle(
                return new_fd;
        }
 
-       dentry = d_alloc_anon(inode);
-       if (dentry == NULL) {
-               iput(inode);
+       dentry = d_obtain_alias(inode);
+       if (IS_ERR(dentry)) {
                put_unused_fd(new_fd);
-               return -XFS_ERROR(ENOMEM);
+               return PTR_ERR(dentry);
        }
 
        /* Ensure umount returns EBUSY on umounts while this file is open. */
        mntget(parfilp->f_path.mnt);
 
        /* Create file pointer. */
-       filp = dentry_open(dentry, parfilp->f_path.mnt, hreq.oflags);
+       filp = dentry_open(dentry, parfilp->f_path.mnt, hreq->oflags, cred);
        if (IS_ERR(filp)) {
                put_unused_fd(new_fd);
                return -XFS_ERROR(-PTR_ERR(filp));
        }
+
        if (inode->i_mode & S_IFREG) {
                /* invisible operation should not change atime */
                filp->f_flags |= O_NOATIME;
-               filp->f_op = &xfs_invis_file_operations;
+               filp->f_mode |= FMODE_NOCMTIME;
        }
 
        fd_install(new_fd, filp);
@@ -363,24 +357,21 @@ do_readlink(
 }
 
 
-STATIC int
+int
 xfs_readlink_by_handle(
        xfs_mount_t             *mp,
-       void                    __user *arg,
+       xfs_fsop_handlereq_t    *hreq,
        struct inode            *parinode)
 {
        struct inode            *inode;
-       xfs_fsop_handlereq_t    hreq;
        __u32                   olen;
        void                    *link;
        int                     error;
 
        if (!capable(CAP_SYS_ADMIN))
                return -XFS_ERROR(EPERM);
-       if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
-               return -XFS_ERROR(EFAULT);
 
-       error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &inode);
+       error = xfs_vget_fsop_handlereq(mp, parinode, hreq, &inode);
        if (error)
                return -error;
 
@@ -390,7 +381,7 @@ xfs_readlink_by_handle(
                goto out_iput;
        }
 
-       if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) {
+       if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
                error = -XFS_ERROR(EFAULT);
                goto out_iput;
        }
@@ -402,7 +393,7 @@ xfs_readlink_by_handle(
        error = -xfs_readlink(XFS_I(inode), link);
        if (error)
                goto out_kfree;
-       error = do_readlink(hreq.ohandle, olen, link);
+       error = do_readlink(hreq->ohandle, olen, link);
        if (error)
                goto out_kfree;
 
@@ -470,6 +461,12 @@ xfs_attrlist_by_handle(
        if (al_hreq.buflen > XATTR_LIST_MAX)
                return -XFS_ERROR(EINVAL);
 
+       /*
+        * Reject flags, only allow namespaces.
+        */
+       if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
+               return -XFS_ERROR(EINVAL);
+
        error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq, &inode);
        if (error)
                goto out;
@@ -495,7 +492,7 @@ xfs_attrlist_by_handle(
        return -error;
 }
 
-STATIC int
+int
 xfs_attrmulti_attr_get(
        struct inode            *inode,
        char                    *name,
@@ -505,14 +502,14 @@ xfs_attrmulti_attr_get(
 {
        char                    *kbuf;
        int                     error = EFAULT;
-       
+
        if (*len > XATTR_SIZE_MAX)
                return EINVAL;
        kbuf = kmalloc(*len, GFP_KERNEL);
        if (!kbuf)
                return ENOMEM;
 
-       error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags, NULL);
+       error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
        if (error)
                goto out_kfree;
 
@@ -524,7 +521,7 @@ xfs_attrmulti_attr_get(
        return error;
 }
 
-STATIC int
+int
 xfs_attrmulti_attr_set(
        struct inode            *inode,
        char                    *name,
@@ -535,8 +532,6 @@ xfs_attrmulti_attr_set(
        char                    *kbuf;
        int                     error = EFAULT;
 
-       if (IS_RDONLY(inode))
-               return -EROFS;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                return EPERM;
        if (len > XATTR_SIZE_MAX)
@@ -548,7 +543,7 @@ xfs_attrmulti_attr_set(
 
        if (copy_from_user(kbuf, ubuf, len))
                goto out_kfree;
-                       
+
        error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
 
  out_kfree:
@@ -556,14 +551,12 @@ xfs_attrmulti_attr_set(
        return error;
 }
 
-STATIC int
+int
 xfs_attrmulti_attr_remove(
        struct inode            *inode,
        char                    *name,
        __uint32_t              flags)
 {
-       if (IS_RDONLY(inode))
-               return -EROFS;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                return EPERM;
        return xfs_attr_remove(XFS_I(inode), name, flags);
@@ -573,6 +566,7 @@ STATIC int
 xfs_attrmulti_by_handle(
        xfs_mount_t             *mp,
        void                    __user *arg,
+       struct file             *parfilp,
        struct inode            *parinode)
 {
        int                     error;
@@ -592,7 +586,7 @@ xfs_attrmulti_by_handle(
                goto out;
 
        error = E2BIG;
-       size = am_hreq.opcount * sizeof(attr_multiop_t);
+       size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
        if (!size || size > 16 * PAGE_SIZE)
                goto out_vn_rele;
 
@@ -626,13 +620,21 @@ xfs_attrmulti_by_handle(
                                        &ops[i].am_length, ops[i].am_flags);
                        break;
                case ATTR_OP_SET:
+                       ops[i].am_error = mnt_want_write(parfilp->f_path.mnt);
+                       if (ops[i].am_error)
+                               break;
                        ops[i].am_error = xfs_attrmulti_attr_set(inode,
                                        attr_name, ops[i].am_attrvalue,
                                        ops[i].am_length, ops[i].am_flags);
+                       mnt_drop_write(parfilp->f_path.mnt);
                        break;
                case ATTR_OP_REMOVE:
+                       ops[i].am_error = mnt_want_write(parfilp->f_path.mnt);
+                       if (ops[i].am_error)
+                               break;
                        ops[i].am_error = xfs_attrmulti_attr_remove(inode,
                                        attr_name, ops[i].am_flags);
+                       mnt_drop_write(parfilp->f_path.mnt);
                        break;
                default:
                        ops[i].am_error = EINVAL;
@@ -651,684 +653,933 @@ xfs_attrmulti_by_handle(
        return -error;
 }
 
-/* prototypes for a few of the stack-hungry cases that have
- * their own functions.  Functions are defined after their use
- * so gcc doesn't get fancy and inline them with -03 */
-
-STATIC int
+int
 xfs_ioc_space(
        struct xfs_inode        *ip,
        struct inode            *inode,
        struct file             *filp,
-       int                     flags,
+       int                     ioflags,
        unsigned int            cmd,
-       void                    __user *arg);
+       xfs_flock64_t           *bf)
+{
+       int                     attr_flags = 0;
+       int                     error;
 
-STATIC int
-xfs_ioc_bulkstat(
-       xfs_mount_t             *mp,
-       unsigned int            cmd,
-       void                    __user *arg);
+       /*
+        * Only allow the sys admin to reserve space unless
+        * unwritten extents are enabled.
+        */
+       if (!xfs_sb_version_hasextflgbit(&ip->i_mount->m_sb) &&
+           !capable(CAP_SYS_ADMIN))
+               return -XFS_ERROR(EPERM);
 
-STATIC int
-xfs_ioc_fsgeometry_v1(
-       xfs_mount_t             *mp,
-       void                    __user *arg);
+       if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
+               return -XFS_ERROR(EPERM);
 
-STATIC int
-xfs_ioc_fsgeometry(
-       xfs_mount_t             *mp,
-       void                    __user *arg);
+       if (!(filp->f_mode & FMODE_WRITE))
+               return -XFS_ERROR(EBADF);
 
-STATIC int
-xfs_ioc_xattr(
-       xfs_inode_t             *ip,
-       struct file             *filp,
-       unsigned int            cmd,
-       void                    __user *arg);
+       if (!S_ISREG(inode->i_mode))
+               return -XFS_ERROR(EINVAL);
 
-STATIC int
-xfs_ioc_fsgetxattr(
-       xfs_inode_t             *ip,
-       int                     attr,
-       void                    __user *arg);
+       if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
+               attr_flags |= XFS_ATTR_NONBLOCK;
+       if (ioflags & IO_INVIS)
+               attr_flags |= XFS_ATTR_DMI;
 
-STATIC int
-xfs_ioc_getbmap(
-       struct xfs_inode        *ip,
-       int                     flags,
-       unsigned int            cmd,
-       void                    __user *arg);
+       error = xfs_change_file_space(ip, cmd, bf, filp->f_pos, attr_flags);
+       return -error;
+}
 
 STATIC int
-xfs_ioc_getbmapx(
-       struct xfs_inode        *ip,
-       void                    __user *arg);
-
-int
-xfs_ioctl(
-       xfs_inode_t             *ip,
-       struct file             *filp,
-       int                     ioflags,
+xfs_ioc_bulkstat(
+       xfs_mount_t             *mp,
        unsigned int            cmd,
        void                    __user *arg)
 {
-       struct inode            *inode = filp->f_path.dentry->d_inode;
-       xfs_mount_t             *mp = ip->i_mount;
+       xfs_fsop_bulkreq_t      bulkreq;
+       int                     count;  /* # of records returned */
+       xfs_ino_t               inlast; /* last inode number */
+       int                     done;
        int                     error;
 
-       xfs_itrace_entry(XFS_I(inode));
-       switch (cmd) {
-
-       case XFS_IOC_ALLOCSP:
-       case XFS_IOC_FREESP:
-       case XFS_IOC_RESVSP:
-       case XFS_IOC_UNRESVSP:
-       case XFS_IOC_ALLOCSP64:
-       case XFS_IOC_FREESP64:
-       case XFS_IOC_RESVSP64:
-       case XFS_IOC_UNRESVSP64:
-               /*
-                * Only allow the sys admin to reserve space unless
-                * unwritten extents are enabled.
-                */
-               if (!XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) &&
-                   !capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-
-               return xfs_ioc_space(ip, inode, filp, ioflags, cmd, arg);
-
-       case XFS_IOC_DIOINFO: {
-               struct dioattr  da;
-               xfs_buftarg_t   *target =
-                       XFS_IS_REALTIME_INODE(ip) ?
-                       mp->m_rtdev_targp : mp->m_ddev_targp;
+       /* done = 1 if there are more stats to get and if bulkstat */
+       /* should be called again (unused here, but used in dmapi) */
 
-               da.d_mem = da.d_miniosz = 1 << target->bt_sshift;
-               da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
 
-               if (copy_to_user(arg, &da, sizeof(da)))
-                       return -XFS_ERROR(EFAULT);
-               return 0;
-       }
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -XFS_ERROR(EIO);
 
-       case XFS_IOC_FSBULKSTAT_SINGLE:
-       case XFS_IOC_FSBULKSTAT:
-       case XFS_IOC_FSINUMBERS:
-               return xfs_ioc_bulkstat(mp, cmd, arg);
+       if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
+               return -XFS_ERROR(EFAULT);
 
-       case XFS_IOC_FSGEOMETRY_V1:
-               return xfs_ioc_fsgeometry_v1(mp, arg);
+       if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
+               return -XFS_ERROR(EFAULT);
 
-       case XFS_IOC_FSGEOMETRY:
-               return xfs_ioc_fsgeometry(mp, arg);
+       if ((count = bulkreq.icount) <= 0)
+               return -XFS_ERROR(EINVAL);
 
-       case XFS_IOC_GETVERSION:
-               return put_user(inode->i_generation, (int __user *)arg);
+       if (bulkreq.ubuffer == NULL)
+               return -XFS_ERROR(EINVAL);
 
-       case XFS_IOC_FSGETXATTR:
-               return xfs_ioc_fsgetxattr(ip, 0, arg);
-       case XFS_IOC_FSGETXATTRA:
-               return xfs_ioc_fsgetxattr(ip, 1, arg);
-       case XFS_IOC_GETXFLAGS:
-       case XFS_IOC_SETXFLAGS:
-       case XFS_IOC_FSSETXATTR:
-               return xfs_ioc_xattr(ip, filp, cmd, arg);
+       if (cmd == XFS_IOC_FSINUMBERS)
+               error = xfs_inumbers(mp, &inlast, &count,
+                                       bulkreq.ubuffer, xfs_inumbers_fmt);
+       else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
+               error = xfs_bulkstat_single(mp, &inlast,
+                                               bulkreq.ubuffer, &done);
+       else    /* XFS_IOC_FSBULKSTAT */
+               error = xfs_bulkstat(mp, &inlast, &count,
+                       (bulkstat_one_pf)xfs_bulkstat_one, NULL,
+                       sizeof(xfs_bstat_t), bulkreq.ubuffer,
+                       BULKSTAT_FG_QUICK, &done);
 
-       case XFS_IOC_FSSETDM: {
-               struct fsdmidata        dmi;
+       if (error)
+               return -error;
 
-               if (copy_from_user(&dmi, arg, sizeof(dmi)))
+       if (bulkreq.ocount != NULL) {
+               if (copy_to_user(bulkreq.lastip, &inlast,
+                                               sizeof(xfs_ino_t)))
                        return -XFS_ERROR(EFAULT);
 
-               error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
-                               dmi.fsd_dmstate);
-               return -error;
+               if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
+                       return -XFS_ERROR(EFAULT);
        }
 
-       case XFS_IOC_GETBMAP:
-       case XFS_IOC_GETBMAPA:
-               return xfs_ioc_getbmap(ip, ioflags, cmd, arg);
-
-       case XFS_IOC_GETBMAPX:
-               return xfs_ioc_getbmapx(ip, arg);
-
-       case XFS_IOC_FD_TO_HANDLE:
-       case XFS_IOC_PATH_TO_HANDLE:
-       case XFS_IOC_PATH_TO_FSHANDLE:
-               return xfs_find_handle(cmd, arg);
-
-       case XFS_IOC_OPEN_BY_HANDLE:
-               return xfs_open_by_handle(mp, arg, filp, inode);
+       return 0;
+}
 
-       case XFS_IOC_FSSETDM_BY_HANDLE:
-               return xfs_fssetdm_by_handle(mp, arg, inode);
+STATIC int
+xfs_ioc_fsgeometry_v1(
+       xfs_mount_t             *mp,
+       void                    __user *arg)
+{
+       xfs_fsop_geom_v1_t      fsgeo;
+       int                     error;
 
-       case XFS_IOC_READLINK_BY_HANDLE:
-               return xfs_readlink_by_handle(mp, arg, inode);
+       error = xfs_fs_geometry(mp, (xfs_fsop_geom_t *)&fsgeo, 3);
+       if (error)
+               return -error;
 
-       case XFS_IOC_ATTRLIST_BY_HANDLE:
-               return xfs_attrlist_by_handle(mp, arg, inode);
+       if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
+               return -XFS_ERROR(EFAULT);
+       return 0;
+}
 
-       case XFS_IOC_ATTRMULTI_BY_HANDLE:
-               return xfs_attrmulti_by_handle(mp, arg, inode);
+STATIC int
+xfs_ioc_fsgeometry(
+       xfs_mount_t             *mp,
+       void                    __user *arg)
+{
+       xfs_fsop_geom_t         fsgeo;
+       int                     error;
 
-       case XFS_IOC_SWAPEXT: {
-               error = xfs_swapext((struct xfs_swapext __user *)arg);
+       error = xfs_fs_geometry(mp, &fsgeo, 4);
+       if (error)
                return -error;
-       }
 
-       case XFS_IOC_FSCOUNTS: {
-               xfs_fsop_counts_t out;
+       if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
+               return -XFS_ERROR(EFAULT);
+       return 0;
+}
 
-               error = xfs_fs_counts(mp, &out);
-               if (error)
-                       return -error;
+/*
+ * Linux extended inode flags interface.
+ */
 
-               if (copy_to_user(arg, &out, sizeof(out)))
-                       return -XFS_ERROR(EFAULT);
-               return 0;
-       }
+STATIC unsigned int
+xfs_merge_ioc_xflags(
+       unsigned int    flags,
+       unsigned int    start)
+{
+       unsigned int    xflags = start;
 
-       case XFS_IOC_SET_RESBLKS: {
-               xfs_fsop_resblks_t inout;
-               __uint64_t         in;
+       if (flags & FS_IMMUTABLE_FL)
+               xflags |= XFS_XFLAG_IMMUTABLE;
+       else
+               xflags &= ~XFS_XFLAG_IMMUTABLE;
+       if (flags & FS_APPEND_FL)
+               xflags |= XFS_XFLAG_APPEND;
+       else
+               xflags &= ~XFS_XFLAG_APPEND;
+       if (flags & FS_SYNC_FL)
+               xflags |= XFS_XFLAG_SYNC;
+       else
+               xflags &= ~XFS_XFLAG_SYNC;
+       if (flags & FS_NOATIME_FL)
+               xflags |= XFS_XFLAG_NOATIME;
+       else
+               xflags &= ~XFS_XFLAG_NOATIME;
+       if (flags & FS_NODUMP_FL)
+               xflags |= XFS_XFLAG_NODUMP;
+       else
+               xflags &= ~XFS_XFLAG_NODUMP;
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       return xflags;
+}
 
-               if (copy_from_user(&inout, arg, sizeof(inout)))
-                       return -XFS_ERROR(EFAULT);
+STATIC unsigned int
+xfs_di2lxflags(
+       __uint16_t      di_flags)
+{
+       unsigned int    flags = 0;
 
-               /* input parameter is passed in resblks field of structure */
-               in = inout.resblks;
-               error = xfs_reserve_blocks(mp, &in, &inout);
-               if (error)
-                       return -error;
+       if (di_flags & XFS_DIFLAG_IMMUTABLE)
+               flags |= FS_IMMUTABLE_FL;
+       if (di_flags & XFS_DIFLAG_APPEND)
+               flags |= FS_APPEND_FL;
+       if (di_flags & XFS_DIFLAG_SYNC)
+               flags |= FS_SYNC_FL;
+       if (di_flags & XFS_DIFLAG_NOATIME)
+               flags |= FS_NOATIME_FL;
+       if (di_flags & XFS_DIFLAG_NODUMP)
+               flags |= FS_NODUMP_FL;
+       return flags;
+}
 
-               if (copy_to_user(arg, &inout, sizeof(inout)))
-                       return -XFS_ERROR(EFAULT);
-               return 0;
+STATIC int
+xfs_ioc_fsgetxattr(
+       xfs_inode_t             *ip,
+       int                     attr,
+       void                    __user *arg)
+{
+       struct fsxattr          fa;
+
+       xfs_ilock(ip, XFS_ILOCK_SHARED);
+       fa.fsx_xflags = xfs_ip2xflags(ip);
+       fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
+       fa.fsx_projid = ip->i_d.di_projid;
+
+       if (attr) {
+               if (ip->i_afp) {
+                       if (ip->i_afp->if_flags & XFS_IFEXTENTS)
+                               fa.fsx_nextents = ip->i_afp->if_bytes /
+                                                       sizeof(xfs_bmbt_rec_t);
+                       else
+                               fa.fsx_nextents = ip->i_d.di_anextents;
+               } else
+                       fa.fsx_nextents = 0;
+       } else {
+               if (ip->i_df.if_flags & XFS_IFEXTENTS)
+                       fa.fsx_nextents = ip->i_df.if_bytes /
+                                               sizeof(xfs_bmbt_rec_t);
+               else
+                       fa.fsx_nextents = ip->i_d.di_nextents;
        }
+       xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
-       case XFS_IOC_GET_RESBLKS: {
-               xfs_fsop_resblks_t out;
+       if (copy_to_user(arg, &fa, sizeof(fa)))
+               return -EFAULT;
+       return 0;
+}
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+STATIC void
+xfs_set_diflags(
+       struct xfs_inode        *ip,
+       unsigned int            xflags)
+{
+       unsigned int            di_flags;
+
+       /* can't set PREALLOC this way, just preserve it */
+       di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
+       if (xflags & XFS_XFLAG_IMMUTABLE)
+               di_flags |= XFS_DIFLAG_IMMUTABLE;
+       if (xflags & XFS_XFLAG_APPEND)
+               di_flags |= XFS_DIFLAG_APPEND;
+       if (xflags & XFS_XFLAG_SYNC)
+               di_flags |= XFS_DIFLAG_SYNC;
+       if (xflags & XFS_XFLAG_NOATIME)
+               di_flags |= XFS_DIFLAG_NOATIME;
+       if (xflags & XFS_XFLAG_NODUMP)
+               di_flags |= XFS_DIFLAG_NODUMP;
+       if (xflags & XFS_XFLAG_PROJINHERIT)
+               di_flags |= XFS_DIFLAG_PROJINHERIT;
+       if (xflags & XFS_XFLAG_NODEFRAG)
+               di_flags |= XFS_DIFLAG_NODEFRAG;
+       if (xflags & XFS_XFLAG_FILESTREAM)
+               di_flags |= XFS_DIFLAG_FILESTREAM;
+       if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+               if (xflags & XFS_XFLAG_RTINHERIT)
+                       di_flags |= XFS_DIFLAG_RTINHERIT;
+               if (xflags & XFS_XFLAG_NOSYMLINKS)
+                       di_flags |= XFS_DIFLAG_NOSYMLINKS;
+               if (xflags & XFS_XFLAG_EXTSZINHERIT)
+                       di_flags |= XFS_DIFLAG_EXTSZINHERIT;
+       } else if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
+               if (xflags & XFS_XFLAG_REALTIME)
+                       di_flags |= XFS_DIFLAG_REALTIME;
+               if (xflags & XFS_XFLAG_EXTSIZE)
+                       di_flags |= XFS_DIFLAG_EXTSIZE;
+       }
 
-               error = xfs_reserve_blocks(mp, NULL, &out);
-               if (error)
-                       return -error;
+       ip->i_d.di_flags = di_flags;
+}
 
-               if (copy_to_user(arg, &out, sizeof(out)))
-                       return -XFS_ERROR(EFAULT);
+STATIC void
+xfs_diflags_to_linux(
+       struct xfs_inode        *ip)
+{
+       struct inode            *inode = VFS_I(ip);
+       unsigned int            xflags = xfs_ip2xflags(ip);
 
-               return 0;
+       if (xflags & XFS_XFLAG_IMMUTABLE)
+               inode->i_flags |= S_IMMUTABLE;
+       else
+               inode->i_flags &= ~S_IMMUTABLE;
+       if (xflags & XFS_XFLAG_APPEND)
+               inode->i_flags |= S_APPEND;
+       else
+               inode->i_flags &= ~S_APPEND;
+       if (xflags & XFS_XFLAG_SYNC)
+               inode->i_flags |= S_SYNC;
+       else
+               inode->i_flags &= ~S_SYNC;
+       if (xflags & XFS_XFLAG_NOATIME)
+               inode->i_flags |= S_NOATIME;
+       else
+               inode->i_flags &= ~S_NOATIME;
+}
+
+#define FSX_PROJID     1
+#define FSX_EXTSIZE    2
+#define FSX_XFLAGS     4
+#define FSX_NONBLOCK   8
+
+STATIC int
+xfs_ioctl_setattr(
+       xfs_inode_t             *ip,
+       struct fsxattr          *fa,
+       int                     mask)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       unsigned int            lock_flags = 0;
+       struct xfs_dquot        *udqp = NULL, *gdqp = NULL;
+       struct xfs_dquot        *olddquot = NULL;
+       int                     code;
+
+       xfs_itrace_entry(ip);
+
+       if (mp->m_flags & XFS_MOUNT_RDONLY)
+               return XFS_ERROR(EROFS);
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return XFS_ERROR(EIO);
+
+       /*
+        * If disk quotas is on, we make sure that the dquots do exist on disk,
+        * before we start any other transactions. Trying to do this later
+        * is messy. We don't care to take a readlock to look at the ids
+        * in inode here, because we can't hold it across the trans_reserve.
+        * If the IDs do change before we take the ilock, we're covered
+        * because the i_*dquot fields will get updated anyway.
+        */
+       if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
+               code = XFS_QM_DQVOPALLOC(mp, ip, ip->i_d.di_uid,
+                                        ip->i_d.di_gid, fa->fsx_projid,
+                                        XFS_QMOPT_PQUOTA, &udqp, &gdqp);
+               if (code)
+                       return code;
        }
 
-       case XFS_IOC_FSGROWFSDATA: {
-               xfs_growfs_data_t in;
+       /*
+        * For the other attributes, we acquire the inode lock and
+        * first do an error checking pass.
+        */
+       tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
+       code = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0);
+       if (code)
+               goto error_return;
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       lock_flags = XFS_ILOCK_EXCL;
+       xfs_ilock(ip, lock_flags);
 
-               if (copy_from_user(&in, arg, sizeof(in)))
-                       return -XFS_ERROR(EFAULT);
+       /*
+        * CAP_FOWNER overrides the following restrictions:
+        *
+        * The user ID of the calling process must be equal
+        * to the file owner ID, except in cases where the
+        * CAP_FSETID capability is applicable.
+        */
+       if (current_fsuid() != ip->i_d.di_uid && !capable(CAP_FOWNER)) {
+               code = XFS_ERROR(EPERM);
+               goto error_return;
+       }
 
-               error = xfs_growfs_data(mp, &in);
-               return -error;
+       /*
+        * Do a quota reservation only if projid is actually going to change.
+        */
+       if (mask & FSX_PROJID) {
+               if (XFS_IS_PQUOTA_ON(mp) &&
+                   ip->i_d.di_projid != fa->fsx_projid) {
+                       ASSERT(tp);
+                       code = XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, udqp, gdqp,
+                                               capable(CAP_FOWNER) ?
+                                               XFS_QMOPT_FORCE_RES : 0);
+                       if (code)       /* out of quota */
+                               goto error_return;
+               }
        }
 
-       case XFS_IOC_FSGROWFSLOG: {
-               xfs_growfs_log_t in;
+       if (mask & FSX_EXTSIZE) {
+               /*
+                * Can't change extent size if any extents are allocated.
+                */
+               if (ip->i_d.di_nextents &&
+                   ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
+                    fa->fsx_extsize)) {
+                       code = XFS_ERROR(EINVAL);       /* EFBIG? */
+                       goto error_return;
+               }
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+               /*
+                * Extent size must be a multiple of the appropriate block
+                * size, if set at all.
+                */
+               if (fa->fsx_extsize != 0) {
+                       xfs_extlen_t    size;
+
+                       if (XFS_IS_REALTIME_INODE(ip) ||
+                           ((mask & FSX_XFLAGS) &&
+                           (fa->fsx_xflags & XFS_XFLAG_REALTIME))) {
+                               size = mp->m_sb.sb_rextsize <<
+                                      mp->m_sb.sb_blocklog;
+                       } else {
+                               size = mp->m_sb.sb_blocksize;
+                       }
+
+                       if (fa->fsx_extsize % size) {
+                               code = XFS_ERROR(EINVAL);
+                               goto error_return;
+                       }
+               }
+       }
 
-               if (copy_from_user(&in, arg, sizeof(in)))
-                       return -XFS_ERROR(EFAULT);
 
-               error = xfs_growfs_log(mp, &in);
-               return -error;
+       if (mask & FSX_XFLAGS) {
+               /*
+                * Can't change realtime flag if any extents are allocated.
+                */
+               if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
+                   (XFS_IS_REALTIME_INODE(ip)) !=
+                   (fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
+                       code = XFS_ERROR(EINVAL);       /* EFBIG? */
+                       goto error_return;
+               }
+
+               /*
+                * If realtime flag is set then must have realtime data.
+                */
+               if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
+                       if ((mp->m_sb.sb_rblocks == 0) ||
+                           (mp->m_sb.sb_rextsize == 0) ||
+                           (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
+                               code = XFS_ERROR(EINVAL);
+                               goto error_return;
+                       }
+               }
+
+               /*
+                * Can't modify an immutable/append-only file unless
+                * we have appropriate permission.
+                */
+               if ((ip->i_d.di_flags &
+                               (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) ||
+                    (fa->fsx_xflags &
+                               (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
+                   !capable(CAP_LINUX_IMMUTABLE)) {
+                       code = XFS_ERROR(EPERM);
+                       goto error_return;
+               }
        }
 
-       case XFS_IOC_FSGROWFSRT: {
-               xfs_growfs_rt_t in;
+       xfs_trans_ijoin(tp, ip, lock_flags);
+       xfs_trans_ihold(tp, ip);
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       /*
+        * Change file ownership.  Must be the owner or privileged.
+        */
+       if (mask & FSX_PROJID) {
+               /*
+                * CAP_FSETID overrides the following restrictions:
+                *
+                * The set-user-ID and set-group-ID bits of a file will be
+                * cleared upon successful return from chown()
+                */
+               if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
+                   !capable(CAP_FSETID))
+                       ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
 
-               if (copy_from_user(&in, arg, sizeof(in)))
-                       return -XFS_ERROR(EFAULT);
+               /*
+                * Change the ownerships and register quota modifications
+                * in the transaction.
+                */
+               if (ip->i_d.di_projid != fa->fsx_projid) {
+                       if (XFS_IS_PQUOTA_ON(mp)) {
+                               olddquot = XFS_QM_DQVOPCHOWN(mp, tp, ip,
+                                                       &ip->i_gdquot, gdqp);
+                       }
+                       ip->i_d.di_projid = fa->fsx_projid;
+
+                       /*
+                        * We may have to rev the inode as well as
+                        * the superblock version number since projids didn't
+                        * exist before DINODE_VERSION_2 and SB_VERSION_NLINK.
+                        */
+                       if (ip->i_d.di_version == 1)
+                               xfs_bump_ino_vers2(tp, ip);
+               }
 
-               error = xfs_growfs_rt(mp, &in);
-               return -error;
        }
 
-       case XFS_IOC_FREEZE:
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       if (mask & FSX_EXTSIZE)
+               ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
+       if (mask & FSX_XFLAGS) {
+               xfs_set_diflags(ip, fa->fsx_xflags);
+               xfs_diflags_to_linux(ip);
+       }
 
-               if (inode->i_sb->s_frozen == SB_UNFROZEN)
-                       freeze_bdev(inode->i_sb->s_bdev);
-               return 0;
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+       xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
 
-       case XFS_IOC_THAW:
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-               if (inode->i_sb->s_frozen != SB_UNFROZEN)
-                       thaw_bdev(inode->i_sb->s_bdev, inode->i_sb);
-               return 0;
+       XFS_STATS_INC(xs_ig_attrchg);
 
-       case XFS_IOC_GOINGDOWN: {
-               __uint32_t in;
+       /*
+        * If this is a synchronous mount, make sure that the
+        * transaction goes to disk before returning to the user.
+        * This is slightly sub-optimal in that truncates require
+        * two sync transactions instead of one for wsync filesystems.
+        * One for the truncate and one for the timestamps since we
+        * don't want to change the timestamps unless we're sure the
+        * truncate worked.  Truncates are less than 1% of the laddis
+        * mix so this probably isn't worth the trouble to optimize.
+        */
+       if (mp->m_flags & XFS_MOUNT_WSYNC)
+               xfs_trans_set_sync(tp);
+       code = xfs_trans_commit(tp, 0);
+       xfs_iunlock(ip, lock_flags);
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       /*
+        * Release any dquot(s) the inode had kept before chown.
+        */
+       XFS_QM_DQRELE(mp, olddquot);
+       XFS_QM_DQRELE(mp, udqp);
+       XFS_QM_DQRELE(mp, gdqp);
 
-               if (get_user(in, (__uint32_t __user *)arg))
-                       return -XFS_ERROR(EFAULT);
+       if (code)
+               return code;
 
-               error = xfs_fs_goingdown(mp, in);
-               return -error;
+       if (DM_EVENT_ENABLED(ip, DM_EVENT_ATTRIBUTE)) {
+               XFS_SEND_NAMESP(mp, DM_EVENT_ATTRIBUTE, ip, DM_RIGHT_NULL,
+                               NULL, DM_RIGHT_NULL, NULL, NULL, 0, 0,
+                               (mask & FSX_NONBLOCK) ? DM_FLAGS_NDELAY : 0);
        }
 
-       case XFS_IOC_ERROR_INJECTION: {
-               xfs_error_injection_t in;
+       return 0;
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+ error_return:
+       XFS_QM_DQRELE(mp, udqp);
+       XFS_QM_DQRELE(mp, gdqp);
+       xfs_trans_cancel(tp, 0);
+       if (lock_flags)
+               xfs_iunlock(ip, lock_flags);
+       return code;
+}
 
-               if (copy_from_user(&in, arg, sizeof(in)))
-                       return -XFS_ERROR(EFAULT);
+STATIC int
+xfs_ioc_fssetxattr(
+       xfs_inode_t             *ip,
+       struct file             *filp,
+       void                    __user *arg)
+{
+       struct fsxattr          fa;
+       unsigned int            mask;
 
-               error = xfs_errortag_add(in.errtag, mp);
-               return -error;
-       }
+       if (copy_from_user(&fa, arg, sizeof(fa)))
+               return -EFAULT;
 
-       case XFS_IOC_ERROR_CLEARALL:
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID;
+       if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
+               mask |= FSX_NONBLOCK;
 
-               error = xfs_errortag_clearall(mp, 1);
-               return -error;
+       return -xfs_ioctl_setattr(ip, &fa, mask);
+}
 
-       default:
-               return -ENOTTY;
-       }
+STATIC int
+xfs_ioc_getxflags(
+       xfs_inode_t             *ip,
+       void                    __user *arg)
+{
+       unsigned int            flags;
+
+       flags = xfs_di2lxflags(ip->i_d.di_flags);
+       if (copy_to_user(arg, &flags, sizeof(flags)))
+               return -EFAULT;
+       return 0;
 }
 
 STATIC int
-xfs_ioc_space(
-       struct xfs_inode        *ip,
-       struct inode            *inode,
+xfs_ioc_setxflags(
+       xfs_inode_t             *ip,
        struct file             *filp,
-       int                     ioflags,
-       unsigned int            cmd,
        void                    __user *arg)
 {
-       xfs_flock64_t           bf;
-       int                     attr_flags = 0;
-       int                     error;
+       struct fsxattr          fa;
+       unsigned int            flags;
+       unsigned int            mask;
 
-       if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
-               return -XFS_ERROR(EPERM);
+       if (copy_from_user(&flags, arg, sizeof(flags)))
+               return -EFAULT;
 
-       if (!(filp->f_mode & FMODE_WRITE))
-               return -XFS_ERROR(EBADF);
+       if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
+                     FS_NOATIME_FL | FS_NODUMP_FL | \
+                     FS_SYNC_FL))
+               return -EOPNOTSUPP;
 
-       if (!S_ISREG(inode->i_mode))
-               return -XFS_ERROR(EINVAL);
+       mask = FSX_XFLAGS;
+       if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
+               mask |= FSX_NONBLOCK;
+       fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
 
-       if (copy_from_user(&bf, arg, sizeof(bf)))
-               return -XFS_ERROR(EFAULT);
+       return -xfs_ioctl_setattr(ip, &fa, mask);
+}
 
-       if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
-               attr_flags |= ATTR_NONBLOCK;
-       if (ioflags & IO_INVIS)
-               attr_flags |= ATTR_DMI;
+STATIC int
+xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
+{
+       struct getbmap __user   *base = *ap;
 
-       error = xfs_change_file_space(ip, cmd, &bf, filp->f_pos,
-                                             NULL, attr_flags);
-       return -error;
+       /* copy only getbmap portion (not getbmapx) */
+       if (copy_to_user(base, bmv, sizeof(struct getbmap)))
+               return XFS_ERROR(EFAULT);
+
+       *ap += sizeof(struct getbmap);
+       return 0;
 }
 
 STATIC int
-xfs_ioc_bulkstat(
-       xfs_mount_t             *mp,
+xfs_ioc_getbmap(
+       struct xfs_inode        *ip,
+       int                     ioflags,
        unsigned int            cmd,
        void                    __user *arg)
 {
-       xfs_fsop_bulkreq_t      bulkreq;
-       int                     count;  /* # of records returned */
-       xfs_ino_t               inlast; /* last inode number */
-       int                     done;
+       struct getbmapx         bmx;
        int                     error;
 
-       /* done = 1 if there are more stats to get and if bulkstat */
-       /* should be called again (unused here, but used in dmapi) */
+       if (copy_from_user(&bmx, arg, sizeof(struct getbmapx)))
+               return -XFS_ERROR(EFAULT);
 
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
+       if (bmx.bmv_count < 2)
+               return -XFS_ERROR(EINVAL);
 
-       if (XFS_FORCED_SHUTDOWN(mp))
-               return -XFS_ERROR(EIO);
-
-       if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
-               return -XFS_ERROR(EFAULT);
-
-       if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
-               return -XFS_ERROR(EFAULT);
-
-       if ((count = bulkreq.icount) <= 0)
-               return -XFS_ERROR(EINVAL);
-
-       if (bulkreq.ubuffer == NULL)
-               return -XFS_ERROR(EINVAL);
-
-       if (cmd == XFS_IOC_FSINUMBERS)
-               error = xfs_inumbers(mp, &inlast, &count,
-                                       bulkreq.ubuffer, xfs_inumbers_fmt);
-       else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
-               error = xfs_bulkstat_single(mp, &inlast,
-                                               bulkreq.ubuffer, &done);
-       else    /* XFS_IOC_FSBULKSTAT */
-               error = xfs_bulkstat(mp, &inlast, &count,
-                       (bulkstat_one_pf)xfs_bulkstat_one, NULL,
-                       sizeof(xfs_bstat_t), bulkreq.ubuffer,
-                       BULKSTAT_FG_QUICK, &done);
+       bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
+       if (ioflags & IO_INVIS)
+               bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
 
+       error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
+                           (struct getbmap *)arg+1);
        if (error)
                return -error;
 
-       if (bulkreq.ocount != NULL) {
-               if (copy_to_user(bulkreq.lastip, &inlast,
-                                               sizeof(xfs_ino_t)))
-                       return -XFS_ERROR(EFAULT);
-
-               if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
-                       return -XFS_ERROR(EFAULT);
-       }
-
+       /* copy back header - only size of getbmap */
+       if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
+               return -XFS_ERROR(EFAULT);
        return 0;
 }
 
 STATIC int
-xfs_ioc_fsgeometry_v1(
-       xfs_mount_t             *mp,
-       void                    __user *arg)
+xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
 {
-       xfs_fsop_geom_v1_t      fsgeo;
-       int                     error;
+       struct getbmapx __user  *base = *ap;
 
-       error = xfs_fs_geometry(mp, (xfs_fsop_geom_t *)&fsgeo, 3);
-       if (error)
-               return -error;
+       if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
+               return XFS_ERROR(EFAULT);
 
-       if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
-               return -XFS_ERROR(EFAULT);
+       *ap += sizeof(struct getbmapx);
        return 0;
 }
 
 STATIC int
-xfs_ioc_fsgeometry(
-       xfs_mount_t             *mp,
+xfs_ioc_getbmapx(
+       struct xfs_inode        *ip,
        void                    __user *arg)
 {
-       xfs_fsop_geom_t         fsgeo;
+       struct getbmapx         bmx;
        int                     error;
 
-       error = xfs_fs_geometry(mp, &fsgeo, 4);
+       if (copy_from_user(&bmx, arg, sizeof(bmx)))
+               return -XFS_ERROR(EFAULT);
+
+       if (bmx.bmv_count < 2)
+               return -XFS_ERROR(EINVAL);
+
+       if (bmx.bmv_iflags & (~BMV_IF_VALID))
+               return -XFS_ERROR(EINVAL);
+
+       error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
+                           (struct getbmapx *)arg+1);
        if (error)
                return -error;
 
-       if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
+       /* copy back header */
+       if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
                return -XFS_ERROR(EFAULT);
+
        return 0;
 }
 
 /*
- * Linux extended inode flags interface.
+ * Note: some of the ioctl's return positive numbers as a
+ * byte count indicating success, such as readlink_by_handle.
+ * So we don't "sign flip" like most other routines.  This means
+ * true errors need to be returned as a negative value.
  */
-
-STATIC unsigned int
-xfs_merge_ioc_xflags(
-       unsigned int    flags,
-       unsigned int    start)
+long
+xfs_file_ioctl(
+       struct file             *filp,
+       unsigned int            cmd,
+       unsigned long           p)
 {
-       unsigned int    xflags = start;
+       struct inode            *inode = filp->f_path.dentry->d_inode;
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_mount        *mp = ip->i_mount;
+       void                    __user *arg = (void __user *)p;
+       int                     ioflags = 0;
+       int                     error;
 
-       if (flags & FS_IMMUTABLE_FL)
-               xflags |= XFS_XFLAG_IMMUTABLE;
-       else
-               xflags &= ~XFS_XFLAG_IMMUTABLE;
-       if (flags & FS_APPEND_FL)
-               xflags |= XFS_XFLAG_APPEND;
-       else
-               xflags &= ~XFS_XFLAG_APPEND;
-       if (flags & FS_SYNC_FL)
-               xflags |= XFS_XFLAG_SYNC;
-       else
-               xflags &= ~XFS_XFLAG_SYNC;
-       if (flags & FS_NOATIME_FL)
-               xflags |= XFS_XFLAG_NOATIME;
-       else
-               xflags &= ~XFS_XFLAG_NOATIME;
-       if (flags & FS_NODUMP_FL)
-               xflags |= XFS_XFLAG_NODUMP;
-       else
-               xflags &= ~XFS_XFLAG_NODUMP;
+       if (filp->f_mode & FMODE_NOCMTIME)
+               ioflags |= IO_INVIS;
 
-       return xflags;
-}
+       xfs_itrace_entry(ip);
 
-STATIC unsigned int
-xfs_di2lxflags(
-       __uint16_t      di_flags)
-{
-       unsigned int    flags = 0;
+       switch (cmd) {
+       case XFS_IOC_ALLOCSP:
+       case XFS_IOC_FREESP:
+       case XFS_IOC_RESVSP:
+       case XFS_IOC_UNRESVSP:
+       case XFS_IOC_ALLOCSP64:
+       case XFS_IOC_FREESP64:
+       case XFS_IOC_RESVSP64:
+       case XFS_IOC_UNRESVSP64: {
+               xfs_flock64_t           bf;
 
-       if (di_flags & XFS_DIFLAG_IMMUTABLE)
-               flags |= FS_IMMUTABLE_FL;
-       if (di_flags & XFS_DIFLAG_APPEND)
-               flags |= FS_APPEND_FL;
-       if (di_flags & XFS_DIFLAG_SYNC)
-               flags |= FS_SYNC_FL;
-       if (di_flags & XFS_DIFLAG_NOATIME)
-               flags |= FS_NOATIME_FL;
-       if (di_flags & XFS_DIFLAG_NODUMP)
-               flags |= FS_NODUMP_FL;
-       return flags;
-}
+               if (copy_from_user(&bf, arg, sizeof(bf)))
+                       return -XFS_ERROR(EFAULT);
+               return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf);
+       }
+       case XFS_IOC_DIOINFO: {
+               struct dioattr  da;
+               xfs_buftarg_t   *target =
+                       XFS_IS_REALTIME_INODE(ip) ?
+                       mp->m_rtdev_targp : mp->m_ddev_targp;
 
-STATIC int
-xfs_ioc_fsgetxattr(
-       xfs_inode_t             *ip,
-       int                     attr,
-       void                    __user *arg)
-{
-       struct fsxattr          fa;
+               da.d_mem = da.d_miniosz = 1 << target->bt_sshift;
+               da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);
 
-       xfs_ilock(ip, XFS_ILOCK_SHARED);
-       fa.fsx_xflags = xfs_ip2xflags(ip);
-       fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
-       fa.fsx_projid = ip->i_d.di_projid;
+               if (copy_to_user(arg, &da, sizeof(da)))
+                       return -XFS_ERROR(EFAULT);
+               return 0;
+       }
 
-       if (attr) {
-               if (ip->i_afp) {
-                       if (ip->i_afp->if_flags & XFS_IFEXTENTS)
-                               fa.fsx_nextents = ip->i_afp->if_bytes /
-                                                       sizeof(xfs_bmbt_rec_t);
-                       else
-                               fa.fsx_nextents = ip->i_d.di_anextents;
-               } else
-                       fa.fsx_nextents = 0;
-       } else {
-               if (ip->i_df.if_flags & XFS_IFEXTENTS)
-                       fa.fsx_nextents = ip->i_df.if_bytes /
-                                               sizeof(xfs_bmbt_rec_t);
-               else
-                       fa.fsx_nextents = ip->i_d.di_nextents;
+       case XFS_IOC_FSBULKSTAT_SINGLE:
+       case XFS_IOC_FSBULKSTAT:
+       case XFS_IOC_FSINUMBERS:
+               return xfs_ioc_bulkstat(mp, cmd, arg);
+
+       case XFS_IOC_FSGEOMETRY_V1:
+               return xfs_ioc_fsgeometry_v1(mp, arg);
+
+       case XFS_IOC_FSGEOMETRY:
+               return xfs_ioc_fsgeometry(mp, arg);
+
+       case XFS_IOC_GETVERSION:
+               return put_user(inode->i_generation, (int __user *)arg);
+
+       case XFS_IOC_FSGETXATTR:
+               return xfs_ioc_fsgetxattr(ip, 0, arg);
+       case XFS_IOC_FSGETXATTRA:
+               return xfs_ioc_fsgetxattr(ip, 1, arg);
+       case XFS_IOC_FSSETXATTR:
+               return xfs_ioc_fssetxattr(ip, filp, arg);
+       case XFS_IOC_GETXFLAGS:
+               return xfs_ioc_getxflags(ip, arg);
+       case XFS_IOC_SETXFLAGS:
+               return xfs_ioc_setxflags(ip, filp, arg);
+
+       case XFS_IOC_FSSETDM: {
+               struct fsdmidata        dmi;
+
+               if (copy_from_user(&dmi, arg, sizeof(dmi)))
+                       return -XFS_ERROR(EFAULT);
+
+               error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
+                               dmi.fsd_dmstate);
+               return -error;
        }
-       xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
-       if (copy_to_user(arg, &fa, sizeof(fa)))
-               return -EFAULT;
-       return 0;
-}
+       case XFS_IOC_GETBMAP:
+       case XFS_IOC_GETBMAPA:
+               return xfs_ioc_getbmap(ip, ioflags, cmd, arg);
 
-STATIC int
-xfs_ioc_xattr(
-       xfs_inode_t             *ip,
-       struct file             *filp,
-       unsigned int            cmd,
-       void                    __user *arg)
-{
-       struct fsxattr          fa;
-       struct bhv_vattr        *vattr;
-       int                     error = 0;
-       int                     attr_flags;
-       unsigned int            flags;
+       case XFS_IOC_GETBMAPX:
+               return xfs_ioc_getbmapx(ip, arg);
 
-       vattr = kmalloc(sizeof(*vattr), GFP_KERNEL);
-       if (unlikely(!vattr))
-               return -ENOMEM;
+       case XFS_IOC_FD_TO_HANDLE:
+       case XFS_IOC_PATH_TO_HANDLE:
+       case XFS_IOC_PATH_TO_FSHANDLE: {
+               xfs_fsop_handlereq_t    hreq;
 
-       switch (cmd) {
-       case XFS_IOC_FSSETXATTR: {
-               if (copy_from_user(&fa, arg, sizeof(fa))) {
-                       error = -EFAULT;
-                       break;
-               }
+               if (copy_from_user(&hreq, arg, sizeof(hreq)))
+                       return -XFS_ERROR(EFAULT);
+               return xfs_find_handle(cmd, &hreq);
+       }
+       case XFS_IOC_OPEN_BY_HANDLE: {
+               xfs_fsop_handlereq_t    hreq;
 
-               attr_flags = 0;
-               if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
-                       attr_flags |= ATTR_NONBLOCK;
+               if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
+                       return -XFS_ERROR(EFAULT);
+               return xfs_open_by_handle(mp, &hreq, filp, inode);
+       }
+       case XFS_IOC_FSSETDM_BY_HANDLE:
+               return xfs_fssetdm_by_handle(mp, arg, inode);
 
-               vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | XFS_AT_PROJID;
-               vattr->va_xflags  = fa.fsx_xflags;
-               vattr->va_extsize = fa.fsx_extsize;
-               vattr->va_projid  = fa.fsx_projid;
+       case XFS_IOC_READLINK_BY_HANDLE: {
+               xfs_fsop_handlereq_t    hreq;
 
-               error = xfs_setattr(ip, vattr, attr_flags, NULL);
-               if (likely(!error))
-                       vn_revalidate(XFS_ITOV(ip));    /* update flags */
-               error = -error;
-               break;
+               if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
+                       return -XFS_ERROR(EFAULT);
+               return xfs_readlink_by_handle(mp, &hreq, inode);
        }
+       case XFS_IOC_ATTRLIST_BY_HANDLE:
+               return xfs_attrlist_by_handle(mp, arg, inode);
 
-       case XFS_IOC_GETXFLAGS: {
-               flags = xfs_di2lxflags(ip->i_d.di_flags);
-               if (copy_to_user(arg, &flags, sizeof(flags)))
-                       error = -EFAULT;
-               break;
+       case XFS_IOC_ATTRMULTI_BY_HANDLE:
+               return xfs_attrmulti_by_handle(mp, arg, filp, inode);
+
+       case XFS_IOC_SWAPEXT: {
+               struct xfs_swapext      sxp;
+
+               if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
+                       return -XFS_ERROR(EFAULT);
+               error = xfs_swapext(&sxp);
+               return -error;
        }
 
-       case XFS_IOC_SETXFLAGS: {
-               if (copy_from_user(&flags, arg, sizeof(flags))) {
-                       error = -EFAULT;
-                       break;
-               }
+       case XFS_IOC_FSCOUNTS: {
+               xfs_fsop_counts_t out;
 
-               if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
-                             FS_NOATIME_FL | FS_NODUMP_FL | \
-                             FS_SYNC_FL)) {
-                       error = -EOPNOTSUPP;
-                       break;
-               }
+               error = xfs_fs_counts(mp, &out);
+               if (error)
+                       return -error;
 
-               attr_flags = 0;
-               if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
-                       attr_flags |= ATTR_NONBLOCK;
+               if (copy_to_user(arg, &out, sizeof(out)))
+                       return -XFS_ERROR(EFAULT);
+               return 0;
+       }
 
-               vattr->va_mask = XFS_AT_XFLAGS;
-               vattr->va_xflags = xfs_merge_ioc_xflags(flags,
-                                                       xfs_ip2xflags(ip));
+       case XFS_IOC_SET_RESBLKS: {
+               xfs_fsop_resblks_t inout;
+               __uint64_t         in;
 
-               error = xfs_setattr(ip, vattr, attr_flags, NULL);
-               if (likely(!error))
-                       vn_revalidate(XFS_ITOV(ip));    /* update flags */
-               error = -error;
-               break;
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               if (copy_from_user(&inout, arg, sizeof(inout)))
+                       return -XFS_ERROR(EFAULT);
+
+               /* input parameter is passed in resblks field of structure */
+               in = inout.resblks;
+               error = xfs_reserve_blocks(mp, &in, &inout);
+               if (error)
+                       return -error;
+
+               if (copy_to_user(arg, &inout, sizeof(inout)))
+                       return -XFS_ERROR(EFAULT);
+               return 0;
        }
 
-       default:
-               error = -ENOTTY;
-               break;
+       case XFS_IOC_GET_RESBLKS: {
+               xfs_fsop_resblks_t out;
+
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               error = xfs_reserve_blocks(mp, NULL, &out);
+               if (error)
+                       return -error;
+
+               if (copy_to_user(arg, &out, sizeof(out)))
+                       return -XFS_ERROR(EFAULT);
+
+               return 0;
        }
 
-       kfree(vattr);
-       return error;
-}
+       case XFS_IOC_FSGROWFSDATA: {
+               xfs_growfs_data_t in;
 
-STATIC int
-xfs_ioc_getbmap(
-       struct xfs_inode        *ip,
-       int                     ioflags,
-       unsigned int            cmd,
-       void                    __user *arg)
-{
-       struct getbmap          bm;
-       int                     iflags;
-       int                     error;
+               if (copy_from_user(&in, arg, sizeof(in)))
+                       return -XFS_ERROR(EFAULT);
 
-       if (copy_from_user(&bm, arg, sizeof(bm)))
-               return -XFS_ERROR(EFAULT);
+               error = xfs_growfs_data(mp, &in);
+               return -error;
+       }
 
-       if (bm.bmv_count < 2)
-               return -XFS_ERROR(EINVAL);
+       case XFS_IOC_FSGROWFSLOG: {
+               xfs_growfs_log_t in;
 
-       iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
-       if (ioflags & IO_INVIS)
-               iflags |= BMV_IF_NO_DMAPI_READ;
+               if (copy_from_user(&in, arg, sizeof(in)))
+                       return -XFS_ERROR(EFAULT);
 
-       error = xfs_getbmap(ip, &bm, (struct getbmap __user *)arg+1, iflags);
-       if (error)
+               error = xfs_growfs_log(mp, &in);
                return -error;
+       }
 
-       if (copy_to_user(arg, &bm, sizeof(bm)))
-               return -XFS_ERROR(EFAULT);
-       return 0;
-}
+       case XFS_IOC_FSGROWFSRT: {
+               xfs_growfs_rt_t in;
 
-STATIC int
-xfs_ioc_getbmapx(
-       struct xfs_inode        *ip,
-       void                    __user *arg)
-{
-       struct getbmapx         bmx;
-       struct getbmap          bm;
-       int                     iflags;
-       int                     error;
+               if (copy_from_user(&in, arg, sizeof(in)))
+                       return -XFS_ERROR(EFAULT);
 
-       if (copy_from_user(&bmx, arg, sizeof(bmx)))
-               return -XFS_ERROR(EFAULT);
+               error = xfs_growfs_rt(mp, &in);
+               return -error;
+       }
 
-       if (bmx.bmv_count < 2)
-               return -XFS_ERROR(EINVAL);
+       case XFS_IOC_GOINGDOWN: {
+               __uint32_t in;
 
-       /*
-        * Map input getbmapx structure to a getbmap
-        * structure for xfs_getbmap.
-        */
-       GETBMAP_CONVERT(bmx, bm);
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
 
-       iflags = bmx.bmv_iflags;
+               if (get_user(in, (__uint32_t __user *)arg))
+                       return -XFS_ERROR(EFAULT);
 
-       if (iflags & (~BMV_IF_VALID))
-               return -XFS_ERROR(EINVAL);
+               error = xfs_fs_goingdown(mp, in);
+               return -error;
+       }
 
-       iflags |= BMV_IF_EXTENDED;
+       case XFS_IOC_ERROR_INJECTION: {
+               xfs_error_injection_t in;
 
-       error = xfs_getbmap(ip, &bm, (struct getbmapx __user *)arg+1, iflags);
-       if (error)
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               if (copy_from_user(&in, arg, sizeof(in)))
+                       return -XFS_ERROR(EFAULT);
+
+               error = xfs_errortag_add(in.errtag, mp);
                return -error;
+       }
 
-       GETBMAP_CONVERT(bm, bmx);
+       case XFS_IOC_ERROR_CLEARALL:
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
 
-       if (copy_to_user(arg, &bmx, sizeof(bmx)))
-               return -XFS_ERROR(EFAULT);
+               error = xfs_errortag_clearall(mp, 1);
+               return -error;
 
-       return 0;
+       default:
+               return -ENOTTY;
+       }
 }