Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[safe/jmp/linux-2.6] / fs / xfs / xfs_inode.c
index cd51858..3ca5d43 100644 (file)
@@ -47,7 +47,6 @@
 #include "xfs_utils.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_quota.h"
-#include "xfs_mac.h"
 #include "xfs_acl.h"
 
 
@@ -443,6 +442,7 @@ xfs_iformat(
                        return XFS_ERROR(EFSCORRUPTED);
                }
                ip->i_d.di_size = 0;
+               ip->i_size = 0;
                ip->i_df.if_u2.if_rdev = INT_GET(dip->di_u.di_dev, ARCH_CONVERT);
                break;
 
@@ -981,6 +981,7 @@ xfs_iread(
        }
 
        ip->i_delayed_blks = 0;
+       ip->i_size = ip->i_d.di_size;
 
        /*
         * Mark the buffer containing the inode as something to keep
@@ -1171,6 +1172,7 @@ xfs_ialloc(
        }
 
        ip->i_d.di_size = 0;
+       ip->i_size = 0;
        ip->i_d.di_nextents = 0;
        ASSERT(ip->i_d.di_nblocks == 0);
        xfs_ichgtime(ip, XFS_ICHGTIME_CHG|XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD);
@@ -1341,7 +1343,7 @@ xfs_file_last_byte(
        } else {
                last_block = 0;
        }
-       size_last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)ip->i_d.di_size);
+       size_last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)ip->i_size);
        last_block = XFS_FILEOFF_MAX(last_block, size_last_block);
 
        last_byte = XFS_FSB_TO_B(mp, last_block);
@@ -1422,7 +1424,7 @@ xfs_itrunc_trace(
  * must be called again with all the same restrictions as the initial
  * call.
  */
