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
*/
2006-01-11 23:17:46 +03:00
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"
# include "xfs_trans.h"
# include "xfs_sb.h"
# include "xfs_ag.h"
# 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_ialloc_btree.h"
# include "xfs_dinode.h"
# include "xfs_inode.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inode_item.h"
# include "xfs_itable.h"
# include "xfs_ialloc.h"
# include "xfs_alloc.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
2009-06-10 19:07:47 +04:00
# include "xfs_acl.h"
2005-04-17 02:20:36 +04:00
# include "xfs_attr.h"
# include "xfs_error.h"
# include "xfs_quota.h"
# include "xfs_utils.h"
2005-11-02 06:38:42 +03:00
# include "xfs_rtalloc.h"
2005-04-17 02:20:36 +04:00
# include "xfs_trans_space.h"
# include "xfs_log_priv.h"
2007-07-11 05:09:12 +04:00
# include "xfs_filestream.h"
2007-08-28 10:12:30 +04: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
2006-06-09 09:27:16 +04:00
/*
* The maximum pathlen is 1024 bytes . Since the minimum file system
* blocksize is 512 bytes , we can get a max of 2 extents back from
* bmapi .
*/
# define SYMLINK_MAPS 2
2007-08-28 07:59:03 +04:00
STATIC int
xfs_readlink_bmap (
xfs_inode_t * ip ,
char * link )
{
xfs_mount_t * mp = ip - > i_mount ;
int pathlen = ip - > i_d . di_size ;
int nmaps = SYMLINK_MAPS ;
xfs_bmbt_irec_t mval [ SYMLINK_MAPS ] ;
xfs_daddr_t d ;
int byte_cnt ;
int n ;
xfs_buf_t * bp ;
int error = 0 ;
2011-09-19 00:40:45 +04:00
error = xfs_bmapi_read ( ip , 0 , XFS_B_TO_FSB ( mp , pathlen ) , mval , & nmaps ,
0 ) ;
2007-08-28 07:59:03 +04:00
if ( error )
goto out ;
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 ) ;
2012-04-23 09:59:07 +04:00
bp = xfs_buf_read ( mp - > m_ddev_targp , d , BTOBB ( byte_cnt ) , 0 ) ;
2011-08-03 06:18:29 +04:00
if ( ! bp )
return XFS_ERROR ( ENOMEM ) ;
2011-08-03 06:18:34 +04:00
error = bp - > b_error ;
2007-08-28 07:59:03 +04:00
if ( error ) {
2011-10-10 20:52:49 +04:00
xfs_buf_ioerror_alert ( bp , __func__ ) ;
2007-08-28 07:59:03 +04:00
xfs_buf_relse ( bp ) ;
goto out ;
}
if ( pathlen < byte_cnt )
byte_cnt = pathlen ;
pathlen - = byte_cnt ;
2011-07-23 03:40:15 +04:00
memcpy ( link , bp - > b_addr , byte_cnt ) ;
2007-08-28 07:59:03 +04:00
xfs_buf_relse ( bp ) ;
}
link [ ip - > i_d . di_size ] = ' \0 ' ;
error = 0 ;
out :
return error ;
}
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_readlink (
2007-08-28 10:12:30 +04:00
xfs_inode_t * ip ,
2007-08-28 07:59:03 +04:00
char * link )
2005-04-17 02:20:36 +04:00
{
2007-08-28 07:59:03 +04:00
xfs_mount_t * mp = ip - > i_mount ;
2011-11-07 20:10:24 +04:00
xfs_fsize_t pathlen ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
2010-06-24 05:57:09 +04:00
trace_xfs_readlink ( ip ) ;
2005-04-17 02:20:36 +04:00
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
xfs_ilock ( ip , XFS_ILOCK_SHARED ) ;
2007-08-28 07:59:03 +04:00
pathlen = ip - > i_d . di_size ;
if ( ! pathlen )
goto out ;
2005-04-17 02:20:36 +04:00
2011-11-07 20:10:24 +04:00
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 ) ;
2012-01-11 22:52:10 +04:00
error = XFS_ERROR ( EFSCORRUPTED ) ;
goto out ;
2011-11-07 20:10:24 +04:00
}
2005-04-17 02:20:36 +04:00
if ( ip - > i_df . if_flags & XFS_IFINLINE ) {
2007-08-28 07:59:03 +04:00
memcpy ( link , ip - > i_df . if_u1 . if_data , pathlen ) ;
link [ pathlen ] = ' \0 ' ;
} else {
error = xfs_readlink_bmap ( ip , link ) ;
2005-04-17 02:20:36 +04:00
}
2007-08-28 07:59:03 +04:00
out :
2005-04-17 02:20:36 +04:00
xfs_iunlock ( ip , XFS_ILOCK_SHARED ) ;
return error ;
}
/*
2007-05-24 09:22:19 +04:00
* This is called by xfs_inactive to free any blocks beyond eof
* when the link count isn ' t zero and by xfs_dm_punch_hole ( ) when
* punching a hole to EOF .
2005-04-17 02:20:36 +04:00
*/
2009-07-02 09:09:33 +04:00
STATIC int
2007-05-24 09:22:19 +04:00
xfs_free_eofblocks (
2005-04-17 02:20:36 +04:00
xfs_mount_t * mp ,
2007-05-24 09:22:19 +04:00
xfs_inode_t * ip ,
2012-07-04 19:13:32 +04:00
bool need_iolock )
2005-04-17 02:20:36 +04:00
{
xfs_trans_t * tp ;
int error ;
xfs_fileoff_t end_fsb ;
xfs_fileoff_t last_fsb ;
xfs_filblks_t map_len ;
int nimaps ;
xfs_bmbt_irec_t imap ;
/*
* Figure out if there are any blocks beyond the end
* of the file . If not , then there is nothing to do .
*/
2011-12-19 00:00:11 +04:00
end_fsb = XFS_B_TO_FSB ( mp , ( xfs_ufsize_t ) XFS_ISIZE ( ip ) ) ;
2012-06-08 09:44:54 +04:00
last_fsb = XFS_B_TO_FSB ( mp , mp - > m_super - > s_maxbytes ) ;
2010-07-20 11:54:28 +04:00
if ( last_fsb < = end_fsb )
2006-01-15 04:37:08 +03:00
return 0 ;
2010-07-20 11:54:28 +04:00
map_len = last_fsb - end_fsb ;
2005-04-17 02:20:36 +04:00
nimaps = 1 ;
xfs_ilock ( ip , XFS_ILOCK_SHARED ) ;
2011-09-19 00:40:45 +04:00
error = xfs_bmapi_read ( ip , end_fsb , map_len , & imap , & nimaps , 0 ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( ip , XFS_ILOCK_SHARED ) ;
if ( ! error & & ( nimaps ! = 0 ) & &
2006-01-11 07:38:31 +03:00
( imap . br_startblock ! = HOLESTARTBLOCK | |
ip - > i_delayed_blks ) ) {
2005-04-17 02:20:36 +04:00
/*
* Attach the dquots to the inode up front .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( ip , 0 ) ;
if ( error )
2006-01-15 04:37:08 +03:00
return error ;
2005-04-17 02:20:36 +04:00
/*
* There are blocks after the end of file .
* Free them up now by truncating the file to
* its current size .
*/
tp = xfs_trans_alloc ( mp , XFS_TRANS_INACTIVE ) ;
2012-07-04 19:13:32 +04:00
if ( need_iolock ) {
2009-10-19 08:03:46 +04:00
if ( ! xfs_ilock_nowait ( ip , XFS_IOLOCK_EXCL ) ) {
xfs_trans_cancel ( tp , 0 ) ;
return 0 ;
}
}
2005-04-17 02:20:36 +04:00
error = xfs_trans_reserve ( tp , 0 ,
XFS_ITRUNCATE_LOG_RES ( mp ) ,
0 , XFS_TRANS_PERM_LOG_RES ,
XFS_ITRUNCATE_LOG_COUNT ) ;
if ( error ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( mp ) ) ;
xfs_trans_cancel ( tp , 0 ) ;
2012-07-04 19:13:32 +04:00
if ( need_iolock )
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
2006-01-15 04:37:08 +03:00
return error ;
2005-04-17 02:20:36 +04:00
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
2011-12-19 00:00:04 +04:00
/*
* Do not update the on - disk file size . If we update the
* on - disk file size and then the system crashes before the
* contents of the file are flushed to disk then the files
* may be full of holes ( ie NULL files bug ) .
*/
error = xfs_itruncate_extents ( & tp , ip , XFS_DATA_FORK ,
2011-12-19 00:00:11 +04:00
XFS_ISIZE ( ip ) ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
2011-07-08 16:34:34 +04:00
/*
* If we get an error at this point we simply don ' t
* bother truncating the file .
*/
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( tp ,
( XFS_TRANS_RELEASE_LOG_RES |
XFS_TRANS_ABORT ) ) ;
} else {
error = xfs_trans_commit ( tp ,
2007-05-08 07:48:42 +04:00
XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
}
2012-07-04 19:13:32 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
if ( need_iolock )
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-15 04:37:08 +03:00
return error ;
2005-04-17 02:20:36 +04:00
}
/*
* Free a symlink that has blocks associated with it .
*/
STATIC int
xfs_inactive_symlink_rmt (
xfs_inode_t * ip ,
xfs_trans_t * * tpp )
{
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 [ SYMLINK_MAPS ] ;
int nmaps ;
xfs_trans_t * ntp ;
int size ;
xfs_trans_t * tp ;
tp = * tpp ;
mp = ip - > i_mount ;
ASSERT ( ip - > i_d . di_size > XFS_IFORK_DSIZE ( ip ) ) ;
/*
* 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 ) ;
2012-07-04 19:13:29 +04:00
2005-04-17 02:20:36 +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 ;
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2006-03-24 14:15:34 +03:00
nmaps = ARRAY_SIZE ( mval ) ;
2011-09-19 00:40:45 +04:00
error = xfs_bmapi_read ( ip , 0 , XFS_B_TO_FSB ( mp , size ) ,
mval , & nmaps , 0 ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto error0 ;
/*
* Invalidate the block ( s ) .
*/
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 ) ;
2011-09-20 17:56:55 +04:00
if ( ! bp ) {
error = ENOMEM ;
goto error1 ;
}
2005-04-17 02:20:36 +04:00
xfs_trans_binval ( tp , bp ) ;
}
/*
* Unmap the dead block ( s ) to the free_list .
*/
if ( ( error = xfs_bunmapi ( tp , ip , 0 , size , XFS_BMAPI_METADATA , nmaps ,
2010-06-23 12:11:15 +04:00
& first_block , & free_list , & done ) ) )
2005-04-17 02:20:36 +04:00
goto error1 ;
ASSERT ( done ) ;
/*
* Commit the first transaction . This logs the EFI and the inode .
*/
2007-02-10 10:37:16 +03:00
if ( ( error = xfs_bmap_finish ( & tp , & free_list , & committed ) ) )
2005-04-17 02:20:36 +04:00
goto error1 ;
/*
* 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 .
*/
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
/*
* Get a new , empty transaction to return to our caller .
*/
ntp = xfs_trans_dup ( tp ) ;
/*
2006-03-29 02:55:14 +04:00
* Commit the transaction containing extent freeing and EFDs .
2005-04-17 02:20:36 +04:00
* If we get an error on the commit here or on the reserve below ,
* we need to unlock the inode since the new transaction doesn ' t
* have the inode attached .
*/
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , 0 ) ;
2005-04-17 02:20:36 +04:00
tp = ntp ;
if ( error ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( mp ) ) ;
goto error0 ;
}
2008-11-17 09:37:10 +03:00
/*
* transaction commit worked ok so we can drop the extra ticket
* reference that we gained in xfs_trans_dup ( )
*/
xfs_log_ticket_put ( tp - > t_ticket ) ;
2005-04-17 02:20:36 +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 ) ;
/*
* Put an itruncate log reservation in the new transaction
* for our caller .
*/
if ( ( error = xfs_trans_reserve ( tp , 0 , XFS_ITRUNCATE_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES , XFS_ITRUNCATE_LOG_COUNT ) ) ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( mp ) ) ;
goto error0 ;
}
2012-07-04 19:13:29 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
* tpp = tp ;
return 0 ;
error1 :
xfs_bmap_cancel ( & free_list ) ;
error0 :
return error ;
}
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_release (
2007-08-28 10:12:30 +04:00
xfs_inode_t * ip )
2005-04-17 02:20:36 +04:00
{
2007-08-28 10:12:30 +04:00
xfs_mount_t * mp = ip - > i_mount ;
2005-04-17 02:20:36 +04:00
int error ;
2008-04-22 11:33:25 +04:00
if ( ! S_ISREG ( ip - > i_d . di_mode ) | | ( ip - > i_d . di_mode = = 0 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
/* If this is a read-only mount, don't do this (would generate I/O) */
2007-08-30 11:21:12 +04:00
if ( mp - > m_flags & XFS_MOUNT_RDONLY )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-07-11 05:09:12 +04:00
if ( ! XFS_FORCED_SHUTDOWN ( mp ) ) {
2007-08-29 05:44:37 +04:00
int truncated ;
2007-07-11 05:09:12 +04:00
/*
* If we are using filestreams , and we have an unlinked
* file that we are processing the last close on , then nothing
* will be able to reopen and write to this file . Purge this
* inode from the filestreams cache so that it doesn ' t delay
* teardown of the inode .
*/
if ( ( ip - > i_d . di_nlink = = 0 ) & & xfs_inode_is_filestream ( ip ) )
xfs_filestream_deassociate ( ip ) ;
2007-06-28 10:46:47 +04:00
/*
* If we previously truncated this file and removed old data
* in the process , we want to initiate " early " writeout on
* the last close . This is an attempt to combat the notorious
2011-03-31 05:57:33 +04:00
* NULL files problem which is particularly noticeable from a
2007-06-28 10:46:47 +04:00
* truncate down , buffered ( re - ) write ( delalloc ) , followed by
* a crash . What we are effectively doing here is
* significantly reducing the time window where we ' d otherwise
* be exposed to that problem .
*/
2007-08-29 05:44:50 +04:00
truncated = xfs_iflags_test_and_clear ( ip , XFS_ITRUNCATED ) ;
2011-06-23 05:35:00 +04:00
if ( truncated ) {
xfs_iflags_clear ( ip , XFS_IDIRTY_RELEASE ) ;
if ( VN_DIRTY ( VFS_I ( ip ) ) & & ip - > i_delayed_blks > 0 )
xfs_flush_pages ( ip , 0 , - 1 , XBF_ASYNC , FI_NONE ) ;
}
2007-06-28 10:46:47 +04:00
}
2010-12-23 04:02:31 +03:00
if ( ip - > i_d . di_nlink = = 0 )
return 0 ;
2009-10-19 08:03:46 +04:00
2011-07-26 10:31:30 +04:00
if ( ( S_ISREG ( ip - > i_d . di_mode ) & &
2011-12-19 00:00:11 +04:00
( VFS_I ( ip ) - > i_size > 0 | |
( VN_CACHED ( VFS_I ( ip ) ) > 0 | | ip - > i_delayed_blks > 0 ) ) & &
2010-12-23 04:02:31 +03:00
( ip - > i_df . if_flags & XFS_IFEXTENTS ) ) & &
( ! ( ip - > i_d . di_flags & ( XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND ) ) ) ) {
/*
* If we can ' t get the iolock just skip truncating the blocks
* past EOF because we could deadlock with the mmap_sem
* otherwise . We ' ll get another chance to drop them once the
* last reference to the inode is dropped , so we ' ll never leak
* blocks permanently .
*
* Further , check if the inode is being opened , written and
* closed frequently and we have delayed allocation blocks
2011-03-31 05:57:33 +04:00
* outstanding ( e . g . streaming writes from the NFS server ) ,
2010-12-23 04:02:31 +03:00
* truncating the blocks past EOF will cause fragmentation to
* occur .
*
* In this case don ' t do the truncation , either , but we have to
* be careful how we detect this case . Blocks beyond EOF show
* up as i_delayed_blks even when the inode is clean , so we
* need to truncate them away first before checking for a dirty
* release . Hence on the first dirty close we will still remove
* the speculative allocation , but after that we will leave it
* in place .
*/
if ( xfs_iflags_test ( ip , XFS_IDIRTY_RELEASE ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2012-07-04 19:13:32 +04:00
error = xfs_free_eofblocks ( mp , ip , true ) ;
2010-12-23 04:02:31 +03:00
if ( error )
return error ;
/* delalloc blocks after truncation means it really is dirty */
if ( ip - > i_delayed_blks )
xfs_iflags_set ( ip , XFS_IDIRTY_RELEASE ) ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* xfs_inactive
*
* This is called when the vnode reference count for the vnode
* goes to zero . If the file has been unlinked , then it must
* now be truncated . Also , we clear all of the read - ahead state
* kept for the inode here since the file is now closed .
*/
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_inactive (
2007-08-28 10:12:30 +04:00
xfs_inode_t * ip )
2005-04-17 02:20:36 +04:00
{
2006-06-09 11:00:52 +04:00
xfs_bmap_free_t free_list ;
2005-04-17 02:20:36 +04:00
xfs_fsblock_t first_block ;
int committed ;
xfs_trans_t * tp ;
xfs_mount_t * mp ;
int error ;
2012-07-04 19:13:29 +04:00
int truncate = 0 ;
2005-04-17 02:20:36 +04:00
/*
* If the inode is already free , then there can be nothing
* to clean up here .
*/
2009-03-16 10:25:25 +03:00
if ( ip - > i_d . di_mode = = 0 | | is_bad_inode ( VFS_I ( ip ) ) ) {
2005-04-17 02:20:36 +04:00
ASSERT ( ip - > i_df . if_real_bytes = = 0 ) ;
ASSERT ( ip - > i_df . if_broot_bytes = = 0 ) ;
return VN_INACTIVE_CACHE ;
}
mp = ip - > i_mount ;
error = 0 ;
/* If this is a read-only mount, don't do this (would generate I/O) */
2007-08-30 11:21:12 +04:00
if ( mp - > m_flags & XFS_MOUNT_RDONLY )
2005-04-17 02:20:36 +04:00
goto out ;
if ( ip - > i_d . di_nlink ! = 0 ) {
2011-07-26 10:31:30 +04:00
if ( ( S_ISREG ( ip - > i_d . di_mode ) & &
2011-12-19 00:00:11 +04:00
( VFS_I ( ip ) - > i_size > 0 | |
( VN_CACHED ( VFS_I ( ip ) ) > 0 | | ip - > i_delayed_blks > 0 ) ) & &
( ip - > i_df . if_flags & XFS_IFEXTENTS ) & &
( ! ( ip - > i_d . di_flags &
2006-01-11 07:28:28 +03:00
( XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND ) ) | |
2011-12-19 00:00:11 +04:00
ip - > i_delayed_blks ! = 0 ) ) ) {
2012-07-04 19:13:32 +04:00
error = xfs_free_eofblocks ( mp , ip , false ) ;
2007-05-24 09:22:19 +04:00
if ( error )
2006-01-15 04:37:08 +03:00
return VN_INACTIVE_CACHE ;
2005-04-17 02:20:36 +04:00
}
goto out ;
}
2012-07-04 19:13:29 +04:00
if ( S_ISREG ( ip - > i_d . di_mode ) & &
( ip - > i_d . di_size ! = 0 | | XFS_ISIZE ( ip ) ! = 0 | |
ip - > i_d . di_nextents > 0 | | ip - > i_delayed_blks > 0 ) )
truncate = 1 ;
2005-04-17 02:20:36 +04:00
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( ip , 0 ) ;
if ( error )
2006-01-15 04:37:08 +03:00
return VN_INACTIVE_CACHE ;
2005-04-17 02:20:36 +04:00
tp = xfs_trans_alloc ( mp , XFS_TRANS_INACTIVE ) ;
2012-07-04 19:13:29 +04:00
error = xfs_trans_reserve ( tp , 0 ,
( truncate | | S_ISLNK ( ip - > i_d . di_mode ) ) ?
XFS_ITRUNCATE_LOG_RES ( mp ) :
XFS_IFREE_LOG_RES ( mp ) ,
0 ,
XFS_TRANS_PERM_LOG_RES ,
XFS_ITRUNCATE_LOG_COUNT ) ;
if ( error ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( mp ) ) ;
xfs_trans_cancel ( tp , 0 ) ;
return VN_INACTIVE_CACHE ;
}
2005-04-17 02:20:36 +04:00
2012-07-04 19:13:31 +04:00
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2012-07-04 19:13:29 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
2012-07-04 19:13:29 +04:00
if ( S_ISLNK ( ip - > i_d . di_mode ) ) {
/*
* Zero length symlinks _can_ exist .
*/
if ( ip - > i_d . di_size > XFS_IFORK_DSIZE ( ip ) ) {
error = xfs_inactive_symlink_rmt ( ip , & tp ) ;
if ( error )
goto out_cancel ;
} else if ( ip - > i_df . if_bytes > 0 ) {
xfs_idata_realloc ( ip , - ( ip - > i_df . if_bytes ) ,
XFS_DATA_FORK ) ;
ASSERT ( ip - > i_df . if_bytes = = 0 ) ;
}
} else if ( truncate ) {
2011-12-19 00:00:04 +04:00
ip - > i_d . di_size = 0 ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
error = xfs_itruncate_extents ( & tp , ip , XFS_DATA_FORK , 0 ) ;
2012-07-04 19:13:29 +04:00
if ( error )
goto out_cancel ;
2011-12-19 00:00:04 +04:00
ASSERT ( ip - > i_d . di_nextents = = 0 ) ;
2005-04-17 02:20:36 +04:00
}
/*
2012-07-04 19:13:30 +04:00
* If there are attributes associated with the file then blow them away
* now . The code calls a routine that recursively deconstructs the
* attribute fork . We need to just commit the current transaction
2005-04-17 02:20:36 +04:00
* because we can ' t use it for xfs_attr_inactive ( ) .
*/
if ( ip - > i_d . di_anextents > 0 ) {
2012-07-04 19:13:30 +04:00
ASSERT ( ip - > i_d . di_forkoff ! = 0 ) ;
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
if ( error )
2012-07-04 19:13:31 +04:00
goto out_unlock ;
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2012-07-04 19:13:30 +04:00
error = xfs_attr_inactive ( ip ) ;
if ( error )
2012-07-04 19:13:31 +04:00
goto out ;
2012-07-04 19:13:30 +04:00
tp = xfs_trans_alloc ( mp , XFS_TRANS_INACTIVE ) ;
error = xfs_trans_reserve ( tp , 0 ,
XFS_IFREE_LOG_RES ( mp ) ,
0 , XFS_TRANS_PERM_LOG_RES ,
XFS_INACTIVE_LOG_COUNT ) ;
2012-07-04 19:13:31 +04:00
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
goto out ;
}
2012-07-04 19:13:30 +04:00
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2012-07-04 19:13:30 +04:00
if ( ip - > i_afp )
xfs_idestroy_fork ( ip , XFS_ATTR_FORK ) ;
ASSERT ( ip - > i_d . di_anextents = = 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Free the inode .
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2005-04-17 02:20:36 +04:00
error = xfs_ifree ( tp , ip , & free_list ) ;
if ( error ) {
/*
* If we fail to free the inode , shut down . The cancel
* might do that , we need to make sure . Otherwise the
* inode might be lost for a long time or forever .
*/
if ( ! XFS_FORCED_SHUTDOWN ( mp ) ) {
2011-03-07 02:08:35 +03:00
xfs_notice ( mp , " %s: xfs_ifree returned error %d " ,
__func__ , error ) ;
2006-06-09 08:58:38 +04:00
xfs_force_shutdown ( mp , SHUTDOWN_META_IO_ERROR ) ;
2005-04-17 02:20:36 +04:00
}
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
} else {
/*
* Credit the quota account ( s ) . The inode is gone .
*/
2009-06-08 17:33:32 +04:00
xfs_trans_mod_dquot_byino ( tp , ip , XFS_TRANS_DQ_ICOUNT , - 1 ) ;
2005-04-17 02:20:36 +04:00
/*
2008-04-10 06:24:17 +04:00
* Just ignore errors at this point . There is nothing we can
* do except to try to keep going . Make sure it ' s not a silent
* error .
2005-04-17 02:20:36 +04:00
*/
2008-04-10 06:24:17 +04:00
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
if ( error )
2011-03-07 02:05:35 +03:00
xfs_notice ( mp , " %s: xfs_bmap_finish returned error %d " ,
__func__ , error ) ;
2008-04-10 06:24:17 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
if ( error )
2011-03-07 02:05:35 +03:00
xfs_notice ( mp , " %s: xfs_trans_commit returned error %d " ,
__func__ , error ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-08 17:33:32 +04:00
2005-04-17 02:20:36 +04:00
/*
* Release the dquots held by inode , if any .
*/
2009-06-08 17:33:32 +04:00
xfs_qm_dqdetach ( ip ) ;
2012-07-04 19:13:31 +04:00
out_unlock :
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2012-07-04 19:13:29 +04:00
out :
return VN_INACTIVE_CACHE ;
out_cancel :
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
2012-07-04 19:13:31 +04:00
goto out_unlock ;
2005-04-17 02:20:36 +04:00
}
2008-05-21 10:58:22 +04:00
/*
* Lookups up an inode from " name " . If ci_name is not NULL , then a CI match
* is allowed , otherwise it has to be an exact match . If a CI match is found ,
* ci_name - > name will point to a the actual name ( caller must free ) or
* will be set to NULL if an exact match is found .
*/
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_lookup (
2007-08-28 10:12:30 +04:00
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name ,
2008-05-21 10:58:22 +04:00
xfs_inode_t * * ipp ,
struct xfs_name * ci_name )
2005-04-17 02:20:36 +04:00
{
2008-04-22 11:33:52 +04:00
xfs_ino_t inum ;
2005-04-17 02:20:36 +04:00
int error ;
uint lock_mode ;
2010-06-24 05:57:09 +04:00
trace_xfs_lookup ( dp , name ) ;
2005-04-17 02:20:36 +04:00
if ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) )
return XFS_ERROR ( EIO ) ;
lock_mode = xfs_ilock_map_shared ( dp ) ;
2008-05-21 10:58:22 +04:00
error = xfs_dir_lookup ( NULL , dp , name , & inum , ci_name ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock_map_shared ( dp , lock_mode ) ;
2008-04-22 11:33:52 +04:00
if ( error )
goto out ;
2010-06-24 05:35:17 +04:00
error = xfs_iget ( dp - > i_mount , NULL , inum , 0 , 0 , ipp ) ;
2008-04-22 11:33:52 +04:00
if ( error )
2008-05-21 10:58:22 +04:00
goto out_free_name ;
2008-04-22 11:33:52 +04:00
return 0 ;
2008-05-21 10:58:22 +04:00
out_free_name :
if ( ci_name )
kmem_free ( ci_name - > name ) ;
out :
2008-04-22 11:33:52 +04:00
* ipp = NULL ;
2005-04-17 02:20:36 +04:00
return error ;
}
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_create (
2007-08-28 10:12:30 +04:00
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name ,
2011-07-26 10:50:15 +04:00
umode_t mode ,
2007-10-11 12:09:12 +04:00
xfs_dev_t rdev ,
2010-10-06 22:41:17 +04:00
xfs_inode_t * * ipp )
2005-04-17 02:20:36 +04:00
{
2009-02-09 10:38:02 +03:00
int is_dir = S_ISDIR ( mode ) ;
struct xfs_mount * mp = dp - > i_mount ;
struct xfs_inode * ip = NULL ;
struct xfs_trans * tp = NULL ;
2008-04-10 06:22:07 +04:00
int error ;
2005-04-17 02:20:36 +04:00
xfs_bmap_free_t free_list ;
xfs_fsblock_t first_block ;
2007-08-28 10:12:30 +04:00
boolean_t unlock_dp_on_error = B_FALSE ;
2005-04-17 02:20:36 +04:00
uint cancel_flags ;
int committed ;
2010-09-26 10:10:18 +04:00
prid_t prid ;
2009-02-09 10:38:02 +03:00
struct xfs_dquot * udqp = NULL ;
struct xfs_dquot * gdqp = NULL ;
2005-04-17 02:20:36 +04:00
uint resblks ;
2009-02-09 10:38:02 +03:00
uint log_res ;
uint log_count ;
2005-04-17 02:20:36 +04:00
2010-06-24 05:57:09 +04:00
trace_xfs_create ( dp , name ) ;
2005-04-17 02:20:36 +04:00
2009-02-09 10:38:02 +03:00
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
2005-06-21 09:39:12 +04:00
if ( dp - > i_d . di_flags & XFS_DIFLAG_PROJINHERIT )
2010-09-26 10:10:18 +04:00
prid = xfs_get_projid ( dp ) ;
2005-04-17 02:20:36 +04:00
else
2010-09-26 10:10:18 +04:00
prid = XFS_PROJID_DEFAULT ;
2005-04-17 02:20:36 +04:00
/*
* Make sure that we have allocated dquot ( s ) on disk .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_vop_dqalloc ( dp , current_fsuid ( ) , current_fsgid ( ) , prid ,
2009-02-09 10:38:02 +03:00
XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT , & udqp , & gdqp ) ;
2005-04-17 02:20:36 +04:00
if ( error )
2011-02-13 16:26:42 +03:00
return error ;
2005-04-17 02:20:36 +04:00
2009-02-09 10:38:02 +03:00
if ( is_dir ) {
rdev = 0 ;
resblks = XFS_MKDIR_SPACE_RES ( mp , name - > len ) ;
log_res = XFS_MKDIR_LOG_RES ( mp ) ;
log_count = XFS_MKDIR_LOG_COUNT ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_MKDIR ) ;
} else {
resblks = XFS_CREATE_SPACE_RES ( mp , name - > len ) ;
log_res = XFS_CREATE_LOG_RES ( mp ) ;
log_count = XFS_CREATE_LOG_COUNT ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_CREATE ) ;
}
2005-04-17 02:20:36 +04:00
cancel_flags = XFS_TRANS_RELEASE_LOG_RES ;
2009-02-09 10:38:02 +03:00
2005-04-17 02:20:36 +04:00
/*
* Initially assume that the file does not exist and
* reserve the resources for that case . If that is not
* the case we ' ll drop the one we have and get a more
* appropriate transaction later .
*/
2009-02-09 10:38:02 +03:00
error = xfs_trans_reserve ( tp , resblks , log_res , 0 ,
XFS_TRANS_PERM_LOG_RES , log_count ) ;
2005-04-17 02:20:36 +04:00
if ( error = = ENOSPC ) {
2009-04-06 20:48:30 +04:00
/* flush outstanding delalloc blocks and retry */
xfs_flush_inodes ( dp ) ;
2009-09-10 03:19:02 +04:00
error = xfs_trans_reserve ( tp , resblks , log_res , 0 ,
XFS_TRANS_PERM_LOG_RES , log_count ) ;
2009-04-06 20:48:30 +04:00
}
if ( error = = ENOSPC ) {
/* No space at all so try a "no-allocation" reservation */
2005-04-17 02:20:36 +04:00
resblks = 0 ;
2009-02-09 10:38:02 +03:00
error = xfs_trans_reserve ( tp , 0 , log_res , 0 ,
XFS_TRANS_PERM_LOG_RES , log_count ) ;
2005-04-17 02:20:36 +04:00
}
if ( error ) {
cancel_flags = 0 ;
2009-02-09 10:38:02 +03:00
goto out_trans_cancel ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 07:50:19 +04:00
xfs_ilock ( dp , XFS_ILOCK_EXCL | XFS_ILOCK_PARENT ) ;
2007-08-28 10:12:30 +04:00
unlock_dp_on_error = B_TRUE ;
2005-04-17 02:20:36 +04:00
2009-02-09 10:38:02 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2005-04-17 02:20:36 +04:00
/*
* Reserve disk quota and the inode .
*/
2009-06-08 17:33:32 +04:00
error = xfs_trans_reserve_quota ( tp , mp , udqp , gdqp , resblks , 1 , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
2009-02-09 10:38:02 +03:00
goto out_trans_cancel ;
2005-04-17 02:20:36 +04:00
2008-04-10 06:22:07 +04:00
error = xfs_dir_canenter ( tp , dp , name , resblks ) ;
if ( error )
2009-02-09 10:38:02 +03:00
goto out_trans_cancel ;
/*
* A newly created regular or special file just has one directory
* entry pointing to them , but a directory also the " . " entry
* pointing to itself .
*/
2010-10-06 22:41:17 +04:00
error = xfs_dir_ialloc ( & tp , dp , mode , is_dir ? 2 : 1 , rdev ,
2009-02-09 10:38:02 +03:00
prid , resblks > 0 , & ip , & committed ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
if ( error = = ENOSPC )
2009-02-09 10:38:02 +03:00
goto out_trans_cancel ;
goto out_trans_abort ;
2005-04-17 02:20:36 +04:00
}
/*
2007-08-28 10:12:30 +04:00
* Now we join the directory inode to the transaction . We do not do it
* earlier because xfs_dir_ialloc might commit the previous transaction
* ( and release all the locks ) . An error from here on will result in
* the transaction cancel unlocking dp so don ' t do it explicitly in the
* error path .
2005-04-17 02:20:36 +04:00
*/
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , dp , XFS_ILOCK_EXCL ) ;
2007-08-28 10:12:30 +04:00
unlock_dp_on_error = B_FALSE ;
2005-04-17 02:20:36 +04:00
2008-04-10 06:22:07 +04:00
error = xfs_dir_createname ( tp , dp , name , ip - > i_ino ,
2006-06-20 07:04:51 +04:00
& first_block , & free_list , resblks ?
resblks - XFS_IALLOC_SPACE_RES ( mp ) : 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
ASSERT ( error ! = ENOSPC ) ;
2009-02-09 10:38:02 +03:00
goto out_trans_abort ;
2005-04-17 02:20:36 +04:00
}
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , dp , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
xfs_trans_log_inode ( tp , dp , XFS_ILOG_CORE ) ;
2009-02-09 10:38:02 +03:00
if ( is_dir ) {
error = xfs_dir_init ( tp , ip , dp ) ;
if ( error )
goto out_bmap_cancel ;
error = xfs_bumplink ( tp , dp ) ;
if ( error )
goto out_bmap_cancel ;
}
2005-04-17 02:20:36 +04:00
/*
* If this is a synchronous mount , make sure that the
* create transaction goes to disk before returning to
* the user .
*/
2009-02-09 10:38:02 +03:00
if ( mp - > m_flags & ( XFS_MOUNT_WSYNC | XFS_MOUNT_DIRSYNC ) )
2005-04-17 02:20:36 +04:00
xfs_trans_set_sync ( tp ) ;
/*
* Attach the dquot ( s ) to the inodes and modify them incore .
* These ids of the inode couldn ' t have changed since the new
* inode has been locked ever since it was created .
*/
2009-06-08 17:33:32 +04:00
xfs_qm_vop_create_dqattach ( tp , ip , udqp , gdqp ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 10:37:16 +03:00
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
2009-02-09 10:38:02 +03:00
if ( error )
2011-02-13 16:26:42 +03:00
goto out_bmap_cancel ;
2005-04-17 02:20:36 +04:00
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2011-02-13 16:26:42 +03:00
if ( error )
goto out_release_inode ;
2005-04-17 02:20:36 +04:00
2009-06-08 17:33:32 +04:00
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2005-04-17 02:20:36 +04:00
2008-03-06 05:46:05 +03:00
* ipp = ip ;
2010-06-23 12:11:15 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2009-02-09 10:38:02 +03:00
out_bmap_cancel :
xfs_bmap_cancel ( & free_list ) ;
out_trans_abort :
2005-04-17 02:20:36 +04:00
cancel_flags | = XFS_TRANS_ABORT ;
2009-02-09 10:38:02 +03:00
out_trans_cancel :
xfs_trans_cancel ( tp , cancel_flags ) ;
2011-02-13 16:26:42 +03:00
out_release_inode :
/*
* Wait until after the current transaction is aborted to
* release the inode . This prevents recursive transactions
* and deadlocks from xfs_inactive .
*/
if ( ip )
IRELE ( ip ) ;
2009-06-08 17:33:32 +04:00
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2005-04-17 02:20:36 +04:00
2007-08-28 10:12:30 +04:00
if ( unlock_dp_on_error )
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
2010-06-23 12:11:15 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
# ifdef DEBUG
int xfs_locked_n ;
int xfs_small_retries ;
int xfs_middle_retries ;
int xfs_lots_retries ;
int xfs_lock_delays ;
# endif
2007-05-08 07:50:19 +04:00
/*
* Bump the subclass so xfs_lock_inodes ( ) acquires each lock with
* a different value
*/
static inline int
xfs_lock_inumorder ( int lock_mode , int subclass )
{
if ( lock_mode & ( XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL ) )
2007-06-29 11:26:09 +04:00
lock_mode | = ( subclass + XFS_LOCK_INUMORDER ) < < XFS_IOLOCK_SHIFT ;
2007-05-08 07:50:19 +04:00
if ( lock_mode & ( XFS_ILOCK_SHARED | XFS_ILOCK_EXCL ) )
2007-06-29 11:26:09 +04:00
lock_mode | = ( subclass + XFS_LOCK_INUMORDER ) < < XFS_ILOCK_SHIFT ;
2007-05-08 07:50:19 +04:00
return lock_mode ;
}
2005-04-17 02:20:36 +04:00
/*
* The following routine will lock n inodes in exclusive mode .
* We assume the caller calls us with the inodes in i_ino order .
*
* We need to detect deadlock where an inode that we lock
* is in the AIL and we start waiting for another inode that is locked
* by a thread in a long running transaction ( such as truncate ) . This can
* result in deadlock since the long running trans might need to wait
* for the inode we just locked in order to push the tail and free space
* in the log .
*/
void
xfs_lock_inodes (
xfs_inode_t * * ips ,
int inodes ,
uint lock_mode )
{
int attempts = 0 , i , j , try_lock ;
xfs_log_item_t * lp ;
ASSERT ( ips & & ( inodes > = 2 ) ) ; /* we need at least two */
2008-04-22 11:34:06 +04:00
try_lock = 0 ;
i = 0 ;
2005-04-17 02:20:36 +04:00
again :
for ( ; i < inodes ; i + + ) {
ASSERT ( ips [ i ] ) ;
if ( i & & ( ips [ i ] = = ips [ i - 1 ] ) ) /* Already locked */
continue ;
/*
* If try_lock is not set yet , make sure all locked inodes
* are not in the AIL .
* If any are , set try_lock to be used later .
*/
if ( ! try_lock ) {
for ( j = ( i - 1 ) ; j > = 0 & & ! try_lock ; j - - ) {
lp = ( xfs_log_item_t * ) ips [ j ] - > i_itemp ;
if ( lp & & ( lp - > li_flags & XFS_LI_IN_AIL ) ) {
try_lock + + ;
}
}
}
/*
* If any of the previous locks we have locked is in the AIL ,
* we must TRY to get the second and subsequent locks . If
* we can ' t get any , we must release all we have
* and try again .
*/
if ( try_lock ) {
/* try_lock must be 0 if i is 0. */
/*
* try_lock means we have an inode locked
* that is in the AIL .
*/
ASSERT ( i ! = 0 ) ;
2007-05-08 07:50:19 +04:00
if ( ! xfs_ilock_nowait ( ips [ i ] , xfs_lock_inumorder ( lock_mode , i ) ) ) {
2005-04-17 02:20:36 +04:00
attempts + + ;
/*
* Unlock all previous guys and try again .
* xfs_iunlock will try to push the tail
* if the inode is in the AIL .
*/
for ( j = i - 1 ; j > = 0 ; j - - ) {
/*
* Check to see if we ' ve already
* unlocked this one .
* Not the first one going back ,
* and the inode ptr is the same .
*/
if ( ( j ! = ( i - 1 ) ) & & ips [ j ] = =
ips [ j + 1 ] )
continue ;
xfs_iunlock ( ips [ j ] , lock_mode ) ;
}
if ( ( attempts % 5 ) = = 0 ) {
delay ( 1 ) ; /* Don't just spin the CPU */
# ifdef DEBUG
xfs_lock_delays + + ;
# endif
}
i = 0 ;
try_lock = 0 ;
goto again ;
}
} else {
2007-05-08 07:50:19 +04:00
xfs_ilock ( ips [ i ] , xfs_lock_inumorder ( lock_mode , i ) ) ;
2005-04-17 02:20:36 +04:00
}
}
# ifdef DEBUG
if ( attempts ) {
if ( attempts < 5 ) xfs_small_retries + + ;
else if ( attempts < 100 ) xfs_middle_retries + + ;
else xfs_lots_retries + + ;
} else {
xfs_locked_n + + ;
}
# endif
}
2008-09-17 10:51:21 +04:00
/*
* xfs_lock_two_inodes ( ) can only be used to lock one type of lock
* at a time - the iolock or the ilock , but not both at once . If
* we lock both at once , lockdep will report false positives saying
* we have violated locking orders .
*/
2008-08-13 10:18:07 +04:00
void
xfs_lock_two_inodes (
xfs_inode_t * ip0 ,
xfs_inode_t * ip1 ,
uint lock_mode )
{
xfs_inode_t * temp ;
int attempts = 0 ;
xfs_log_item_t * lp ;
2008-09-17 10:51:21 +04:00
if ( lock_mode & ( XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL ) )
ASSERT ( ( lock_mode & ( XFS_ILOCK_SHARED | XFS_ILOCK_EXCL ) ) = = 0 ) ;
2008-08-13 10:18:07 +04:00
ASSERT ( ip0 - > i_ino ! = ip1 - > i_ino ) ;
if ( ip0 - > i_ino > ip1 - > i_ino ) {
temp = ip0 ;
ip0 = ip1 ;
ip1 = temp ;
}
again :
xfs_ilock ( ip0 , xfs_lock_inumorder ( lock_mode , 0 ) ) ;
/*
* If the first lock we have locked is in the AIL , we must TRY to get
* the second lock . If we can ' t get it , we must release the first one
* and try again .
*/
lp = ( xfs_log_item_t * ) ip0 - > i_itemp ;
if ( lp & & ( lp - > li_flags & XFS_LI_IN_AIL ) ) {
if ( ! xfs_ilock_nowait ( ip1 , xfs_lock_inumorder ( lock_mode , 1 ) ) ) {
xfs_iunlock ( ip0 , lock_mode ) ;
if ( ( + + attempts % 5 ) = = 0 )
delay ( 1 ) ; /* Don't just spin the CPU */
goto again ;
}
} else {
xfs_ilock ( ip1 , xfs_lock_inumorder ( lock_mode , 1 ) ) ;
}
}
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_remove (
2007-08-28 10:12:30 +04:00
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name ,
xfs_inode_t * ip )
2005-04-17 02:20:36 +04:00
{
2007-08-28 10:12:30 +04:00
xfs_mount_t * mp = dp - > i_mount ;
2005-04-17 02:20:36 +04:00
xfs_trans_t * tp = NULL ;
2008-06-23 07:25:17 +04:00
int is_dir = S_ISDIR ( ip - > i_d . di_mode ) ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
xfs_bmap_free_t free_list ;
xfs_fsblock_t first_block ;
int cancel_flags ;
int committed ;
int link_zero ;
uint resblks ;
2008-06-23 07:25:17 +04:00
uint log_count ;
2005-04-17 02:20:36 +04:00
2010-06-24 05:57:09 +04:00
trace_xfs_remove ( dp , name ) ;
2005-04-17 02:20:36 +04:00
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( dp , 0 ) ;
2008-06-23 07:25:17 +04:00
if ( error )
goto std_return ;
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( ip , 0 ) ;
2008-06-23 07:25:17 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto std_return ;
2008-06-23 07:25:17 +04:00
if ( is_dir ) {
tp = xfs_trans_alloc ( mp , XFS_TRANS_RMDIR ) ;
log_count = XFS_DEFAULT_LOG_COUNT ;
} else {
tp = xfs_trans_alloc ( mp , XFS_TRANS_REMOVE ) ;
log_count = XFS_REMOVE_LOG_COUNT ;
}
2005-04-17 02:20:36 +04:00
cancel_flags = XFS_TRANS_RELEASE_LOG_RES ;
2008-06-23 07:25:17 +04:00
2005-04-17 02:20:36 +04:00
/*
* We try to get the real space reservation first ,
* allowing for directory btree deletion ( s ) implying
* possible bmap insert ( s ) . If we can ' t get the space
* reservation then we use 0 instead , and avoid the bmap
* btree insert ( s ) in the directory code by , if the bmap
* insert tries to happen , instead trimming the LAST
* block from the directory .
*/
resblks = XFS_REMOVE_SPACE_RES ( mp ) ;
error = xfs_trans_reserve ( tp , resblks , XFS_REMOVE_LOG_RES ( mp ) , 0 ,
2008-06-23 07:25:17 +04:00
XFS_TRANS_PERM_LOG_RES , log_count ) ;
2005-04-17 02:20:36 +04:00
if ( error = = ENOSPC ) {
resblks = 0 ;
error = xfs_trans_reserve ( tp , 0 , XFS_REMOVE_LOG_RES ( mp ) , 0 ,
2008-06-23 07:25:17 +04:00
XFS_TRANS_PERM_LOG_RES , log_count ) ;
2005-04-17 02:20:36 +04:00
}
if ( error ) {
ASSERT ( error ! = ENOSPC ) ;
2008-06-23 07:25:17 +04:00
cancel_flags = 0 ;
goto out_trans_cancel ;
2005-04-17 02:20:36 +04:00
}
2008-08-13 10:18:07 +04:00
xfs_lock_two_inodes ( dp , ip , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ijoin ( tp , ip , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
2008-06-23 07:25:17 +04:00
/*
* If we ' re removing a directory perform some additional validation .
*/
if ( is_dir ) {
ASSERT ( ip - > i_d . di_nlink > = 2 ) ;
if ( ip - > i_d . di_nlink ! = 2 ) {
error = XFS_ERROR ( ENOTEMPTY ) ;
goto out_trans_cancel ;
}
if ( ! xfs_dir_isempty ( ip ) ) {
error = XFS_ERROR ( ENOTEMPTY ) ;
goto out_trans_cancel ;
}
}
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2008-04-10 06:22:07 +04:00
error = xfs_dir_removename ( tp , dp , name , ip - > i_ino ,
2008-04-22 11:33:46 +04:00
& first_block , & free_list , resblks ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
ASSERT ( error ! = ENOENT ) ;
2008-06-23 07:25:17 +04:00
goto out_bmap_cancel ;
2005-04-17 02:20:36 +04:00
}
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , dp , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
2008-06-23 07:25:17 +04:00
if ( is_dir ) {
/*
* Drop the link from ip ' s " .. " .
*/
error = xfs_droplink ( tp , dp ) ;
if ( error )
goto out_bmap_cancel ;
/*
2008-10-30 09:55:18 +03:00
* Drop the " . " link from ip to self .
2008-06-23 07:25:17 +04:00
*/
error = xfs_droplink ( tp , ip ) ;
if ( error )
goto out_bmap_cancel ;
} else {
/*
* When removing a non - directory we need to log the parent
2008-11-28 06:23:37 +03:00
* inode here . For a directory this is done implicitly
* by the xfs_droplink call for the " .. " entry .
2008-06-23 07:25:17 +04:00
*/
xfs_trans_log_inode ( tp , dp , XFS_ILOG_CORE ) ;
2005-04-17 02:20:36 +04:00
}
2008-06-23 07:25:17 +04:00
/*
2008-10-30 09:55:18 +03:00
* Drop the link from dp to ip .
2008-06-23 07:25:17 +04:00
*/
error = xfs_droplink ( tp , ip ) ;
if ( error )
goto out_bmap_cancel ;
/*
* Determine if this is the last link while
2005-04-17 02:20:36 +04:00
* we are in the transaction .
*/
2008-06-23 07:25:17 +04:00
link_zero = ( ip - > i_d . di_nlink = = 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* If this is a synchronous mount , make sure that the
* remove transaction goes to disk before returning to
* the user .
*/
2008-06-23 07:25:17 +04:00
if ( mp - > m_flags & ( XFS_MOUNT_WSYNC | XFS_MOUNT_DIRSYNC ) )
2005-04-17 02:20:36 +04:00
xfs_trans_set_sync ( tp ) ;
2007-02-10 10:37:16 +03:00
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
2008-06-23 07:25:17 +04:00
if ( error )
goto out_bmap_cancel ;
2005-04-17 02:20:36 +04:00
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2008-04-22 11:34:24 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto std_return ;
2007-07-11 05:09:12 +04:00
/*
* If we are using filestreams , kill the stream association .
* If the file is still open it may get a new one but that
* will get killed on last close in xfs_close ( ) so we don ' t
* have to worry about that .
*/
2008-06-23 07:25:17 +04:00
if ( ! is_dir & & link_zero & & xfs_inode_is_filestream ( ip ) )
2007-07-11 05:09:12 +04:00
xfs_filestream_deassociate ( ip ) ;
2010-06-23 12:11:15 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2008-06-23 07:25:17 +04:00
out_bmap_cancel :
2005-04-17 02:20:36 +04:00
xfs_bmap_cancel ( & free_list ) ;
cancel_flags | = XFS_TRANS_ABORT ;
2008-06-23 07:25:17 +04:00
out_trans_cancel :
2005-04-17 02:20:36 +04:00
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
}
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_link (
2007-08-28 10:12:30 +04:00
xfs_inode_t * tdp ,
2008-03-06 05:46:12 +03:00
xfs_inode_t * sip ,
2008-04-10 06:22:07 +04:00
struct xfs_name * target_name )
2005-04-17 02:20:36 +04:00
{
2007-08-28 10:12:30 +04:00
xfs_mount_t * mp = tdp - > i_mount ;
2005-04-17 02:20:36 +04:00
xfs_trans_t * tp ;
int error ;
xfs_bmap_free_t free_list ;
xfs_fsblock_t first_block ;
int cancel_flags ;
int committed ;
int resblks ;
2010-06-24 05:57:09 +04:00
trace_xfs_link ( tdp , target_name ) ;
2005-04-17 02:20:36 +04:00
2008-03-06 05:46:12 +03:00
ASSERT ( ! S_ISDIR ( sip - > i_d . di_mode ) ) ;
2005-04-17 02:20:36 +04:00
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( sip , 0 ) ;
2009-02-04 11:34:20 +03:00
if ( error )
goto std_return ;
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( tdp , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto std_return ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_LINK ) ;
cancel_flags = XFS_TRANS_RELEASE_LOG_RES ;
2008-04-10 06:22:07 +04:00
resblks = XFS_LINK_SPACE_RES ( mp , target_name - > len ) ;
2005-04-17 02:20:36 +04:00
error = xfs_trans_reserve ( tp , resblks , XFS_LINK_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES , XFS_LINK_LOG_COUNT ) ;
if ( error = = ENOSPC ) {
resblks = 0 ;
error = xfs_trans_reserve ( tp , 0 , XFS_LINK_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES , XFS_LINK_LOG_COUNT ) ;
}
if ( error ) {
cancel_flags = 0 ;
goto error_return ;
}
2008-08-13 10:18:07 +04:00
xfs_lock_two_inodes ( sip , tdp , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , sip , XFS_ILOCK_EXCL ) ;
xfs_trans_ijoin ( tp , tdp , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
2005-06-21 09:39:12 +04:00
/*
* If we are using project inheritance , we only allow hard link
* creation in our tree when the project IDs are the same ; else
* the tree quota mechanism could be circumvented .
*/
if ( unlikely ( ( tdp - > i_d . di_flags & XFS_DIFLAG_PROJINHERIT ) & &
2010-09-26 10:10:18 +04:00
( xfs_get_projid ( tdp ) ! = xfs_get_projid ( sip ) ) ) ) {
2006-05-08 13:51:42 +04:00
error = XFS_ERROR ( EXDEV ) ;
2005-06-21 09:39:12 +04:00
goto error_return ;
}
2008-04-10 06:22:07 +04:00
error = xfs_dir_canenter ( tp , tdp , target_name , resblks ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto error_return ;
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2005-04-17 02:20:36 +04:00
2008-04-10 06:22:07 +04:00
error = xfs_dir_createname ( tp , tdp , target_name , sip - > i_ino ,
& first_block , & free_list , resblks ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto abort_return ;
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , tdp , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
xfs_trans_log_inode ( tp , tdp , XFS_ILOG_CORE ) ;
error = xfs_bumplink ( tp , sip ) ;
2006-06-27 06:45:17 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto abort_return ;
/*
* If this is a synchronous mount , make sure that the
* link 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 ) ;
goto abort_return ;
}
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_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
}
2006-06-27 06:45:17 +04:00
2007-08-28 10:12:30 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_symlink (
2007-08-28 10:12:30 +04:00
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * link_name ,
const char * target_path ,
2011-07-26 10:50:15 +04:00
umode_t mode ,
2010-10-06 22:41:17 +04:00
xfs_inode_t * * ipp )
2005-04-17 02:20:36 +04:00
{
2007-08-28 10:12:30 +04:00
xfs_mount_t * mp = dp - > i_mount ;
2005-04-17 02:20:36 +04:00
xfs_trans_t * tp ;
xfs_inode_t * ip ;
int error ;
int pathlen ;
xfs_bmap_free_t free_list ;
xfs_fsblock_t first_block ;
2007-08-28 10:12:30 +04:00
boolean_t unlock_dp_on_error = B_FALSE ;
2005-04-17 02:20:36 +04:00
uint cancel_flags ;
int committed ;
xfs_fileoff_t first_fsb ;
xfs_filblks_t fs_blocks ;
int nmaps ;
xfs_bmbt_irec_t mval [ SYMLINK_MAPS ] ;
xfs_daddr_t d ;
2008-04-10 06:22:07 +04:00
const char * cur_chunk ;
2005-04-17 02:20:36 +04:00
int byte_cnt ;
int n ;
xfs_buf_t * bp ;
2010-09-26 10:10:18 +04:00
prid_t prid ;
2005-04-17 02:20:36 +04:00
struct xfs_dquot * udqp , * gdqp ;
uint resblks ;
2008-03-06 05:46:19 +03:00
* ipp = NULL ;
2005-04-17 02:20:36 +04:00
error = 0 ;
ip = NULL ;
tp = NULL ;
2010-06-24 05:57:09 +04:00
trace_xfs_symlink ( dp , link_name ) ;
2005-04-17 02:20:36 +04:00
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 ;
2005-06-21 09:39:12 +04:00
if ( dp - > i_d . di_flags & XFS_DIFLAG_PROJINHERIT )
2010-09-26 10:10:18 +04:00
prid = xfs_get_projid ( dp ) ;
2005-04-17 02:20:36 +04:00
else
2010-09-26 10:10:18 +04:00
prid = XFS_PROJID_DEFAULT ;
2005-04-17 02:20:36 +04:00
/*
* Make sure that we have allocated dquot ( s ) on disk .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_vop_dqalloc ( dp , current_fsuid ( ) , current_fsgid ( ) , prid ,
2005-04-17 02:20:36 +04:00
XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT , & udqp , & gdqp ) ;
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 ) )
fs_blocks = 0 ;
else
fs_blocks = XFS_B_TO_FSB ( mp , pathlen ) ;
2008-04-10 06:22:07 +04:00
resblks = XFS_SYMLINK_SPACE_RES ( mp , link_name - > len , fs_blocks ) ;
2005-04-17 02:20:36 +04:00
error = xfs_trans_reserve ( tp , resblks , XFS_SYMLINK_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES , XFS_SYMLINK_LOG_COUNT ) ;
if ( error = = ENOSPC & & fs_blocks = = 0 ) {
resblks = 0 ;
error = xfs_trans_reserve ( tp , 0 , XFS_SYMLINK_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES , XFS_SYMLINK_LOG_COUNT ) ;
}
if ( error ) {
cancel_flags = 0 ;
goto error_return ;
}
2007-05-08 07:50:19 +04:00
xfs_ilock ( dp , XFS_ILOCK_EXCL | XFS_ILOCK_PARENT ) ;
2007-08-28 10:12:30 +04:00
unlock_dp_on_error = B_TRUE ;
2005-04-17 02:20:36 +04:00
/*
* 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 .
*/
2009-06-08 17:33:32 +04:00
error = xfs_trans_reserve_quota ( tp , mp , udqp , gdqp , resblks , 1 , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto error_return ;
/*
* Check for ability to enter directory entry , if no space reserved .
*/
2008-04-10 06:22:07 +04:00
error = xfs_dir_canenter ( tp , dp , link_name , resblks ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto error_return ;
/*
* Initialize the bmap freelist prior to calling either
* bmapi or the directory create code .
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2005-04-17 02:20:36 +04:00
/*
* Allocate an inode for the symlink .
*/
2010-10-06 22:41:17 +04:00
error = xfs_dir_ialloc ( & tp , dp , S_IFLNK | ( mode & ~ S_IFMT ) , 1 , 0 ,
prid , resblks > 0 , & ip , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
if ( error = = ENOSPC )
goto error_return ;
goto error1 ;
}
2007-08-28 10:12:30 +04:00
/*
* 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 .
*/
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , dp , XFS_ILOCK_EXCL ) ;
2007-08-28 10:12:30 +04:00
unlock_dp_on_error = B_FALSE ;
2005-04-17 02:20:36 +04:00
/*
* Also attach the dquot ( s ) to it , if applicable .
*/
2009-06-08 17:33:32 +04:00
xfs_qm_vop_create_dqattach ( tp , ip , udqp , gdqp ) ;
2005-04-17 02:20:36 +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 {
first_fsb = 0 ;
nmaps = SYMLINK_MAPS ;
2011-09-19 00:40:52 +04:00
error = xfs_bmapi_write ( tp , ip , first_fsb , fs_blocks ,
XFS_BMAPI_METADATA , & first_block , resblks ,
mval , & nmaps , & free_list ) ;
2011-02-13 16:26:42 +03:00
if ( error )
goto error2 ;
2005-04-17 02:20:36 +04:00
if ( resblks )
resblks - = fs_blocks ;
ip - > i_d . di_size = pathlen ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
cur_chunk = target_path ;
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 ) ;
bp = xfs_trans_get_buf ( tp , mp - > m_ddev_targp , d ,
BTOBB ( byte_cnt ) , 0 ) ;
2011-09-20 17:56:55 +04:00
if ( ! bp ) {
error = ENOMEM ;
goto error2 ;
}
2005-04-17 02:20:36 +04:00
if ( pathlen < byte_cnt ) {
byte_cnt = pathlen ;
}
pathlen - = byte_cnt ;
2011-07-23 03:40:15 +04:00
memcpy ( bp - > b_addr , cur_chunk , byte_cnt ) ;
2005-04-17 02:20:36 +04:00
cur_chunk + = byte_cnt ;
xfs_trans_log_buf ( tp , bp , 0 , byte_cnt - 1 ) ;
}
}
/*
* Create the directory entry for the symlink .
*/
2008-04-10 06:22:07 +04:00
error = xfs_dir_createname ( tp , dp , link_name , ip - > i_ino ,
& first_block , & free_list , resblks ) ;
2006-06-20 07:04:51 +04:00
if ( error )
2011-02-13 16:26:42 +03:00
goto error2 ;
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , dp , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
}
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 ) {
goto error2 ;
}
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2009-06-08 17:33:32 +04:00
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2005-04-17 02:20:36 +04:00
2010-06-23 12:11:15 +04:00
* ipp = ip ;
return 0 ;
2005-04-17 02:20:36 +04:00
error2 :
IRELE ( ip ) ;
error1 :
xfs_bmap_cancel ( & free_list ) ;
cancel_flags | = XFS_TRANS_ABORT ;
error_return :
xfs_trans_cancel ( tp , cancel_flags ) ;
2009-06-08 17:33:32 +04:00
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2005-04-17 02:20:36 +04:00
2007-08-28 10:12:30 +04:00
if ( unlock_dp_on_error )
2005-04-17 02:20:36 +04:00
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
2010-06-23 12:11:15 +04:00
std_return :
return error ;
2005-04-17 02:20:36 +04:00
}
int
2007-08-28 10:12:30 +04:00
xfs_set_dmattrs (
xfs_inode_t * ip ,
2005-04-17 02:20:36 +04:00
u_int evmask ,
2007-08-28 10:12:30 +04:00
u_int16_t state )
2005-04-17 02:20:36 +04:00
{
2007-08-28 10:12:30 +04:00
xfs_mount_t * mp = ip - > i_mount ;
2005-04-17 02:20:36 +04:00
xfs_trans_t * tp ;
int error ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return XFS_ERROR ( EPERM ) ;
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_SET_DMATTRS ) ;
error = xfs_trans_reserve ( tp , 0 , XFS_ICHANGE_LOG_RES ( mp ) , 0 , 0 , 0 ) ;
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
return error ;
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
2007-10-11 11:44:08 +04:00
ip - > i_d . di_dmevmask = evmask ;
ip - > i_d . di_dmstate = state ;
2005-04-17 02:20:36 +04:00
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , 0 ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/*
* xfs_alloc_file_space ( )
* This routine allocates disk space for the given file .
*
* If alloc_type = = 0 , this request is for an ALLOCSP type
* request which will change the file size . In this case , no
* DMAPI event will be generated by the call . A TRUNCATE event
* will be generated later by xfs_setattr .
*
* If alloc_type ! = 0 , this request is for a RESVSP type
* request , and a DMAPI DM_EVENT_WRITE will be generated if the
* lower block boundary byte address is less than the file ' s
* length .
*
* RETURNS :
* 0 on success
* errno on error
*
*/
2005-06-21 09:36:52 +04:00
STATIC int
2005-04-17 02:20:36 +04:00
xfs_alloc_file_space (
xfs_inode_t * ip ,
xfs_off_t offset ,
xfs_off_t len ,
int alloc_type ,
int attr_flags )
{
2006-01-11 07:28:28 +03:00
xfs_mount_t * mp = ip - > i_mount ;
xfs_off_t count ;
2005-04-17 02:20:36 +04:00
xfs_filblks_t allocated_fsb ;
xfs_filblks_t allocatesize_fsb ;
2006-01-11 07:28:28 +03:00
xfs_extlen_t extsz , temp ;
xfs_fileoff_t startoffset_fsb ;
2005-04-17 02:20:36 +04:00
xfs_fsblock_t firstfsb ;
2006-01-11 07:28:28 +03:00
int nimaps ;
int quota_flag ;
2005-04-17 02:20:36 +04:00
int rt ;
xfs_trans_t * tp ;
2006-01-11 07:28:28 +03:00
xfs_bmbt_irec_t imaps [ 1 ] , * imapp ;
xfs_bmap_free_t free_list ;
uint qblocks , resblks , resrtextents ;
int committed ;
int error ;
2005-04-17 02:20:36 +04:00
2010-06-24 05:57:09 +04:00
trace_xfs_alloc_file_space ( ip ) ;
2005-04-17 02:20:36 +04:00
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( ip , 0 ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
if ( len < = 0 )
return XFS_ERROR ( EINVAL ) ;
2007-06-18 10:50:37 +04:00
rt = XFS_IS_REALTIME_INODE ( ip ) ;
extsz = xfs_get_extsz_hint ( ip ) ;
2005-04-17 02:20:36 +04:00
count = len ;
imapp = & imaps [ 0 ] ;
2006-01-11 07:28:28 +03:00
nimaps = 1 ;
2005-04-17 02:20:36 +04:00
startoffset_fsb = XFS_B_TO_FSBT ( mp , offset ) ;
allocatesize_fsb = XFS_B_TO_FSB ( mp , count ) ;
/*
2006-01-11 07:28:28 +03:00
* Allocate file space until done or until there is an error
2005-04-17 02:20:36 +04:00
*/
while ( allocatesize_fsb & & ! error ) {
2006-01-11 07:28:28 +03:00
xfs_fileoff_t s , e ;
2005-04-17 02:20:36 +04:00
/*
2006-01-11 07:33:02 +03:00
* Determine space reservations for data / realtime .
2005-04-17 02:20:36 +04:00
*/
2006-01-11 07:28:28 +03:00
if ( unlikely ( extsz ) ) {
2005-04-17 02:20:36 +04:00
s = startoffset_fsb ;
2006-01-11 07:28:28 +03:00
do_div ( s , extsz ) ;
s * = extsz ;
e = startoffset_fsb + allocatesize_fsb ;
if ( ( temp = do_mod ( startoffset_fsb , extsz ) ) )
e + = temp ;
if ( ( temp = do_mod ( e , extsz ) ) )
e + = extsz - temp ;
} else {
s = 0 ;
e = allocatesize_fsb ;
}
2010-09-03 06:19:33 +04:00
/*
* The transaction reservation is limited to a 32 - bit block
* count , hence we need to limit the number of blocks we are
* trying to reserve to avoid an overflow . We can ' t allocate
* more than @ nimaps extents , and an extent is limited on disk
* to MAXEXTLEN ( 21 bits ) , so use that to enforce the limit .
*/
resblks = min_t ( xfs_fileoff_t , ( e - s ) , ( MAXEXTLEN * nimaps ) ) ;
2006-01-11 07:28:28 +03:00
if ( unlikely ( rt ) ) {
2010-09-03 06:19:33 +04:00
resrtextents = qblocks = resblks ;
2006-01-11 07:28:28 +03:00
resrtextents / = mp - > m_sb . sb_rextsize ;
resblks = XFS_DIOSTRAT_SPACE_RES ( mp , 0 ) ;
quota_flag = XFS_QMOPT_RES_RTBLKS ;
2005-04-17 02:20:36 +04:00
} else {
2006-01-11 07:28:28 +03:00
resrtextents = 0 ;
2010-09-03 06:19:33 +04:00
resblks = qblocks = XFS_DIOSTRAT_SPACE_RES ( mp , resblks ) ;
2006-01-11 07:28:28 +03:00
quota_flag = XFS_QMOPT_RES_REGBLKS ;
2005-04-17 02:20:36 +04:00
}
/*
2006-01-11 07:28:28 +03:00
* Allocate and setup the transaction .
2005-04-17 02:20:36 +04:00
*/
tp = xfs_trans_alloc ( mp , XFS_TRANS_DIOSTRAT ) ;
2006-01-11 07:28:28 +03:00
error = xfs_trans_reserve ( tp , resblks ,
XFS_WRITE_LOG_RES ( mp ) , resrtextents ,
2005-04-17 02:20:36 +04:00
XFS_TRANS_PERM_LOG_RES ,
XFS_WRITE_LOG_COUNT ) ;
/*
2006-01-11 07:28:28 +03:00
* Check for running out of space
2005-04-17 02:20:36 +04:00
*/
if ( error ) {
/*
* Free the transaction structure .
*/
ASSERT ( error = = ENOSPC | | XFS_FORCED_SHUTDOWN ( mp ) ) ;
xfs_trans_cancel ( tp , 0 ) ;
break ;
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2009-06-08 17:33:32 +04:00
error = xfs_trans_reserve_quota_nblks ( tp , ip , qblocks ,
0 , quota_flag ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto error1 ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & firstfsb ) ;
2011-09-19 00:40:52 +04:00
error = xfs_bmapi_write ( tp , ip , startoffset_fsb ,
allocatesize_fsb , alloc_type , & firstfsb ,
0 , imapp , & nimaps , & free_list ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
goto error0 ;
}
/*
2006-01-11 07:28:28 +03:00
* Complete the transaction
2005-04-17 02:20:36 +04:00
*/
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 ) {
goto error0 ;
}
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
if ( error ) {
break ;
}
allocated_fsb = imapp - > br_blockcount ;
2006-01-11 07:28:28 +03:00
if ( nimaps = = 0 ) {
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( ENOSPC ) ;
break ;
}
startoffset_fsb + = allocated_fsb ;
allocatesize_fsb - = allocated_fsb ;
}
return error ;
2006-01-11 07:28:28 +03:00
error0 : /* Cancel bmap, unlock inode, unreserve quota blocks, cancel trans */
2005-04-17 02:20:36 +04:00
xfs_bmap_cancel ( & free_list ) ;
2012-05-08 14:48:53 +04:00
xfs_trans_unreserve_quota_nblks ( tp , ip , ( long ) qblocks , 0 , quota_flag ) ;
2006-01-11 07:28:28 +03:00
error1 : /* Just cancel transaction */
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2010-06-23 12:11:15 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
/*
* Zero file bytes between startoff and endoff inclusive .
* The iolock is held exclusive and no blocks are buffered .
2008-09-17 10:52:50 +04:00
*
* This function is used by xfs_free_file_space ( ) to zero
* partial blocks when the range to free is not block aligned .
* When unreserving space with boundaries that are not block
* aligned we round up the start and round down the end
* boundaries and then use this function to zero the parts of
* the blocks that got dropped during the rounding .
2005-04-17 02:20:36 +04:00
*/
STATIC int
xfs_zero_remaining_bytes (
xfs_inode_t * ip ,
xfs_off_t startoff ,
xfs_off_t endoff )
{
xfs_bmbt_irec_t imap ;
xfs_fileoff_t offset_fsb ;
xfs_off_t lastoffset ;
xfs_off_t offset ;
xfs_buf_t * bp ;
xfs_mount_t * mp = ip - > i_mount ;
int nimap ;
int error = 0 ;
2008-09-17 10:52:50 +04:00
/*
* Avoid doing I / O beyond eof - it ' s not necessary
* since nothing can read beyond eof . The space will
* be zeroed when the file is extended anyway .
*/
2011-12-19 00:00:11 +04:00
if ( startoff > = XFS_ISIZE ( ip ) )
2008-09-17 10:52:50 +04:00
return 0 ;
2011-12-19 00:00:11 +04:00
if ( endoff > XFS_ISIZE ( ip ) )
endoff = XFS_ISIZE ( ip ) ;
2008-09-17 10:52:50 +04:00
2010-09-24 14:07:47 +04:00
bp = xfs_buf_get_uncached ( XFS_IS_REALTIME_INODE ( ip ) ?
mp - > m_rtdev_targp : mp - > m_ddev_targp ,
2012-04-23 09:58:56 +04:00
BTOBB ( mp - > m_sb . sb_blocksize ) , 0 ) ;
2008-12-05 05:16:15 +03:00
if ( ! bp )
return XFS_ERROR ( ENOMEM ) ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:36:25 +04:00
xfs_buf_unlock ( bp ) ;
2005-04-17 02:20:36 +04:00
for ( offset = startoff ; offset < = endoff ; offset = lastoffset + 1 ) {
offset_fsb = XFS_B_TO_FSBT ( mp , offset ) ;
nimap = 1 ;
2011-09-19 00:40:45 +04:00
error = xfs_bmapi_read ( ip , offset_fsb , 1 , & imap , & nimap , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error | | nimap < 1 )
break ;
ASSERT ( imap . br_blockcount > = 1 ) ;
ASSERT ( imap . br_startoff = = offset_fsb ) ;
lastoffset = XFS_FSB_TO_B ( mp , imap . br_startoff + 1 ) - 1 ;
if ( lastoffset > endoff )
lastoffset = endoff ;
if ( imap . br_startblock = = HOLESTARTBLOCK )
continue ;
ASSERT ( imap . br_startblock ! = DELAYSTARTBLOCK ) ;
if ( imap . br_state = = XFS_EXT_UNWRITTEN )
continue ;
XFS_BUF_UNDONE ( bp ) ;
XFS_BUF_UNWRITE ( bp ) ;
XFS_BUF_READ ( bp ) ;
2009-01-15 08:22:07 +03:00
XFS_BUF_SET_ADDR ( bp , xfs_fsb_to_db ( ip , imap . br_startblock ) ) ;
2005-04-17 02:20:36 +04:00
xfsbdstrat ( mp , bp ) ;
2010-10-06 22:41:18 +04:00
error = xfs_buf_iowait ( bp ) ;
2008-04-10 06:22:17 +04:00
if ( error ) {
2011-10-10 20:52:49 +04:00
xfs_buf_ioerror_alert ( bp ,
" xfs_zero_remaining_bytes(read) " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2011-07-23 03:40:15 +04:00
memset ( bp - > b_addr +
2005-04-17 02:20:36 +04:00
( offset - XFS_FSB_TO_B ( mp , imap . br_startoff ) ) ,
0 , lastoffset - offset + 1 ) ;
XFS_BUF_UNDONE ( bp ) ;
XFS_BUF_UNREAD ( bp ) ;
XFS_BUF_WRITE ( bp ) ;
xfsbdstrat ( mp , bp ) ;
2010-10-06 22:41:18 +04:00
error = xfs_buf_iowait ( bp ) ;
2008-04-10 06:22:17 +04:00
if ( error ) {
2011-10-10 20:52:49 +04:00
xfs_buf_ioerror_alert ( bp ,
" xfs_zero_remaining_bytes(write) " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
xfs_buf_free ( bp ) ;
return error ;
}
/*
* xfs_free_file_space ( )
* This routine frees disk space for the given file .
*
* This routine is only called by xfs_change_file_space
* for an UNRESVSP type call .
*
* RETURNS :
* 0 on success
* errno on error
*
*/
STATIC int
xfs_free_file_space (
xfs_inode_t * ip ,
xfs_off_t offset ,
xfs_off_t len ,
int attr_flags )
{
int committed ;
int done ;
xfs_fileoff_t endoffset_fsb ;
int error ;
xfs_fsblock_t firstfsb ;
xfs_bmap_free_t free_list ;
xfs_bmbt_irec_t imap ;
xfs_off_t ioffset ;
xfs_extlen_t mod = 0 ;
xfs_mount_t * mp ;
int nimap ;
uint resblks ;
2006-09-28 04:56:26 +04:00
uint rounding ;
2005-04-17 02:20:36 +04:00
int rt ;
xfs_fileoff_t startoffset_fsb ;
xfs_trans_t * tp ;
2005-05-06 00:27:19 +04:00
int need_iolock = 1 ;
2005-04-17 02:20:36 +04:00
mp = ip - > i_mount ;
2010-06-24 05:57:09 +04:00
trace_xfs_free_file_space ( ip ) ;
2005-06-21 09:47:39 +04:00
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( ip , 0 ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
error = 0 ;
if ( len < = 0 ) /* if nothing being freed */
return error ;
2007-11-23 08:29:42 +03:00
rt = XFS_IS_REALTIME_INODE ( ip ) ;
2005-04-17 02:20:36 +04:00
startoffset_fsb = XFS_B_TO_FSB ( mp , offset ) ;
2010-06-23 12:11:15 +04:00
endoffset_fsb = XFS_B_TO_FSBT ( mp , offset + len ) ;
2005-04-17 02:20:36 +04:00
2008-07-18 11:13:28 +04:00
if ( attr_flags & XFS_ATTR_NOLOCK )
2005-05-06 00:27:19 +04:00
need_iolock = 0 ;
2006-03-22 04:44:35 +03:00
if ( need_iolock ) {
2005-04-17 02:20:36 +04:00
xfs_ilock ( ip , XFS_IOLOCK_EXCL ) ;
2008-12-03 14:20:39 +03:00
/* wait for the completion of any pending DIOs */
2011-08-23 12:28:13 +04:00
inode_dio_wait ( VFS_I ( ip ) ) ;
2006-03-22 04:44:35 +03:00
}
2005-05-06 00:27:19 +04:00
2007-11-23 08:30:42 +03:00
rounding = max_t ( uint , 1 < < mp - > m_sb . sb_blocklog , PAGE_CACHE_SIZE ) ;
2005-04-17 02:20:36 +04:00
ioffset = offset & ~ ( rounding - 1 ) ;
2005-06-21 09:47:39 +04:00
2008-08-13 10:22:09 +04:00
if ( VN_CACHED ( VFS_I ( ip ) ) ! = 0 ) {
2007-11-23 08:30:42 +03:00
error = xfs_flushinval_pages ( ip , ioffset , - 1 , FI_REMAPF_LOCKED ) ;
2007-05-08 07:49:27 +04:00
if ( error )
goto out_unlock_iolock ;
2005-06-21 09:47:39 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Need to zero the stuff we ' re not freeing , on disk .
2009-03-29 11:55:42 +04:00
* If it ' s a realtime file & can ' t use unwritten extents then we
2005-04-17 02:20:36 +04:00
* actually need to zero the extent edges . Otherwise xfs_bunmapi
* will take care of it for us .
*/
2008-03-06 05:44:28 +03:00
if ( rt & & ! xfs_sb_version_hasextflgbit ( & mp - > m_sb ) ) {
2005-04-17 02:20:36 +04:00
nimap = 1 ;
2011-09-19 00:40:45 +04:00
error = xfs_bmapi_read ( ip , startoffset_fsb , 1 ,
& imap , & nimap , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out_unlock_iolock ;
ASSERT ( nimap = = 0 | | nimap = = 1 ) ;
if ( nimap & & imap . br_startblock ! = HOLESTARTBLOCK ) {
xfs_daddr_t block ;
ASSERT ( imap . br_startblock ! = DELAYSTARTBLOCK ) ;
block = imap . br_startblock ;
mod = do_div ( block , mp - > m_sb . sb_rextsize ) ;
if ( mod )
startoffset_fsb + = mp - > m_sb . sb_rextsize - mod ;
}
nimap = 1 ;
2011-09-19 00:40:45 +04:00
error = xfs_bmapi_read ( ip , endoffset_fsb - 1 , 1 ,
& imap , & nimap , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out_unlock_iolock ;
ASSERT ( nimap = = 0 | | nimap = = 1 ) ;
if ( nimap & & imap . br_startblock ! = HOLESTARTBLOCK ) {
ASSERT ( imap . br_startblock ! = DELAYSTARTBLOCK ) ;
mod + + ;
if ( mod & & ( mod ! = mp - > m_sb . sb_rextsize ) )
endoffset_fsb - = mod ;
}
}
if ( ( done = ( endoffset_fsb < = startoffset_fsb ) ) )
/*
* One contiguous piece to clear
*/
error = xfs_zero_remaining_bytes ( ip , offset , offset + len - 1 ) ;
else {
/*
* Some full blocks , possibly two pieces to clear
*/
if ( offset < XFS_FSB_TO_B ( mp , startoffset_fsb ) )
error = xfs_zero_remaining_bytes ( ip , offset ,
XFS_FSB_TO_B ( mp , startoffset_fsb ) - 1 ) ;
if ( ! error & &
XFS_FSB_TO_B ( mp , endoffset_fsb ) < offset + len )
error = xfs_zero_remaining_bytes ( ip ,
XFS_FSB_TO_B ( mp , endoffset_fsb ) ,
offset + len - 1 ) ;
}
/*
* free file space until done or until there is an error
*/
resblks = XFS_DIOSTRAT_SPACE_RES ( mp , 0 ) ;
while ( ! error & & ! done ) {
/*
2007-07-19 10:28:30 +04:00
* allocate and setup the transaction . Allow this
* transaction to dip into the reserve blocks to ensure
* the freeing of the space succeeds at ENOSPC .
2005-04-17 02:20:36 +04:00
*/
tp = xfs_trans_alloc ( mp , XFS_TRANS_DIOSTRAT ) ;
2007-07-19 10:28:30 +04:00
tp - > t_flags | = XFS_TRANS_RESERVE ;
2005-04-17 02:20:36 +04:00
error = xfs_trans_reserve ( tp ,
resblks ,
XFS_WRITE_LOG_RES ( mp ) ,
0 ,
XFS_TRANS_PERM_LOG_RES ,
XFS_WRITE_LOG_COUNT ) ;
/*
* check for running out of space
*/
if ( error ) {
/*
* Free the transaction structure .
*/
ASSERT ( error = = ENOSPC | | XFS_FORCED_SHUTDOWN ( mp ) ) ;
xfs_trans_cancel ( tp , 0 ) ;
break ;
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2009-06-08 17:33:32 +04:00
error = xfs_trans_reserve_quota ( tp , mp ,
ip - > i_udquot , ip - > i_gdquot ,
resblks , 0 , XFS_QMOPT_RES_REGBLKS ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto error1 ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* issue the bunmapi ( ) call to free the blocks
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & firstfsb ) ;
2007-10-11 11:34:33 +04:00
error = xfs_bunmapi ( tp , ip , startoffset_fsb ,
2005-04-17 02:20:36 +04:00
endoffset_fsb - startoffset_fsb ,
2010-06-23 12:11:15 +04:00
0 , 2 , & firstfsb , & free_list , & done ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
goto error0 ;
}
/*
* complete the transaction
*/
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 ) {
goto error0 ;
}
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
}
out_unlock_iolock :
if ( need_iolock )
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
return error ;
error0 :
xfs_bmap_cancel ( & free_list ) ;
error1 :
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
xfs_iunlock ( ip , need_iolock ? ( XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL ) :
XFS_ILOCK_EXCL ) ;
return error ;
}
/*
* xfs_change_file_space ( )
* This routine allocates or frees disk space for the given file .
* The user specified parameters are checked for alignment and size
* limitations .
*
* RETURNS :
* 0 on success
* errno on error
*
*/
int
xfs_change_file_space (
2007-08-28 10:12:30 +04:00
xfs_inode_t * ip ,
2005-04-17 02:20:36 +04:00
int cmd ,
xfs_flock64_t * bf ,
xfs_off_t offset ,
int attr_flags )
{
2007-08-28 10:12:30 +04:00
xfs_mount_t * mp = ip - > i_mount ;
2005-04-17 02:20:36 +04:00
int clrprealloc ;
int error ;
xfs_fsize_t fsize ;
int setprealloc ;
xfs_off_t startoffset ;
xfs_off_t llen ;
xfs_trans_t * tp ;
2008-07-18 11:13:28 +04:00
struct iattr iattr ;
2010-08-24 06:02:11 +04:00
int prealloc_type ;
2005-04-17 02:20:36 +04:00
2007-08-28 10:12:30 +04:00
if ( ! S_ISREG ( ip - > i_d . di_mode ) )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EINVAL ) ;
switch ( bf - > l_whence ) {
case 0 : /*SEEK_SET*/
break ;
case 1 : /*SEEK_CUR*/
bf - > l_start + = offset ;
break ;
case 2 : /*SEEK_END*/
2011-12-19 00:00:11 +04:00
bf - > l_start + = XFS_ISIZE ( ip ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
return XFS_ERROR ( EINVAL ) ;
}
llen = bf - > l_len > 0 ? bf - > l_len - 1 : bf - > l_len ;
2012-06-08 09:44:54 +04:00
if ( bf - > l_start < 0 | |
bf - > l_start > mp - > m_super - > s_maxbytes | |
bf - > l_start + llen < 0 | |
bf - > l_start + llen > mp - > m_super - > s_maxbytes )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EINVAL ) ;
bf - > l_whence = 0 ;
startoffset = bf - > l_start ;
2011-12-19 00:00:11 +04:00
fsize = XFS_ISIZE ( ip ) ;
2005-04-17 02:20:36 +04:00
/*
* XFS_IOC_RESVSP and XFS_IOC_UNRESVSP will reserve or unreserve
* file space .
* These calls do NOT zero the data space allocated to the file ,
* nor do they change the file size .
*
* XFS_IOC_ALLOCSP and XFS_IOC_FREESP will allocate and free file
* space .
* These calls cause the new file data to be zeroed and the file
* size to be changed .
*/
setprealloc = clrprealloc = 0 ;
2010-08-24 06:02:11 +04:00
prealloc_type = XFS_BMAPI_PREALLOC ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
2010-08-24 06:02:11 +04:00
case XFS_IOC_ZERO_RANGE :
prealloc_type | = XFS_BMAPI_CONVERT ;
xfs_tosspages ( ip , startoffset , startoffset + bf - > l_len , 0 ) ;
/* FALLTHRU */
2005-04-17 02:20:36 +04:00
case XFS_IOC_RESVSP :
case XFS_IOC_RESVSP64 :
error = xfs_alloc_file_space ( ip , startoffset , bf - > l_len ,
2010-08-24 06:02:11 +04:00
prealloc_type , attr_flags ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
setprealloc = 1 ;
break ;
case XFS_IOC_UNRESVSP :
case XFS_IOC_UNRESVSP64 :
if ( ( error = xfs_free_file_space ( ip , startoffset , bf - > l_len ,
attr_flags ) ) )
return error ;
break ;
case XFS_IOC_ALLOCSP :
case XFS_IOC_ALLOCSP64 :
case XFS_IOC_FREESP :
case XFS_IOC_FREESP64 :
2012-04-23 09:58:57 +04:00
/*
* These operations actually do IO when extending the file , but
* the allocation is done seperately to the zeroing that is
* done . This set of operations need to be serialised against
* other IO operations , such as truncate and buffered IO . We
* need to take the IOLOCK here to serialise the allocation and
* zeroing IO to prevent other IOLOCK holders ( e . g . getbmap ,
* truncate , direct IO ) from racing against the transient
* allocated but not written state we can have here .
*/
xfs_ilock ( ip , XFS_IOLOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
if ( startoffset > fsize ) {
error = xfs_alloc_file_space ( ip , fsize ,
2012-04-23 09:58:57 +04:00
startoffset - fsize , 0 ,
attr_flags | XFS_ATTR_NOLOCK ) ;
if ( error ) {
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
break ;
2012-04-23 09:58:57 +04:00
}
2005-04-17 02:20:36 +04:00
}
2008-07-18 11:13:28 +04:00
iattr . ia_valid = ATTR_SIZE ;
iattr . ia_size = startoffset ;
2005-04-17 02:20:36 +04:00
2012-04-23 09:58:57 +04:00
error = xfs_setattr_size ( ip , & iattr ,
attr_flags | XFS_ATTR_NOLOCK ) ;
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
clrprealloc = 1 ;
break ;
default :
ASSERT ( 0 ) ;
return XFS_ERROR ( EINVAL ) ;
}
/*
* update the inode timestamp , mode , and prealloc flag bits
*/
tp = xfs_trans_alloc ( mp , XFS_TRANS_WRITEID ) ;
if ( ( error = xfs_trans_reserve ( tp , 0 , XFS_WRITEID_LOG_RES ( mp ) ,
0 , 0 , 0 ) ) ) {
/* ASSERT(0); */
xfs_trans_cancel ( tp , 0 ) ;
return error ;
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , XFS_ILOCK_EXCL ) ;
2005-04-17 02:20:36 +04:00
2008-07-18 11:13:28 +04:00
if ( ( attr_flags & XFS_ATTR_DMI ) = = 0 ) {
2005-04-17 02:20:36 +04:00
ip - > i_d . di_mode & = ~ S_ISUID ;
/*
* Note that we don ' t have to worry about mandatory
* file locking being disabled here because we only
* clear the S_ISGID bit if the Group execute bit is
* on , but if it was on then mandatory locking wouldn ' t
* have been enabled .
*/
if ( ip - > i_d . di_mode & S_IXGRP )
ip - > i_d . di_mode & = ~ S_ISGID ;
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , ip , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
}
if ( setprealloc )
ip - > i_d . di_flags | = XFS_DIFLAG_PREALLOC ;
else if ( clrprealloc )
ip - > i_d . di_flags & = ~ XFS_DIFLAG_PREALLOC ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
2011-03-26 01:13:08 +03:00
if ( attr_flags & XFS_ATTR_SYNC )
xfs_trans_set_sync ( tp ) ;
2011-09-19 00:47:51 +04:00
return xfs_trans_commit ( tp , 0 ) ;
2005-04-17 02:20:36 +04:00
}