2013-04-03 09:11:18 +04:00
/*
* Copyright ( c ) 2000 - 2006 Silicon Graphics , Inc .
* Copyright ( c ) 2012 - 2013 Red Hat , Inc .
* All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation .
*
* 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 .
*
* 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
*/
# include "xfs.h"
2013-10-23 03:50:10 +04:00
# include "xfs_shared.h"
2013-04-03 09:11:18 +04:00
# include "xfs_fs.h"
2013-08-12 14:49:26 +04:00
# include "xfs_format.h"
2013-10-23 03:50:10 +04:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2013-04-03 09:11:18 +04:00
# include "xfs_bit.h"
# include "xfs_sb.h"
# include "xfs_ag.h"
# include "xfs_mount.h"
2013-10-15 02:17:51 +04:00
# include "xfs_da_format.h"
2013-08-12 14:49:37 +04:00
# include "xfs_dir2.h"
2013-04-03 09:11:18 +04:00
# include "xfs_bmap_btree.h"
# include "xfs_ialloc_btree.h"
# include "xfs_dinode.h"
# include "xfs_inode.h"
# include "xfs_ialloc.h"
# include "xfs_alloc.h"
# include "xfs_bmap.h"
2013-08-12 14:49:42 +04:00
# include "xfs_bmap_util.h"
2013-04-03 09:11:18 +04:00
# include "xfs_error.h"
# include "xfs_quota.h"
# include "xfs_trans_space.h"
# include "xfs_trace.h"
# include "xfs_symlink.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2013-09-02 04:32:00 +04:00
# include "xfs_buf_item.h"
2013-10-23 03:50:10 +04:00
# include "xfs_log.h"
2013-04-03 09:11:18 +04:00
/* ----- Kernel only functions below ----- */
STATIC int
xfs_readlink_bmap (
2013-04-03 09:11:19 +04:00
struct xfs_inode * ip ,
char * link )
2013-04-03 09:11:18 +04:00
{
2013-04-03 09:11:19 +04:00
struct xfs_mount * mp = ip - > i_mount ;
struct xfs_bmbt_irec mval [ XFS_SYMLINK_MAPS ] ;
struct xfs_buf * bp ;
xfs_daddr_t d ;
char * cur_chunk ;
int pathlen = ip - > i_d . di_size ;
int nmaps = XFS_SYMLINK_MAPS ;
int byte_cnt ;
int n ;
int error = 0 ;
int fsblocks = 0 ;
int offset ;
2013-04-03 09:11:18 +04:00
2013-04-03 09:11:19 +04:00
fsblocks = xfs_symlink_blocks ( mp , pathlen ) ;
error = xfs_bmapi_read ( ip , 0 , fsblocks , mval , & nmaps , 0 ) ;
2013-04-03 09:11:18 +04:00
if ( error )
goto out ;
2013-04-03 09:11:19 +04:00
offset = 0 ;
2013-04-03 09:11:18 +04:00
for ( n = 0 ; n < nmaps ; n + + ) {
d = XFS_FSB_TO_DADDR ( mp , mval [ n ] . br_startblock ) ;
byte_cnt = XFS_FSB_TO_B ( mp , mval [ n ] . br_blockcount ) ;
2013-04-03 09:11:19 +04:00
bp = xfs_buf_read ( mp - > m_ddev_targp , d , BTOBB ( byte_cnt ) , 0 ,
& xfs_symlink_buf_ops ) ;
2013-04-03 09:11:18 +04:00
if ( ! bp )
return XFS_ERROR ( ENOMEM ) ;
error = bp - > b_error ;
if ( error ) {
xfs_buf_ioerror_alert ( bp , __func__ ) ;
xfs_buf_relse ( bp ) ;
goto out ;
}
2013-04-03 09:11:19 +04:00
byte_cnt = XFS_SYMLINK_BUF_SPACE ( mp , byte_cnt ) ;
2013-04-03 09:11:18 +04:00
if ( pathlen < byte_cnt )
byte_cnt = pathlen ;
2013-04-03 09:11:19 +04:00
cur_chunk = bp - > b_addr ;
if ( xfs_sb_version_hascrc ( & mp - > m_sb ) ) {
if ( ! xfs_symlink_hdr_ok ( mp , ip - > i_ino , offset ,
byte_cnt , bp ) ) {
error = EFSCORRUPTED ;
xfs_alert ( mp ,
" symlink header does not match required off/len/owner (0x%x/Ox%x,0x%llx) " ,
offset , byte_cnt , ip - > i_ino ) ;
xfs_buf_relse ( bp ) ;
goto out ;
}
cur_chunk + = sizeof ( struct xfs_dsymlink_hdr ) ;
}
memcpy ( link + offset , bp - > b_addr , byte_cnt ) ;
2013-04-03 09:11:18 +04:00
pathlen - = byte_cnt ;
2013-04-03 09:11:19 +04:00
offset + = byte_cnt ;
2013-04-03 09:11:18 +04:00
xfs_buf_relse ( bp ) ;
}
2013-04-03 09:11:19 +04:00
ASSERT ( pathlen = = 0 ) ;
2013-04-03 09:11:18 +04:00
link [ ip - > i_d . di_size ] = ' \0 ' ;
error = 0 ;
out :
return error ;
}
int
xfs_readlink (
2013-04-03 09:11:19 +04:00
struct xfs_inode * ip ,
2013-04-03 09:11:18 +04:00
char * link )
{
2013-04-03 09:11:19 +04:00
struct xfs_mount * mp = ip - > i_mount ;
2013-04-03 09:11:18 +04:00
xfs_fsize_t pathlen ;
int error = 0 ;
trace_xfs_readlink ( ip ) ;
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
xfs_ilock ( ip , XFS_ILOCK_SHARED ) ;
pathlen = ip - > i_d . di_size ;
if ( ! pathlen )
goto out ;
if ( pathlen < 0 | | pathlen > MAXPATHLEN ) {
xfs_alert ( mp , " %s: inode (%llu) bad symlink length (%lld) " ,
__func__ , ( unsigned long long ) ip - > i_ino ,
( long long ) pathlen ) ;
ASSERT ( 0 ) ;
error = XFS_ERROR ( EFSCORRUPTED ) ;
goto out ;
}
if ( ip - > i_df . if_flags & XFS_IFINLINE ) {
memcpy ( link , ip - > i_df . if_u1 . if_data , pathlen ) ;
link [ pathlen ] = ' \0 ' ;
} else {
error = xfs_readlink_bmap ( ip , link ) ;
}
out :
xfs_iunlock ( ip , XFS_ILOCK_SHARED ) ;
return error ;
}
int
xfs_symlink (
2013-04-03 09:11:19 +04:00
struct xfs_inode * dp ,
2013-04-03 09:11:18 +04:00
struct xfs_name * link_name ,
const char * target_path ,
umode_t mode ,
2013-04-03 09:11:19 +04:00
struct xfs_inode * * ipp )
2013-04-03 09:11:18 +04:00
{
2013-04-03 09:11:19 +04:00
struct xfs_mount * mp = dp - > i_mount ;
struct xfs_trans * tp = NULL ;
struct xfs_inode * ip = NULL ;
int error = 0 ;
2013-04-03 09:11:18 +04:00
int pathlen ;
2013-04-03 09:11:19 +04:00
struct xfs_bmap_free free_list ;
2013-04-03 09:11:18 +04:00
xfs_fsblock_t first_block ;
2013-04-03 09:11:19 +04:00
bool unlock_dp_on_error = false ;
2013-04-03 09:11:18 +04:00
uint cancel_flags ;
int committed ;
xfs_fileoff_t first_fsb ;
xfs_filblks_t fs_blocks ;
int nmaps ;
2013-04-03 09:11:19 +04:00
struct xfs_bmbt_irec mval [ XFS_SYMLINK_MAPS ] ;
2013-04-03 09:11:18 +04:00
xfs_daddr_t d ;
const char * cur_chunk ;
int byte_cnt ;
int n ;
xfs_buf_t * bp ;
prid_t prid ;
2013-06-28 02:25:07 +04:00
struct xfs_dquot * udqp = NULL ;
struct xfs_dquot * gdqp = NULL ;
2013-07-11 09:00:40 +04:00
struct xfs_dquot * pdqp = NULL ;
2013-04-03 09:11:18 +04:00
uint resblks ;
* ipp = NULL ;
trace_xfs_symlink ( dp , link_name ) ;
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
/*
* Check component lengths of the target path name .
*/
pathlen = strlen ( target_path ) ;
if ( pathlen > = MAXPATHLEN ) /* total string too long */
return XFS_ERROR ( ENAMETOOLONG ) ;
udqp = gdqp = NULL ;
if ( dp - > i_d . di_flags & XFS_DIFLAG_PROJINHERIT )
prid = xfs_get_projid ( dp ) ;
else
prid = XFS_PROJID_DEFAULT ;
/*
* Make sure that we have allocated dquot ( s ) on disk .
*/
2013-08-15 22:08:01 +04:00
error = xfs_qm_vop_dqalloc ( dp ,
xfs_kuid_to_uid ( current_fsuid ( ) ) ,
xfs_kgid_to_gid ( current_fsgid ( ) ) , prid ,
XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT ,
& udqp , & gdqp , & pdqp ) ;
2013-04-03 09:11:18 +04:00
if ( error )
goto std_return ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_SYMLINK ) ;
cancel_flags = XFS_TRANS_RELEASE_LOG_RES ;
/*
* The symlink will fit into the inode data fork ?
* There can ' t be any attributes so we get the whole variable part .
*/
if ( pathlen < = XFS_LITINO ( mp , dp - > i_d . di_version ) )
fs_blocks = 0 ;
else
2013-05-27 10:38:20 +04:00
fs_blocks = xfs_symlink_blocks ( mp , pathlen ) ;
2013-04-03 09:11:18 +04:00
resblks = XFS_SYMLINK_SPACE_RES ( mp , link_name - > len , fs_blocks ) ;
2013-08-12 14:49:59 +04:00
error = xfs_trans_reserve ( tp , & M_RES ( mp ) - > tr_symlink , resblks , 0 ) ;
2013-04-03 09:11:18 +04:00
if ( error = = ENOSPC & & fs_blocks = = 0 ) {
resblks = 0 ;
2013-08-12 14:49:59 +04:00
error = xfs_trans_reserve ( tp , & M_RES ( mp ) - > tr_symlink , 0 , 0 ) ;
2013-04-03 09:11:18 +04:00
}
if ( error ) {
cancel_flags = 0 ;
goto error_return ;
}
xfs_ilock ( dp , XFS_ILOCK_EXCL | XFS_ILOCK_PARENT ) ;
unlock_dp_on_error = true ;
/*
* Check whether the directory allows new symlinks or not .
*/
if ( dp - > i_d . di_flags & XFS_DIFLAG_NOSYMLINKS ) {
error = XFS_ERROR ( EPERM ) ;
goto error_return ;
}
/*
* Reserve disk quota : blocks and inode .
*/
2013-07-11 09:00:40 +04:00
error = xfs_trans_reserve_quota ( tp , mp , udqp , gdqp ,
pdqp , resblks , 1 , 0 ) ;
2013-04-03 09:11:18 +04:00
if ( error )
goto error_return ;
/*
* Check for ability to enter directory entry , if no space reserved .
*/
error = xfs_dir_canenter ( tp , dp , link_name , resblks ) ;
if ( error )
goto error_return ;
/*
* Initialize the bmap freelist prior to calling either
* bmapi or the directory create code .
*/
xfs_bmap_init ( & free_list , & first_block ) ;
/*
* Allocate an inode for the symlink .
*/
error = xfs_dir_ialloc ( & tp , dp , S_IFLNK | ( mode & ~ S_IFMT ) , 1 , 0 ,
prid , resblks > 0 , & ip , NULL ) ;
if ( error ) {
if ( error = = ENOSPC )
goto error_return ;
goto error1 ;
}
/*
* An error after we ' ve joined dp to the transaction will result in the
* transaction cancel unlocking dp so don ' t do it explicitly in the
* error path .
*/
xfs_trans_ijoin ( tp , dp , XFS_ILOCK_EXCL ) ;
unlock_dp_on_error = false ;
/*
* Also attach the dquot ( s ) to it , if applicable .
*/
2013-07-11 09:00:40 +04:00
xfs_qm_vop_create_dqattach ( tp , ip , udqp , gdqp , pdqp ) ;
2013-04-03 09:11:18 +04:00
if ( resblks )
resblks - = XFS_IALLOC_SPACE_RES ( mp ) ;
/*
* If the symlink will fit into the inode , write it inline .
*/
if ( pathlen < = XFS_IFORK_DSIZE ( ip ) ) {
xfs_idata_realloc ( ip , pathlen , XFS_DATA_FORK ) ;
memcpy ( ip - > i_df . if_u1 . if_data , target_path , pathlen ) ;
ip - > i_d . di_size = pathlen ;
/*
* The inode was initially created in extent format .
*/
ip - > i_df . if_flags & = ~ ( XFS_IFEXTENTS | XFS_IFBROOT ) ;
ip - > i_df . if_flags | = XFS_IFINLINE ;
ip - > i_d . di_format = XFS_DINODE_FMT_LOCAL ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_DDATA | XFS_ILOG_CORE ) ;
} else {
2013-04-03 09:11:19 +04:00
int offset ;
2013-04-03 09:11:18 +04:00
first_fsb = 0 ;
nmaps = XFS_SYMLINK_MAPS ;
error = xfs_bmapi_write ( tp , ip , first_fsb , fs_blocks ,
XFS_BMAPI_METADATA , & first_block , resblks ,
mval , & nmaps , & free_list ) ;
if ( error )
goto error2 ;
if ( resblks )
resblks - = fs_blocks ;
ip - > i_d . di_size = pathlen ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
cur_chunk = target_path ;
2013-04-03 09:11:19 +04:00
offset = 0 ;
2013-04-03 09:11:18 +04:00
for ( n = 0 ; n < nmaps ; n + + ) {
2013-05-27 10:38:20 +04:00
char * buf ;
2013-04-03 09:11:19 +04:00
2013-04-03 09:11:18 +04:00
d = XFS_FSB_TO_DADDR ( mp , mval [ n ] . br_startblock ) ;
byte_cnt = XFS_FSB_TO_B ( mp , mval [ n ] . br_blockcount ) ;
bp = xfs_trans_get_buf ( tp , mp - > m_ddev_targp , d ,
BTOBB ( byte_cnt ) , 0 ) ;
if ( ! bp ) {
error = ENOMEM ;
goto error2 ;
}
2013-04-03 09:11:19 +04:00
bp - > b_ops = & xfs_symlink_buf_ops ;
byte_cnt = XFS_SYMLINK_BUF_SPACE ( mp , byte_cnt ) ;
2013-05-27 10:38:20 +04:00
byte_cnt = min ( byte_cnt , pathlen ) ;
2013-04-03 09:11:18 +04:00
2013-04-03 09:11:19 +04:00
buf = bp - > b_addr ;
buf + = xfs_symlink_hdr_set ( mp , ip - > i_ino , offset ,
byte_cnt , bp ) ;
memcpy ( buf , cur_chunk , byte_cnt ) ;
2013-04-03 09:11:18 +04:00
cur_chunk + = byte_cnt ;
2013-04-03 09:11:19 +04:00
pathlen - = byte_cnt ;
offset + = byte_cnt ;
2013-04-03 09:11:18 +04:00
2013-09-02 04:32:00 +04:00
xfs_trans_buf_set_type ( tp , bp , XFS_BLFT_SYMLINK_BUF ) ;
2013-04-03 09:11:19 +04:00
xfs_trans_log_buf ( tp , bp , 0 , ( buf + byte_cnt - 1 ) -
( char * ) bp - > b_addr ) ;
2013-04-03 09:11:18 +04:00
}
2013-05-27 10:38:20 +04:00
ASSERT ( pathlen = = 0 ) ;
2013-04-03 09:11:18 +04:00
}
/*
* Create the directory entry for the symlink .
*/
error = xfs_dir_createname ( tp , dp , link_name , ip - > i_ino ,
& first_block , & free_list , resblks ) ;
if ( error )
goto error2 ;
xfs_trans_ichgtime ( tp , dp , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
xfs_trans_log_inode ( tp , dp , XFS_ILOG_CORE ) ;
/*
* If this is a synchronous mount , make sure that the
* symlink transaction goes to disk before returning to
* the user .
*/
if ( mp - > m_flags & ( XFS_MOUNT_WSYNC | XFS_MOUNT_DIRSYNC ) ) {
xfs_trans_set_sync ( tp ) ;
}
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
if ( error ) {
goto error2 ;
}
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2013-07-11 09:00:40 +04:00
xfs_qm_dqrele ( pdqp ) ;
2013-04-03 09:11:18 +04:00
* ipp = ip ;
return 0 ;
error2 :
IRELE ( ip ) ;
error1 :
xfs_bmap_cancel ( & free_list ) ;
cancel_flags | = XFS_TRANS_ABORT ;
error_return :
xfs_trans_cancel ( tp , cancel_flags ) ;
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2013-07-11 09:00:40 +04:00
xfs_qm_dqrele ( pdqp ) ;
2013-04-03 09:11:18 +04:00
if ( unlock_dp_on_error )
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
std_return :
return error ;
}
/*
* Free a symlink that has blocks associated with it .
*/
2013-06-18 00:35:57 +04:00
STATIC int
2013-04-03 09:11:18 +04:00
xfs_inactive_symlink_rmt (
2013-09-20 19:06:09 +04:00
struct xfs_inode * ip )
2013-04-03 09:11:18 +04:00
{
xfs_buf_t * bp ;
int committed ;
int done ;
int error ;
xfs_fsblock_t first_block ;
xfs_bmap_free_t free_list ;
int i ;
xfs_mount_t * mp ;
xfs_bmbt_irec_t mval [ XFS_SYMLINK_MAPS ] ;
int nmaps ;
int size ;
xfs_trans_t * tp ;
mp = ip - > i_mount ;
2013-06-18 00:35:57 +04:00
ASSERT ( ip - > i_df . if_flags & XFS_IFEXTENTS ) ;
2013-04-03 09:11:18 +04:00
/*
* We ' re freeing a symlink that has some
* blocks allocated to it . Free the
* blocks here . We know that we ' ve got
* either 1 or 2 extents and that we can
* free them all in one bunmapi call .
*/
ASSERT ( ip - > i_d . di_nextents > 0 & & ip - > i_d . di_nextents < = 2 ) ;
2013-09-20 19:06:09 +04:00
tp = xfs_trans_alloc ( mp , XFS_TRANS_INACTIVE ) ;
error = xfs_trans_reserve ( tp , & M_RES ( mp ) - > tr_itruncate , 0 , 0 ) ;
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
return error ;
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
xfs_trans_ijoin ( tp , ip , 0 ) ;
2013-04-03 09:11:18 +04:00
/*
* Lock the inode , fix the size , and join it to the transaction .
* Hold it so in the normal path , we still have it locked for
* the second transaction . In the error paths we need it
* held so the cancel won ' t rele it , see below .
*/
size = ( int ) ip - > i_d . di_size ;
ip - > i_d . di_size = 0 ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
/*
* Find the block ( s ) so we can inval and unmap them .
*/
done = 0 ;
xfs_bmap_init ( & free_list , & first_block ) ;
nmaps = ARRAY_SIZE ( mval ) ;
2013-04-03 09:11:19 +04:00
error = xfs_bmapi_read ( ip , 0 , xfs_symlink_blocks ( mp , size ) ,
2013-04-03 09:11:18 +04:00
mval , & nmaps , 0 ) ;
if ( error )
2013-09-20 19:06:09 +04:00
goto error_trans_cancel ;
2013-04-03 09:11:18 +04:00
/*
2013-04-03 09:11:19 +04:00
* Invalidate the block ( s ) . No validation is done .
2013-04-03 09:11:18 +04:00
*/
for ( i = 0 ; i < nmaps ; i + + ) {
bp = xfs_trans_get_buf ( tp , mp - > m_ddev_targp ,
XFS_FSB_TO_DADDR ( mp , mval [ i ] . br_startblock ) ,
XFS_FSB_TO_BB ( mp , mval [ i ] . br_blockcount ) , 0 ) ;
if ( ! bp ) {
error = ENOMEM ;
2013-09-20 19:06:09 +04:00
goto error_bmap_cancel ;
2013-04-03 09:11:18 +04:00
}
xfs_trans_binval ( tp , bp ) ;
}
/*
* Unmap the dead block ( s ) to the free_list .
*/
2013-09-20 19:06:09 +04:00
error = xfs_bunmapi ( tp , ip , 0 , size , XFS_BMAPI_METADATA , nmaps ,
& first_block , & free_list , & done ) ;
if ( error )
goto error_bmap_cancel ;
2013-04-03 09:11:18 +04:00
ASSERT ( done ) ;
/*
* Commit the first transaction . This logs the EFI and the inode .
*/
2013-09-20 19:06:09 +04:00
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
if ( error )
goto error_bmap_cancel ;
2013-04-03 09:11:18 +04:00
/*
* The transaction must have been committed , since there were
* actually extents freed by xfs_bunmapi . See xfs_bmap_finish .
* The new tp has the extent freeing and EFDs .
*/
ASSERT ( committed ) ;
/*
* The first xact was committed , so add the inode to the new one .
* Mark it dirty so it will be logged and moved forward in the log as
* part of every commit .
*/
xfs_trans_ijoin ( tp , ip , 0 ) ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
/*
* Commit the transaction containing extent freeing and EFDs .
*/
2013-09-20 19:06:09 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2013-04-03 09:11:18 +04:00
if ( error ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( mp ) ) ;
2013-09-20 19:06:09 +04:00
goto error_unlock ;
2013-04-03 09:11:18 +04:00
}
/*
* Remove the memory for extent descriptions ( just bookkeeping ) .
*/
if ( ip - > i_df . if_bytes )
xfs_idata_realloc ( ip , - ip - > i_df . if_bytes , XFS_DATA_FORK ) ;
ASSERT ( ip - > i_df . if_bytes = = 0 ) ;
2013-09-20 19:06:09 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2013-04-03 09:11:18 +04:00
return 0 ;
2013-09-20 19:06:09 +04:00
error_bmap_cancel :
2013-04-03 09:11:18 +04:00
xfs_bmap_cancel ( & free_list ) ;
2013-09-20 19:06:09 +04:00
error_trans_cancel :
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
error_unlock :
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2013-04-03 09:11:18 +04:00
return error ;
}
2013-06-18 00:35:57 +04:00
/*
* xfs_inactive_symlink - free a symlink
*/
int
xfs_inactive_symlink (
2013-09-20 19:06:09 +04:00
struct xfs_inode * ip )
2013-06-18 00:35:57 +04:00
{
struct xfs_mount * mp = ip - > i_mount ;
int pathlen ;
trace_xfs_inactive_symlink ( ip ) ;
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
2013-09-20 19:06:09 +04:00
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2013-06-18 00:35:57 +04:00
/*
* Zero length symlinks _can_ exist .
*/
pathlen = ( int ) ip - > i_d . di_size ;
2013-09-20 19:06:09 +04:00
if ( ! pathlen ) {
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2013-06-18 00:35:57 +04:00
return 0 ;
2013-09-20 19:06:09 +04:00
}
2013-06-18 00:35:57 +04:00
if ( pathlen < 0 | | pathlen > MAXPATHLEN ) {
xfs_alert ( mp , " %s: inode (0x%llx) bad symlink length (%d) " ,
__func__ , ( unsigned long long ) ip - > i_ino , pathlen ) ;
2013-09-20 19:06:09 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2013-06-18 00:35:57 +04:00
ASSERT ( 0 ) ;
return XFS_ERROR ( EFSCORRUPTED ) ;
}
if ( ip - > i_df . if_flags & XFS_IFINLINE ) {
2013-09-20 19:06:09 +04:00
if ( ip - > i_df . if_bytes > 0 )
2013-06-18 00:35:57 +04:00
xfs_idata_realloc ( ip , - ( ip - > i_df . if_bytes ) ,
XFS_DATA_FORK ) ;
2013-09-20 19:06:09 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2013-06-18 00:35:57 +04:00
ASSERT ( ip - > i_df . if_bytes = = 0 ) ;
return 0 ;
}
2013-09-20 19:06:09 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2013-06-18 00:35:57 +04:00
/* remove the remote symlink */
2013-09-20 19:06:09 +04:00
return xfs_inactive_symlink_rmt ( ip ) ;
2013-06-18 00:35:57 +04:00
}