diff options
Diffstat (limited to 'fs/xfs/xfs_symlink.c')
| -rw-r--r-- | fs/xfs/xfs_symlink.c | 91 | 
1 files changed, 38 insertions, 53 deletions
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 3e376d24c7c1..17aee806ec2e 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -25,6 +25,8 @@  #include "xfs_error.h"  #include "xfs_health.h"  #include "xfs_symlink_remote.h" +#include "xfs_parent.h" +#include "xfs_defer.h"  int  xfs_readlink( @@ -100,6 +102,7 @@ xfs_symlink(  	struct xfs_dquot	*pdqp = NULL;  	uint			resblks;  	xfs_ino_t		ino; +	struct xfs_parent_args	*ppargs;  	*ipp = NULL; @@ -130,18 +133,24 @@ xfs_symlink(  	/*  	 * The symlink will fit into the inode data fork? -	 * There can't be any attributes so we get the whole variable part. +	 * If there are no parent pointers, then there wont't be any attributes. +	 * So we get the whole variable part, and do not need to reserve extra +	 * blocks.  Otherwise, we need to reserve the blocks.  	 */ -	if (pathlen <= XFS_LITINO(mp)) +	if (pathlen <= XFS_LITINO(mp) && !xfs_has_parent(mp))  		fs_blocks = 0;  	else  		fs_blocks = xfs_symlink_blocks(mp, pathlen); -	resblks = XFS_SYMLINK_SPACE_RES(mp, link_name->len, fs_blocks); +	resblks = xfs_symlink_space_res(mp, link_name->len, fs_blocks); + +	error = xfs_parent_start(mp, &ppargs); +	if (error) +		goto out_release_dquots;  	error = xfs_trans_alloc_icreate(mp, &M_RES(mp)->tr_symlink, udqp, gdqp,  			pdqp, resblks, &tp);  	if (error) -		goto out_release_dquots; +		goto out_parent;  	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);  	unlock_dp_on_error = true; @@ -161,7 +170,7 @@ xfs_symlink(  	if (!error)  		error = xfs_init_new_inode(idmap, tp, dp, ino,  				S_IFLNK | (mode & ~S_IFMT), 1, 0, prid, -				false, &ip); +				xfs_has_parent(mp), &ip);  	if (error)  		goto out_trans_cancel; @@ -172,8 +181,7 @@ xfs_symlink(  	 * the transaction cancel unlocking dp so don't do it explicitly in the  	 * error path.  	 */ -	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); -	unlock_dp_on_error = false; +	xfs_trans_ijoin(tp, dp, 0);  	/*  	 * Also attach the dquot(s) to it, if applicable. @@ -181,8 +189,8 @@ xfs_symlink(  	xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);  	resblks -= XFS_IALLOC_SPACE_RES(mp); -	error = xfs_symlink_write_target(tp, ip, target_path, pathlen, -			fs_blocks, resblks); +	error = xfs_symlink_write_target(tp, ip, ip->i_ino, target_path, +			pathlen, fs_blocks, resblks);  	if (error)  		goto out_trans_cancel;  	resblks -= fs_blocks; @@ -196,6 +204,14 @@ xfs_symlink(  		goto out_trans_cancel;  	xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);  	xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); + +	/* Add parent pointer for the new symlink. */ +	if (ppargs) { +		error = xfs_parent_addname(tp, ppargs, dp, link_name, ip); +		if (error) +			goto out_trans_cancel; +	} +  	xfs_dir_update_hook(dp, ip, 1, link_name);  	/* @@ -215,6 +231,9 @@ xfs_symlink(  	xfs_qm_dqrele(pdqp);  	*ipp = ip; +	xfs_iunlock(ip, XFS_ILOCK_EXCL); +	xfs_iunlock(dp, XFS_ILOCK_EXCL); +	xfs_parent_finish(mp, ppargs);  	return 0;  out_trans_cancel: @@ -226,9 +245,12 @@ out_release_inode:  	 * transactions and deadlocks from xfs_inactive.  	 */  	if (ip) { +		xfs_iunlock(ip, XFS_ILOCK_EXCL);  		xfs_finish_inode_setup(ip);  		xfs_irele(ip);  	} +out_parent: +	xfs_parent_finish(mp, ppargs);  out_release_dquots:  	xfs_qm_dqrele(udqp);  	xfs_qm_dqrele(gdqp); @@ -250,19 +272,12 @@ out_release_dquots:   */  STATIC int  xfs_inactive_symlink_rmt( -	struct xfs_inode *ip) +	struct xfs_inode	*ip)  { -	struct xfs_buf	*bp; -	int		done; -	int		error; -	int		i; -	xfs_mount_t	*mp; -	xfs_bmbt_irec_t	mval[XFS_SYMLINK_MAPS]; -	int		nmaps; -	int		size; -	xfs_trans_t	*tp; - -	mp = ip->i_mount; +	struct xfs_mount	*mp = ip->i_mount; +	struct xfs_trans	*tp; +	int			error; +  	ASSERT(!xfs_need_iread_extents(&ip->i_df));  	/*  	 * We're freeing a symlink that has some @@ -286,44 +301,14 @@ xfs_inactive_symlink_rmt(  	 * locked for the second transaction.  In the error paths we need it  	 * held so the cancel won't rele it, see below.  	 */ -	size = (int)ip->i_disk_size;  	ip->i_disk_size = 0;  	VFS_I(ip)->i_mode = (VFS_I(ip)->i_mode & ~S_IFMT) | S_IFREG;  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); -	/* -	 * Find the block(s) so we can inval and unmap them. -	 */ -	done = 0; -	nmaps = ARRAY_SIZE(mval); -	error = xfs_bmapi_read(ip, 0, xfs_symlink_blocks(mp, size), -				mval, &nmaps, 0); -	if (error) -		goto error_trans_cancel; -	/* -	 * Invalidate the block(s). No validation is done. -	 */ -	for (i = 0; i < nmaps; i++) { -		error = xfs_trans_get_buf(tp, mp->m_ddev_targp, -				XFS_FSB_TO_DADDR(mp, mval[i].br_startblock), -				XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0, -				&bp); -		if (error) -			goto error_trans_cancel; -		xfs_trans_binval(tp, bp); -	} -	/* -	 * Unmap the dead block(s) to the dfops. -	 */ -	error = xfs_bunmapi(tp, ip, 0, size, 0, nmaps, &done); + +	error = xfs_symlink_remote_truncate(tp, ip);  	if (error)  		goto error_trans_cancel; -	ASSERT(done); -	/* -	 * Commit the transaction. This first logs the EFI and the inode, then -	 * rolls and commits the transaction that frees the extents. -	 */ -	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);  	error = xfs_trans_commit(tp);  	if (error) {  		ASSERT(xfs_is_shutdown(mp));  |