2013-08-12 14:49:39 +04:00
/*
* Copyright ( c ) 2000 - 2005 Silicon Graphics , Inc .
* Copyright ( c ) 2013 Red Hat , Inc .
* All Rights Reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include "xfs.h"
# include "xfs_fs.h"
2013-10-23 03:36:05 +04:00
# include "xfs_shared.h"
2013-10-23 03:50:10 +04:00
# include "xfs_format.h"
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2013-08-12 14:49:39 +04:00
# include "xfs_bit.h"
# include "xfs_sb.h"
# include "xfs_ag.h"
# include "xfs_mount.h"
2013-10-15 02:17:51 +04:00
# include "xfs_da_format.h"
2013-08-12 14:49:39 +04:00
# include "xfs_da_btree.h"
2013-10-23 03:51:50 +04:00
# include "xfs_inode.h"
2013-08-12 14:49:39 +04:00
# include "xfs_alloc.h"
# include "xfs_attr_remote.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2013-08-12 14:49:39 +04:00
# include "xfs_inode_item.h"
# include "xfs_bmap.h"
# include "xfs_attr.h"
# include "xfs_attr_leaf.h"
# include "xfs_error.h"
# include "xfs_quota.h"
# include "xfs_trace.h"
2013-10-23 03:51:50 +04:00
# include "xfs_dinode.h"
2013-10-29 15:11:51 +04:00
# include "xfs_dir2.h"
2013-08-12 14:49:39 +04:00
/*
* Look at all the extents for this logical region ,
* invalidate any buffers that are incore / in transactions .
*/
STATIC int
xfs_attr3_leaf_freextent (
struct xfs_trans * * trans ,
struct xfs_inode * dp ,
xfs_dablk_t blkno ,
int blkcnt )
{
struct xfs_bmbt_irec map ;
struct xfs_buf * bp ;
xfs_dablk_t tblkno ;
xfs_daddr_t dblkno ;
int tblkcnt ;
int dblkcnt ;
int nmap ;
int error ;
/*
* Roll through the " value " , invalidating the attribute value ' s
* blocks .
*/
tblkno = blkno ;
tblkcnt = blkcnt ;
while ( tblkcnt > 0 ) {
/*
* Try to remember where we decided to put the value .
*/
nmap = 1 ;
error = xfs_bmapi_read ( dp , ( xfs_fileoff_t ) tblkno , tblkcnt ,
& map , & nmap , XFS_BMAPI_ATTRFORK ) ;
if ( error ) {
return ( error ) ;
}
ASSERT ( nmap = = 1 ) ;
ASSERT ( map . br_startblock ! = DELAYSTARTBLOCK ) ;
/*
* If it ' s a hole , these are already unmapped
* so there ' s nothing to invalidate .
*/
if ( map . br_startblock ! = HOLESTARTBLOCK ) {
dblkno = XFS_FSB_TO_DADDR ( dp - > i_mount ,
map . br_startblock ) ;
dblkcnt = XFS_FSB_TO_BB ( dp - > i_mount ,
map . br_blockcount ) ;
bp = xfs_trans_get_buf ( * trans ,
dp - > i_mount - > m_ddev_targp ,
dblkno , dblkcnt , 0 ) ;
if ( ! bp )
return ENOMEM ;
xfs_trans_binval ( * trans , bp ) ;
/*
* Roll to next transaction .
*/
error = xfs_trans_roll ( trans , dp ) ;
if ( error )
return ( error ) ;
}
tblkno + = map . br_blockcount ;
tblkcnt - = map . br_blockcount ;
}
return ( 0 ) ;
}
/*
* Invalidate all of the " remote " value regions pointed to by a particular
* leaf block .
* Note that we must release the lock on the buffer so that we are not
* caught holding something that the logging code wants to flush to disk .
*/
STATIC int
xfs_attr3_leaf_inactive (
struct xfs_trans * * trans ,
struct xfs_inode * dp ,
struct xfs_buf * bp )
{
struct xfs_attr_leafblock * leaf ;
struct xfs_attr3_icleaf_hdr ichdr ;
struct xfs_attr_leaf_entry * entry ;
struct xfs_attr_leaf_name_remote * name_rmt ;
struct xfs_attr_inactive_list * list ;
struct xfs_attr_inactive_list * lp ;
int error ;
int count ;
int size ;
int tmp ;
int i ;
leaf = bp - > b_addr ;
xfs_attr3_leaf_hdr_from_disk ( & ichdr , leaf ) ;
/*
* Count the number of " remote " value extents .
*/
count = 0 ;
entry = xfs_attr3_leaf_entryp ( leaf ) ;
for ( i = 0 ; i < ichdr . count ; entry + + , i + + ) {
if ( be16_to_cpu ( entry - > nameidx ) & &
( ( entry - > flags & XFS_ATTR_LOCAL ) = = 0 ) ) {
name_rmt = xfs_attr3_leaf_name_remote ( leaf , i ) ;
if ( name_rmt - > valueblk )
count + + ;
}
}
/*
* If there are no " remote " values , we ' re done .
*/
if ( count = = 0 ) {
xfs_trans_brelse ( * trans , bp ) ;
return 0 ;
}
/*
* Allocate storage for a list of all the " remote " value extents .
*/
size = count * sizeof ( xfs_attr_inactive_list_t ) ;
list = kmem_alloc ( size , KM_SLEEP ) ;
/*
* Identify each of the " remote " value extents .
*/
lp = list ;
entry = xfs_attr3_leaf_entryp ( leaf ) ;
for ( i = 0 ; i < ichdr . count ; entry + + , i + + ) {
if ( be16_to_cpu ( entry - > nameidx ) & &
( ( entry - > flags & XFS_ATTR_LOCAL ) = = 0 ) ) {
name_rmt = xfs_attr3_leaf_name_remote ( leaf , i ) ;
if ( name_rmt - > valueblk ) {
lp - > valueblk = be32_to_cpu ( name_rmt - > valueblk ) ;
lp - > valuelen = xfs_attr3_rmt_blocks ( dp - > i_mount ,
be32_to_cpu ( name_rmt - > valuelen ) ) ;
lp + + ;
}
}
}
xfs_trans_brelse ( * trans , bp ) ; /* unlock for trans. in freextent() */
/*
* Invalidate each of the " remote " value extents .
*/
error = 0 ;
for ( lp = list , i = 0 ; i < count ; i + + , lp + + ) {
tmp = xfs_attr3_leaf_freextent ( trans , dp ,
lp - > valueblk , lp - > valuelen ) ;
if ( error = = 0 )
error = tmp ; /* save only the 1st errno */
}
kmem_free ( list ) ;
return error ;
}
/*
* Recurse ( gasp ! ) through the attribute nodes until we find leaves .
* We ' re doing a depth - first traversal in order to invalidate everything .
*/
STATIC int
xfs_attr3_node_inactive (
struct xfs_trans * * trans ,
struct xfs_inode * dp ,
struct xfs_buf * bp ,
int level )
{
xfs_da_blkinfo_t * info ;
xfs_da_intnode_t * node ;
xfs_dablk_t child_fsb ;
xfs_daddr_t parent_blkno , child_blkno ;
int error , i ;
struct xfs_buf * child_bp ;
struct xfs_da_node_entry * btree ;
struct xfs_da3_icnode_hdr ichdr ;
/*
* Since this code is recursive ( gasp ! ) we must protect ourselves .
*/
if ( level > XFS_DA_NODE_MAXDEPTH ) {
xfs_trans_brelse ( * trans , bp ) ; /* no locks for later trans */
return XFS_ERROR ( EIO ) ;
}
node = bp - > b_addr ;
2013-10-29 15:11:52 +04:00
dp - > d_ops - > node_hdr_from_disk ( & ichdr , node ) ;
2013-08-12 14:49:39 +04:00
parent_blkno = bp - > b_bn ;
if ( ! ichdr . count ) {
xfs_trans_brelse ( * trans , bp ) ;
return 0 ;
}
2013-10-29 15:11:51 +04:00
btree = dp - > d_ops - > node_tree_p ( node ) ;
2013-08-12 14:49:39 +04:00
child_fsb = be32_to_cpu ( btree [ 0 ] . before ) ;
xfs_trans_brelse ( * trans , bp ) ; /* no locks for later trans */
/*
* If this is the node level just above the leaves , simply loop
* over the leaves removing all of them . If this is higher up
* in the tree , recurse downward .
*/
for ( i = 0 ; i < ichdr . count ; i + + ) {
/*
* Read the subsidiary block to see what we have to work with .
* Don ' t do this in a transaction . This is a depth - first
* traversal of the tree so we may deal with many blocks
* before we come back to this one .
*/
error = xfs_da3_node_read ( * trans , dp , child_fsb , - 2 , & child_bp ,
XFS_ATTR_FORK ) ;
if ( error )
return ( error ) ;
if ( child_bp ) {
/* save for re-read later */
child_blkno = XFS_BUF_ADDR ( child_bp ) ;
/*
* Invalidate the subtree , however we have to .
*/
info = child_bp - > b_addr ;
switch ( info - > magic ) {
case cpu_to_be16 ( XFS_DA_NODE_MAGIC ) :
case cpu_to_be16 ( XFS_DA3_NODE_MAGIC ) :
error = xfs_attr3_node_inactive ( trans , dp ,
child_bp , level + 1 ) ;
break ;
case cpu_to_be16 ( XFS_ATTR_LEAF_MAGIC ) :
case cpu_to_be16 ( XFS_ATTR3_LEAF_MAGIC ) :
error = xfs_attr3_leaf_inactive ( trans , dp ,
child_bp ) ;
break ;
default :
error = XFS_ERROR ( EIO ) ;
xfs_trans_brelse ( * trans , child_bp ) ;
break ;
}
if ( error )
return error ;
/*
* Remove the subsidiary block from the cache
* and from the log .
*/
error = xfs_da_get_buf ( * trans , dp , 0 , child_blkno ,
& child_bp , XFS_ATTR_FORK ) ;
if ( error )
return error ;
xfs_trans_binval ( * trans , child_bp ) ;
}
/*
* If we ' re not done , re - read the parent to get the next
* child block number .
*/
if ( i + 1 < ichdr . count ) {
error = xfs_da3_node_read ( * trans , dp , 0 , parent_blkno ,
& bp , XFS_ATTR_FORK ) ;
if ( error )
return error ;
child_fsb = be32_to_cpu ( btree [ i + 1 ] . before ) ;
xfs_trans_brelse ( * trans , bp ) ;
}
/*
* Atomically commit the whole invalidate stuff .
*/
error = xfs_trans_roll ( trans , dp ) ;
if ( error )
return error ;
}
return 0 ;
}
/*
* Indiscriminately delete the entire attribute fork
*
* Recurse ( gasp ! ) through the attribute nodes until we find leaves .
* We ' re doing a depth - first traversal in order to invalidate everything .
*/
int
xfs_attr3_root_inactive (
struct xfs_trans * * trans ,
struct xfs_inode * dp )
{
struct xfs_da_blkinfo * info ;
struct xfs_buf * bp ;
xfs_daddr_t blkno ;
int error ;
/*
* Read block 0 to see what we have to work with .
* We only get here if we have extents , since we remove
* the extents in reverse order the extent containing
* block 0 must still be there .
*/
error = xfs_da3_node_read ( * trans , dp , 0 , - 1 , & bp , XFS_ATTR_FORK ) ;
if ( error )
return error ;
blkno = bp - > b_bn ;
/*
* Invalidate the tree , even if the " tree " is only a single leaf block .
* This is a depth - first traversal !
*/
info = bp - > b_addr ;
switch ( info - > magic ) {
case cpu_to_be16 ( XFS_DA_NODE_MAGIC ) :
case cpu_to_be16 ( XFS_DA3_NODE_MAGIC ) :
error = xfs_attr3_node_inactive ( trans , dp , bp , 1 ) ;
break ;
case cpu_to_be16 ( XFS_ATTR_LEAF_MAGIC ) :
case cpu_to_be16 ( XFS_ATTR3_LEAF_MAGIC ) :
error = xfs_attr3_leaf_inactive ( trans , dp , bp ) ;
break ;
default :
error = XFS_ERROR ( EIO ) ;
xfs_trans_brelse ( * trans , bp ) ;
break ;
}
if ( error )
return error ;
/*
* Invalidate the incore copy of the root block .
*/
error = xfs_da_get_buf ( * trans , dp , 0 , blkno , & bp , XFS_ATTR_FORK ) ;
if ( error )
return error ;
xfs_trans_binval ( * trans , bp ) ; /* remove from cache */
/*
* Commit the invalidate and start the next transaction .
*/
error = xfs_trans_roll ( trans , dp ) ;
return error ;
}
int
xfs_attr_inactive ( xfs_inode_t * dp )
{
xfs_trans_t * trans ;
xfs_mount_t * mp ;
int error ;
mp = dp - > i_mount ;
ASSERT ( ! XFS_NOT_DQATTACHED ( mp , dp ) ) ;
xfs_ilock ( dp , XFS_ILOCK_SHARED ) ;
if ( ! xfs_inode_hasattr ( dp ) | |
dp - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) {
xfs_iunlock ( dp , XFS_ILOCK_SHARED ) ;
return 0 ;
}
xfs_iunlock ( dp , XFS_ILOCK_SHARED ) ;
/*
* Start our first transaction of the day .
*
* All future transactions during this code must be " chained " off
* this one via the trans_dup ( ) call . All transactions will contain
* the inode , and the inode will always be marked with trans_ihold ( ) .
* Since the inode will be locked in all transactions , we must log
* the inode in every transaction to let it float upward through
* the log .
*/
trans = xfs_trans_alloc ( mp , XFS_TRANS_ATTRINVAL ) ;
2013-08-12 14:49:59 +04:00
error = xfs_trans_reserve ( trans , & M_RES ( mp ) - > tr_attrinval , 0 , 0 ) ;
if ( error ) {
2013-08-12 14:49:39 +04:00
xfs_trans_cancel ( trans , 0 ) ;
return ( error ) ;
}
xfs_ilock ( dp , XFS_ILOCK_EXCL ) ;
/*
* No need to make quota reservations here . We expect to release some
* blocks , not allocate , in the common case .
*/
xfs_trans_ijoin ( trans , dp , 0 ) ;
/*
* Decide on what work routines to call based on the inode size .
*/
if ( ! xfs_inode_hasattr ( dp ) | |
dp - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) {
error = 0 ;
goto out ;
}
error = xfs_attr3_root_inactive ( & trans , dp ) ;
if ( error )
goto out ;
error = xfs_itruncate_extents ( & trans , dp , XFS_ATTR_FORK , 0 ) ;
if ( error )
goto out ;
error = xfs_trans_commit ( trans , XFS_TRANS_RELEASE_LOG_RES ) ;
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
return ( error ) ;
out :
xfs_trans_cancel ( trans , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
return ( error ) ;
}