xfs
[Top] [All Lists]

[PATCH 2/2] Add support to RENAME_EXCHANGE flag

To: xfs@xxxxxxxxxxx
Subject: [PATCH 2/2] Add support to RENAME_EXCHANGE flag
From: Carlos Maiolino <cmaiolino@xxxxxxxxxx>
Date: Mon, 10 Nov 2014 15:41:12 -0200
Delivered-to: xfs@xxxxxxxxxxx
In-reply-to: <1415641272-29383-1-git-send-email-cmaiolino@xxxxxxxxxx>
References: <1415641272-29383-1-git-send-email-cmaiolino@xxxxxxxxxx>
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

<Prev in Thread] Current Thread [Next in Thread>