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