xfs: add O_TMPFILE support
authorZhi Yong Wu <wuzhy@linux.vnet.ibm.com>
Wed, 18 Dec 2013 00:22:40 +0000 (08:22 +0800)
committerBen Myers <bpm@sgi.com>
Mon, 6 Jan 2014 19:50:06 +0000 (13:50 -0600)
Add two functions xfs_create_tmpfile() and xfs_vn_tmpfile()
to support O_TMPFILE file creation.

In contrast to xfs_create(), xfs_create_tmpfile() has a different
log reservation to the regular file creation because there is no
directory modification, and doesn't check if an entry can be added
to the directory, but the reservation quotas is required appropriately,
and finally its inode is added to the unlinked list.

xfs_vn_tmpfile() add one O_TMPFILE method to VFS interface and directly
invoke xfs_create_tmpfile().

Signed-off-by: Zhi Yong Wu <wuzhy@linux.vnet.ibm.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Ben Myers <bpm@sgi.com>
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_iops.c
fs/xfs/xfs_shared.h
fs/xfs/xfs_trans_resv.c
fs/xfs/xfs_trans_resv.h

index c79b875..ac133ea 100644 (file)
@@ -1333,6 +1333,113 @@ xfs_create(
 }
 
 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,
        xfs_inode_t             *sip,
index 6c58349..3a97820 100644 (file)
@@ -333,6 +333,8 @@ int         xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
                           struct xfs_inode **ipp, struct xfs_name *ci_name);
 int            xfs_create(struct xfs_inode *dp, struct xfs_name *name,
                           umode_t mode, xfs_dev_t rdev, struct xfs_inode **ipp);
+int            xfs_create_tmpfile(struct xfs_inode *dp, struct dentry *dentry,
+                          umode_t mode);
 int            xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
                           struct xfs_inode *ip);
 int            xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
index 0ce1d75..cb06cc4 100644 (file)
@@ -39,6 +39,7 @@
 #include "xfs_da_btree.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_dinode.h"
+#include "xfs_trans_space.h"
 
 #include <linux/capability.h>
 #include <linux/xattr.h>
@@ -1043,6 +1044,19 @@ xfs_vn_fiemap(
        return 0;
 }
 
