]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/xfs/xfs_ioctl.c
Merge remote-tracking branch 'hid/for-next'
[karo-tx-linux.git] / fs / xfs / xfs_ioctl.c
index 668e8f4ccf5e7201e8e6a360cd26668484f6d515..4d613401a5e056a08dd2f8b21b77833bfe14cbc6 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_ioctl.h"
+#include "xfs_alloc.h"
 #include "xfs_rtalloc.h"
 #include "xfs_itable.h"
 #include "xfs_error.h"
 #include "xfs_attr.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
-#include "xfs_buf_item.h"
 #include "xfs_fsops.h"
 #include "xfs_discard.h"
 #include "xfs_quota.h"
-#include "xfs_inode_item.h"
 #include "xfs_export.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
 #include "xfs_symlink.h"
+#include "xfs_dinode.h"
+#include "xfs_trans.h"
 
 #include <linux/capability.h>
 #include <linux/dcache.h>
@@ -641,7 +640,11 @@ xfs_ioc_space(
        unsigned int            cmd,
        xfs_flock64_t           *bf)
 {
-       int                     attr_flags = 0;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       struct iattr            iattr;
+       bool                    setprealloc = false;
+       bool                    clrprealloc = false;
        int                     error;
 
        /*
@@ -661,19 +664,128 @@ xfs_ioc_space(
        if (!S_ISREG(inode->i_mode))
                return -XFS_ERROR(EINVAL);
 
-       if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
-               attr_flags |= XFS_ATTR_NONBLOCK;
+       error = mnt_want_write_file(filp);
+       if (error)
+               return error;
 
-       if (filp->f_flags & O_DSYNC)
-               attr_flags |= XFS_ATTR_SYNC;
+       xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+       switch (bf->l_whence) {
+       case 0: /*SEEK_SET*/
+               break;
+       case 1: /*SEEK_CUR*/
+               bf->l_start += filp->f_pos;
+               break;
+       case 2: /*SEEK_END*/
+               bf->l_start += XFS_ISIZE(ip);
+               break;
+       default:
+               error = XFS_ERROR(EINVAL);
+               goto out_unlock;
+       }
 
-       if (ioflags & IO_INVIS)
-               attr_flags |= XFS_ATTR_DMI;
+       /*
+        * 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) {
+                       error = XFS_ERROR(EINVAL);
+                       goto out_unlock;
+               }
+               break;
+       default:
+               bf->l_len = 0;
+               break;
+       }
+
+       if (bf->l_start < 0 ||
+           bf->l_start > mp->m_super->s_maxbytes ||
+           bf->l_start + bf->l_len < 0 ||
+           bf->l_start + bf->l_len >= mp->m_super->s_maxbytes) {
+               error = XFS_ERROR(EINVAL);
+               goto out_unlock;
+       }
+
+       switch (cmd) {
+       case XFS_IOC_ZERO_RANGE:
+               error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
+               if (!error)
+                       setprealloc = true;
+               break;
+       case XFS_IOC_RESVSP:
+       case XFS_IOC_RESVSP64:
+               error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len,
+                                               XFS_BMAPI_PREALLOC);
+               if (!error)
+                       setprealloc = true;
+               break;
+       case XFS_IOC_UNRESVSP:
+       case XFS_IOC_UNRESVSP64:
+               error = xfs_free_file_space(ip, bf->l_start, bf->l_len);
+               break;
+       case XFS_IOC_ALLOCSP:
+       case XFS_IOC_ALLOCSP64:
+       case XFS_IOC_FREESP:
+       case XFS_IOC_FREESP64:
+               if (bf->l_start > XFS_ISIZE(ip)) {
+                       error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
+                                       bf->l_start - XFS_ISIZE(ip), 0);
+                       if (error)
+                               goto out_unlock;
+               }
+
+               iattr.ia_valid = ATTR_SIZE;
+               iattr.ia_size = bf->l_start;
+
+               error = xfs_setattr_size(ip, &iattr);
+               if (!error)
+                       clrprealloc = true;
+               break;
+       default:
+               ASSERT(0);
+               error = XFS_ERROR(EINVAL);
+       }
 
-       error = mnt_want_write_file(filp);
        if (error)
-               return error;
-       error = xfs_change_file_space(ip, cmd, bf, filp->f_pos, attr_flags);
+               goto out_unlock;
+
+       tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID);
+       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_writeid, 0, 0);
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               goto out_unlock;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+
+       if (!(ioflags & IO_INVIS)) {
+               ip->i_d.di_mode &= ~S_ISUID;
+               if (ip->i_d.di_mode & S_IXGRP)
+                       ip->i_d.di_mode &= ~S_ISGID;
+               xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+       }
+
+       if (setprealloc)
+               ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
+       else if (clrprealloc)
+               ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
+
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+       if (filp->f_flags & O_DSYNC)
+               xfs_trans_set_sync(tp);
+       error = xfs_trans_commit(tp, 0);
+
+out_unlock:
+       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        mnt_drop_write_file(filp);
        return -error;
 }