2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_fs.h"
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"
2005-11-02 06:38:42 +03:00
# include "xfs_bit.h"
2005-04-17 02:20:36 +04:00
# 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"
2005-11-02 06:38:42 +03:00
# include "xfs_da_btree.h"
# include "xfs_attr_sf.h"
2005-04-17 02:20:36 +04:00
# include "xfs_inode.h"
2005-11-02 06:38:42 +03:00
# include "xfs_alloc.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inode_item.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
2013-08-12 14:49:42 +04:00
# include "xfs_bmap_util.h"
2013-10-23 03:51:50 +04:00
# include "xfs_bmap_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_attr.h"
# include "xfs_attr_leaf.h"
2013-04-03 09:11:27 +04:00
# include "xfs_attr_remote.h"
2005-04-17 02:20:36 +04:00
# include "xfs_error.h"
# include "xfs_quota.h"
# include "xfs_trans_space.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2013-10-23 03:51:50 +04:00
# include "xfs_dinode.h"
2005-04-17 02:20:36 +04:00
/*
* xfs_attr . c
*
* Provide the external interfaces to manage attribute lists .
*/
/*========================================================================
* Function prototypes for the kernel .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
* Internal routines when attribute list fits inside the inode .
*/
STATIC int xfs_attr_shortform_addname ( xfs_da_args_t * args ) ;
/*
* Internal routines when attribute list is one block .
*/
2005-06-21 09:36:52 +04:00
STATIC int xfs_attr_leaf_get ( xfs_da_args_t * args ) ;
2005-04-17 02:20:36 +04:00
STATIC int xfs_attr_leaf_addname ( xfs_da_args_t * args ) ;
STATIC int xfs_attr_leaf_removename ( xfs_da_args_t * args ) ;
/*
* Internal routines when attribute list is more than one block .
*/
2005-06-21 09:36:52 +04:00
STATIC int xfs_attr_node_get ( xfs_da_args_t * args ) ;
2005-04-17 02:20:36 +04:00
STATIC int xfs_attr_node_addname ( xfs_da_args_t * args ) ;
STATIC int xfs_attr_node_removename ( xfs_da_args_t * args ) ;
STATIC int xfs_attr_fillstate ( xfs_da_state_t * state ) ;
STATIC int xfs_attr_refillstate ( xfs_da_state_t * state ) ;
2008-04-22 11:34:31 +04:00
STATIC int
xfs_attr_name_to_xname (
struct xfs_name * xname ,
2010-01-20 02:47:48 +03:00
const unsigned char * aname )
2008-04-22 11:34:31 +04:00
{
if ( ! aname )
return EINVAL ;
xname - > name = aname ;
2010-01-20 02:47:48 +03:00
xname - > len = strlen ( ( char * ) aname ) ;
2008-04-22 11:34:31 +04:00
if ( xname - > len > = MAXNAMELEN )
return EFAULT ; /* match IRIX behaviour */
return 0 ;
}
2005-04-17 02:20:36 +04:00
2013-08-12 14:49:38 +04:00
int
2008-06-23 07:23:41 +04:00
xfs_inode_hasattr (
struct xfs_inode * ip )
{
if ( ! XFS_IFORK_Q ( ip ) | |
( ip - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
ip - > i_d . di_anextents = = 0 ) )
return 0 ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
/*========================================================================
* Overall external interface routines .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2009-11-14 19:17:20 +03:00
STATIC int
xfs_attr_get_int (
struct xfs_inode * ip ,
struct xfs_name * name ,
2010-01-20 02:47:48 +03:00
unsigned char * value ,
2009-11-14 19:17:20 +03:00
int * valuelenp ,
int flags )
2005-04-17 02:20:36 +04:00
{
xfs_da_args_t args ;
int error ;
2008-06-23 07:23:41 +04:00
if ( ! xfs_inode_hasattr ( ip ) )
return ENOATTR ;
2005-04-17 02:20:36 +04:00
/*
* Fill in the arg structure for this request .
*/
memset ( ( char * ) & args , 0 , sizeof ( args ) ) ;
2008-04-22 11:34:31 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2005-04-17 02:20:36 +04:00
args . value = value ;
args . valuelen = * valuelenp ;
args . flags = flags ;
args . hashval = xfs_da_hashname ( args . name , args . namelen ) ;
args . dp = ip ;
args . whichfork = XFS_ATTR_FORK ;
/*
* Decide on what work routines to call based on the inode size .
*/
2008-06-23 07:23:41 +04:00
if ( ip - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) {
2005-04-17 02:20:36 +04:00
error = xfs_attr_shortform_getvalue ( & args ) ;
} else if ( xfs_bmap_one_block ( ip , XFS_ATTR_FORK ) ) {
error = xfs_attr_leaf_get ( & args ) ;
} else {
error = xfs_attr_node_get ( & args ) ;
}
/*
* Return the number of bytes in the value to the caller .
*/
* valuelenp = args . valuelen ;
if ( error = = EEXIST )
error = 0 ;
return ( error ) ;
}
int
2007-08-28 10:12:30 +04:00
xfs_attr_get (
xfs_inode_t * ip ,
2010-01-20 02:47:48 +03:00
const unsigned char * name ,
unsigned char * value ,
2007-08-28 10:12:30 +04:00
int * valuelenp ,
2008-04-22 11:34:31 +04:00
int flags )
2005-04-17 02:20:36 +04:00
{
2008-04-22 11:34:31 +04:00
int error ;
struct xfs_name xname ;
2013-12-07 00:30:15 +04:00
uint lock_mode ;
2005-04-17 02:20:36 +04:00
XFS_STATS_INC ( xs_attr_get ) ;
if ( XFS_FORCED_SHUTDOWN ( ip - > i_mount ) )
return ( EIO ) ;
2008-04-22 11:34:31 +04:00
error = xfs_attr_name_to_xname ( & xname , name ) ;
if ( error )
return error ;
2013-12-07 00:30:15 +04:00
lock_mode = xfs_ilock_attr_map_shared ( ip ) ;
2009-11-14 19:17:20 +03:00
error = xfs_attr_get_int ( ip , & xname , value , valuelenp , flags ) ;
2013-12-07 00:30:15 +04:00
xfs_iunlock ( ip , lock_mode ) ;
2005-04-17 02:20:36 +04:00
return ( error ) ;
}
2008-08-13 10:03:35 +04:00
/*
* Calculate how many blocks we need for the new attribute ,
*/
2009-11-19 18:52:00 +03:00
STATIC int
2008-08-13 10:03:35 +04:00
xfs_attr_calc_size (
struct xfs_inode * ip ,
int namelen ,
int valuelen ,
int * local )
{
struct xfs_mount * mp = ip - > i_mount ;
int size ;
int nblks ;
/*
* Determine space new attribute will use , and if it would be
* " local " or " remote " ( note : local ! = inline ) .
*/
size = xfs_attr_leaf_newentsize ( namelen , valuelen ,
mp - > m_sb . sb_blocksize , local ) ;
nblks = XFS_DAENTER_SPACE_RES ( mp , XFS_ATTR_FORK ) ;
if ( * local ) {
if ( size > ( mp - > m_sb . sb_blocksize > > 1 ) ) {
/* Double split possible */
nblks * = 2 ;
}
} else {
/*
* Out of line attribute , cannot double split , but
* make room for the attribute value itself .
*/
uint dblocks = XFS_B_TO_FSB ( mp , valuelen ) ;
nblks + = dblocks ;
nblks + = XFS_NEXTENTADD_SPACE_RES ( mp , dblocks , XFS_ATTR_FORK ) ;
}
return nblks ;
}
2008-04-22 11:34:31 +04:00
STATIC int
2010-01-20 02:47:48 +03:00
xfs_attr_set_int (
struct xfs_inode * dp ,
struct xfs_name * name ,
unsigned char * value ,
int valuelen ,
int flags )
2005-04-17 02:20:36 +04:00
{
2013-08-12 14:49:59 +04:00
xfs_da_args_t args ;
xfs_fsblock_t firstblock ;
xfs_bmap_free_t flist ;
int error , err2 , committed ;
struct xfs_mount * mp = dp - > i_mount ;
struct xfs_trans_res tres ;
int rsvd = ( flags & ATTR_ROOT ) ! = 0 ;
int local ;
2005-04-17 02:20:36 +04:00
/*
* Attach the dquots to the inode .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( dp , 0 ) ;
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
/*
* If the inode doesn ' t have an attribute fork , add one .
* ( inode must not be locked when we call this routine )
*/
if ( XFS_IFORK_Q ( dp ) = = 0 ) {
2007-02-10 10:35:58 +03:00
int sf_size = sizeof ( xfs_attr_sf_hdr_t ) +
2008-04-22 11:34:31 +04:00
XFS_ATTR_SF_ENTSIZE_BYNAME ( name - > len , valuelen ) ;
2007-02-10 10:35:58 +03:00
if ( ( error = xfs_bmap_add_attrfork ( dp , sf_size , rsvd ) ) )
2005-04-17 02:20:36 +04:00
return ( error ) ;
}
/*
* Fill in the arg structure for this request .
*/
memset ( ( char * ) & args , 0 , sizeof ( args ) ) ;
2008-04-22 11:34:31 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2005-04-17 02:20:36 +04:00
args . value = value ;
args . valuelen = valuelen ;
args . flags = flags ;
args . hashval = xfs_da_hashname ( args . name , args . namelen ) ;
args . dp = dp ;
args . firstblock = & firstblock ;
args . flist = & flist ;
args . whichfork = XFS_ATTR_FORK ;
2008-05-21 10:42:05 +04:00
args . op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT ;
2005-04-17 02:20:36 +04:00
/* Size is now blocks for attribute data */
2008-08-13 10:03:35 +04:00
args . total = xfs_attr_calc_size ( dp , name - > len , valuelen , & local ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 .
*/
args . trans = xfs_trans_alloc ( mp , XFS_TRANS_ATTR_SET ) ;
/*
* Root fork attributes can use reserved data blocks for this
* operation if necessary
*/
if ( rsvd )
args . trans - > t_flags | = XFS_TRANS_RESERVE ;
2013-08-12 14:49:59 +04:00
tres . tr_logres = M_RES ( mp ) - > tr_attrsetm . tr_logres +
M_RES ( mp ) - > tr_attrsetrt . tr_logres * args . total ;
tres . tr_logcount = XFS_ATTRSET_LOG_COUNT ;
tres . tr_logflags = XFS_TRANS_PERM_LOG_RES ;
error = xfs_trans_reserve ( args . trans , & tres , args . total , 0 ) ;
2013-01-28 17:27:53 +04:00
if ( error ) {
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( args . trans , 0 ) ;
return ( error ) ;
}
xfs_ilock ( dp , XFS_ILOCK_EXCL ) ;
2009-06-08 17:33:32 +04:00
error = xfs_trans_reserve_quota_nblks ( args . trans , dp , args . total , 0 ,
2008-08-13 10:03:35 +04:00
rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
XFS_QMOPT_RES_REGBLKS ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
xfs_trans_cancel ( args . trans , XFS_TRANS_RELEASE_LOG_RES ) ;
return ( error ) ;
}
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args . trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
2006-03-29 02:55:14 +04:00
* If the attribute list is non - existent or a shortform list ,
2005-04-17 02:20:36 +04:00
* upgrade it to a single - leaf - block attribute list .
*/
if ( ( dp - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) | |
( ( dp - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS ) & &
( dp - > i_d . di_anextents = = 0 ) ) ) {
/*
* Build initial attribute list ( if required ) .
*/
if ( dp - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS )
2005-11-02 02:34:53 +03:00
xfs_attr_shortform_create ( & args ) ;
2005-04-17 02:20:36 +04:00
/*
* Try to add the attr to the attribute list in
* the inode .
*/
error = xfs_attr_shortform_addname ( & args ) ;
if ( error ! = ENOSPC ) {
/*
* Commit the shortform mods , and we ' re done .
* NOTE : this is also the error path ( EEXIST , etc ) .
*/
ASSERT ( args . trans ! = NULL ) ;
/*
* If this is a synchronous mount , make sure that
* the transaction goes to disk before returning
* to the user .
*/
if ( mp - > m_flags & XFS_MOUNT_WSYNC ) {
xfs_trans_set_sync ( args . trans ) ;
}
2010-09-28 06:27:25 +04:00
if ( ! error & & ( flags & ATTR_KERNOTIME ) = = 0 ) {
xfs_trans_ichgtime ( args . trans , dp ,
XFS_ICHGTIME_CHG ) ;
}
2005-04-17 02:20:36 +04:00
err2 = xfs_trans_commit ( args . trans ,
2007-05-08 07:48:42 +04:00
XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
return ( error = = 0 ? err2 : error ) ;
}
/*
* It won ' t fit in the shortform , transform to a leaf block .
* GROT : another possible req ' mt for a double - split btree op .
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args . flist , args . firstblock ) ;
2005-04-17 02:20:36 +04:00
error = xfs_attr_shortform_to_leaf ( & args ) ;
if ( ! error ) {
error = xfs_bmap_finish ( & args . trans , args . flist ,
2007-02-10 10:37:16 +03:00
& committed ) ;
2005-04-17 02:20:36 +04:00
}
if ( error ) {
ASSERT ( committed ) ;
args . trans = NULL ;
xfs_bmap_cancel ( & flist ) ;
goto out ;
}
/*
* bmap_finish ( ) may have committed the last trans and started
* a new one . We need the inode to be in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args . trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Commit the leaf transformation . We ' ll need another ( linked )
* transaction to add the new attribute to the leaf .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args . trans , dp ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto out ;
}
if ( xfs_bmap_one_block ( dp , XFS_ATTR_FORK ) ) {
error = xfs_attr_leaf_addname ( & args ) ;
} else {
error = xfs_attr_node_addname ( & args ) ;
}
if ( error ) {
goto out ;
}
/*
* If this is a synchronous mount , make sure that the
* transaction goes to disk before returning to the user .
*/
if ( mp - > m_flags & XFS_MOUNT_WSYNC ) {
xfs_trans_set_sync ( args . trans ) ;
}
2010-09-28 06:27:25 +04:00
if ( ( flags & ATTR_KERNOTIME ) = = 0 )
xfs_trans_ichgtime ( args . trans , dp , XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
/*
* Commit the last in the sequence of transactions .
*/
xfs_trans_log_inode ( args . trans , dp , XFS_ILOG_CORE ) ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( args . trans , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
return ( error ) ;
out :
if ( args . trans )
xfs_trans_cancel ( args . trans ,
XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
return ( error ) ;
}
2005-11-02 02:33:33 +03:00
int
2007-08-28 10:12:30 +04:00
xfs_attr_set (
xfs_inode_t * dp ,
2010-01-20 02:47:48 +03:00
const unsigned char * name ,
unsigned char * value ,
2007-08-28 10:12:30 +04:00
int valuelen ,
int flags )
2005-04-17 02:20:36 +04:00
{
2008-04-22 11:34:31 +04:00
int error ;
struct xfs_name xname ;
2005-04-17 02:20:36 +04:00
2005-11-02 02:33:33 +03:00
XFS_STATS_INC ( xs_attr_set ) ;
2005-04-17 02:20:36 +04:00
2005-11-02 02:33:33 +03:00
if ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) )
2005-04-17 02:20:36 +04:00
return ( EIO ) ;
2008-04-22 11:34:31 +04:00
error = xfs_attr_name_to_xname ( & xname , name ) ;
if ( error )
return error ;
return xfs_attr_set_int ( dp , & xname , value , valuelen , flags ) ;
2005-11-02 02:33:33 +03:00
}
/*
* Generic handler routine to remove a name from an attribute list .
* Transitions attribute list from Btree to shortform as necessary .
*/
2008-04-22 11:34:31 +04:00
STATIC int
xfs_attr_remove_int ( xfs_inode_t * dp , struct xfs_name * name , int flags )
2005-11-02 02:33:33 +03:00
{
xfs_da_args_t args ;
xfs_fsblock_t firstblock ;
xfs_bmap_free_t flist ;
int error ;
xfs_mount_t * mp = dp - > i_mount ;
2005-04-17 02:20:36 +04:00
/*
* Fill in the arg structure for this request .
*/
memset ( ( char * ) & args , 0 , sizeof ( args ) ) ;
2008-04-22 11:34:31 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2005-04-17 02:20:36 +04:00
args . flags = flags ;
args . hashval = xfs_da_hashname ( args . name , args . namelen ) ;
args . dp = dp ;
args . firstblock = & firstblock ;
args . flist = & flist ;
args . total = 0 ;
args . whichfork = XFS_ATTR_FORK ;
2011-06-23 05:35:01 +04:00
/*
* we have no control over the attribute names that userspace passes us
* to remove , so we have to allow the name lookup prior to attribute
* removal to fail .
*/
args . op_flags = XFS_DA_OP_OKNOENT ;
2005-04-17 02:20:36 +04:00
/*
* Attach the dquots to the inode .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( dp , 0 ) ;
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
/*
* 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 .
*/
args . trans = xfs_trans_alloc ( mp , XFS_TRANS_ATTR_RM ) ;
/*
* Root fork attributes can use reserved data blocks for this
* operation if necessary
*/
if ( flags & ATTR_ROOT )
args . trans - > t_flags | = XFS_TRANS_RESERVE ;
2013-08-12 14:49:59 +04:00
error = xfs_trans_reserve ( args . trans , & M_RES ( mp ) - > tr_attrrm ,
XFS_ATTRRM_SPACE_RES ( mp ) , 0 ) ;
if ( error ) {
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( args . 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 .
*/
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args . trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Decide on what work routines to call based on the inode size .
*/
2008-06-23 07:23:41 +04:00
if ( ! xfs_inode_hasattr ( dp ) ) {
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( ENOATTR ) ;
goto out ;
}
if ( dp - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) {
ASSERT ( dp - > i_afp - > if_flags & XFS_IFINLINE ) ;
error = xfs_attr_shortform_remove ( & args ) ;
if ( error ) {
goto out ;
}
} else if ( xfs_bmap_one_block ( dp , XFS_ATTR_FORK ) ) {
error = xfs_attr_leaf_removename ( & args ) ;
} else {
error = xfs_attr_node_removename ( & args ) ;
}
if ( error ) {
goto out ;
}
/*
* If this is a synchronous mount , make sure that the
* transaction goes to disk before returning to the user .
*/
if ( mp - > m_flags & XFS_MOUNT_WSYNC ) {
xfs_trans_set_sync ( args . trans ) ;
}
2010-09-28 06:27:25 +04:00
if ( ( flags & ATTR_KERNOTIME ) = = 0 )
xfs_trans_ichgtime ( args . trans , dp , XFS_ICHGTIME_CHG ) ;
2005-04-17 02:20:36 +04:00
/*
* Commit the last in the sequence of transactions .
*/
xfs_trans_log_inode ( args . trans , dp , XFS_ILOG_CORE ) ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( args . trans , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
return ( error ) ;
out :
if ( args . trans )
xfs_trans_cancel ( args . trans ,
XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
return ( error ) ;
}
2005-11-02 02:33:33 +03:00
int
2007-08-28 10:12:30 +04:00
xfs_attr_remove (
xfs_inode_t * dp ,
2010-01-20 02:47:48 +03:00
const unsigned char * name ,
2007-08-28 10:12:30 +04:00
int flags )
2005-11-02 02:33:33 +03:00
{
2008-04-22 11:34:31 +04:00
int error ;
struct xfs_name xname ;
2005-11-02 02:33:33 +03:00
XFS_STATS_INC ( xs_attr_remove ) ;
if ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) )
return ( EIO ) ;
2008-04-22 11:34:31 +04:00
error = xfs_attr_name_to_xname ( & xname , name ) ;
if ( error )
return error ;
2005-11-02 02:33:33 +03:00
xfs_ilock ( dp , XFS_ILOCK_SHARED ) ;
2008-06-23 07:23:41 +04:00
if ( ! xfs_inode_hasattr ( dp ) ) {
2005-11-02 02:33:33 +03:00
xfs_iunlock ( dp , XFS_ILOCK_SHARED ) ;
2008-06-23 07:23:41 +04:00
return XFS_ERROR ( ENOATTR ) ;
2005-11-02 02:33:33 +03:00
}
xfs_iunlock ( dp , XFS_ILOCK_SHARED ) ;
2008-04-22 11:34:31 +04:00
return xfs_attr_remove_int ( dp , & xname , flags ) ;
2005-11-02 02:33:33 +03:00
}
2005-04-17 02:20:36 +04:00
/*========================================================================
* External routines when attribute list is inside the inode
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
* Add a name to the shortform attribute list structure
* This is the external routine .
*/
STATIC int
xfs_attr_shortform_addname ( xfs_da_args_t * args )
{
2005-11-02 02:34:53 +03:00
int newsize , forkoff , retval ;
2005-04-17 02:20:36 +04:00
2012-03-22 09:15:13 +04:00
trace_xfs_attr_sf_addname ( args ) ;
2005-04-17 02:20:36 +04:00
retval = xfs_attr_shortform_lookup ( args ) ;
if ( ( args - > flags & ATTR_REPLACE ) & & ( retval = = ENOATTR ) ) {
return ( retval ) ;
} else if ( retval = = EEXIST ) {
if ( args - > flags & ATTR_CREATE )
return ( retval ) ;
retval = xfs_attr_shortform_remove ( args ) ;
ASSERT ( retval = = 0 ) ;
}
2005-11-02 02:34:53 +03:00
if ( args - > namelen > = XFS_ATTR_SF_ENTSIZE_MAX | |
args - > valuelen > = XFS_ATTR_SF_ENTSIZE_MAX )
return ( XFS_ERROR ( ENOSPC ) ) ;
2005-04-17 02:20:36 +04:00
newsize = XFS_ATTR_SF_TOTSIZE ( args - > dp ) ;
newsize + = XFS_ATTR_SF_ENTSIZE_BYNAME ( args - > namelen , args - > valuelen ) ;
2005-11-02 02:34:53 +03:00
forkoff = xfs_attr_shortform_bytesfit ( args - > dp , newsize ) ;
if ( ! forkoff )
2005-04-17 02:20:36 +04:00
return ( XFS_ERROR ( ENOSPC ) ) ;
2005-11-02 02:34:53 +03:00
xfs_attr_shortform_add ( args , forkoff ) ;
2005-04-17 02:20:36 +04:00
return ( 0 ) ;
}
/*========================================================================
* External routines when attribute list is one block
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
* Add a name to the leaf attribute list structure
*
* This leaf block cannot have a " remote " value , we only call this routine
* if bmap_one_block ( ) says there is only one block ( ie : no remote blks ) .
*/
2007-11-23 08:28:09 +03:00
STATIC int
2005-04-17 02:20:36 +04:00
xfs_attr_leaf_addname ( xfs_da_args_t * args )
{
xfs_inode_t * dp ;
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ;
2005-11-02 02:34:53 +03:00
int retval , error , committed , forkoff ;
2005-04-17 02:20:36 +04:00
2012-03-22 09:15:13 +04:00
trace_xfs_attr_leaf_addname ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Read the ( only ) block in the attribute list in .
*/
dp = args - > dp ;
args - > blkno = 0 ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_read ( args - > trans , args - > dp , args - > blkno , - 1 , & bp ) ;
2005-04-17 02:20:36 +04:00
if ( error )
2012-11-12 15:54:16 +04:00
return error ;
2005-04-17 02:20:36 +04:00
/*
* Look up the given attribute in the leaf block . Figure out if
* the given flags produce an error or call for an atomic rename .
*/
2013-04-24 12:58:55 +04:00
retval = xfs_attr3_leaf_lookup_int ( bp , args ) ;
2005-04-17 02:20:36 +04:00
if ( ( args - > flags & ATTR_REPLACE ) & & ( retval = = ENOATTR ) ) {
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , bp ) ;
2013-04-24 12:58:55 +04:00
return retval ;
2005-04-17 02:20:36 +04:00
} else if ( retval = = EEXIST ) {
if ( args - > flags & ATTR_CREATE ) { /* pure create op */
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , bp ) ;
2013-04-24 12:58:55 +04:00
return retval ;
2005-04-17 02:20:36 +04:00
}
2012-03-22 09:15:13 +04:00
trace_xfs_attr_leaf_replace ( args ) ;
2008-05-21 10:42:05 +04:00
args - > op_flags | = XFS_DA_OP_RENAME ; /* an atomic rename */
2005-04-17 02:20:36 +04:00
args - > blkno2 = args - > blkno ; /* set 2nd entry info*/
args - > index2 = args - > index ;
args - > rmtblkno2 = args - > rmtblkno ;
args - > rmtblkcnt2 = args - > rmtblkcnt ;
}
/*
* Add the attribute to the leaf block , transitioning to a Btree
* if required .
*/
2013-04-24 12:58:55 +04:00
retval = xfs_attr3_leaf_add ( bp , args ) ;
2005-04-17 02:20:36 +04:00
if ( retval = = ENOSPC ) {
/*
* Promote the attribute list to the Btree format , then
* Commit that transaction so that the node_addname ( ) call
* can manage its own transactions .
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_to_node ( args ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
2007-02-10 10:37:16 +03:00
& committed ) ;
2005-04-17 02:20:36 +04:00
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
return ( error ) ;
}
/*
* bmap_finish ( ) may have committed the last trans and started
* a new one . We need the inode to be in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Commit the current trans ( including the inode ) and start
* a new one .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args - > trans , dp ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return ( error ) ;
/*
* Fob the whole rest of the problem off on the Btree code .
*/
error = xfs_attr_node_addname ( args ) ;
return ( error ) ;
}
/*
* Commit the transaction that added the attr name so that
* later routines can manage their own transactions .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args - > trans , dp ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return ( error ) ;
/*
* If there was an out - of - line value , allocate the blocks we
* identified for its storage and copy the value . This is done
* after we create the attribute so that we don ' t overflow the
* maximum size of a transaction and / or hit a deadlock .
*/
if ( args - > rmtblkno > 0 ) {
error = xfs_attr_rmtval_set ( args ) ;
if ( error )
return ( error ) ;
}
/*
* If this is an atomic rename operation , we must " flip " the
* incomplete flags on the " new " and " old " attribute / value pairs
* so that one disappears and one appears atomically . Then we
* must remove the " old " attribute / value pair .
*/
2008-05-21 10:42:05 +04:00
if ( args - > op_flags & XFS_DA_OP_RENAME ) {
2005-04-17 02:20:36 +04:00
/*
* In a separate transaction , set the incomplete flag on the
* " old " attr and clear the incomplete flag on the " new " attr .
*/
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_flipflags ( args ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return ( error ) ;
/*
* Dismantle the " old " attribute / value pair by removing
* a " remote " value ( if it exists ) .
*/
args - > index = args - > index2 ;
args - > blkno = args - > blkno2 ;
args - > rmtblkno = args - > rmtblkno2 ;
args - > rmtblkcnt = args - > rmtblkcnt2 ;
if ( args - > rmtblkno ) {
error = xfs_attr_rmtval_remove ( args ) ;
if ( error )
return ( error ) ;
}
/*
* Read in the block containing the " old " attr , then
* remove the " old " attr from that block ( neat , huh ! )
*/
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_read ( args - > trans , args - > dp , args - > blkno ,
2012-11-12 15:54:16 +04:00
- 1 , & bp ) ;
2005-04-17 02:20:36 +04:00
if ( error )
2012-11-12 15:54:16 +04:00
return error ;
2013-04-24 12:58:55 +04:00
xfs_attr3_leaf_remove ( bp , args ) ;
2005-04-17 02:20:36 +04:00
/*
* If the result is small enough , shrink it all into the inode .
*/
2005-11-02 02:34:53 +03:00
if ( ( forkoff = xfs_attr_shortform_allfit ( bp , dp ) ) ) {
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_to_shortform ( bp , args , forkoff ) ;
2005-04-17 02:20:36 +04:00
/* bp is gone due to xfs_da_shrink_inode */
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans ,
args - > flist ,
& committed ) ;
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
return ( error ) ;
}
/*
* bmap_finish ( ) may have committed the last trans
* and started a new one . We need the inode to be
* in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2012-06-22 12:50:14 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Commit the remove and start the next trans in series .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args - > trans , dp ) ;
2005-04-17 02:20:36 +04:00
} else if ( args - > rmtblkno > 0 ) {
/*
* Added a " remote " value , just clear the incomplete flag .
*/
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_clearflag ( args ) ;
2005-04-17 02:20:36 +04:00
}
2013-04-24 12:58:55 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
/*
* Remove a name from the leaf attribute list structure
*
* This leaf block cannot have a " remote " value , we only call this routine
* if bmap_one_block ( ) says there is only one block ( ie : no remote blks ) .
*/
STATIC int
xfs_attr_leaf_removename ( xfs_da_args_t * args )
{
xfs_inode_t * dp ;
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ;
2005-11-02 02:34:53 +03:00
int error , committed , forkoff ;
2005-04-17 02:20:36 +04:00
2012-03-22 09:15:13 +04:00
trace_xfs_attr_leaf_removename ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Remove the attribute .
*/
dp = args - > dp ;
args - > blkno = 0 ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_read ( args - > trans , args - > dp , args - > blkno , - 1 , & bp ) ;
2012-11-12 15:54:16 +04:00
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_lookup_int ( bp , args ) ;
2005-04-17 02:20:36 +04:00
if ( error = = ENOATTR ) {
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , bp ) ;
2013-04-24 12:58:55 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
2013-04-24 12:58:55 +04:00
xfs_attr3_leaf_remove ( bp , args ) ;
2005-04-17 02:20:36 +04:00
/*
* If the result is small enough , shrink it all into the inode .
*/
2005-11-02 02:34:53 +03:00
if ( ( forkoff = xfs_attr_shortform_allfit ( bp , dp ) ) ) {
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_to_shortform ( bp , args , forkoff ) ;
2005-04-17 02:20:36 +04:00
/* bp is gone due to xfs_da_shrink_inode */
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
2007-02-10 10:37:16 +03:00
& committed ) ;
2005-04-17 02:20:36 +04:00
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
2013-04-24 12:58:55 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
/*
* bmap_finish ( ) may have committed the last trans and started
* a new one . We need the inode to be in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2012-06-22 12:50:14 +04:00
}
2013-04-24 12:58:55 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Look up a name in a leaf attribute list structure .
*
* This leaf block cannot have a " remote " value , we only call this routine
* if bmap_one_block ( ) says there is only one block ( ie : no remote blks ) .
*/
2005-06-21 09:36:52 +04:00
STATIC int
2005-04-17 02:20:36 +04:00
xfs_attr_leaf_get ( xfs_da_args_t * args )
{
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ;
2005-04-17 02:20:36 +04:00
int error ;
2012-11-12 15:53:53 +04:00
trace_xfs_attr_leaf_get ( args ) ;
2005-04-17 02:20:36 +04:00
args - > blkno = 0 ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_read ( args - > trans , args - > dp , args - > blkno , - 1 , & bp ) ;
2005-04-17 02:20:36 +04:00
if ( error )
2012-11-12 15:54:16 +04:00
return error ;
2005-04-17 02:20:36 +04:00
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_lookup_int ( bp , args ) ;
2005-04-17 02:20:36 +04:00
if ( error ! = EEXIST ) {
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , bp ) ;
2013-04-24 12:58:55 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_getvalue ( bp , args ) ;
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , bp ) ;
2005-04-17 02:20:36 +04:00
if ( ! error & & ( args - > rmtblkno > 0 ) & & ! ( args - > flags & ATTR_KERNOVAL ) ) {
error = xfs_attr_rmtval_get ( args ) ;
}
2013-04-24 12:58:55 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
/*========================================================================
* External routines when attribute list size > XFS_LBSIZE ( mp ) .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
* Add a name to a Btree - format attribute list .
*
* This will involve walking down the Btree , and may involve splitting
* leaf nodes and even splitting intermediate nodes up to and including
* the root node ( a special case of an intermediate node ) .
*
* " Remote " attribute values confuse the issue and atomic rename operations
* add a whole extra layer of confusion on top of that .
*/
STATIC int
xfs_attr_node_addname ( xfs_da_args_t * args )
{
xfs_da_state_t * state ;
xfs_da_state_blk_t * blk ;
xfs_inode_t * dp ;
xfs_mount_t * mp ;
int committed , retval , error ;
2012-03-22 09:15:13 +04:00
trace_xfs_attr_node_addname ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Fill in bucket of arguments / results / context to carry around .
*/
dp = args - > dp ;
mp = dp - > i_mount ;
restart :
state = xfs_da_state_alloc ( ) ;
state - > args = args ;
state - > mp = mp ;
state - > blocksize = state - > mp - > m_sb . sb_blocksize ;
state - > node_ents = state - > mp - > m_attr_node_ents ;
/*
* Search to see if name already exists , and get back a pointer
* to where it should go .
*/
2013-04-24 12:58:02 +04:00
error = xfs_da3_node_lookup_int ( state , & retval ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
blk = & state - > path . blk [ state - > path . active - 1 ] ;
ASSERT ( blk - > magic = = XFS_ATTR_LEAF_MAGIC ) ;
if ( ( args - > flags & ATTR_REPLACE ) & & ( retval = = ENOATTR ) ) {
goto out ;
} else if ( retval = = EEXIST ) {
if ( args - > flags & ATTR_CREATE )
goto out ;
2012-03-22 09:15:13 +04:00
trace_xfs_attr_node_replace ( args ) ;
2008-05-21 10:42:05 +04:00
args - > op_flags | = XFS_DA_OP_RENAME ; /* atomic rename op */
2005-04-17 02:20:36 +04:00
args - > blkno2 = args - > blkno ; /* set 2nd entry info*/
args - > index2 = args - > index ;
args - > rmtblkno2 = args - > rmtblkno ;
args - > rmtblkcnt2 = args - > rmtblkcnt ;
args - > rmtblkno = 0 ;
args - > rmtblkcnt = 0 ;
}
2013-04-24 12:58:55 +04:00
retval = xfs_attr3_leaf_add ( blk - > bp , state - > args ) ;
2005-04-17 02:20:36 +04:00
if ( retval = = ENOSPC ) {
if ( state - > path . active = = 1 ) {
/*
* Its really a single leaf node , but it had
* out - of - line values so it looked like it * might *
* have been a b - tree .
*/
xfs_da_state_free ( state ) ;
2013-08-01 05:18:54 +04:00
state = NULL ;
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_to_node ( args ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans ,
args - > flist ,
& committed ) ;
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
goto out ;
}
/*
* bmap_finish ( ) may have committed the last trans
* and started a new one . We need the inode to be
* in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Commit the node conversion and start the next
* trans in the chain .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args - > trans , dp ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto out ;
goto restart ;
}
/*
* Split as many Btree elements as required .
* This code tracks the new and old attr ' s location
* in the index / blkno / rmtblkno / rmtblkcnt fields and
* in the index2 / blkno2 / rmtblkno2 / rmtblkcnt2 fields .
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:02 +04:00
error = xfs_da3_split ( state ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
2007-02-10 10:37:16 +03:00
& committed ) ;
2005-04-17 02:20:36 +04:00
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
goto out ;
}
/*
* bmap_finish ( ) may have committed the last trans and started
* a new one . We need the inode to be in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
} else {
/*
* Addition succeeded , update Btree hashvals .
*/
2013-04-24 12:58:02 +04:00
xfs_da3_fixhashpath ( state , & state - > path ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Kill the state structure , we ' re done with it and need to
* allow the buffers to come back later .
*/
xfs_da_state_free ( state ) ;
state = NULL ;
/*
* Commit the leaf addition or btree split and start the next
* trans in the chain .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args - > trans , dp ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto out ;
/*
* If there was an out - of - line value , allocate the blocks we
* identified for its storage and copy the value . This is done
* after we create the attribute so that we don ' t overflow the
* maximum size of a transaction and / or hit a deadlock .
*/
if ( args - > rmtblkno > 0 ) {
error = xfs_attr_rmtval_set ( args ) ;
if ( error )
return ( error ) ;
}
/*
* If this is an atomic rename operation , we must " flip " the
* incomplete flags on the " new " and " old " attribute / value pairs
* so that one disappears and one appears atomically . Then we
* must remove the " old " attribute / value pair .
*/
2008-05-21 10:42:05 +04:00
if ( args - > op_flags & XFS_DA_OP_RENAME ) {
2005-04-17 02:20:36 +04:00
/*
* In a separate transaction , set the incomplete flag on the
* " old " attr and clear the incomplete flag on the " new " attr .
*/
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_flipflags ( args ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
/*
* Dismantle the " old " attribute / value pair by removing
* a " remote " value ( if it exists ) .
*/
args - > index = args - > index2 ;
args - > blkno = args - > blkno2 ;
args - > rmtblkno = args - > rmtblkno2 ;
args - > rmtblkcnt = args - > rmtblkcnt2 ;
if ( args - > rmtblkno ) {
error = xfs_attr_rmtval_remove ( args ) ;
if ( error )
return ( error ) ;
}
/*
* Re - find the " old " attribute entry after any split ops .
* The INCOMPLETE flag means that we will find the " old "
* attr , not the " new " one .
*/
args - > flags | = XFS_ATTR_INCOMPLETE ;
state = xfs_da_state_alloc ( ) ;
state - > args = args ;
state - > mp = mp ;
state - > blocksize = state - > mp - > m_sb . sb_blocksize ;
state - > node_ents = state - > mp - > m_attr_node_ents ;
state - > inleaf = 0 ;
2013-04-24 12:58:02 +04:00
error = xfs_da3_node_lookup_int ( state , & retval ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
/*
* Remove the name and update the hashvals in the tree .
*/
blk = & state - > path . blk [ state - > path . active - 1 ] ;
ASSERT ( blk - > magic = = XFS_ATTR_LEAF_MAGIC ) ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_remove ( blk - > bp , args ) ;
2013-04-24 12:58:02 +04:00
xfs_da3_fixhashpath ( state , & state - > path ) ;
2005-04-17 02:20:36 +04:00
/*
* Check to see if the tree needs to be collapsed .
*/
if ( retval & & ( state - > path . active > 1 ) ) {
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:02 +04:00
error = xfs_da3_join ( state ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans ,
args - > flist ,
& committed ) ;
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
goto out ;
}
/*
* bmap_finish ( ) may have committed the last trans
* and started a new one . We need the inode to be
* in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Commit and start the next trans in the chain .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args - > trans , dp ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto out ;
} else if ( args - > rmtblkno > 0 ) {
/*
* Added a " remote " value , just clear the incomplete flag .
*/
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_clearflag ( args ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
}
retval = error = 0 ;
out :
if ( state )
xfs_da_state_free ( state ) ;
if ( error )
return ( error ) ;
return ( retval ) ;
}
/*
* Remove a name from a B - tree attribute list .
*
* This will involve walking down the Btree , and may involve joining
* leaf nodes and even joining intermediate nodes up to and including
* the root node ( a special case of an intermediate node ) .
*/
STATIC int
xfs_attr_node_removename ( xfs_da_args_t * args )
{
xfs_da_state_t * state ;
xfs_da_state_blk_t * blk ;
xfs_inode_t * dp ;
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ;
2005-11-02 02:34:53 +03:00
int retval , error , committed , forkoff ;
2005-04-17 02:20:36 +04:00
2012-03-22 09:15:13 +04:00
trace_xfs_attr_node_removename ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Tie a string around our finger to remind us where we are .
*/
dp = args - > dp ;
state = xfs_da_state_alloc ( ) ;
state - > args = args ;
state - > mp = dp - > i_mount ;
state - > blocksize = state - > mp - > m_sb . sb_blocksize ;
state - > node_ents = state - > mp - > m_attr_node_ents ;
/*
* Search to see if name exists , and get back a pointer to it .
*/
2013-04-24 12:58:02 +04:00
error = xfs_da3_node_lookup_int ( state , & retval ) ;
2005-04-17 02:20:36 +04:00
if ( error | | ( retval ! = EEXIST ) ) {
if ( error = = 0 )
error = retval ;
goto out ;
}
/*
* If there is an out - of - line value , de - allocate the blocks .
* This is done before we remove the attribute so that we don ' t
* overflow the maximum size of a transaction and / or hit a deadlock .
*/
blk = & state - > path . blk [ state - > path . active - 1 ] ;
ASSERT ( blk - > bp ! = NULL ) ;
ASSERT ( blk - > magic = = XFS_ATTR_LEAF_MAGIC ) ;
if ( args - > rmtblkno > 0 ) {
/*
* Fill in disk block numbers in the state structure
* so that we can get the buffers back after we commit
* several transactions in the following calls .
*/
error = xfs_attr_fillstate ( state ) ;
if ( error )
goto out ;
/*
* Mark the attribute as INCOMPLETE , then bunmapi ( ) the
* remote value .
*/
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_setflag ( args ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
error = xfs_attr_rmtval_remove ( args ) ;
if ( error )
goto out ;
/*
* Refill the state structure with buffers , the prior calls
* released our buffers .
*/
error = xfs_attr_refillstate ( state ) ;
if ( error )
goto out ;
}
/*
* Remove the name and update the hashvals in the tree .
*/
blk = & state - > path . blk [ state - > path . active - 1 ] ;
ASSERT ( blk - > magic = = XFS_ATTR_LEAF_MAGIC ) ;
2013-04-24 12:58:55 +04:00
retval = xfs_attr3_leaf_remove ( blk - > bp , args ) ;
2013-04-24 12:58:02 +04:00
xfs_da3_fixhashpath ( state , & state - > path ) ;
2005-04-17 02:20:36 +04:00
/*
* Check to see if the tree needs to be collapsed .
*/
if ( retval & & ( state - > path . active > 1 ) ) {
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:02 +04:00
error = xfs_da3_join ( state ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
2007-02-10 10:37:16 +03:00
& committed ) ;
2005-04-17 02:20:36 +04:00
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
goto out ;
}
/*
* bmap_finish ( ) may have committed the last trans and started
* a new one . We need the inode to be in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Commit the Btree join operation and start a new trans .
*/
2008-08-13 10:05:49 +04:00
error = xfs_trans_roll ( & args - > trans , dp ) ;
if ( error )
2005-04-17 02:20:36 +04:00
goto out ;
}
/*
* If the result is small enough , push it all into the inode .
*/
if ( xfs_bmap_one_block ( dp , XFS_ATTR_FORK ) ) {
/*
* Have to get rid of the copy of this dabuf in the state .
*/
ASSERT ( state - > path . active = = 1 ) ;
ASSERT ( state - > path . blk [ 0 ] . bp ) ;
state - > path . blk [ 0 ] . bp = NULL ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_read ( args - > trans , args - > dp , 0 , - 1 , & bp ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
2005-11-02 02:34:53 +03:00
if ( ( forkoff = xfs_attr_shortform_allfit ( bp , dp ) ) ) {
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( args - > flist , args - > firstblock ) ;
2013-04-24 12:58:55 +04:00
error = xfs_attr3_leaf_to_shortform ( bp , args , forkoff ) ;
2005-04-17 02:20:36 +04:00
/* bp is gone due to xfs_da_shrink_inode */
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans ,
args - > flist ,
& committed ) ;
}
if ( error ) {
ASSERT ( committed ) ;
args - > trans = NULL ;
xfs_bmap_cancel ( args - > flist ) ;
goto out ;
}
/*
* bmap_finish ( ) may have committed the last trans
* and started a new one . We need the inode to be
* in all transactions .
*/
2010-06-24 05:36:58 +04:00
if ( committed )
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( args - > trans , dp , 0 ) ;
2005-04-17 02:20:36 +04:00
} else
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , bp ) ;
2005-04-17 02:20:36 +04:00
}
error = 0 ;
out :
xfs_da_state_free ( state ) ;
return ( error ) ;
}
/*
* Fill in the disk block numbers in the state structure for the buffers
* that are attached to the state structure .
* This is done so that we can quickly reattach ourselves to those buffers
2006-03-29 02:55:14 +04:00
* after some set of transaction commits have released these buffers .
2005-04-17 02:20:36 +04:00
*/
STATIC int
xfs_attr_fillstate ( xfs_da_state_t * state )
{
xfs_da_state_path_t * path ;
xfs_da_state_blk_t * blk ;
int level ;
2012-11-12 15:53:53 +04:00
trace_xfs_attr_fillstate ( state - > args ) ;
2005-04-17 02:20:36 +04:00
/*
* Roll down the " path " in the state structure , storing the on - disk
* block number for those buffers in the " path " .
*/
path = & state - > path ;
ASSERT ( ( path - > active > = 0 ) & & ( path - > active < XFS_DA_NODE_MAXDEPTH ) ) ;
for ( blk = path - > blk , level = 0 ; level < path - > active ; blk + + , level + + ) {
if ( blk - > bp ) {
2012-06-22 12:50:14 +04:00
blk - > disk_blkno = XFS_BUF_ADDR ( blk - > bp ) ;
2005-04-17 02:20:36 +04:00
blk - > bp = NULL ;
} else {
blk - > disk_blkno = 0 ;
}
}
/*
* Roll down the " altpath " in the state structure , storing the on - disk
* block number for those buffers in the " altpath " .
*/
path = & state - > altpath ;
ASSERT ( ( path - > active > = 0 ) & & ( path - > active < XFS_DA_NODE_MAXDEPTH ) ) ;
for ( blk = path - > blk , level = 0 ; level < path - > active ; blk + + , level + + ) {
if ( blk - > bp ) {
2012-06-22 12:50:14 +04:00
blk - > disk_blkno = XFS_BUF_ADDR ( blk - > bp ) ;
2005-04-17 02:20:36 +04:00
blk - > bp = NULL ;
} else {
blk - > disk_blkno = 0 ;
}
}
return ( 0 ) ;
}
/*
* Reattach the buffers to the state structure based on the disk block
* numbers stored in the state structure .
2006-03-29 02:55:14 +04:00
* This is done after some set of transaction commits have released those
2005-04-17 02:20:36 +04:00
* buffers from our grip .
*/
STATIC int
xfs_attr_refillstate ( xfs_da_state_t * state )
{
xfs_da_state_path_t * path ;
xfs_da_state_blk_t * blk ;
int level , error ;
2012-11-12 15:53:53 +04:00
trace_xfs_attr_refillstate ( state - > args ) ;
2005-04-17 02:20:36 +04:00
/*
* Roll down the " path " in the state structure , storing the on - disk
* block number for those buffers in the " path " .
*/
path = & state - > path ;
ASSERT ( ( path - > active > = 0 ) & & ( path - > active < XFS_DA_NODE_MAXDEPTH ) ) ;
for ( blk = path - > blk , level = 0 ; level < path - > active ; blk + + , level + + ) {
if ( blk - > disk_blkno ) {
2013-04-24 12:58:02 +04:00
error = xfs_da3_node_read ( state - > args - > trans ,
2005-04-17 02:20:36 +04:00
state - > args - > dp ,
blk - > blkno , blk - > disk_blkno ,
2012-11-12 15:54:17 +04:00
& blk - > bp , XFS_ATTR_FORK ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return ( error ) ;
} else {
blk - > bp = NULL ;
}
}
/*
* Roll down the " altpath " in the state structure , storing the on - disk
* block number for those buffers in the " altpath " .
*/
path = & state - > altpath ;
ASSERT ( ( path - > active > = 0 ) & & ( path - > active < XFS_DA_NODE_MAXDEPTH ) ) ;
for ( blk = path - > blk , level = 0 ; level < path - > active ; blk + + , level + + ) {
if ( blk - > disk_blkno ) {
2013-04-24 12:58:02 +04:00
error = xfs_da3_node_read ( state - > args - > trans ,
2005-04-17 02:20:36 +04:00
state - > args - > dp ,
blk - > blkno , blk - > disk_blkno ,
2012-11-12 15:54:17 +04:00
& blk - > bp , XFS_ATTR_FORK ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return ( error ) ;
} else {
blk - > bp = NULL ;
}
}
return ( 0 ) ;
}
/*
* Look up a filename in a node attribute list .
*
* This routine gets called for any attribute fork that has more than one
* block , ie : both true Btree attr lists and for single - leaf - blocks with
* " remote " values taking up more blocks .
*/
2005-06-21 09:36:52 +04:00
STATIC int
2005-04-17 02:20:36 +04:00
xfs_attr_node_get ( xfs_da_args_t * args )
{
xfs_da_state_t * state ;
xfs_da_state_blk_t * blk ;
int error , retval ;
int i ;
2012-11-12 15:53:53 +04:00
trace_xfs_attr_node_get ( args ) ;
2005-04-17 02:20:36 +04:00
state = xfs_da_state_alloc ( ) ;
state - > args = args ;
state - > mp = args - > dp - > i_mount ;
state - > blocksize = state - > mp - > m_sb . sb_blocksize ;
state - > node_ents = state - > mp - > m_attr_node_ents ;
/*
* Search to see if name exists , and get back a pointer to it .
*/
2013-04-24 12:58:02 +04:00
error = xfs_da3_node_lookup_int ( state , & retval ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
retval = error ;
} else if ( retval = = EEXIST ) {
blk = & state - > path . blk [ state - > path . active - 1 ] ;
ASSERT ( blk - > bp ! = NULL ) ;
ASSERT ( blk - > magic = = XFS_ATTR_LEAF_MAGIC ) ;
/*
* Get the value , local or " remote "
*/
2013-04-24 12:58:55 +04:00
retval = xfs_attr3_leaf_getvalue ( blk - > bp , args ) ;
2005-04-17 02:20:36 +04:00
if ( ! retval & & ( args - > rmtblkno > 0 )
& & ! ( args - > flags & ATTR_KERNOVAL ) ) {
retval = xfs_attr_rmtval_get ( args ) ;
}
}
/*
* If not in a transaction , we have to release all the buffers .
*/
for ( i = 0 ; i < state - > path . active ; i + + ) {
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , state - > path . blk [ i ] . bp ) ;
2005-04-17 02:20:36 +04:00
state - > path . blk [ i ] . bp = NULL ;
}
xfs_da_state_free ( state ) ;
return ( retval ) ;
}