@ -3973,7 +3973,27 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
return sys_linkat ( AT_FDCWD , oldname , AT_FDCWD , newname , 0 ) ;
}
/*
/**
* vfs_rename - rename a filesystem object
* @ old_dir : parent of source
* @ old_dentry : source
* @ new_dir : parent of destination
* @ new_dentry : destination
* @ delegated_inode : returns an inode needing a delegation break
*
* The caller must hold multiple mutexes - - see lock_rename ( ) ) .
*
* If vfs_rename discovers a delegation in need of breaking at either
* the source or destination , it will return - EWOULDBLOCK and return a
* reference to the inode in delegated_inode . The caller should then
* break the delegation and retry . Because breaking a delegation may
* take a long time , the caller should drop all locks before doing
* so .
*
* Alternatively , a caller may pass NULL for delegated_inode . This may
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported .
*
* The worst of all namespace operations - renaming directory . " Perverted "
* doesn ' t even start to describe it . Somebody in UCB had a heck of a trip . . .
* Problems :
@ -4001,19 +4021,39 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
* - > i_mutex on parents , which works but leads to some truly excessive
* locking ] .
*/
static int vfs_rename_dir ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
int vfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry ,
struct inode * * delegated_inode )
{
int error = 0 ;
int error ;
bool is_dir = d_is_dir ( old_dentry ) ;
const unsigned char * old_name ;
struct inode * source = old_dentry - > d_inode ;
struct inode * target = new_dentry - > d_inode ;
unsigned max_links = new_dir - > i_sb - > s_max_links ;
if ( source = = target )
return 0 ;
error = may_delete ( old_dir , old_dentry , is_dir ) ;
if ( error )
return error ;
if ( ! target )
error = may_create ( new_dir , new_dentry ) ;
else
error = may_delete ( new_dir , new_dentry , is_dir ) ;
if ( error )
return error ;
if ( ! old_dir - > i_op - > rename )
return - EPERM ;
/*
* If we are going to change the parent - check write permissions ,
* we ' ll need to flip ' . . ' .
*/
if ( new_dir ! = old_dir ) {
error = inode_permission ( old_dentry - > d_inode , MAY_WRITE ) ;
if ( is_dir & & new_dir ! = old_dir ) {
error = inode_permission ( sourc e, MAY_WRITE ) ;
if ( error )
return error ;
}
@ -4022,134 +4062,57 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
if ( error )
return error ;
old_name = fsnotify_oldname_init ( old_dentry - > d_name . name ) ;
dget ( new_dentry ) ;
if ( target )
if ( ! is_dir )
lock_two_nondirectories ( source , target ) ;
else if ( target )
mutex_lock ( & target - > i_mutex ) ;
error = - EBUSY ;
if ( d_mountpoint ( old_dentry ) | | d_mountpoint ( new_dentry ) )
goto out ;
error = - EMLINK ;
if ( max_links & & ! target & & new_dir ! = old_dir & &
new_dir - > i_nlink > = max_links )
goto out ;
if ( target )
shrink_dcache_parent ( new_dentry ) ;
error = old_dir - > i_op - > rename ( old_dir , old_dentry , new_dir , new_dentry ) ;
if ( error )
goto out ;
if ( target ) {
target - > i_flags | = S_DEAD ;
dont_mount ( new_dentry ) ;
}
if ( ! ( old_dir - > i_sb - > s_type - > fs_flags & FS_RENAME_DOES_D_MOVE ) )
d_move ( old_dentry , new_dentry ) ;
out :
if ( target )
mutex_unlock ( & target - > i_mutex ) ;
dput ( new_dentry ) ;
return error ;
}
static int vfs_rename_other ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry ,
struct inode * * delegated_inode )
{
struct inode * target = new_dentry - > d_inode ;
struct inode * source = old_dentry - > d_inode ;
int error ;
error = security_inode_rename ( old_dir , old_dentry , new_dir , new_dentry ) ;
if ( error )
return error ;
dget ( new_dentry ) ;
lock_two_nondirectories ( source , target ) ;
if ( is_dir ) {
unsigned max_links = new_dir - > i_sb - > s_max_links ;
error = - EBUSY ;
if ( d_mountpoint ( old_dentry ) | | d_mountpoint ( new_dentry ) )
goto out ;
error = - EMLINK ;
if ( max_links & & ! target & & new_dir ! = old_dir & &
new_dir - > i_nlink > = max_links )
goto out ;
error = try_break_deleg ( source , delegated_inode ) ;
if ( error )
goto out ;
if ( target ) {
error = try_break_deleg ( target , delegated_inode ) ;
if ( target )
shrink_dcache_parent ( new_dentry ) ;
} else {
error = try_break_deleg ( source , delegated_inode ) ;
if ( error )
goto out ;
if ( target ) {
error = try_break_deleg ( target , delegated_inode ) ;
if ( error )
goto out ;
}
}
error = old_dir - > i_op - > rename ( old_dir , old_dentry , new_dir , new_dentry ) ;
if ( error )
goto out ;
if ( target )
if ( target ) {
if ( is_dir )
target - > i_flags | = S_DEAD ;
dont_mount ( new_dentry ) ;
}
if ( ! ( old_dir - > i_sb - > s_type - > fs_flags & FS_RENAME_DOES_D_MOVE ) )
d_move ( old_dentry , new_dentry ) ;
out :
unlock_two_nondirectories ( source , target ) ;
if ( ! is_dir )
unlock_two_nondirectories ( source , target ) ;
else if ( target )
mutex_unlock ( & target - > i_mutex ) ;
dput ( new_dentry ) ;
return error ;
}
/**
* vfs_rename - rename a filesystem object
* @ old_dir : parent of source
* @ old_dentry : source
* @ new_dir : parent of destination
* @ new_dentry : destination
* @ delegated_inode : returns an inode needing a delegation break
*
* The caller must hold multiple mutexes - - see lock_rename ( ) ) .
*
* If vfs_rename discovers a delegation in need of breaking at either
* the source or destination , it will return - EWOULDBLOCK and return a
* reference to the inode in delegated_inode . The caller should then
* break the delegation and retry . Because breaking a delegation may
* take a long time , the caller should drop all locks before doing
* so .
*
* Alternatively , a caller may pass NULL for delegated_inode . This may
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported .
*/
int vfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry ,
struct inode * * delegated_inode )
{
int error ;
int is_dir = d_is_dir ( old_dentry ) ;
const unsigned char * old_name ;
if ( old_dentry - > d_inode = = new_dentry - > d_inode )
return 0 ;
error = may_delete ( old_dir , old_dentry , is_dir ) ;
if ( error )
return error ;
if ( ! new_dentry - > d_inode )
error = may_create ( new_dir , new_dentry ) ;
else
error = may_delete ( new_dir , new_dentry , is_dir ) ;
if ( error )
return error ;
if ( ! old_dir - > i_op - > rename )
return - EPERM ;
old_name = fsnotify_oldname_init ( old_dentry - > d_name . name ) ;
if ( is_dir )
error = vfs_rename_dir ( old_dir , old_dentry , new_dir , new_dentry ) ;
else
error = vfs_rename_other ( old_dir , old_dentry , new_dir , new_dentry , delegated_inode ) ;
if ( ! error )
fsnotify_move ( old_dir , new_dir , old_name , is_dir ,
new_den try - > d_inod e, old_dentry ) ;
target , old_dentry ) ;
fsnotify_oldname_free ( old_name ) ;
return error ;