2005-04-17 02:20:36 +04:00
/*
2006-06-09 08:48:12 +04:00
* Copyright ( c ) 2000 - 2006 Silicon Graphics , Inc .
2013-04-03 09:11:18 +04:00
* Copyright ( c ) 2012 Red Hat , 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"
2012-10-08 14:56:11 +04:00
# include "xfs_icache.h"
2013-04-03 09:11:18 +04:00
# include "xfs_symlink.h"
2005-04-17 02:20:36 +04:00
/*
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
*/
2012-11-06 18:50:41 +04:00
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 ) ;
2012-11-06 18:50:41 +04:00
return EAGAIN ;
2009-10-19 08:03:46 +04:00
}
}
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 ) ;
2012-11-06 18:50:38 +04:00
if ( ! error )
xfs_inode_clear_eofblocks_tag ( ip ) ;
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
}
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 ) ;
2012-11-12 15:53:56 +04:00
if ( VN_DIRTY ( VFS_I ( ip ) ) & & ip - > i_delayed_blks > 0 ) {
error = - filemap_flush ( VFS_I ( ip ) - > i_mapping ) ;
if ( error )
return error ;
}
2011-06-23 05:35:00 +04:00
}
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
2012-11-06 18:50:40 +04:00
if ( xfs_can_free_eofblocks ( ip , false ) ) {
2010-12-23 04:02:31 +03:00
/*
* 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 ) ;
2012-11-06 18:50:41 +04:00
if ( error & & error ! = EAGAIN )
2010-12-23 04:02:31 +03:00
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 ) {
2012-11-06 18:50:40 +04:00
/*
* force is true because we are evicting an inode from the
* cache . Post - eof blocks must be freed , lest we end up with
* broken free space accounting .
*/
if ( xfs_can_free_eofblocks ( ip , true ) ) {
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 ) ) {
2013-06-18 00:35:57 +04:00
error = xfs_inactive_symlink ( ip , & tp ) ;
if ( error )
goto out_cancel ;
2012-07-04 19:13:29 +04:00
} 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 ;
2012-11-13 03:32:59 +04:00
bool unlock_dp_on_error = 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 ;
2013-07-11 09:00:40 +04:00
struct xfs_dquot * pdqp = 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 ,
2013-07-11 09:00:40 +04:00
XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT ,
& udqp , & gdqp , & pdqp ) ;
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 */
2012-10-08 14:56:04 +04:00
xfs_flush_inodes ( mp ) ;
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 ) ;
2012-11-13 03:32:59 +04:00
unlock_dp_on_error = 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 .
*/
2013-07-11 09:00:40 +04:00
error = xfs_trans_reserve_quota ( tp , mp , udqp , gdqp ,
pdqp , 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 ) ;
2012-11-13 03:32:59 +04:00
unlock_dp_on_error = 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 .
*/
2013-07-11 09:00:40 +04:00
xfs_qm_vop_create_dqattach ( tp , ip , udqp , gdqp , pdqp ) ;
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 ) ;
2013-07-11 09:00:40 +04:00
xfs_qm_dqrele ( pdqp ) ;
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 ) ;
2013-07-11 09:00:40 +04:00
xfs_qm_dqrele ( pdqp ) ;
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
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 ;
2013-05-20 03:51:09 +04:00
xfs_off_t 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
2013-05-20 03:51:09 +04:00
rounding = max_t ( xfs_off_t , 1 < < mp - > m_sb . sb_blocklog , PAGE_CACHE_SIZE ) ;
2005-04-17 02:20:36 +04:00
ioffset = offset & ~ ( rounding - 1 ) ;
2012-11-12 15:53:57 +04:00
error = - filemap_write_and_wait_range ( VFS_I ( ip ) - > i_mapping ,
ioffset , - 1 ) ;
if ( error )
goto out_unlock_iolock ;
truncate_pagecache_range ( VFS_I ( ip ) , ioffset , - 1 ) ;
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 ,
2013-07-11 09:00:40 +04:00
ip - > i_udquot , ip - > i_gdquot , ip - > i_pdquot ,
2009-06-08 17:33:32 +04:00
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 ;
}
2012-11-29 08:26:33 +04:00
STATIC int
xfs_zero_file_space (
struct xfs_inode * ip ,
xfs_off_t offset ,
xfs_off_t len ,
int attr_flags )
{
struct xfs_mount * mp = ip - > i_mount ;
uint granularity ;
xfs_off_t start_boundary ;
xfs_off_t end_boundary ;
int error ;
granularity = max_t ( uint , 1 < < mp - > m_sb . sb_blocklog , PAGE_CACHE_SIZE ) ;
/*
* Round the range of extents we are going to convert inwards . If the
* offset is aligned , then it doesn ' t get changed so we zero from the
* start of the block offset points to .
*/
start_boundary = round_up ( offset , granularity ) ;
end_boundary = round_down ( offset + len , granularity ) ;
ASSERT ( start_boundary > = offset ) ;
ASSERT ( end_boundary < = offset + len ) ;
if ( ! ( attr_flags & XFS_ATTR_NOLOCK ) )
xfs_ilock ( ip , XFS_IOLOCK_EXCL ) ;
if ( start_boundary < end_boundary - 1 ) {
/* punch out the page cache over the conversion range */
truncate_pagecache_range ( VFS_I ( ip ) , start_boundary ,
end_boundary - 1 ) ;
/* convert the blocks */
error = xfs_alloc_file_space ( ip , start_boundary ,
end_boundary - start_boundary - 1 ,
XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT ,
attr_flags ) ;
if ( error )
goto out_unlock ;
/* We've handled the interior of the range, now for the edges */
if ( start_boundary ! = offset )
error = xfs_iozero ( ip , offset , start_boundary - offset ) ;
if ( error )
goto out_unlock ;
if ( end_boundary ! = offset + len )
error = xfs_iozero ( ip , end_boundary ,
offset + len - end_boundary ) ;
} else {
/*
* It ' s either a sub - granularity range or the range spanned lies
* partially across two adjacent blocks .
*/
error = xfs_iozero ( ip , offset , len ) ;
}
out_unlock :
if ( ! ( attr_flags & XFS_ATTR_NOLOCK ) )
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
return error ;
}
2005-04-17 02:20:36 +04:00
/*
* 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_trans_t * tp ;
2008-07-18 11:13:28 +04:00
struct iattr iattr ;
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 ) ;
}
2012-11-14 10:42:47 +04:00
/*
* length of < = 0 for resv / unresv / zero is invalid . length for
* alloc / free is ignored completely and we have no idea what userspace
* might have set it to , so set it to zero to allow range
* checks to pass .
*/
switch ( cmd ) {
case XFS_IOC_ZERO_RANGE :
case XFS_IOC_RESVSP :
case XFS_IOC_RESVSP64 :
case XFS_IOC_UNRESVSP :
case XFS_IOC_UNRESVSP64 :
if ( bf - > l_len < = 0 )
return XFS_ERROR ( EINVAL ) ;
break ;
default :
bf - > l_len = 0 ;
break ;
}
2005-04-17 02:20:36 +04:00
2012-06-08 09:44:54 +04:00
if ( bf - > l_start < 0 | |
bf - > l_start > mp - > m_super - > s_maxbytes | |
2012-11-14 10:42:47 +04:00
bf - > l_start + bf - > l_len < 0 | |
bf - > l_start + bf - > l_len > = 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
setprealloc = clrprealloc = 0 ;
switch ( cmd ) {
2010-08-24 06:02:11 +04:00
case XFS_IOC_ZERO_RANGE :
2012-11-29 08:26:33 +04:00
error = xfs_zero_file_space ( ip , startoffset , bf - > l_len ,
attr_flags ) ;
if ( error )
return error ;
setprealloc = 1 ;
break ;
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 ,
2012-11-29 08:26:33 +04:00
XFS_BMAPI_PREALLOC , 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
}