diff options
Diffstat (limited to 'fs/xfs/xfs_inode.c')
| -rw-r--r-- | fs/xfs/xfs_inode.c | 136 | 
1 files changed, 135 insertions, 1 deletions
| diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 41f804e740d7..daafa1f6d260 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1995,6 +1995,7 @@ xfs_iunlink(  	agi->agi_unlinked[bucket_index] = cpu_to_be32(agino);  	offset = offsetof(xfs_agi_t, agi_unlinked) +  		(sizeof(xfs_agino_t) * bucket_index); +	xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);  	xfs_trans_log_buf(tp, agibp, offset,  			  (offset + sizeof(xfs_agino_t) - 1));  	return 0; @@ -2086,6 +2087,7 @@ xfs_iunlink_remove(  		agi->agi_unlinked[bucket_index] = cpu_to_be32(next_agino);  		offset = offsetof(xfs_agi_t, agi_unlinked) +  			(sizeof(xfs_agino_t) * bucket_index); +		xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);  		xfs_trans_log_buf(tp, agibp, offset,  				  (offset + sizeof(xfs_agino_t) - 1));  	} else { @@ -2656,6 +2658,124 @@ xfs_sort_for_rename(  }  /* + * xfs_cross_rename() + * + * responsible for handling RENAME_EXCHANGE flag in renameat2() sytemcall + */ +STATIC int +xfs_cross_rename( +	struct xfs_trans	*tp, +	struct xfs_inode	*dp1, +	struct xfs_name		*name1, +	struct xfs_inode	*ip1, +	struct xfs_inode	*dp2, +	struct xfs_name		*name2, +	struct xfs_inode	*ip2, +	struct xfs_bmap_free	*free_list, +	xfs_fsblock_t		*first_block, +	int			spaceres) +{ +	int		error = 0; +	int		ip1_flags = 0; +	int		ip2_flags = 0; +	int		dp2_flags = 0; + +	/* Swap inode number for dirent in first parent */ +	error = xfs_dir_replace(tp, dp1, name1, +				ip2->i_ino, +				first_block, free_list, spaceres); +	if (error) +		goto out; + +	/* Swap inode number for dirent in second parent */ +	error = xfs_dir_replace(tp, dp2, name2, +				ip1->i_ino, +				first_block, free_list, spaceres); +	if (error) +		goto out; + +	/* +	 * If we're renaming one or more directories across different parents, +	 * update the respective ".." entries (and link counts) to match the new +	 * parents. +	 */ +	if (dp1 != dp2) { +		dp2_flags = XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; + +		if (S_ISDIR(ip2->i_d.di_mode)) { +			error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot, +						dp1->i_ino, first_block, +						free_list, spaceres); +			if (error) +				goto out; + +			/* transfer ip2 ".." reference to dp1 */ +			if (!S_ISDIR(ip1->i_d.di_mode)) { +				error = xfs_droplink(tp, dp2); +				if (error) +					goto out; +				error = xfs_bumplink(tp, dp1); +				if (error) +					goto out; +			} + +			/* +			 * Although ip1 isn't changed here, userspace needs +			 * to be warned about the change, so that applications +			 * relying on it (like backup ones), will properly +			 * notify the change +			 */ +			ip1_flags |= XFS_ICHGTIME_CHG; +			ip2_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; +		} + +		if (S_ISDIR(ip1->i_d.di_mode)) { +			error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot, +						dp2->i_ino, first_block, +						free_list, spaceres); +			if (error) +				goto out; + +			/* transfer ip1 ".." reference to dp2 */ +			if (!S_ISDIR(ip2->i_d.di_mode)) { +				error = xfs_droplink(tp, dp1); +				if (error) +					goto out; +				error = xfs_bumplink(tp, dp2); +				if (error) +					goto out; +			} + +			/* +			 * Although ip2 isn't changed here, userspace needs +			 * to be warned about the change, so that applications +			 * relying on it (like backup ones), will properly +			 * notify the change +			 */ +			ip1_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; +			ip2_flags |= XFS_ICHGTIME_CHG; +		} +	} + +	if (ip1_flags) { +		xfs_trans_ichgtime(tp, ip1, ip1_flags); +		xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE); +	} +	if (ip2_flags) { +		xfs_trans_ichgtime(tp, ip2, ip2_flags); +		xfs_trans_log_inode(tp, ip2, XFS_ILOG_CORE); +	} +	if (dp2_flags) { +		xfs_trans_ichgtime(tp, dp2, dp2_flags); +		xfs_trans_log_inode(tp, dp2, XFS_ILOG_CORE); +	} +	xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); +	xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); +out: +	return error; +} + +/*   * xfs_rename   */  int @@ -2665,7 +2785,8 @@ xfs_rename(  	xfs_inode_t	*src_ip,  	xfs_inode_t	*target_dp,  	struct xfs_name	*target_name, -	xfs_inode_t	*target_ip) +	xfs_inode_t	*target_ip, +	unsigned int	flags)  {  	xfs_trans_t	*tp = NULL;  	xfs_mount_t	*mp = src_dp->i_mount; @@ -2743,6 +2864,18 @@ xfs_rename(  	}  	/* +	 * Handle RENAME_EXCHANGE flags +	 */ +	if (flags & RENAME_EXCHANGE) { +		error = xfs_cross_rename(tp, src_dp, src_name, src_ip, +					 target_dp, target_name, target_ip, +					 &free_list, &first_block, spaceres); +		if (error) +			goto abort_return; +		goto finish_rename; +	} + +	/*  	 * Set up the target.  	 */  	if (target_ip == NULL) { @@ -2881,6 +3014,7 @@ xfs_rename(  	if (new_parent)  		xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); +finish_rename:  	/*  	 * If this is a synchronous mount, make sure that the  	 * rename transaction goes to disk before returning to |