2005-04-17 02:20:36 +04:00
/*
2006-06-09 08:48:12 +04:00
* Copyright ( c ) 2000 - 2006 Silicon Graphics , Inc .
2005-11-02 06:58:39 +03:00
* 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"
2005-11-02 06:38:42 +03:00
# include "xfs_bit.h"
2005-04-17 02:20:36 +04:00
# 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"
2005-11-02 06:38:42 +03:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# include "xfs_dir2.h"
# include "xfs_dmapi.h"
# include "xfs_mount.h"
# include "xfs_bmap_btree.h"
2005-11-02 06:38:42 +03:00
# include "xfs_alloc_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_ialloc_btree.h"
# include "xfs_dir2_sf.h"
2005-11-02 06:38:42 +03:00
# include "xfs_attr_sf.h"
2005-04-17 02:20:36 +04:00
# 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"
2005-11-02 06:38:42 +03:00
# include "xfs_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_ialloc.h"
# include "xfs_itable.h"
# include "xfs_dfrag.h"
# include "xfs_error.h"
# include "xfs_rw.h"
2007-08-29 04:58:01 +04:00
# include "xfs_vnodeops.h"
2005-04-17 02:20:36 +04:00
/*
* Syssgi interface for swapext
*/
int
xfs_swapext (
2005-11-02 02:29:04 +03:00
xfs_swapext_t __user * sxu )
2005-04-17 02:20:36 +04:00
{
2005-11-02 02:29:04 +03:00
xfs_swapext_t * sxp ;
2008-02-05 04:13:07 +03:00
xfs_inode_t * ip , * tip ;
struct file * file , * target_file ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
2005-11-02 02:29:04 +03:00
sxp = kmem_alloc ( sizeof ( xfs_swapext_t ) , KM_MAYFAIL ) ;
2006-06-09 08:48:12 +04:00
if ( ! sxp ) {
2005-11-02 02:29:04 +03:00
error = XFS_ERROR ( ENOMEM ) ;
2008-02-05 04:13:07 +03:00
goto out ;
2005-11-02 02:29:04 +03:00
}
if ( copy_from_user ( sxp , sxu , sizeof ( xfs_swapext_t ) ) ) {
error = XFS_ERROR ( EFAULT ) ;
2008-02-05 04:13:07 +03:00
goto out_free_sxp ;
2005-11-02 02:29:04 +03:00
}
2005-04-17 02:20:36 +04:00
/* Pull information for the target fd */
2008-02-05 04:13:07 +03:00
file = fget ( ( int ) sxp - > sx_fdtarget ) ;
if ( ! file ) {
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( EINVAL ) ;
2008-02-05 04:13:07 +03:00
goto out_free_sxp ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 04:13:15 +03:00
if ( ! ( file - > f_mode & FMODE_WRITE ) | | ( file - > f_flags & O_APPEND ) ) {
error = XFS_ERROR ( EBADF ) ;
goto out_put_file ;
}
2008-02-05 04:13:07 +03:00
target_file = fget ( ( int ) sxp - > sx_fdtmp ) ;
if ( ! target_file ) {
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( EINVAL ) ;
2008-02-05 04:13:07 +03:00
goto out_put_file ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 04:13:15 +03:00
if ( ! ( target_file - > f_mode & FMODE_WRITE ) | |
( target_file - > f_flags & O_APPEND ) ) {
error = XFS_ERROR ( EBADF ) ;
goto out_put_target_file ;
}
2008-02-05 04:13:07 +03:00
ip = XFS_I ( file - > f_path . dentry - > d_inode ) ;
tip = XFS_I ( target_file - > f_path . dentry - > d_inode ) ;
2005-04-17 02:20:36 +04:00
if ( ip - > i_mount ! = tip - > i_mount ) {
2008-02-05 04:13:07 +03:00
error = XFS_ERROR ( EINVAL ) ;
goto out_put_target_file ;
2005-04-17 02:20:36 +04:00
}
if ( ip - > i_ino = = tip - > i_ino ) {
2008-02-05 04:13:07 +03:00
error = XFS_ERROR ( EINVAL ) ;
goto out_put_target_file ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 04:13:07 +03:00
if ( XFS_FORCED_SHUTDOWN ( ip - > i_mount ) ) {
error = XFS_ERROR ( EIO ) ;
goto out_put_target_file ;
2005-04-17 02:20:36 +04:00
}
2007-10-11 11:34:33 +04:00
error = xfs_swap_extents ( ip , tip , sxp ) ;
2006-06-09 08:48:12 +04:00
2008-02-05 04:13:07 +03:00
out_put_target_file :
fput ( target_file ) ;
out_put_file :
fput ( file ) ;
out_free_sxp :
2008-05-19 10:31:57 +04:00
kmem_free ( sxp ) ;
2008-02-05 04:13:07 +03:00
out :
2006-06-09 08:48:12 +04:00
return error ;
}
int
xfs_swap_extents (
xfs_inode_t * ip ,
xfs_inode_t * tip ,
xfs_swapext_t * sxp )
{
xfs_mount_t * mp ;
xfs_inode_t * ips [ 2 ] ;
xfs_trans_t * tp ;
xfs_bstat_t * sbp = & sxp - > sx_stat ;
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , * tvp ;
2006-06-09 08:48:12 +04:00
xfs_ifork_t * tempifp , * ifp , * tifp ;
int ilf_fields , tilf_fields ;
static uint lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL ;
int error = 0 ;
int aforkblks = 0 ;
int taforkblks = 0 ;
__uint64_t tmp ;
char locked = 0 ;
mp = ip - > i_mount ;
tempifp = kmem_alloc ( sizeof ( xfs_ifork_t ) , KM_MAYFAIL ) ;
if ( ! tempifp ) {
error = XFS_ERROR ( ENOMEM ) ;
goto error0 ;
}
sbp = & sxp - > sx_stat ;
vp = XFS_ITOV ( ip ) ;
tvp = XFS_ITOV ( tip ) ;
2005-04-17 02:20:36 +04:00
/* Lock in i_ino order */
if ( ip - > i_ino < tip - > i_ino ) {
ips [ 0 ] = ip ;
ips [ 1 ] = tip ;
} else {
ips [ 0 ] = tip ;
ips [ 1 ] = ip ;
}
2005-11-02 02:29:04 +03:00
2008-04-22 11:34:06 +04:00
xfs_lock_inodes ( ips , 2 , lock_flags ) ;
2006-06-09 08:48:12 +04:00
locked = 1 ;
2005-04-17 02:20:36 +04:00
/* Verify that both files have the same format */
if ( ( ip - > i_d . di_mode & S_IFMT ) ! = ( tip - > i_d . di_mode & S_IFMT ) ) {
error = XFS_ERROR ( EINVAL ) ;
goto error0 ;
}
/* Verify both files are either real-time or non-realtime */
2007-11-23 08:29:42 +03:00
if ( XFS_IS_REALTIME_INODE ( ip ) ! = XFS_IS_REALTIME_INODE ( tip ) ) {
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( EINVAL ) ;
goto error0 ;
}
/* Should never get a local format */
if ( ip - > i_d . di_format = = XFS_DINODE_FMT_LOCAL | |
tip - > i_d . di_format = = XFS_DINODE_FMT_LOCAL ) {
error = XFS_ERROR ( EINVAL ) ;
goto error0 ;
}
2005-06-21 09:47:39 +04:00
if ( VN_CACHED ( tvp ) ! = 0 ) {
2007-10-11 11:44:08 +04:00
xfs_inval_cached_trace ( tip , 0 , - 1 , 0 , - 1 ) ;
2007-08-29 04:58:01 +04:00
error = xfs_flushinval_pages ( tip , 0 , - 1 ,
FI_REMAPF_LOCKED ) ;
2007-05-08 07:49:27 +04:00
if ( error )
goto error0 ;
2005-06-21 09:47:39 +04:00
}
2005-04-17 02:20:36 +04:00
/* Verify O_DIRECT for ftmp */
if ( VN_CACHED ( tvp ) ! = 0 ) {
error = XFS_ERROR ( EINVAL ) ;
goto error0 ;
}
/* Verify all data are being swapped */
2005-11-02 02:29:04 +03:00
if ( sxp - > sx_offset ! = 0 | |
sxp - > sx_length ! = ip - > i_d . di_size | |
sxp - > sx_length ! = tip - > i_d . di_size ) {
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( EFAULT ) ;
goto error0 ;
}
/*
* If the target has extended attributes , the tmp file
* must also in order to ensure the correct data fork
* format .
*/
if ( XFS_IFORK_Q ( ip ) ! = XFS_IFORK_Q ( tip ) ) {
error = XFS_ERROR ( EINVAL ) ;
goto error0 ;
}
/*
* Compare the current change & modify times with that
* passed in . If they differ , we abort this swap .
* This is the mechanism used to ensure the calling
* process that the file was not changed out from
* under it .
*/
if ( ( sbp - > bs_ctime . tv_sec ! = ip - > i_d . di_ctime . t_sec ) | |
( sbp - > bs_ctime . tv_nsec ! = ip - > i_d . di_ctime . t_nsec ) | |
( sbp - > bs_mtime . tv_sec ! = ip - > i_d . di_mtime . t_sec ) | |
( sbp - > bs_mtime . tv_nsec ! = ip - > i_d . di_mtime . t_nsec ) ) {
error = XFS_ERROR ( EBUSY ) ;
goto error0 ;
}
/* We need to fail if the file is memory mapped. Once we have tossed
* all existing pages , the page fault will have no option
* but to go to the filesystem for pages . By making the page fault call
2006-06-09 11:00:52 +04:00
* vop_read ( or write in the case of autogrow ) they block on the iolock
2005-04-17 02:20:36 +04:00
* until we have switched the extents .
*/
if ( VN_MAPPED ( vp ) ) {
error = XFS_ERROR ( EBUSY ) ;
goto error0 ;
}
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
xfs_iunlock ( tip , XFS_ILOCK_EXCL ) ;
/*
* There is a race condition here since we gave up the
* ilock . However , the data fork will not change since
* we have the iolock ( locked for truncation too ) so we
* are safe . We don ' t really care if non - io related
* fields change .
*/
2007-08-29 04:58:01 +04:00
xfs_tosspages ( ip , 0 , - 1 , FI_REMAPF ) ;
2005-04-17 02:20:36 +04:00
tp = xfs_trans_alloc ( mp , XFS_TRANS_SWAPEXT ) ;
if ( ( error = xfs_trans_reserve ( tp , 0 ,
XFS_ICHANGE_LOG_RES ( mp ) , 0 ,
0 , 0 ) ) ) {
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
xfs_iunlock ( tip , XFS_IOLOCK_EXCL ) ;
xfs_trans_cancel ( tp , 0 ) ;
2005-11-02 02:29:04 +03:00
locked = 0 ;
goto error0 ;
2005-04-17 02:20:36 +04:00
}
2008-04-22 11:34:06 +04:00
xfs_lock_inodes ( ips , 2 , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
/*
* Count the number of extended attribute blocks
*/
if ( ( ( XFS_IFORK_Q ( ip ) ! = 0 ) & & ( ip - > i_d . di_anextents > 0 ) ) & &
( ip - > i_d . di_aformat ! = XFS_DINODE_FMT_LOCAL ) ) {
error = xfs_bmap_count_blocks ( tp , ip , XFS_ATTR_FORK , & aforkblks ) ;
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
2005-11-02 02:29:04 +03:00
goto error0 ;
2005-04-17 02:20:36 +04:00
}
}
if ( ( ( XFS_IFORK_Q ( tip ) ! = 0 ) & & ( tip - > i_d . di_anextents > 0 ) ) & &
( tip - > i_d . di_aformat ! = XFS_DINODE_FMT_LOCAL ) ) {
error = xfs_bmap_count_blocks ( tp , tip , XFS_ATTR_FORK ,
& taforkblks ) ;
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
2005-11-02 02:29:04 +03:00
goto error0 ;
2005-04-17 02:20:36 +04:00
}
}
/*
* Swap the data forks of the inodes
*/
ifp = & ip - > i_df ;
tifp = & tip - > i_df ;
2005-11-02 02:29:04 +03:00
* tempifp = * ifp ; /* struct copy */
* ifp = * tifp ; /* struct copy */
* tifp = * tempifp ; /* struct copy */
2005-04-17 02:20:36 +04:00
/*
* Fix the on - disk inode values
*/
tmp = ( __uint64_t ) ip - > i_d . di_nblocks ;
ip - > i_d . di_nblocks = tip - > i_d . di_nblocks - taforkblks + aforkblks ;
tip - > i_d . di_nblocks = tmp + taforkblks - aforkblks ;
tmp = ( __uint64_t ) ip - > i_d . di_nextents ;
ip - > i_d . di_nextents = tip - > i_d . di_nextents ;
tip - > i_d . di_nextents = tmp ;
tmp = ( __uint64_t ) ip - > i_d . di_format ;
ip - > i_d . di_format = tip - > i_d . di_format ;
tip - > i_d . di_format = tmp ;
ilf_fields = XFS_ILOG_CORE ;
switch ( ip - > i_d . di_format ) {
case XFS_DINODE_FMT_EXTENTS :
/* If the extents fit in the inode, fix the
* pointer . Otherwise it ' s already NULL or
* pointing to the extent .
*/
if ( ip - > i_d . di_nextents < = XFS_INLINE_EXTS ) {
ifp - > if_u1 . if_extents =
ifp - > if_u2 . if_inline_ext ;
}
ilf_fields | = XFS_ILOG_DEXT ;
break ;
case XFS_DINODE_FMT_BTREE :
ilf_fields | = XFS_ILOG_DBROOT ;
break ;
}
tilf_fields = XFS_ILOG_CORE ;
switch ( tip - > i_d . di_format ) {
case XFS_DINODE_FMT_EXTENTS :
/* If the extents fit in the inode, fix the
* pointer . Otherwise it ' s already NULL or
* pointing to the extent .
*/
if ( tip - > i_d . di_nextents < = XFS_INLINE_EXTS ) {
tifp - > if_u1 . if_extents =
tifp - > if_u2 . if_inline_ext ;
}
tilf_fields | = XFS_ILOG_DEXT ;
break ;
case XFS_DINODE_FMT_BTREE :
tilf_fields | = XFS_ILOG_DBROOT ;
break ;
}
/*
* Increment vnode ref counts since xfs_trans_commit &
* xfs_trans_cancel will both unlock the inodes and
* decrement the associated ref counts .
*/
VN_HOLD ( vp ) ;
VN_HOLD ( tvp ) ;
xfs_trans_ijoin ( tp , ip , lock_flags ) ;
xfs_trans_ijoin ( tp , tip , lock_flags ) ;
xfs_trans_log_inode ( tp , ip , ilf_fields ) ;
xfs_trans_log_inode ( tp , tip , tilf_fields ) ;
/*
* If this is a synchronous mount , make sure that the
* transaction goes to disk before returning to the user .
*/
if ( mp - > m_flags & XFS_MOUNT_WSYNC ) {
xfs_trans_set_sync ( tp ) ;
}
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_SWAPEXT ) ;
2005-11-02 02:29:04 +03:00
locked = 0 ;
2005-04-17 02:20:36 +04:00
error0 :
if ( locked ) {
xfs_iunlock ( ip , lock_flags ) ;
xfs_iunlock ( tip , lock_flags ) ;
}
2005-11-02 02:29:04 +03:00
if ( tempifp ! = NULL )
2008-05-19 10:31:57 +04:00
kmem_free ( tempifp ) ;
2005-04-17 02:20:36 +04:00
return error ;
}