Commit c1f0bad4 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: create libxfs helper to link an existing inode into a directory



Create a new libxfs function to link an existing inode into a directory.
The upcoming metadata directory feature will need this to create a
metadata directory tree.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 1fa2e819
Loading
Loading
Loading
Loading
+68 −3
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "xfs_bmap_btree.h"
#include "xfs_trans_space.h"
#include "xfs_parent.h"
#include "xfs_ag.h"

const struct xfs_name xfs_name_dotdot = {
	.name	= (const unsigned char *)"..",
@@ -587,9 +588,9 @@ xfs_dir_replace(
 */
int
xfs_dir_canenter(
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
	struct xfs_name	*name)		/* name of entry to add */
	struct xfs_trans	*tp,
	struct xfs_inode	*dp,
	const struct xfs_name	*name)		/* name of entry to add */
{
	return xfs_dir_createname(tp, dp, name, 0, 0);
}
@@ -809,3 +810,67 @@ xfs_dir_create_child(

	return 0;
}

/*
 * Given a directory @dp, an existing non-directory inode @ip, and a @name,
 * link @ip into @dp under the given @name.  Both inodes must have the ILOCK
 * held.
 */
int
xfs_dir_add_child(
	struct xfs_trans	*tp,
	unsigned int		resblks,
	struct xfs_dir_update	*du)
{
	struct xfs_inode	*dp = du->dp;
	const struct xfs_name	*name = du->name;
	struct xfs_inode	*ip = du->ip;
	struct xfs_mount	*mp = tp->t_mountp;
	int			error;

	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
	xfs_assert_ilocked(dp, XFS_ILOCK_EXCL);
	ASSERT(!S_ISDIR(VFS_I(ip)->i_mode));

	if (!resblks) {
		error = xfs_dir_canenter(tp, dp, name);
		if (error)
			return error;
	}

	/*
	 * Handle initial link state of O_TMPFILE inode
	 */
	if (VFS_I(ip)->i_nlink == 0) {
		struct xfs_perag	*pag;

		pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
		error = xfs_iunlink_remove(tp, pag, ip);
		xfs_perag_put(pag);
		if (error)
			return error;
	}

	error = xfs_dir_createname(tp, dp, name, ip->i_ino, resblks);
	if (error)
		return error;

	xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);

	xfs_bumplink(tp, ip);

	/*
	 * If we have parent pointers, we now need to add the parent record to
	 * the attribute fork of the inode. If this is the initial parent
	 * attribute, we need to create it correctly, otherwise we can just add
	 * the parent to the inode.
	 */
	if (du->ppargs) {
		error = xfs_parent_addname(tp, du->ppargs, dp, name, ip);
		if (error)
			return error;
	}

	return 0;
}
+3 −1
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
				const struct xfs_name *name, xfs_ino_t inum,
				xfs_extlen_t tot);
extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
				struct xfs_name *name);
				const struct xfs_name *name);

int xfs_dir_lookup_args(struct xfs_da_args *args);
int xfs_dir_createname_args(struct xfs_da_args *args);
@@ -320,5 +320,7 @@ struct xfs_dir_update {

int xfs_dir_create_child(struct xfs_trans *tp, unsigned int resblks,
		struct xfs_dir_update *du);
int xfs_dir_add_child(struct xfs_trans *tp, unsigned int resblks,
		struct xfs_dir_update *du);

#endif	/* __XFS_DIR2_H__ */
+10 −42
Original line number Diff line number Diff line
@@ -952,11 +952,15 @@ xfs_link(
	struct xfs_inode	*sip,
	struct xfs_name		*target_name)
{
	struct xfs_dir_update	du = {
		.dp		= tdp,
		.name		= target_name,
		.ip		= sip,
	};
	struct xfs_mount	*mp = tdp->i_mount;
	struct xfs_trans	*tp;
	int			error, nospace_error = 0;
	int			resblks;
	struct xfs_parent_args	*ppargs;

	trace_xfs_link(tdp, target_name);

@@ -975,7 +979,7 @@ xfs_link(
	if (error)
		goto std_return;

	error = xfs_parent_start(mp, &ppargs);
	error = xfs_parent_start(mp, &du.ppargs);
	if (error)
		goto std_return;

@@ -990,7 +994,7 @@ xfs_link(
	 * pointers are enabled because we can't back out if the xattrs must
	 * grow.
	 */
	if (ppargs && nospace_error) {
	if (du.ppargs && nospace_error) {
		error = nospace_error;
		goto error_return;
	}
@@ -1017,45 +1021,9 @@ xfs_link(
		}
	}

	if (!resblks) {
		error = xfs_dir_canenter(tp, tdp, target_name);
		if (error)
			goto error_return;
	}

	/*
	 * Handle initial link state of O_TMPFILE inode
	 */
	if (VFS_I(sip)->i_nlink == 0) {
		struct xfs_perag	*pag;

		pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, sip->i_ino));
		error = xfs_iunlink_remove(tp, pag, sip);
		xfs_perag_put(pag);
		if (error)
			goto error_return;
	}

	error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino,
				   resblks);
	error = xfs_dir_add_child(tp, resblks, &du);
	if (error)
		goto error_return;
	xfs_trans_ichgtime(tp, tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE);

	xfs_bumplink(tp, sip);

	/*
	 * If we have parent pointers, we now need to add the parent record to
	 * the attribute fork of the inode. If this is the initial parent
	 * attribute, we need to create it correctly, otherwise we can just add
	 * the parent to the inode.
	 */
	if (ppargs) {
		error = xfs_parent_addname(tp, ppargs, tdp, target_name, sip);
		if (error)
			goto error_return;
	}

	xfs_dir_update_hook(tdp, sip, 1, target_name);

@@ -1070,7 +1038,7 @@ xfs_link(
	error = xfs_trans_commit(tp);
	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
	xfs_iunlock(sip, XFS_ILOCK_EXCL);
	xfs_parent_finish(mp, ppargs);
	xfs_parent_finish(mp, du.ppargs);
	return error;

 error_return:
@@ -1078,7 +1046,7 @@ xfs_link(
	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
	xfs_iunlock(sip, XFS_ILOCK_EXCL);
 out_parent:
	xfs_parent_finish(mp, ppargs);
	xfs_parent_finish(mp, du.ppargs);
 std_return:
	if (error == -ENOSPC && nospace_error)
		error = nospace_error;