Merge tag 'mfd-for-linus-3.6-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / fs / xfs / xfs_vnodeops.c
index b6a82d8..2a5c637 100644 (file)
@@ -145,11 +145,6 @@ xfs_readlink(
        return error;
 }
 
-/*
- * Flags for xfs_free_eofblocks
- */
-#define XFS_FREE_EOF_TRYLOCK   (1<<0)
-
 /*
  * This is called by xfs_inactive to free any blocks beyond eof
  * when the link count isn't zero and by xfs_dm_punch_hole() when
@@ -159,7 +154,7 @@ STATIC int
 xfs_free_eofblocks(
        xfs_mount_t     *mp,
        xfs_inode_t     *ip,
-       int             flags)
+       bool            need_iolock)
 {
        xfs_trans_t     *tp;
        int             error;
@@ -174,7 +169,7 @@ xfs_free_eofblocks(
         * of the file.  If not, then there is nothing to do.
         */
        end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_ISIZE(ip));
-       last_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+       last_fsb = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
        if (last_fsb <= end_fsb)
                return 0;
        map_len = last_fsb - end_fsb;
@@ -201,13 +196,11 @@ xfs_free_eofblocks(
                 */
                tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
 
-               if (flags & XFS_FREE_EOF_TRYLOCK) {
+               if (need_iolock) {
                        if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {
                                xfs_trans_cancel(tp, 0);
                                return 0;
                        }
-               } else {
-                       xfs_ilock(ip, XFS_IOLOCK_EXCL);
                }
 
                error = xfs_trans_reserve(tp, 0,
@@ -217,7 +210,8 @@ xfs_free_eofblocks(
                if (error) {
                        ASSERT(XFS_FORCED_SHUTDOWN(mp));
                        xfs_trans_cancel(tp, 0);
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+                       if (need_iolock)
+                               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
                        return error;
                }
 
@@ -244,7 +238,10 @@ xfs_free_eofblocks(
                        error = xfs_trans_commit(tp,
                                                XFS_TRANS_RELEASE_LOG_RES);
                }
-               xfs_iunlock(ip, XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL);
+
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               if (need_iolock)
+                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        }
        return error;
 }
@@ -282,23 +279,15 @@ xfs_inactive_symlink_rmt(
         * free them all in one bunmapi call.
         */
        ASSERT(ip->i_d.di_nextents > 0 && ip->i_d.di_nextents <= 2);
-       if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
-                       XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT))) {
-               ASSERT(XFS_FORCED_SHUTDOWN(mp));
-               xfs_trans_cancel(tp, 0);
-               *tpp = NULL;
-               return error;
-       }
+
        /*
         * Lock the inode, fix the size, and join it to the transaction.
         * Hold it so in the normal path, we still have it locked for
         * the second transaction.  In the error paths we need it
         * held so the cancel won't rele it, see below.
         */
-       xfs_ilock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
        size = (int)ip->i_d.di_size;
        ip->i_d.di_size = 0;
-       xfs_trans_ijoin(tp, ip, 0);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
        /*
         * Find the block(s) so we can inval and unmap them.
@@ -385,114 +374,14 @@ xfs_inactive_symlink_rmt(
                ASSERT(XFS_FORCED_SHUTDOWN(mp));
                goto error0;
        }
-       /*
-        * Return with the inode locked but not joined to the transaction.
-        */
+
+       xfs_trans_ijoin(tp, ip, 0);
        *tpp = tp;
        return 0;
 
  error1:
        xfs_bmap_cancel(&free_list);
  error0:
