summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2020-02-19 17:02:44 -0800
committerDarrick J. Wong <darrick.wong@oracle.com>2020-03-03 18:47:51 -0800
commit087352cc5679cf42ed5a1e4d27d2a0a1e6e876cb (patch)
treedc8bf3833621e2a92e559696bc1b40cbaad75a37
parenteca2fa7216b1a3a7744d21c3dcb7e12dcff8acc2 (diff)
xfs: ensure metadata directory paths exist before creating files
Since xfs_imeta_create can create new metadata files arbitrarily deep in the metadata directory tree, we must supply a function that can ensure that all directories in a path exist, and call it before the quota functions create the quota inodes. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/libxfs/xfs_imeta.h2
-rw-r--r--fs/xfs/xfs_inode.c102
-rw-r--r--fs/xfs/xfs_qm.c13
3 files changed, 117 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_imeta.h b/fs/xfs/libxfs/xfs_imeta.h
index c9f143ab0999..8d3d1aa2b1ab 100644
--- a/fs/xfs/libxfs/xfs_imeta.h
+++ b/fs/xfs/libxfs/xfs_imeta.h
@@ -62,5 +62,7 @@ unsigned int xfs_imeta_unlink_space_res(struct xfs_mount *mp);
int xfs_imeta_iget(struct xfs_mount *mp, xfs_ino_t ino, unsigned char ftype,
struct xfs_inode **ipp);
void xfs_imeta_irele(struct xfs_inode *ip);
+int xfs_imeta_ensure_dirpath(struct xfs_mount *mp,
+ const struct xfs_imeta_path *path);
#endif /* __XFS_IMETA_H__ */
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 5167ea31cf75..cfd370e6c106 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -39,6 +39,7 @@
#include "xfs_dquot_item.h"
#include "xfs_dquot.h"
#include "xfs_health.h"
+#include "xfs_imeta.h"
kmem_zone_t *xfs_inode_zone;
@@ -931,6 +932,107 @@ xfs_create_tmpfile(
return error;
}
+/* Create a metadata for the last component of the path. */
+STATIC int
+xfs_imeta_mkdir(
+ struct xfs_mount *mp,
+ const struct xfs_imeta_path *path)
+{
+ struct xfs_imeta_end ic;
+ struct xfs_inode *ip = NULL;
+ struct xfs_trans *tp = NULL;
+ struct xfs_dquot *udqp = NULL;
+ struct xfs_dquot *gdqp = NULL;
+ struct xfs_dquot *pdqp = NULL;
+ uint resblks;
+ int error;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return -EIO;
+
+ /* Grab the dquots from the metadata directory root. */
+ error = xfs_qm_vop_dqalloc(mp->m_metadirip, 0, 0, 0,
+ XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT,
+ &udqp, &gdqp, &pdqp);
+ if (error)
+ return error;
+
+ /* Allocate a transaction to create the last directory. */
+ resblks = xfs_imeta_create_space_res(mp);
+ error = xfs_trans_alloc(mp, &M_RES(mp)->tr_imeta_create, resblks,
+ 0, 0, &tp);
+ if (error)
+ goto out_dqrele;
+
+ /* Reserve quota for the new directory. */
+ error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, pdqp,
+ resblks, 1, 0);
+ if (error) {
+ xfs_trans_cancel(tp);
+ goto out_dqrele;
+ }
+
+ /* Create the subdirectory. */
+ error = xfs_imeta_create(&tp, path, S_IFDIR, &ip, &ic);
+ if (error) {
+ xfs_trans_cancel(tp);
+ xfs_imeta_end_update(mp, &ic, error);
+ goto out_irele;
+ }
+
+ /*
+ * 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);
+
+ error = xfs_trans_commit(tp);
+ xfs_imeta_end_update(mp, &ic, error);
+
+out_irele:
+ /* Have to finish setting up the inode to ensure it's deleted. */
+ if (ip) {
+ xfs_finish_inode_setup(ip);
+ xfs_irele(ip);
+ }
+
+out_dqrele:
+ xfs_qm_dqrele(udqp);
+ xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
+ return error;
+}
+
+/*
+ * Make sure that every metadata directory path component exists and is a
+ * directory.
+ */
+int
+xfs_imeta_ensure_dirpath(
+ struct xfs_mount *mp,
+ const struct xfs_imeta_path *path)
+{
+ struct xfs_imeta_path temp_path = {
+ .im_path = path->im_path,
+ .im_depth = 1,
+ };
+ unsigned int i;
+ int error = 0;
+
+ if (!xfs_sb_version_hasmetadir(&mp->m_sb))
+ return 0;
+
+ for (i = 0; i < path->im_depth - 1; i++) {
+ temp_path.im_depth = i + 1;
+ error = xfs_imeta_mkdir(mp, &temp_path);
+ if (error && error != -EEXIST)
+ break;
+ }
+
+ return error == -EEXIST ? 0 : error;
+}
+
int
xfs_link(
xfs_inode_t *tdp,
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 8699342306f4..b0c4ace8a3ee 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -836,6 +836,7 @@ xfs_qm_qino_alloc(
struct xfs_imeta_end ic;
struct xfs_trans *tp;
const struct xfs_imeta_path *path = xfs_qflags_to_imeta(flags);
+ uint old_qflags;
int error;
bool need_alloc = true;
@@ -844,6 +845,18 @@ xfs_qm_qino_alloc(
if (error)
return error;
+ /*
+ * Ensure the quota directory exists, being careful to disable quotas
+ * while we do this. We'll have to quotacheck anyway, so the loss
+ * of one inode shouldn't affect the quota count.
+ */
+ old_qflags = mp->m_qflags & XFS_ALL_QUOTA_ACCT;
+ mp->m_qflags &= ~XFS_ALL_QUOTA_ACCT;
+ error = xfs_imeta_ensure_dirpath(mp, xfs_qflags_to_imeta(flags));
+ mp->m_qflags |= old_qflags;
+ if (error)
+ return error;
+
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_imeta_create,
xfs_imeta_create_space_res(mp), 0, 0, &tp);
if (error)