From: Darrick J. Wong <djwong@xxxxxxxxxx> 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 <djwong@xxxxxxxxxx> --- fs/xfs/xfs_imeta_utils.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_imeta_utils.h | 3 ++ fs/xfs/xfs_qm.c | 17 +++++++++++ 3 files changed, 91 insertions(+) diff --git a/fs/xfs/xfs_imeta_utils.c b/fs/xfs/xfs_imeta_utils.c index a8ff46a3d502e..9fbaa4323e3b2 100644 --- a/fs/xfs/xfs_imeta_utils.c +++ b/fs/xfs/xfs_imeta_utils.c @@ -265,3 +265,74 @@ xfs_imeta_cancel_update( xfs_imeta_teardown(upd, 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_update upd; + struct xfs_inode *ip = NULL; + int error; + + if (xfs_is_shutdown(mp)) + return -EIO; + + /* Allocate a transaction to create the last directory. */ + error = xfs_imeta_start_create(mp, path, &upd); + if (error) + return error; + + /* Create the subdirectory and take our reference. */ + error = xfs_imeta_create(&upd, S_IFDIR, &ip); + if (error) + goto out_cancel; + + error = xfs_imeta_commit_update(&upd); + + /* + * We don't pass the directory we just created to the caller, so finish + * setting up the inode, then release the dir and the dquots. + */ + goto out_irele; + +out_cancel: + xfs_imeta_cancel_update(&upd, 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); + } + 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, + .im_ftype = XFS_DIR3_FT_DIR, + }; + unsigned int i; + int error = 0; + + if (!xfs_has_metadir(mp)) + return 0; + + for (i = 0; i < path->im_depth - 1; i++, temp_path.im_depth++) { + error = xfs_imeta_mkdir(mp, &temp_path); + if (error && error != -EEXIST) + return error; + } + + return 0; +} diff --git a/fs/xfs/xfs_imeta_utils.h b/fs/xfs/xfs_imeta_utils.h index 0235f7048ff1d..f1bd6699997bb 100644 --- a/fs/xfs/xfs_imeta_utils.h +++ b/fs/xfs/xfs_imeta_utils.h @@ -18,6 +18,9 @@ int xfs_imeta_start_unlink(struct xfs_mount *mp, const struct xfs_imeta_path *path, struct xfs_inode *ip, struct xfs_imeta_update *upd); +int xfs_imeta_ensure_dirpath(struct xfs_mount *mp, + const struct xfs_imeta_path *path); + int xfs_imeta_commit_update(struct xfs_imeta_update *upd); void xfs_imeta_cancel_update(struct xfs_imeta_update *upd, int error); diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 2d8e420f3ad34..1d28f0982840c 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -836,6 +836,23 @@ xfs_qm_qino_alloc( return error; if (need_alloc) { + /* + * Ensure the quota directory exists, being careful to disable + * quotas while we do this. We'll have to quotacheck anyway, + * so the temporary undercount of the directory tree shouldn't + * affect the quota count. + */ + if (xfs_has_metadir(mp)) { + unsigned int old_qflags; + + old_qflags = mp->m_qflags & XFS_ALL_QUOTA_ACCT; + mp->m_qflags &= ~XFS_ALL_QUOTA_ACCT; + error = xfs_imeta_ensure_dirpath(mp, path); + mp->m_qflags |= old_qflags; + if (error) + return error; + } + error = xfs_imeta_start_create(mp, path, &upd); } else { error = xfs_trans_alloc(mp, &M_RES(mp)->tr_create, 0, 0, 0,