[PATCH 2/2] Add support to RENAME_EXCHANGE flag

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Adds a new function named xfs_cross_rename(), responsible to handle requests
from sys_renameat2() using RENAME_EXCHANGE flag.

Changelog:

	V2: refactor xfs_cross_rename() to not duplicate code from xfs_rename()

Signed-off-by: Carlos Maiolino <cmaiolino@xxxxxxxxxx>
---
 fs/xfs/xfs_inode.c | 315 +++++++++++++++++++++++++++++++++++------------------
 fs/xfs/xfs_inode.h |   8 +-
 fs/xfs/xfs_iops.c  |   4 +-
 3 files changed, 220 insertions(+), 107 deletions(-)

diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index fea3c92..bf09bfc 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2674,12 +2674,14 @@ 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;
 	int		new_parent;		/* moving to a new dir */
 	int		src_is_directory;	/* src_name is a directory */
+	int		tgt_is_directory;
 	int		error;
 	xfs_bmap_free_t free_list;
 	xfs_fsblock_t   first_block;
@@ -2752,141 +2754,158 @@ xfs_rename(
 	}
 
 	/*
-	 * Set up the target.
-	 */
-	if (target_ip == NULL) {
+	 * Handle RENAME_EXCHANGE flags
+	*/
+	if (flags & RENAME_EXCHANGE) {
 		/*
-		 * If there's no space reservation, check the entry will
-		 * fit before actually inserting it.
+		 * target_ip will always exist if RENAME_EXCHANGE flag is set
 		 */
-		error = xfs_dir_canenter(tp, target_dp, target_name, spaceres);
+		tgt_is_directory = S_ISDIR(target_ip->i_d.di_mode);
+
+		error = xfs_cross_rename(src_dp, src_name, src_ip, target_dp, target_name, target_ip,
+					 new_parent, src_is_directory, tgt_is_directory,
+					 &free_list, &first_block, tp, spaceres);
 		if (error)
-			goto error_return;
+			goto abort_return;
+	} else {
 		/*
-		 * If target does not exist and the rename crosses
-		 * directories, adjust the target directory link count
-		 * to account for the ".." reference from the new entry.
+		 * Set up the target.
 		 */
-		error = xfs_dir_createname(tp, target_dp, target_name,
-						src_ip->i_ino, &first_block,
-						&free_list, spaceres);
-		if (error == -ENOSPC)
-			goto error_return;
-		if (error)
-			goto abort_return;
+		if (target_ip == NULL) {
+			/*
+			 * If there's no space reservation, check the entry will
+			 * fit before actually inserting it.
+			 */
+			error = xfs_dir_canenter(tp, target_dp, target_name, spaceres);
+			if (error)
+				goto error_return;
+			/*
+			 * If target does not exist and the rename crosses
+			 * directories, adjust the target directory link count
+			 * to account for the ".." reference from the new entry.
+			 */
+			error = xfs_dir_createname(tp, target_dp, target_name,
+							src_ip->i_ino, &first_block,
+							&free_list, spaceres);
+			if (error == -ENOSPC)
+				goto error_return;
+			if (error)
+				goto abort_return;
 
-		xfs_trans_ichgtime(tp, target_dp,
-					XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+			xfs_trans_ichgtime(tp, target_dp,
+						XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
 
-		if (new_parent && src_is_directory) {
-			error = xfs_bumplink(tp, target_dp);
+			if (new_parent && src_is_directory) {
+				error = xfs_bumplink(tp, target_dp);
+				if (error)
+					goto abort_return;
+			}
+		} else { /* target_ip != NULL */
+			/*
+			 * If target exists and it's a directory, check that both
+			 * target and source are directories and that target can be
+			 * destroyed, or that neither is a directory.
+			 */
+			if (S_ISDIR(target_ip->i_d.di_mode)) {
+				/*
+				 * Make sure target dir is empty.
+				 */
+				if (!(xfs_dir_isempty(target_ip)) ||
+				    (target_ip->i_d.di_nlink > 2)) {
+					error = -EEXIST;
+					goto error_return;
+				}
+			}
+
+			/*
+			 * Link the source inode under the target name.
+			 * If the source inode is a directory and we are moving
+			 * it across directories, its ".." entry will be
+			 * inconsistent until we replace that down below.
+			 *
+			 * In case there is already an entry with the same
+			 * name at the destination directory, remove it first.
+			 */
+			error = xfs_dir_replace(tp, target_dp, target_name,
+						src_ip->i_ino,
+						&first_block, &free_list, spaceres);
 			if (error)
 				goto abort_return;
-		}
-	} else { /* target_ip != NULL */
+
+			xfs_trans_ichgtime(tp, target_dp,
+						XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+			/*
+			 * Decrement the link count on the target since the target
+			 * dir no longer points to it.
+			 */
+			error = xfs_droplink(tp, target_ip);
+			if (error)
+				goto abort_return;
+
+			if (src_is_directory) {
+				/*
+				 * Drop the link from the old "." entry.
+				 */
+				error = xfs_droplink(tp, target_ip);
+				if (error)
+					goto abort_return;
+			}
+		} /* target_ip != NULL */
+
 		/*
-		 * If target exists and it's a directory, check that both
-		 * target and source are directories and that target can be
-		 * destroyed, or that neither is a directory.
+		 * Remove the source.
 		 */
-		if (S_ISDIR(target_ip->i_d.di_mode)) {
+		if (new_parent && src_is_directory) {
 			/*
-			 * Make sure target dir is empty.
+			 * Rewrite the ".." entry to point to the new
+			 * directory.
 			 */
-			if (!(xfs_dir_isempty(target_ip)) ||
-			    (target_ip->i_d.di_nlink > 2)) {
-				error = -EEXIST;
-				goto error_return;
-			}
+			error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot,
+						target_dp->i_ino,
+						&first_block, &free_list, spaceres);
+			ASSERT(error != -EEXIST);
+			if (error)
+				goto abort_return;
 		}
 
 		/*
-		 * Link the source inode under the target name.
-		 * If the source inode is a directory and we are moving
-		 * it across directories, its ".." entry will be
-		 * inconsistent until we replace that down below.
+		 * We always want to hit the ctime on the source inode.
 		 *
-		 * In case there is already an entry with the same
-		 * name at the destination directory, remove it first.
+		 * This isn't strictly required by the standards since the source
+		 * inode isn't really being changed, but old unix file systems did
+		 * it and some incremental backup programs won't work without it.
 		 */
-		error = xfs_dir_replace(tp, target_dp, target_name,
-					src_ip->i_ino,
-					&first_block, &free_list, spaceres);
-		if (error)
-			goto abort_return;
-
-		xfs_trans_ichgtime(tp, target_dp,
-					XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+		xfs_trans_ichgtime(tp, src_ip, XFS_ICHGTIME_CHG);
+		xfs_trans_log_inode(tp, src_ip, XFS_ILOG_CORE);
 
 		/*
-		 * Decrement the link count on the target since the target
-		 * dir no longer points to it.
+		 * Adjust the link count on src_dp.  This is necessary when
+		 * renaming a directory, either within one parent when
+		 * the target existed, or across two parent directories.
 		 */
-		error = xfs_droplink(tp, target_ip);
-		if (error)
-			goto abort_return;
+		if (src_is_directory && (new_parent || target_ip != NULL)) {
 
-		if (src_is_directory) {
 			/*
-			 * Drop the link from the old "." entry.
+			 * Decrement link count on src_directory since the
+			 * entry that's moved no longer points to it.
 			 */
-			error = xfs_droplink(tp, target_ip);
+			error = xfs_droplink(tp, src_dp);
 			if (error)
 				goto abort_return;
 		}
-	} /* target_ip != NULL */
-
-	/*
-	 * Remove the source.
-	 */
-	if (new_parent && src_is_directory) {
-		/*
-		 * Rewrite the ".." entry to point to the new
-		 * directory.
-		 */
-		error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot,
-					target_dp->i_ino,
-					&first_block, &free_list, spaceres);
-		ASSERT(error != -EEXIST);
-		if (error)
-			goto abort_return;
-	}
 
-	/*
-	 * We always want to hit the ctime on the source inode.
-	 *
-	 * This isn't strictly required by the standards since the source
-	 * inode isn't really being changed, but old unix file systems did
-	 * it and some incremental backup programs won't work without it.
-	 */
-	xfs_trans_ichgtime(tp, src_ip, XFS_ICHGTIME_CHG);
-	xfs_trans_log_inode(tp, src_ip, XFS_ILOG_CORE);
-
-	/*
-	 * Adjust the link count on src_dp.  This is necessary when
-	 * renaming a directory, either within one parent when
-	 * the target existed, or across two parent directories.
-	 */
-	if (src_is_directory && (new_parent || target_ip != NULL)) {
-
-		/*
-		 * Decrement link count on src_directory since the
-		 * entry that's moved no longer points to it.
-		 */
-		error = xfs_droplink(tp, src_dp);
+		error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
+						&first_block, &free_list, spaceres);
 		if (error)
 			goto abort_return;
-	}
 
-	error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
-					&first_block, &free_list, spaceres);
-	if (error)
-		goto abort_return;
+		xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+		xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
+		if (new_parent)
+			xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
 
-	xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
-	xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
-	if (new_parent)
-		xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
+	} /* RENAME_EXCHANGE */
 
 	/*
 	 * If this is a synchronous mount, make sure that the
@@ -2920,6 +2939,94 @@ xfs_rename(
 	return error;
 }
 
+/* xfs_cross_rename()
+ *
+ * responsible to handle RENAME_EXCHANGE flag
+ * in renameat2() sytemcall
+ */
+int
+xfs_cross_rename(
+	xfs_inode_t	*src_dp,
+	struct xfs_name	*src_name,
+	xfs_inode_t	*src_ip,
+	xfs_inode_t	*target_dp,
+	struct xfs_name	*target_name,
+	xfs_inode_t	*target_ip,
+	int		new_parent,
+	int		src_is_directory,
+	int		tgt_is_directory,
+	xfs_bmap_free_t	*free_list,
+	xfs_fsblock_t	*first_block,
+	xfs_trans_t	*tp,
+	int		spaceres)
+{
+	int		error = 0;
+
+	/* Replace source inode */
+	error = xfs_dir_replace(tp, src_dp, src_name,
+				target_ip->i_ino,
+				first_block, free_list, spaceres);
+	if (error)
+		goto out_abort;
+
+	/* Replace target inode */
+	error = xfs_dir_replace(tp, target_dp, target_name,
+				src_ip->i_ino,
+				first_block, free_list, spaceres);
+	if (error)
+		goto out_abort;
+
+	xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+	/*
+	 * Update ".." entry to match the new parents
+	 *
+	 * In case we are crossing different file types between different
+	 * parents, we must update parent's link count to match the ".."
+	 * entry of the new child (or the removal of it).
+	 */
+	if (new_parent) {
+		xfs_trans_ichgtime(tp, target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+		if (tgt_is_directory) {
+			error = xfs_dir_replace(tp, target_ip, &xfs_name_dotdot, src_dp->i_ino,
+						first_block, free_list, spaceres);
+			if (error)
+				goto out_abort;
+			if (!src_is_directory) {
+				error = xfs_droplink(tp, target_dp);
+				if (error)
+					goto out_abort;
+				error = xfs_bumplink(tp, src_dp);
+				if (error)
+					goto out_abort;
+			}
+		}
+
+		if (src_is_directory) {
+			error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot, target_dp->i_ino,
+						first_block, free_list, spaceres);
+			if (error)
+				goto out_abort;
+			if (!tgt_is_directory) {
+				error = xfs_droplink(tp, src_dp);
+				if (error)
+					goto out_abort;
+				error = xfs_bumplink(tp, target_dp);
+				if (error)
+					goto out_abort;
+			}
+		}
+		xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
+	}
+	xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
+	xfs_trans_log_inode(tp, src_ip, XFS_ILOG_CORE);
+	xfs_trans_log_inode(tp, target_ip, XFS_ILOG_CORE);
+
+out_abort:
+	return error;
+}
+
 STATIC int
 xfs_iflush_cluster(
 	xfs_inode_t	*ip,
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index c10e3fa..c34dd41 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -340,7 +340,13 @@ int		xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
 int		xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name,
 			   struct xfs_inode *src_ip, struct xfs_inode *target_dp,
 			   struct xfs_name *target_name,
-			   struct xfs_inode *target_ip);
+			   struct xfs_inode *target_ip, unsigned int flags);
+int		xfs_cross_rename(struct xfs_inode *src_dp, struct xfs_name *src_name,
+			   struct xfs_inode *src_ip, struct xfs_inode *target_dp,
+			   struct xfs_name *target_name, struct xfs_inode *target_ip,
+			   int new_parent, int src_is_directory, int tgt_is_directory,
+			   struct xfs_bmap_free *free_list, xfs_fsblock_t *first_block,
+			   struct xfs_trans *tp, int spaceres);
 
 void		xfs_ilock(xfs_inode_t *, uint);
 int		xfs_ilock_nowait(xfs_inode_t *, uint);
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 8519442..d5ba974 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -391,7 +391,7 @@ xfs_vn_rename(
 	struct xfs_name	nname;
 
 	/* XFS does not support RENAME_EXCHANGE yet */
-	if (flags & ~RENAME_NOREPLACE)
+	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
 		return -EINVAL;
 
 	xfs_dentry_to_name(&oname, odentry, 0);
@@ -399,7 +399,7 @@ xfs_vn_rename(
 
 	return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode),
 			  XFS_I(ndir), &nname,
-			  new_inode ? XFS_I(new_inode) : NULL);
+			  new_inode ? XFS_I(new_inode) : NULL, flags);
 }
 
 /*
-- 
2.1.0

_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs




[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux