diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2020-10-25 17:16:05 -0700 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2020-10-26 18:32:31 -0700 |
commit | 576d96d42395dfae790ffb2b9af91a8ad9ca6fad (patch) | |
tree | a96f8411129e7efed59c2b3c981c7e546e63b425 /fs | |
parent | fcce10df855ca8fd2050b1d39be1316be2756e31 (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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/libxfs/xfs_imeta.h | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.c | 103 | ||||
-rw-r--r-- | fs/xfs/xfs_qm.c | 13 |
3 files changed, 118 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_imeta.h b/fs/xfs/libxfs/xfs_imeta.h index ef80182c4f9a..6d652ffc2981 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 2598b4c31ed3..6a8388f8d726 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -40,6 +40,7 @@ #include "xfs_dquot_item.h" #include "xfs_dquot.h" #include "xfs_health.h" +#include "xfs_imeta.h" kmem_zone_t *xfs_inode_zone; @@ -915,6 +916,108 @@ 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, GLOBAL_ROOT_UID, + GLOBAL_ROOT_GID, 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 defea0132b81..351cb0643df4 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -839,6 +839,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; @@ -847,6 +848,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, need_alloc ? xfs_imeta_create_space_res(mp) : 0, 0, 0, &tp); |