[XFS] Really fix use after free in xfs_iunpin.
[pandora-kernel.git] / fs / xfs / xfs_inode.c
index 1f8ecff..c27d7d4 100644 (file)
@@ -854,7 +854,8 @@ xfs_iread(
        xfs_trans_t     *tp,
        xfs_ino_t       ino,
        xfs_inode_t     **ipp,
-       xfs_daddr_t     bno)
+       xfs_daddr_t     bno,
+       uint            imap_flags)
 {
        xfs_buf_t       *bp;
        xfs_dinode_t    *dip;
@@ -866,6 +867,7 @@ xfs_iread(
        ip = kmem_zone_zalloc(xfs_inode_zone, KM_SLEEP);
        ip->i_ino = ino;
        ip->i_mount = mp;
+       spin_lock_init(&ip->i_flags_lock);
 
        /*
         * Get pointer's to the on-disk inode and the buffer containing it.
@@ -874,7 +876,7 @@ xfs_iread(
         * return NULL as well.  Set i_blkno to 0 so that xfs_itobp() will
         * know that this is a new incore inode.
         */
-       error = xfs_itobp(mp, tp, ip, &dip, &bp, bno, 0);
+       error = xfs_itobp(mp, tp, ip, &dip, &bp, bno, imap_flags);
        if (error) {
                kmem_zone_free(xfs_inode_zone, ip);
                return error;
@@ -1113,7 +1115,7 @@ xfs_ialloc(
         * to prevent others from looking at until we're done.
         */
        error = xfs_trans_iget(tp->t_mountp, tp, ino,
-                       IGET_CREATE, XFS_ILOCK_EXCL, &ip);
+                               XFS_IGET_CREATE, XFS_ILOCK_EXCL, &ip);
        if (error != 0) {
                return error;
        }
@@ -2213,7 +2215,9 @@ xfs_ifree_cluster(
 
                        if (ip == free_ip) {
                                if (xfs_iflock_nowait(ip)) {
+                                       spin_lock(&ip->i_flags_lock);
                                        ip->i_flags |= XFS_ISTALE;
+                                       spin_unlock(&ip->i_flags_lock);
 
                                        if (xfs_inode_clean(ip)) {
                                                xfs_ifunlock(ip);
@@ -2227,7 +2231,9 @@ xfs_ifree_cluster(
 
                        if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
                                if (xfs_iflock_nowait(ip)) {
+                                       spin_lock(&ip->i_flags_lock);
                                        ip->i_flags |= XFS_ISTALE;
+                                       spin_unlock(&ip->i_flags_lock);
 
                                        if (xfs_inode_clean(ip)) {
                                                xfs_ifunlock(ip);
@@ -2257,7 +2263,9 @@ xfs_ifree_cluster(
                                AIL_LOCK(mp,s);
                                iip->ili_flush_lsn = iip->ili_item.li_lsn;
                                AIL_UNLOCK(mp, s);
+                               spin_lock(&iip->ili_inode->i_flags_lock);
                                iip->ili_inode->i_flags |= XFS_ISTALE;
+                               spin_unlock(&iip->ili_inode->i_flags_lock);
                                pre_flushed++;
                        }
                        lip = lip->li_bio_list;
@@ -2753,19 +2761,29 @@ xfs_iunpin(
                 * call as the inode reclaim may be blocked waiting for
                 * the inode to become unpinned.
                 */
+               struct inode *inode = NULL;
+
+               spin_lock(&ip->i_flags_lock);
                if (!(ip->i_flags & (XFS_IRECLAIM|XFS_IRECLAIMABLE))) {
                        bhv_vnode_t     *vp = XFS_ITOV_NULL(ip);
 
                        /* make sync come back and flush this inode */
                        if (vp) {
-                               struct inode    *inode = vn_to_inode(vp);
+                               inode = vn_to_inode(vp);
 
                                if (!(inode->i_state &
-                                               (I_NEW|I_FREEING|I_CLEAR)))
-                                       mark_inode_dirty_sync(inode);
+                                               (I_NEW|I_FREEING|I_CLEAR))) {
+                                       inode = igrab(inode);
+                                       if (inode)
+                                               mark_inode_dirty_sync(inode);
+                               } else
+                                       inode = NULL;
                        }
                }
+               spin_unlock(&ip->i_flags_lock);
                wake_up(&ip->i_ipin_wait);
+               if (inode)
+                       iput(inode);
        }
 }