-       /*
-        * Have to come here with the inode locked and either
-        * (held and in the transaction) or (not in the transaction).
-        * If the inode isn't held then cancel would iput it, but
-        * that's wrong since this is inactive and the vnode ref
-        * count is 0 already.
-        * Cancel won't do anything to the inode if held, but it still
-        * needs to be locked until the cancel is done, if it was
-        * joined to the transaction.
-        */
-       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
-       *tpp = NULL;
-       return error;
-
-}
-
-STATIC int
-xfs_inactive_symlink_local(
-       xfs_inode_t     *ip,
-       xfs_trans_t     **tpp)
-{
-       int             error;
-
-       ASSERT(ip->i_d.di_size <= XFS_IFORK_DSIZE(ip));
-       /*
-        * We're freeing a symlink which fit into
-        * the inode.  Just free the memory used
-        * to hold the old symlink.
-        */
-       error = xfs_trans_reserve(*tpp, 0,
-                                 XFS_ITRUNCATE_LOG_RES(ip->i_mount),
-                                 0, XFS_TRANS_PERM_LOG_RES,
-                                 XFS_ITRUNCATE_LOG_COUNT);
-
-       if (error) {
-               xfs_trans_cancel(*tpp, 0);
-               *tpp = NULL;
-               return error;
-       }
-       xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
-
-       /*
-        * Zero length symlinks _can_ exist.
-        */
-       if (ip->i_df.if_bytes > 0) {
-               xfs_idata_realloc(ip,
-                                 -(ip->i_df.if_bytes),
-                                 XFS_DATA_FORK);
-               ASSERT(ip->i_df.if_bytes == 0);
-       }
-       return 0;
-}
-
-STATIC int
-xfs_inactive_attrs(
-       xfs_inode_t     *ip,
-       xfs_trans_t     **tpp)
-{
-       xfs_trans_t     *tp;
-       int             error;
-       xfs_mount_t     *mp;
-
-       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
-       tp = *tpp;
-       mp = ip->i_mount;
-       ASSERT(ip->i_d.di_forkoff != 0);
-       error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
-       if (error)
-               goto error_unlock;
-
-       error = xfs_attr_inactive(ip);
-       if (error)
-               goto error_unlock;
-
-       tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
-       error = xfs_trans_reserve(tp, 0,
-                                 XFS_IFREE_LOG_RES(mp),
-                                 0, XFS_TRANS_PERM_LOG_RES,
-                                 XFS_INACTIVE_LOG_COUNT);
-       if (error)
-               goto error_cancel;
-
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       xfs_trans_ijoin(tp, ip, 0);
-       xfs_idestroy_fork(ip, XFS_ATTR_FORK);
-
-       ASSERT(ip->i_d.di_anextents == 0);
-
-       *tpp = tp;
-       return 0;
-
-error_cancel:
-       ASSERT(XFS_FORCED_SHUTDOWN(mp));
-       xfs_trans_cancel(tp, 0);
-error_unlock:
-       *tpp = NULL;
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        return error;
 }
 
@@ -574,8 +463,7 @@ xfs_release(
                if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE))
                        return 0;
 
-               error = xfs_free_eofblocks(mp, ip,
-                                          XFS_FREE_EOF_TRYLOCK);
+               error = xfs_free_eofblocks(mp, ip, true);
                if (error)
                        return error;
 
@@ -604,7 +492,7 @@ xfs_inactive(
        xfs_trans_t     *tp;
        xfs_mount_t     *mp;
        int             error;
-       int             truncate;
+       int             truncate = 0;
 
        /*
         * If the inode is already free, then there can be nothing
@@ -616,17 +504,6 @@ xfs_inactive(
                return VN_INACTIVE_CACHE;
        }
 
-       /*
-        * Only do a truncate if it's a regular file with
-        * some actual space in it.  It's OK to look at the
-        * inode's fields without the lock because we're the
-        * only one with a reference to the inode.
-        */
-       truncate = ((ip->i_d.di_nlink == 0) &&
-           ((ip->i_d.di_size != 0) || XFS_ISIZE(ip) != 0 ||
-            (ip->i_d.di_nextents > 0) || (ip->i_delayed_blks > 0)) &&
-           S_ISREG(ip->i_d.di_mode));
-
        mp = ip->i_mount;
 
        error = 0;
@@ -643,99 +520,100 @@ xfs_inactive(
                    (!(ip->i_d.di_flags &
                                (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) ||
                     ip->i_delayed_blks != 0))) {
-                       error = xfs_free_eofblocks(mp, ip, 0);
+                       error = xfs_free_eofblocks(mp, ip, false);
                        if (error)
                                return VN_INACTIVE_CACHE;
                }
                goto out;
        }
 
-       ASSERT(ip->i_d.di_nlink == 0);
+       if (S_ISREG(ip->i_d.di_mode) &&
+           (ip->i_d.di_size != 0 || XFS_ISIZE(ip) != 0 ||
+            ip->i_d.di_nextents > 0 || ip->i_delayed_blks > 0))
+               truncate = 1;
 
        error = xfs_qm_dqattach(ip, 0);
        if (error)
                return VN_INACTIVE_CACHE;
 
        tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
