Merge branch 'xfs-O_TMPFILE-support' into for-next
[cascardo/linux.git] / fs / xfs / xfs_inode.c
index 3a137e9..5e7a38f 100644 (file)
@@ -42,7 +42,6 @@
 #include "xfs_bmap_util.h"
 #include "xfs_error.h"
 #include "xfs_quota.h"
-#include "xfs_dinode.h"
 #include "xfs_filestream.h"
 #include "xfs_cksum.h"
 #include "xfs_trace.h"
@@ -62,6 +61,8 @@ kmem_zone_t *xfs_inode_zone;
 
 STATIC int xfs_iflush_int(xfs_inode_t *, xfs_buf_t *);
 
+STATIC int xfs_iunlink_remove(xfs_trans_t *, xfs_inode_t *);
+
 /*
  * helper function to extract extent size hint from inode
  */
@@ -1115,7 +1116,7 @@ xfs_bumplink(
 {
        xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
 
-       ASSERT(ip->i_d.di_nlink > 0);
+       ASSERT(ip->i_d.di_nlink > 0 || (VFS_I(ip)->i_state & I_LINKABLE));
        ip->i_d.di_nlink++;
        inc_nlink(VFS_I(ip));
        if ((ip->i_d.di_version == 1) &&
@@ -1165,10 +1166,7 @@ xfs_create(
        if (XFS_FORCED_SHUTDOWN(mp))
                return XFS_ERROR(EIO);
 
-       if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
-               prid = xfs_get_projid(dp);
-       else
-               prid = XFS_PROJID_DEFAULT;
+       prid = xfs_get_initial_prid(dp);
 
        /*
         * Make sure that we have allocated dquot(s) on disk.
@@ -1332,6 +1330,113 @@ xfs_create(
        return error;
 }
 
+int
+xfs_create_tmpfile(
+       struct xfs_inode        *dp,
+       struct dentry           *dentry,
+       umode_t                 mode)
+{
+       struct xfs_mount        *mp = dp->i_mount;
+       struct xfs_inode        *ip = NULL;
+       struct xfs_trans        *tp = NULL;
+       int                     error;
+       uint                    cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+       prid_t                  prid;
+       struct xfs_dquot        *udqp = NULL;
+       struct xfs_dquot        *gdqp = NULL;
+       struct xfs_dquot        *pdqp = NULL;
+       struct xfs_trans_res    *tres;
+       uint                    resblks;
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return XFS_ERROR(EIO);
+
+       prid = xfs_get_initial_prid(dp);
+
+       /*
+        * Make sure that we have allocated dquot(s) on disk.
+        */
+       error = xfs_qm_vop_dqalloc(dp, xfs_kuid_to_uid(current_fsuid()),
+                               xfs_kgid_to_gid(current_fsgid()), prid,
+                               XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT,
+                               &udqp, &gdqp, &pdqp);
+       if (error)
+               return error;
+
+       resblks = XFS_IALLOC_SPACE_RES(mp);
+       tp = xfs_trans_alloc(mp, XFS_TRANS_CREATE_TMPFILE);
+
+       tres = &M_RES(mp)->tr_create_tmpfile;
+       error = xfs_trans_reserve(tp, tres, resblks, 0);
+       if (error == ENOSPC) {
+               /* No space at all so try a "no-allocation" reservation */
+               resblks = 0;
+               error = xfs_trans_reserve(tp, tres, 0, 0);
+       }
+       if (error) {
+               cancel_flags = 0;
+               goto out_trans_cancel;
+       }
+
+       error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
+                                               pdqp, resblks, 1, 0);
+       if (error)
+               goto out_trans_cancel;
+
+       error = xfs_dir_ialloc(&tp, dp, mode, 1, 0,
+                               prid, resblks > 0, &ip, NULL);
+       if (error) {
+               if (error == ENOSPC)
+                       goto out_trans_cancel;
+               goto out_trans_abort;
+       }
+
+       if (mp->m_flags & XFS_MOUNT_WSYNC)
+               xfs_trans_set_sync(tp);
+
+       /*
+        * Attach the dquot(s) to the inodes and modify them incore.
+        * These ids of the inode couldn't have changed since the new
+        * inode has been locked ever since it was created.
+        */
+       xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
+
+       ip->i_d.di_nlink--;
+       d_tmpfile(dentry, VFS_I(ip));
+       error = xfs_iunlink(tp, ip);
+       if (error)
+               goto out_trans_abort;
+
+       error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+       if (error)
+               goto out_release_inode;
+
+       xfs_qm_dqrele(udqp);
+       xfs_qm_dqrele(gdqp);
+       xfs_qm_dqrele(pdqp);
+
+       return 0;
+
+ out_trans_abort:
+       cancel_flags |= XFS_TRANS_ABORT;
+ out_trans_cancel:
+       xfs_trans_cancel(tp, cancel_flags);
+ out_release_inode:
+       /*
+        * Wait until after the current transaction is aborted to
+        * release the inode.  This prevents recursive transactions
+        * and deadlocks from xfs_inactive.
+        */
+       if (ip)
+               IRELE(ip);
+
+       xfs_qm_dqrele(udqp);
+       xfs_qm_dqrele(gdqp);
+       xfs_qm_dqrele(pdqp);
+
+       return error;
+}
+
 int
 xfs_link(
        xfs_inode_t             *tdp,
@@ -1397,6 +1502,12 @@ xfs_link(
 
        xfs_bmap_init(&free_list, &first_block);
 
+       if (sip->i_d.di_nlink == 0) {
+               error = xfs_iunlink_remove(tp, sip);
+               if (error)
+                       goto abort_return;
+       }
+
        error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino,
                                        &first_block, &free_list, resblks);
        if (error)