+STATIC int
+xfs_vn_tmpfile(
+       struct inode    *dir,
+       struct dentry   *dentry,
+       umode_t         mode)
+{
+       int             error;
+
+       error = xfs_create_tmpfile(XFS_I(dir), dentry, mode);
+
+       return -error;
+}
+
 static const struct inode_operations xfs_inode_operations = {
        .get_acl                = xfs_get_acl,
        .getattr                = xfs_vn_getattr,
@@ -1079,6 +1093,7 @@ static const struct inode_operations xfs_dir_inode_operations = {
        .removexattr            = generic_removexattr,
        .listxattr              = xfs_vn_listxattr,
        .update_time            = xfs_vn_update_time,
+       .tmpfile                = xfs_vn_tmpfile,
 };
 
 static const struct inode_operations xfs_dir_ci_inode_operations = {
@@ -1105,6 +1120,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
        .removexattr            = generic_removexattr,
        .listxattr              = xfs_vn_listxattr,
        .update_time            = xfs_vn_update_time,
+       .tmpfile                = xfs_vn_tmpfile,
 };
 
 static const struct inode_operations xfs_symlink_inode_operations = {
index 8c5035a..4484e51 100644 (file)
@@ -104,7 +104,8 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
 #define        XFS_TRANS_SB_COUNT              41
 #define        XFS_TRANS_CHECKPOINT            42
 #define        XFS_TRANS_ICREATE               43
-#define        XFS_TRANS_TYPE_MAX              43
+#define        XFS_TRANS_CREATE_TMPFILE        44
+#define        XFS_TRANS_TYPE_MAX              44
 /* new transaction types need to be reflected in xfs_logprint(8) */
 
 #define XFS_TRANS_TYPES \
@@ -112,6 +113,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
        { XFS_TRANS_SETATTR_SIZE,       "SETATTR_SIZE" }, \
        { XFS_TRANS_INACTIVE,           "INACTIVE" }, \
        { XFS_TRANS_CREATE,             "CREATE" }, \
+       { XFS_TRANS_CREATE_TMPFILE,     "CREATE_TMPFILE" }, \
        { XFS_TRANS_CREATE_TRUNC,       "CREATE_TRUNC" }, \
        { XFS_TRANS_TRUNCATE_FILE,      "TRUNCATE_FILE" }, \
        { XFS_TRANS_REMOVE,             "REMOVE" }, \
index 2fd59c0..bd3b4b7 100644 (file)
@@ -229,6 +229,18 @@ xfs_calc_link_reservation(
 }
 
 /*
+ * For adding an inode to unlinked list we can modify:
+ *    the agi hash list: sector size
+ *    the unlinked inode: inode size
+ */
+STATIC uint
+xfs_calc_iunlink_add_reservation(xfs_mount_t *mp)
+{
+       return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
+               xfs_calc_inode_res(mp, 1);
+}
+
+/*
  * For removing a directory entry we can modify:
  *    the parent directory inode: inode size
  *    the removed inode: inode size
@@ -245,10 +257,11 @@ xfs_calc_remove_reservation(
        struct xfs_mount        *mp)
 {
        return XFS_DQUOT_LOGRES(mp) +
-               MAX((xfs_calc_inode_res(mp, 2) +
+               xfs_calc_iunlink_add_reservation(mp) +
+               MAX((xfs_calc_inode_res(mp, 1) +
                     xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
                                      XFS_FSB_TO_B(mp, 1))),
-                   (xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) +
+                   (xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
                     xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 2),
                                      XFS_FSB_TO_B(mp, 1))));
 }
@@ -343,6 +356,20 @@ xfs_calc_create_reservation(
 
 }
 
+STATIC uint
+xfs_calc_create_tmpfile_reservation(
+       struct xfs_mount        *mp)
+{
+       uint    res = XFS_DQUOT_LOGRES(mp);
+
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               res += xfs_calc_icreate_resv_alloc(mp);
+       else
+               res += xfs_calc_create_resv_alloc(mp);
+
+       return res + xfs_calc_iunlink_add_reservation(mp);
+}
+
 /*
  * Making a new directory is the same as creating a new file.
  */
@@ -729,6 +756,11 @@ xfs_trans_resv_calc(
        resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
        resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
+       resp->tr_create_tmpfile.tr_logres =
+                       xfs_calc_create_tmpfile_reservation(mp);
+       resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
+       resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
        resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
        resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
        resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
index de7de9a..285621d 100644 (file)
@@ -38,6 +38,7 @@ struct xfs_trans_resv {
        struct xfs_trans_res    tr_remove;      /* unlink trans */
        struct xfs_trans_res    tr_symlink;     /* symlink trans */
        struct xfs_trans_res    tr_create;      /* create trans */
+       struct xfs_trans_res    tr_create_tmpfile; /* create O_TMPFILE trans */
        struct xfs_trans_res    tr_mkdir;       /* mkdir trans */
        struct xfs_trans_res    tr_ifree;       /* inode free trans */
        struct xfs_trans_res    tr_ichange;     /* inode update trans */
@@ -100,6 +101,7 @@ struct xfs_trans_resv {
 #define        XFS_ITRUNCATE_LOG_COUNT         2
 #define XFS_INACTIVE_LOG_COUNT         2
 #define        XFS_CREATE_LOG_COUNT            2
+#define        XFS_CREATE_TMPFILE_LOG_COUNT    2
 #define        XFS_MKDIR_LOG_COUNT             3
 #define        XFS_SYMLINK_LOG_COUNT           3
 #define        XFS_REMOVE_LOG_COUNT            2