-       if (truncate) {
-               xfs_ilock(ip, XFS_IOLOCK_EXCL);
-
-               error = xfs_trans_reserve(tp, 0,
-                                         XFS_ITRUNCATE_LOG_RES(mp),
-                                         0, XFS_TRANS_PERM_LOG_RES,
-                                         XFS_ITRUNCATE_LOG_COUNT);
-               if (error) {
-                       /* Don't call itruncate_cleanup */
-                       ASSERT(XFS_FORCED_SHUTDOWN(mp));
-                       xfs_trans_cancel(tp, 0);
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-                       return VN_INACTIVE_CACHE;
-               }
+       error = xfs_trans_reserve(tp, 0,
+                       (truncate || S_ISLNK(ip->i_d.di_mode)) ?
+                               XFS_ITRUNCATE_LOG_RES(mp) :
+                               XFS_IFREE_LOG_RES(mp),
+                       0,
+                       XFS_TRANS_PERM_LOG_RES,
+                       XFS_ITRUNCATE_LOG_COUNT);
+       if (error) {
+               ASSERT(XFS_FORCED_SHUTDOWN(mp));
+               xfs_trans_cancel(tp, 0);
+               return VN_INACTIVE_CACHE;
+       }
 
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               xfs_trans_ijoin(tp, ip, 0);
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
 
+       if (S_ISLNK(ip->i_d.di_mode)) {
+               /*
+                * Zero length symlinks _can_ exist.
+                */
+               if (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) {
+                       error = xfs_inactive_symlink_rmt(ip, &tp);
+                       if (error)
+                               goto out_cancel;
+               } else if (ip->i_df.if_bytes > 0) {
+                       xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
+                                         XFS_DATA_FORK);
+                       ASSERT(ip->i_df.if_bytes == 0);
+               }
+       } else if (truncate) {
                ip->i_d.di_size = 0;
                xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
                error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0);
-               if (error) {
-                       xfs_trans_cancel(tp,
-                               XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
-                       return VN_INACTIVE_CACHE;
-               }
+               if (error)
+                       goto out_cancel;
 
                ASSERT(ip->i_d.di_nextents == 0);
-       } else if (S_ISLNK(ip->i_d.di_mode)) {
+       }
 
-               /*
-                * If we get an error while cleaning up a
-                * symlink we bail out.
-                */
-               error = (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) ?
-                       xfs_inactive_symlink_rmt(ip, &tp) :
-                       xfs_inactive_symlink_local(ip, &tp);
+       /*
+        * If there are attributes associated with the file then blow them away
+        * now.  The code calls a routine that recursively deconstructs the
+        * attribute fork.  We need to just commit the current transaction
+        * because we can't use it for xfs_attr_inactive().
+        */
+       if (ip->i_d.di_anextents > 0) {
+               ASSERT(ip->i_d.di_forkoff != 0);
 
-               if (error) {
-                       ASSERT(tp == NULL);
-                       return VN_INACTIVE_CACHE;
-               }
+               error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+               if (error)
+                       goto out_unlock;
 
-               xfs_trans_ijoin(tp, ip, 0);
-       } else {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+               error = xfs_attr_inactive(ip);
+               if (error)
+                       goto out;
+
+               tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
                error = xfs_trans_reserve(tp, 0,
                                          XFS_IFREE_LOG_RES(mp),
                                          0, XFS_TRANS_PERM_LOG_RES,
                                          XFS_INACTIVE_LOG_COUNT);
                if (error) {
-                       ASSERT(XFS_FORCED_SHUTDOWN(mp));
                        xfs_trans_cancel(tp, 0);
-                       return VN_INACTIVE_CACHE;
+                       goto out;
                }
 
-               xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
                xfs_trans_ijoin(tp, ip, 0);
        }
 
-       /*
-        * If there are attributes associated with the file
-        * then blow them away now.  The code calls a routine
-        * that recursively deconstructs the attribute fork.
-        * We need to just commit the current transaction
-        * because we can't use it for xfs_attr_inactive().
-        */
-       if (ip->i_d.di_anextents > 0) {
-               error = xfs_inactive_attrs(ip, &tp);
-               /*
-                * If we got an error, the transaction is already
-                * cancelled, and the inode is unlocked. Just get out.
-                */
-                if (error)
-                        return VN_INACTIVE_CACHE;
-       } else if (ip->i_afp) {
+       if (ip->i_afp)
                xfs_idestroy_fork(ip, XFS_ATTR_FORK);
-       }
+
+       ASSERT(ip->i_d.di_anextents == 0);
 
        /*
         * Free the inode.
@@ -779,10 +657,13 @@ xfs_inactive(
         * Release the dquots held by inode, if any.
         */
        xfs_qm_dqdetach(ip);
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
-
- out:
+out_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
        return VN_INACTIVE_CACHE;
+out_cancel:
+       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+       goto out_unlock;
 }
 
 /*
@@ -2262,10 +2143,10 @@ xfs_change_file_space(
 
        llen = bf->l_len > 0 ? bf->l_len - 1 : bf->l_len;
 
-       if (   (bf->l_start < 0)
-           || (bf->l_start > XFS_MAXIOFFSET(mp))
-           || (bf->l_start + llen < 0)
-           || (bf->l_start + llen > XFS_MAXIOFFSET(mp)))
+       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)
                return XFS_ERROR(EINVAL);
 
        bf->l_whence = 0;