|
@@ -1332,6 +1332,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,
|