xfs: fix double unlock in xfs_swap_extents()
[safe/jmp/linux-2.6] / fs / xfs / xfs_dfrag.c
index 99daf8c..7465f9e 100644 (file)
 #include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_dir.h"
 #include "xfs_dir2.h"
 #include "xfs_dmapi.h"
 #include "xfs_mount.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_alloc_btree.h"
 #include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
 #include "xfs_dir2_sf.h"
 #include "xfs_attr_sf.h"
 #include "xfs_dinode.h"
 #include "xfs_itable.h"
 #include "xfs_dfrag.h"
 #include "xfs_error.h"
-#include "xfs_mac.h"
 #include "xfs_rw.h"
+#include "xfs_vnodeops.h"
 
 /*
  * Syssgi interface for swapext
  */
 int
 xfs_swapext(
-       xfs_swapext_t   __user *sxu)
+       xfs_swapext_t   *sxp)
 {
-       xfs_swapext_t   *sxp;
-       xfs_inode_t     *ip=NULL, *tip=NULL;
-       xfs_mount_t     *mp;
-       struct file     *fp = NULL, *tfp = NULL;
-       vnode_t         *vp, *tvp;
+       xfs_inode_t     *ip, *tip;
+       struct file     *file, *target_file;
        int             error = 0;
 
-       sxp = kmem_alloc(sizeof(xfs_swapext_t), KM_MAYFAIL);
-       if (!sxp) {
-               error = XFS_ERROR(ENOMEM);
-               goto error0;
+       /* Pull information for the target fd */
+       file = fget((int)sxp->sx_fdtarget);
+       if (!file) {
+               error = XFS_ERROR(EINVAL);
+               goto out;
        }
 
-       if (copy_from_user(sxp, sxu, sizeof(xfs_swapext_t))) {
-               error = XFS_ERROR(EFAULT);
-               goto error0;
+       if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND)) {
+               error = XFS_ERROR(EBADF);
+               goto out_put_file;
        }
 
-       /* Pull information for the target fd */
-       if (((fp = fget((int)sxp->sx_fdtarget)) == NULL) ||
-           ((vp = vn_from_inode(fp->f_dentry->d_inode)) == NULL))  {
+       target_file = fget((int)sxp->sx_fdtmp);
+       if (!target_file) {
                error = XFS_ERROR(EINVAL);
-               goto error0;
+               goto out_put_file;
        }
 
-       ip = xfs_vtoi(vp);
-       if (ip == NULL) {
+       if (!(target_file->f_mode & FMODE_WRITE) ||
+           (target_file->f_flags & O_APPEND)) {
                error = XFS_ERROR(EBADF);
-               goto error0;
+               goto out_put_target_file;
        }
 
-       if (((tfp = fget((int)sxp->sx_fdtmp)) == NULL) ||
-           ((tvp = vn_from_inode(tfp->f_dentry->d_inode)) == NULL)) {
+       if (IS_SWAPFILE(file->f_path.dentry->d_inode) ||
+           IS_SWAPFILE(target_file->f_path.dentry->d_inode)) {
                error = XFS_ERROR(EINVAL);
-               goto error0;
+               goto out_put_target_file;
        }
 
-       tip = xfs_vtoi(tvp);
-       if (tip == NULL) {
-               error = XFS_ERROR(EBADF);
-               goto error0;
-       }
+       ip = XFS_I(file->f_path.dentry->d_inode);
+       tip = XFS_I(target_file->f_path.dentry->d_inode);
 
        if (ip->i_mount != tip->i_mount) {
-               error =  XFS_ERROR(EINVAL);
-               goto error0;
+               error = XFS_ERROR(EINVAL);
+               goto out_put_target_file;
        }
 
        if (ip->i_ino == tip->i_ino) {
-               error =  XFS_ERROR(EINVAL);
-               goto error0;
+               error = XFS_ERROR(EINVAL);
+               goto out_put_target_file;
        }
 
-       mp = ip->i_mount;
-
-       if (XFS_FORCED_SHUTDOWN(mp)) {
-               error =  XFS_ERROR(EIO);
-               goto error0;
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
+               error = XFS_ERROR(EIO);
+               goto out_put_target_file;
        }
 
-       error = XFS_SWAP_EXTENTS(mp, &ip->i_iocore, &tip->i_iocore, sxp);
-
- error0:
-       if (fp != NULL)
-               fput(fp);
-       if (tfp != NULL)
-               fput(tfp);
-
-       if (sxp != NULL)
-               kmem_free(sxp, sizeof(xfs_swapext_t));
+       error = xfs_swap_extents(ip, tip, sxp);
 
+ out_put_target_file:
+       fput(target_file);
+ out_put_file:
+       fput(file);
+ out:
        return error;
 }
 
@@ -134,81 +120,65 @@ xfs_swap_extents(
        xfs_swapext_t   *sxp)
 {
        xfs_mount_t     *mp;
-       xfs_inode_t     *ips[2];
        xfs_trans_t     *tp;
        xfs_bstat_t     *sbp = &sxp->sx_stat;
-       vnode_t         *vp, *tvp;
        xfs_ifork_t     *tempifp, *ifp, *tifp;
        int             ilf_fields, tilf_fields;
-       static uint     lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL;
        int             error = 0;
        int             aforkblks = 0;
        int             taforkblks = 0;
        __uint64_t      tmp;
-       char            locked = 0;
 
        mp = ip->i_mount;
 
        tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL);
        if (!tempifp) {
                error = XFS_ERROR(ENOMEM);
-               goto error0;
+               goto out;
        }
 
        sbp = &sxp->sx_stat;
-       vp = XFS_ITOV(ip);
-       tvp = XFS_ITOV(tip);
-
-       /* Lock in i_ino order */
-       if (ip->i_ino < tip->i_ino) {
-               ips[0] = ip;
-               ips[1] = tip;
-       } else {
-               ips[0] = tip;
-               ips[1] = ip;
-       }
-
-       xfs_lock_inodes(ips, 2, 0, lock_flags);
-       locked = 1;
 
-       /* Check permissions */
-       error = xfs_iaccess(ip, S_IWUSR, NULL);
-       if (error)
-               goto error0;
-
-       error = xfs_iaccess(tip, S_IWUSR, NULL);
-       if (error)
-               goto error0;
+       /*
+        * we have to do two separate lock calls here to keep lockdep
+        * happy. If we try to get all the locks in one call, lock will
+        * report false positives when we drop the ILOCK and regain them
+        * below.
+        */
+       xfs_lock_two_inodes(ip, tip, XFS_IOLOCK_EXCL);
+       xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
 
        /* Verify that both files have the same format */
        if ((ip->i_d.di_mode & S_IFMT) != (tip->i_d.di_mode & S_IFMT)) {
                error = XFS_ERROR(EINVAL);
-               goto error0;
+               goto out_unlock;
        }
 
        /* Verify both files are either real-time or non-realtime */
-       if ((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) !=
-           (tip->i_d.di_flags & XFS_DIFLAG_REALTIME)) {
+       if (XFS_IS_REALTIME_INODE(ip) != XFS_IS_REALTIME_INODE(tip)) {
                error = XFS_ERROR(EINVAL);
-               goto error0;
+               goto out_unlock;
        }
 
        /* Should never get a local format */
        if (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL ||
            tip->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
                error = XFS_ERROR(EINVAL);
-               goto error0;
+               goto out_unlock;
        }
 
-       if (VN_CACHED(tvp) != 0) {
-               xfs_inval_cached_trace(&tip->i_iocore, 0, -1, 0, -1);
-               VOP_FLUSHINVAL_PAGES(tvp, 0, -1, FI_REMAPF_LOCKED);
+       if (VN_CACHED(VFS_I(tip)) != 0) {
+               xfs_inval_cached_trace(tip, 0, -1, 0, -1);
+               error = xfs_flushinval_pages(tip, 0, -1,
+                               FI_REMAPF_LOCKED);
+               if (error)
+                       goto out_unlock;
        }
 
        /* Verify O_DIRECT for ftmp */
-       if (VN_CACHED(tvp) != 0) {
+       if (VN_CACHED(VFS_I(tip)) != 0) {
                error = XFS_ERROR(EINVAL);
-               goto error0;
+               goto out_unlock;
        }
 
        /* Verify all data are being swapped */
@@ -216,7 +186,7 @@ xfs_swap_extents(
            sxp->sx_length != ip->i_d.di_size ||
            sxp->sx_length != tip->i_d.di_size) {
                error = XFS_ERROR(EFAULT);
-               goto error0;
+               goto out_unlock;
        }
 
        /*
@@ -226,7 +196,7 @@ xfs_swap_extents(
         */
        if ( XFS_IFORK_Q(ip) != XFS_IFORK_Q(tip) ) {
                error = XFS_ERROR(EINVAL);
-               goto error0;
+               goto out_unlock;
        }
 
        /*
@@ -241,18 +211,18 @@ xfs_swap_extents(
            (sbp->bs_mtime.tv_sec != ip->i_d.di_mtime.t_sec) ||
            (sbp->bs_mtime.tv_nsec != ip->i_d.di_mtime.t_nsec)) {
                error = XFS_ERROR(EBUSY);
-               goto error0;
+               goto out_unlock;
        }
 
        /* We need to fail if the file is memory mapped.  Once we have tossed
         * all existing pages, the page fault will have no option
         * but to go to the filesystem for pages. By making the page fault call
-        * VOP_READ (or write in the case of autogrow) they block on the iolock
+        * vop_read (or write in the case of autogrow) they block on the iolock
         * until we have switched the extents.
         */
-       if (VN_MAPPED(vp)) {
+       if (VN_MAPPED(VFS_I(ip))) {
                error = XFS_ERROR(EBUSY);
-               goto error0;
+               goto out_unlock;
        }
 
        xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -266,7 +236,7 @@ xfs_swap_extents(
         * fields change.
         */
 
-       VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF);
+       xfs_tosspages(ip, 0, -1, FI_REMAPF);
 
        tp = xfs_trans_alloc(mp, XFS_TRANS_SWAPEXT);
        if ((error = xfs_trans_reserve(tp, 0,
@@ -275,10 +245,9 @@ xfs_swap_extents(
                xfs_iunlock(ip,  XFS_IOLOCK_EXCL);
                xfs_iunlock(tip, XFS_IOLOCK_EXCL);
                xfs_trans_cancel(tp, 0);
-               locked = 0;
-               goto error0;
+               goto out;
        }
-       xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL);
+       xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
 
        /*
         * Count the number of extended attribute blocks
@@ -286,19 +255,15 @@ xfs_swap_extents(
        if ( ((XFS_IFORK_Q(ip) != 0) && (ip->i_d.di_anextents > 0)) &&
             (ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
                error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &aforkblks);
-               if (error) {
-                       xfs_trans_cancel(tp, 0);
-                       goto error0;
-               }
+               if (error)
+                       goto out_trans_cancel;
        }
        if ( ((XFS_IFORK_Q(tip) != 0) && (tip->i_d.di_anextents > 0)) &&
             (tip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
                error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK,
                        &taforkblks);
-               if (error) {
-                       xfs_trans_cancel(tp, 0);
-                       goto error0;
-               }
+               if (error)
+                       goto out_trans_cancel;
        }
 
        /*
@@ -363,16 +328,12 @@ xfs_swap_extents(
                break;
        }
 
-       /*
-        * Increment vnode ref counts since xfs_trans_commit &
-        * xfs_trans_cancel will both unlock the inodes and
-        * decrement the associated ref counts.
-        */
-       VN_HOLD(vp);
-       VN_HOLD(tvp);
 
-       xfs_trans_ijoin(tp, ip, lock_flags);
-       xfs_trans_ijoin(tp, tip, lock_flags);
+       IHOLD(ip);
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+
+       IHOLD(tip);
+       xfs_trans_ijoin(tp, tip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
 
        xfs_trans_log_inode(tp, ip,  ilf_fields);
        xfs_trans_log_inode(tp, tip, tilf_fields);
@@ -381,19 +342,21 @@ xfs_swap_extents(
         * If this is a synchronous mount, make sure that the
         * transaction goes to disk before returning to the user.
         */
-       if (mp->m_flags & XFS_MOUNT_WSYNC) {
+       if (mp->m_flags & XFS_MOUNT_WSYNC)
                xfs_trans_set_sync(tp);
-       }
 
-       error = xfs_trans_commit(tp, XFS_TRANS_SWAPEXT, NULL);
-       locked = 0;
+       error = xfs_trans_commit(tp, XFS_TRANS_SWAPEXT);
 
- error0:
-       if (locked) {
-               xfs_iunlock(ip,  lock_flags);
-               xfs_iunlock(tip, lock_flags);
-       }
-       if (tempifp != NULL)
-               kmem_free(tempifp, sizeof(xfs_ifork_t));
+out:
+       kmem_free(tempifp);
        return error;
+
+out_unlock:
+       xfs_iunlock(ip,  XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+       xfs_iunlock(tip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+       goto out;
+
+out_trans_cancel:
+       xfs_trans_cancel(tp, 0);
+       goto out_unlock;
 }