-void
+int
 xfs_itruncate_start(
        xfs_inode_t     *ip,
        uint            flags,
@@ -1432,9 +1434,10 @@ xfs_itruncate_start(
        xfs_off_t       toss_start;
        xfs_mount_t     *mp;
        bhv_vnode_t     *vp;
+       int             error = 0;
 
        ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
-       ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
+       ASSERT((new_size == 0) || (new_size <= ip->i_size));
        ASSERT((flags == XFS_ITRUNC_DEFINITE) ||
               (flags == XFS_ITRUNC_MAYBE));
 
@@ -1469,7 +1472,7 @@ xfs_itruncate_start(
                 * file size, so there is no way that the data extended
                 * out there.
                 */
-               return;
+               return 0;
        }
        last_byte = xfs_file_last_byte(ip);
        xfs_itrunc_trace(XFS_ITRUNC_START, ip, flags, new_size, toss_start,
@@ -1478,7 +1481,7 @@ xfs_itruncate_start(
                if (flags & XFS_ITRUNC_DEFINITE) {
                        bhv_vop_toss_pages(vp, toss_start, -1, FI_REMAPF_LOCKED);
                } else {
-                       bhv_vop_flushinval_pages(vp, toss_start, -1, FI_REMAPF_LOCKED);
+                       error = bhv_vop_flushinval_pages(vp, toss_start, -1, FI_REMAPF_LOCKED);
                }
        }
 
@@ -1487,6 +1490,7 @@ xfs_itruncate_start(
                ASSERT(VN_CACHED(vp) == 0);
        }
 #endif
+       return error;
 }
 
 /*
@@ -1557,7 +1561,7 @@ xfs_itruncate_finish(
 
        ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
        ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE) != 0);
-       ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
+       ASSERT((new_size == 0) || (new_size <= ip->i_size));
        ASSERT(*tp != NULL);
        ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
        ASSERT(ip->i_transp == *tp);
@@ -1631,8 +1635,20 @@ xfs_itruncate_finish(
         */
        if (fork == XFS_DATA_FORK) {
                if (ip->i_d.di_nextents > 0) {
-                       ip->i_d.di_size = new_size;
-                       xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
+                       /*
+                        * If we are not changing the file size then do
+                        * not update the on-disk file size - we may be
+                        * called from xfs_inactive_free_eofblocks().  If we
+                        * update the on-disk file size and then the system
+                        * crashes before the contents of the file are
+                        * flushed to disk then the files may be full of
+                        * holes (ie NULL files bug).
+                        */
+                       if (ip->i_size != new_size) {
+                               ip->i_d.di_size = new_size;
+                               ip->i_size = new_size;
+                               xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
+                       }
                }
        } else if (sync) {
                ASSERT(!(mp->m_flags & XFS_MOUNT_WSYNC));
@@ -1699,8 +1715,7 @@ xfs_itruncate_finish(
                 * Duplicate the transaction that has the permanent
                 * reservation and commit the old transaction.
                 */
-               error = xfs_bmap_finish(tp, &free_list, first_block,
-                                       &committed);
+               error = xfs_bmap_finish(tp, &free_list, &committed);
                ntp = *tp;
                if (error) {
                        /*
@@ -1748,7 +1763,7 @@ xfs_itruncate_finish(
                        xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
                }
                ntp = xfs_trans_dup(ntp);
-               (void) xfs_trans_commit(*tp, 0, NULL);
+               (void) xfs_trans_commit(*tp, 0);
                *tp = ntp;
                error = xfs_trans_reserve(ntp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
                                          XFS_TRANS_PERM_LOG_RES,
@@ -1769,7 +1784,19 @@ xfs_itruncate_finish(
         */
        if (fork == XFS_DATA_FORK) {
                xfs_isize_check(mp, ip, new_size);
-               ip->i_d.di_size = new_size;
+               /*
+                * If we are not changing the file size then do
+                * not update the on-disk file size - we may be
+                * called from xfs_inactive_free_eofblocks().  If we
+                * update the on-disk file size and then the system
+                * crashes before the contents of the file are
+                * flushed to disk then the files may be full of
+                * holes (ie NULL files bug).
+                */
+               if (ip->i_size != new_size) {
+                       ip->i_d.di_size = new_size;
+                       ip->i_size = new_size;
+               }
        }
        xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
        ASSERT((new_size != 0) ||
@@ -1802,7 +1829,7 @@ xfs_igrow_start(
 
        ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
        ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
-       ASSERT(new_size > ip->i_d.di_size);
+       ASSERT(new_size > ip->i_size);
 
        /*
         * Zero any pages that may have been created by
@@ -1810,7 +1837,7 @@ xfs_igrow_start(
         * and any blocks between the old and new file sizes.
         */
        error = xfs_zero_eof(XFS_ITOV(ip), &ip->i_iocore, new_size,
-                            ip->i_d.di_size, new_size);
+                            ip->i_size);
        return error;
 }
 
@@ -1834,13 +1861,14 @@ xfs_igrow_finish(
        ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
        ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
        ASSERT(ip->i_transp == tp);
-       ASSERT(new_size > ip->i_d.di_size);
+       ASSERT(new_size > ip->i_size);
 
        /*
         * Update the file size.  Update the inode change timestamp
         * if change_flag set.
         */
        ip->i_d.di_size = new_size;
+       ip->i_size = new_size;
        if (change_flag)
                xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
@@ -2323,7 +2351,7 @@ xfs_ifree(
        ASSERT(ip->i_d.di_nlink == 0);
        ASSERT(ip->i_d.di_nextents == 0);
        ASSERT(ip->i_d.di_anextents == 0);
-       ASSERT((ip->i_d.di_size == 0) ||
+       ASSERT((ip->i_d.di_size == 0 && ip->i_size == 0) ||
               ((ip->i_d.di_mode & S_IFMT) != S_IFREG));
        ASSERT(ip->i_d.di_nblocks == 0);
 
@@ -2707,10 +2735,24 @@ xfs_idestroy(
        ktrace_free(ip->i_dir_trace);
 #endif
        if (ip->i_itemp) {
-               /* XXXdpd should be able to assert this but shutdown
-                * is leaving the AIL behind. */
-               ASSERT(((ip->i_itemp->ili_item.li_flags & XFS_LI_IN_AIL) == 0) ||
-                      XFS_FORCED_SHUTDOWN(ip->i_mount));
+               /*
+                * Only if we are shutting down the fs will we see an
+                * inode still in the AIL. If it is there, we should remove
+                * it to prevent a use-after-free from occurring.
+                */
+               xfs_mount_t     *mp = ip->i_mount;
+               xfs_log_item_t  *lip = &ip->i_itemp->ili_item;
+               int             s;
+
+               ASSERT(((lip->li_flags & XFS_LI_IN_AIL) == 0) ||
+                                      XFS_FORCED_SHUTDOWN(ip->i_mount));
+               if (lip->li_flags & XFS_LI_IN_AIL) {
+                       AIL_LOCK(mp, s);
+                       if (lip->li_flags & XFS_LI_IN_AIL)
+                               xfs_trans_delete_ail(mp, lip, s);
+                       else
+                               AIL_UNLOCK(mp, s);
+               }
                xfs_inode_item_destroy(ip);
        }
        kmem_zone_free(xfs_inode_zone, ip);