2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2003 , 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_fs.h"
2005-04-17 02:20:36 +04:00
# include "xfs_types.h"
# include "xfs_log.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inum.h"
2005-04-17 02:20:36 +04:00
# include "xfs_trans.h"
# include "xfs_sb.h"
2007-08-28 08:00:13 +04:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# include "xfs_dir2.h"
# include "xfs_mount.h"
2005-11-02 06:38:42 +03:00
# include "xfs_da_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap_btree.h"
# include "xfs_dinode.h"
# include "xfs_inode.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inode_item.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
# include "xfs_error.h"
# include "xfs_quota.h"
# include "xfs_utils.h"
# include "xfs_trans_space.h"
2007-11-23 08:28:09 +03:00
# include "xfs_vnodeops.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2005-04-17 02:20:36 +04:00
/*
2008-04-22 11:34:06 +04:00
* Enter all inodes for a rename transaction into a sorted array .
2005-04-17 02:20:36 +04:00
*/
2008-04-22 11:34:06 +04:00
STATIC void
xfs_sort_for_rename (
2008-04-10 06:22:07 +04:00
xfs_inode_t * dp1 , /* in: old (source) directory inode */
xfs_inode_t * dp2 , /* in: new (target) directory inode */
xfs_inode_t * ip1 , /* in: inode of old entry */
2008-04-22 11:34:06 +04:00
xfs_inode_t * ip2 , /* in: inode of new entry, if it
2005-04-17 02:20:36 +04:00
already exists , NULL otherwise . */
2008-04-10 06:22:07 +04:00
xfs_inode_t * * i_tab , /* out: array of inode returned, sorted */
int * num_inodes ) /* out: number of inodes in array */
2005-04-17 02:20:36 +04:00
{
2008-04-10 06:22:07 +04:00
xfs_inode_t * temp ;
2005-04-17 02:20:36 +04:00
int i , j ;
/*
* i_tab contains a list of pointers to inodes . We initialize
* the table here & we ' ll sort it . We will then use it to
* order the acquisition of the inode locks .
*
* Note that the table may contain duplicates . e . g . , dp1 = = dp2 .
*/
i_tab [ 0 ] = dp1 ;
i_tab [ 1 ] = dp2 ;
i_tab [ 2 ] = ip1 ;
2008-04-22 11:34:06 +04:00
if ( ip2 ) {
2005-04-17 02:20:36 +04:00
* num_inodes = 4 ;
i_tab [ 3 ] = ip2 ;
2008-04-22 11:34:06 +04:00
} else {
* num_inodes = 3 ;
i_tab [ 3 ] = NULL ;
2005-04-17 02:20:36 +04:00
}
/*
* Sort the elements via bubble sort . ( Remember , there are at
* most 4 elements to sort , so this is adequate . )
*/
2008-04-22 11:34:06 +04:00
for ( i = 0 ; i < * num_inodes ; i + + ) {
for ( j = 1 ; j < * num_inodes ; j + + ) {
2005-04-17 02:20:36 +04:00
if ( i_tab [ j ] - > i_ino < i_tab [ j - 1 ] - > i_ino ) {
temp = i_tab [ j ] ;
i_tab [ j ] = i_tab [ j - 1 ] ;
i_tab [ j - 1 ] = temp ;
}
}
}
}
/*
* xfs_rename
*/
int
xfs_rename (
2007-08-28 10:12:30 +04:00
xfs_inode_t * src_dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * src_name ,
xfs_inode_t * src_ip ,
2008-03-06 05:46:19 +03:00
xfs_inode_t * target_dp ,
2008-04-22 11:34:06 +04:00
struct xfs_name * target_name ,
xfs_inode_t * target_ip )
2005-04-17 02:20:36 +04:00
{
2008-04-22 11:34:06 +04:00
xfs_trans_t * tp = NULL ;
2007-08-28 10:12:30 +04:00
xfs_mount_t * mp = src_dp - > i_mount ;
2005-04-17 02:20:36 +04:00
int new_parent ; /* moving to a new dir */
int src_is_directory ; /* src_name is a directory */
int error ;
xfs_bmap_free_t free_list ;
xfs_fsblock_t first_block ;
int cancel_flags ;
int committed ;
xfs_inode_t * inodes [ 4 ] ;
int spaceres ;
int num_inodes ;
2010-06-24 05:57:09 +04:00
trace_xfs_rename ( src_dp , target_dp , src_name , target_name ) ;
2005-04-17 02:20:36 +04:00
2008-04-22 11:34:06 +04:00
new_parent = ( src_dp ! = target_dp ) ;
2011-07-26 10:31:30 +04:00
src_is_directory = S_ISDIR ( src_ip - > i_d . di_mode ) ;
2005-04-17 02:20:36 +04:00
2008-04-22 11:34:06 +04:00
if ( src_is_directory ) {
2005-04-17 02:20:36 +04:00
/*
* Check for link count overflow on target_dp
*/
2008-04-22 11:34:06 +04:00
if ( target_ip = = NULL & & new_parent & &
2005-04-17 02:20:36 +04:00
target_dp - > i_d . di_nlink > = XFS_MAXLINK ) {
error = XFS_ERROR ( EMLINK ) ;
2008-04-22 11:34:06 +04:00
goto std_return ;
2005-04-17 02:20:36 +04:00
}
}
2008-04-22 11:34:06 +04:00
xfs_sort_for_rename ( src_dp , target_dp , src_ip , target_ip ,
inodes , & num_inodes ) ;
2005-04-17 02:20:36 +04:00
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2005-04-17 02:20:36 +04:00
tp = xfs_trans_alloc ( mp , XFS_TRANS_RENAME ) ;
cancel_flags = XFS_TRANS_RELEASE_LOG_RES ;
2008-04-10 06:22:07 +04:00
spaceres = XFS_RENAME_SPACE_RES ( mp , target_name - > len ) ;
2005-04-17 02:20:36 +04:00
error = xfs_trans_reserve ( tp , spaceres , XFS_RENAME_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES , XFS_RENAME_LOG_COUNT ) ;
if ( error = = ENOSPC ) {
spaceres = 0 ;
error = xfs_trans_reserve ( tp , 0 , XFS_RENAME_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES , XFS_RENAME_LOG_COUNT ) ;
}
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
2008-04-22 11:34:12 +04:00
goto std_return ;
2005-04-17 02:20:36 +04:00
}
/*
* Attach the dquots to the inodes
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_vop_rename_dqattach ( inodes ) ;
if ( error ) {
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( tp , cancel_flags ) ;
2008-04-22 11:34:12 +04:00
goto std_return ;
2005-04-17 02:20:36 +04:00
}
/*
2008-04-22 11:34:06 +04:00
* Lock all the participating inodes . Depending upon whether
* the target_name exists in the target directory , and
* whether the target directory is the same as the source
* directory , we can lock from 2 to 4 inodes .
*/
xfs_lock_inodes ( inodes , num_inodes , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
/*
* Join all the inodes to the transaction . From this point on ,
* we can rely on either trans_commit or trans_cancel to unlock
2010-06-24 05:36:58 +04:00
* them .
2005-04-17 02:20:36 +04:00
*/
2010-06-24 05:36:58 +04:00
xfs_trans_ijoin_ref ( tp , src_dp , XFS_ILOCK_EXCL ) ;
if ( new_parent )
xfs_trans_ijoin_ref ( tp , target_dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ijoin_ref ( tp , src_ip , XFS_ILOCK_EXCL ) ;
if ( target_ip )
xfs_trans_ijoin_ref ( tp , target_ip , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
2008-12-09 12:47:31 +03:00
/*
* If we are using project inheritance , we only allow renames
* into our tree when the project IDs are the same ; else the
* tree quota mechanism would be circumvented .
*/
if ( unlikely ( ( target_dp - > i_d . di_flags & XFS_DIFLAG_PROJINHERIT ) & &
2010-09-26 10:10:18 +04:00
( xfs_get_projid ( target_dp ) ! = xfs_get_projid ( src_ip ) ) ) ) {
2008-12-09 12:47:31 +03:00
error = XFS_ERROR ( EXDEV ) ;
goto error_return ;
}
2005-04-17 02:20:36 +04:00
/*
* Set up the target .
*/
if ( target_ip = = NULL ) {
/*
* If there ' s no space reservation , check the entry will
* fit before actually inserting it .
*/
2008-04-10 06:22:07 +04:00
error = xfs_dir_canenter ( tp , target_dp , target_name , spaceres ) ;
if ( error )
2005-04-17 02:20:36 +04:00
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 .
*/
2006-06-20 07:04:51 +04:00
error = xfs_dir_createname ( tp , target_dp , target_name ,
2008-04-10 06:22:07 +04:00
src_ip - > i_ino , & first_block ,
& free_list , spaceres ) ;
2006-06-20 07:04:51 +04:00
if ( error = = ENOSPC )
2005-04-17 02:20:36 +04:00
goto error_return ;
2006-06-20 07:04:51 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , target_dp ,
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
if ( new_parent & & src_is_directory ) {
error = xfs_bumplink ( tp , target_dp ) ;
2006-06-20 07:04:51 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
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 .
*/
2011-07-26 10:31:30 +04:00
if ( S_ISDIR ( target_ip - > i_d . di_mode ) ) {
2005-04-17 02:20:36 +04:00
/*
* Make sure target dir is empty .
*/
2006-06-20 07:04:51 +04:00
if ( ! ( xfs_dir_isempty ( target_ip ) ) | |
2005-04-17 02:20:36 +04:00
( target_ip - > i_d . di_nlink > 2 ) ) {
error = XFS_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 .
*/
2006-06-20 07:04:51 +04:00
error = xfs_dir_replace ( tp , target_dp , target_name ,
2008-04-10 06:22:07 +04:00
src_ip - > i_ino ,
2006-06-20 07:04:51 +04:00
& first_block , & free_list , spaceres ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , target_dp ,
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
/*
* Decrement the link count on the target since the target
* dir no longer points to it .
*/
error = xfs_droplink ( tp , target_ip ) ;
2006-06-20 07:04:51 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
if ( src_is_directory ) {
/*
* Drop the link from the old " . " entry .
*/
error = xfs_droplink ( tp , target_ip ) ;
2006-06-20 07:04:51 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
}
} /* target_ip != NULL */
/*
* Remove the source .
*/
if ( new_parent & & src_is_directory ) {
/*
* Rewrite the " .. " entry to point to the new
* directory .
*/
2008-04-10 06:22:07 +04:00
error = xfs_dir_replace ( tp , src_ip , & xfs_name_dotdot ,
target_dp - > i_ino ,
2006-06-20 07:04:51 +04:00
& first_block , & free_list , spaceres ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( error ! = EEXIST ) ;
2006-06-20 07:04:51 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
}
2008-06-27 07:34:26 +04:00
/*
* 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 .
*/
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , src_ip , XFS_ICHGTIME_CHG ) ;
2010-12-07 13:16:41 +03:00
xfs_trans_log_inode ( tp , src_ip , XFS_ILOG_CORE ) ;
2008-06-27 07:34:26 +04:00
2005-04-17 02:20:36 +04:00
/*
* 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 ) ;
2006-06-20 07:04:51 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
}
2008-04-10 06:22:07 +04:00
error = xfs_dir_removename ( tp , src_dp , src_name , src_ip - > i_ino ,
& first_block , & free_list , spaceres ) ;
2006-06-20 07:04:51 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , src_dp , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
xfs_trans_log_inode ( tp , src_dp , XFS_ILOG_CORE ) ;
2008-11-28 06:23:37 +03:00
if ( new_parent )
2005-04-17 02:20:36 +04:00
xfs_trans_log_inode ( tp , target_dp , XFS_ILOG_CORE ) ;
/*
* If this is a synchronous mount , make sure that the
* rename transaction goes to disk before returning to
* the user .
*/
if ( mp - > m_flags & ( XFS_MOUNT_WSYNC | XFS_MOUNT_DIRSYNC ) ) {
xfs_trans_set_sync ( tp ) ;
}
2007-02-10 10:37:16 +03:00
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
xfs_bmap_cancel ( & free_list ) ;
xfs_trans_cancel ( tp , ( XFS_TRANS_RELEASE_LOG_RES |
XFS_TRANS_ABORT ) ) ;
goto std_return ;
}
/*
* trans_commit will unlock src_ip , target_ip & decrement
* the vnode references .
*/
2010-06-23 12:11:15 +04:00
return xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
abort_return :
cancel_flags | = XFS_TRANS_ABORT ;
error_return :
xfs_bmap_cancel ( & free_list ) ;
xfs_trans_cancel ( tp , cancel_flags ) ;
2010-06-23 12:11:15 +04:00
std_return :
return error ;
2005-04-17 02:20:36 +04:00
}