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
*/
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
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"
2005-11-02 06:38:42 +03:00
# include "xfs_inum.h"
2005-04-17 02:20:36 +04:00
# include "xfs_trans.h"
# include "xfs_sb.h"
# include "xfs_ag.h"
# include "xfs_dir2.h"
# include "xfs_dmapi.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"
2005-11-02 06:38:42 +03:00
# include "xfs_alloc_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_ialloc_btree.h"
# include "xfs_dir2_sf.h"
2005-11-02 06:38:42 +03:00
# include "xfs_attr_sf.h"
2005-04-17 02:20:36 +04:00
# include "xfs_dinode.h"
# include "xfs_inode.h"
2005-11-02 06:38:42 +03:00
# include "xfs_alloc.h"
# include "xfs_btree.h"
# include "xfs_inode_item.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
# include "xfs_attr.h"
# include "xfs_attr_leaf.h"
# include "xfs_error.h"
# include "xfs_quota.h"
# include "xfs_trans_space.h"
# include "xfs_acl.h"
2005-11-02 06:38:42 +03:00
# include "xfs_rw.h"
2005-04-17 02:20:36 +04:00
/*
* xfs_attr . c
*
* Provide the external interfaces to manage attribute lists .
*/
2005-06-21 09:36:52 +04:00
# define ATTR_SYSCOUNT 2
STATIC struct attrnames posix_acl_access ;
STATIC struct attrnames posix_acl_default ;
STATIC struct attrnames * attr_system_names [ ATTR_SYSCOUNT ] ;
2005-04-17 02:20:36 +04:00
/*========================================================================
* 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 ) ;
STATIC int xfs_attr_leaf_list ( xfs_attr_list_context_t * context ) ;
/*
* 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_node_list ( xfs_attr_list_context_t * context ) ;
STATIC int xfs_attr_fillstate ( xfs_da_state_t * state ) ;
STATIC int xfs_attr_refillstate ( xfs_da_state_t * state ) ;
/*
* Routines to manipulate out - of - line attribute values .
*/
STATIC int xfs_attr_rmtval_set ( xfs_da_args_t * args ) ;
STATIC int xfs_attr_rmtval_remove ( xfs_da_args_t * args ) ;
# define ATTR_RMTVALUE_MAPSIZE 1 /* # of map entries at once */
# if defined(XFS_ATTR_TRACE)
ktrace_t * xfs_attr_trace_buf ;
# endif
/*========================================================================
* Overall external interface routines .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
int
2005-11-02 03:43:04 +03:00
xfs_attr_fetch ( xfs_inode_t * ip , const char * name , int namelen ,
2005-04-17 02:20:36 +04:00
char * value , int * valuelenp , int flags , struct cred * cred )
{
xfs_da_args_t args ;
int error ;
if ( ( XFS_IFORK_Q ( ip ) = = 0 ) | |
( ip - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
ip - > i_d . di_anextents = = 0 ) )
return ( ENOATTR ) ;
/*
* Fill in the arg structure for this request .
*/
memset ( ( char * ) & args , 0 , sizeof ( args ) ) ;
args . name = name ;
args . namelen = namelen ;
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 .
*/
if ( XFS_IFORK_Q ( ip ) = = 0 | |
( ip - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
ip - > i_d . di_anextents = = 0 ) ) {
error = XFS_ERROR ( ENOATTR ) ;
} else if ( ip - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) {
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
2005-11-02 03:43:04 +03:00
xfs_attr_get ( bhv_desc_t * bdp , const char * name , char * value , int * valuelenp ,
2005-04-17 02:20:36 +04:00
int flags , struct cred * cred )
{
xfs_inode_t * ip = XFS_BHVTOI ( bdp ) ;
int error , namelen ;
XFS_STATS_INC ( xs_attr_get ) ;
if ( ! name )
return ( EINVAL ) ;
namelen = strlen ( name ) ;
if ( namelen > = MAXNAMELEN )
return ( EFAULT ) ; /* match IRIX behaviour */
if ( XFS_FORCED_SHUTDOWN ( ip - > i_mount ) )
return ( EIO ) ;
xfs_ilock ( ip , XFS_ILOCK_SHARED ) ;
error = xfs_attr_fetch ( ip , name , namelen , value , valuelenp , flags , cred ) ;
xfs_iunlock ( ip , XFS_ILOCK_SHARED ) ;
return ( error ) ;
}
2006-09-28 05:01:37 +04:00
int
2005-11-02 03:43:04 +03:00
xfs_attr_set_int ( xfs_inode_t * dp , const char * name , int namelen ,
2005-11-02 02:33:33 +03:00
char * value , int valuelen , int flags )
2005-04-17 02:20:36 +04:00
{
xfs_da_args_t args ;
xfs_fsblock_t firstblock ;
xfs_bmap_free_t flist ;
int error , err2 , committed ;
int local , size ;
uint nblks ;
2005-11-02 02:33:33 +03:00
xfs_mount_t * mp = dp - > i_mount ;
2005-04-17 02:20:36 +04:00
int rsvd = ( flags & ATTR_ROOT ) ! = 0 ;
/*
* Attach the dquots to the inode .
*/
if ( ( error = XFS_QM_DQATTACH ( mp , dp , 0 ) ) )
return ( error ) ;
2005-11-02 02:34:53 +03:00
/*
* 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 ) ;
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 ) {
2005-11-02 02:34:53 +03:00
if ( ( error = xfs_bmap_add_attrfork ( dp , 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 ) ) ;
args . name = name ;
args . namelen = namelen ;
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 ;
2005-11-02 02:34:53 +03:00
args . addname = 1 ;
2005-04-17 02:20:36 +04:00
args . oknoent = 1 ;
nblks = XFS_DAENTER_SPACE_RES ( mp , XFS_ATTR_FORK ) ;
if ( local ) {
if ( size > ( mp - > m_sb . sb_blocksize > > 1 ) ) {
/* Double split possible */
nblks < < = 1 ;
}
} else {
uint dblocks = XFS_B_TO_FSB ( mp , valuelen ) ;
/* Out of line attribute, cannot double split, but make
* room for the attribute value itself .
*/
nblks + = dblocks ;
nblks + = XFS_NEXTENTADD_SPACE_RES ( mp , dblocks , XFS_ATTR_FORK ) ;
}
/* Size is now blocks for attribute data */
args . total = nblks ;
/*
* 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 ;
if ( ( error = xfs_trans_reserve ( args . trans , ( uint ) nblks ,
XFS_ATTRSET_LOG_RES ( mp , nblks ) ,
0 , XFS_TRANS_PERM_LOG_RES ,
XFS_ATTRSET_LOG_COUNT ) ) ) {
xfs_trans_cancel ( args . trans , 0 ) ;
return ( error ) ;
}
xfs_ilock ( dp , XFS_ILOCK_EXCL ) ;
error = XFS_TRANS_RESERVE_QUOTA_NBLKS ( mp , args . trans , dp , nblks , 0 ,
rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
XFS_QMOPT_RES_REGBLKS ) ;
if ( error ) {
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
xfs_trans_cancel ( args . trans , XFS_TRANS_RELEASE_LOG_RES ) ;
return ( error ) ;
}
xfs_trans_ijoin ( args . trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args . trans , dp ) ;
/*
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 ) ;
}
err2 = xfs_trans_commit ( args . trans ,
XFS_TRANS_RELEASE_LOG_RES ,
NULL ) ;
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
/*
* Hit the inode change time .
*/
if ( ! error & & ( flags & ATTR_KERNOTIME ) = = 0 ) {
xfs_ichgtime ( dp , XFS_ICHGTIME_CHG ) ;
}
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 .
*/
XFS_BMAP_INIT ( args . flist , args . firstblock ) ;
error = xfs_attr_shortform_to_leaf ( & args ) ;
if ( ! error ) {
error = xfs_bmap_finish ( & args . trans , args . flist ,
* args . firstblock , & committed ) ;
}
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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args . trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args . trans , dp ) ;
}
/*
* Commit the leaf transformation . We ' ll need another ( linked )
* transaction to add the new attribute to the leaf .
*/
if ( ( error = xfs_attr_rolltrans ( & args . trans , dp ) ) )
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 ) ;
}
/*
* Commit the last in the sequence of transactions .
*/
xfs_trans_log_inode ( args . trans , dp , XFS_ILOG_CORE ) ;
error = xfs_trans_commit ( args . trans , XFS_TRANS_RELEASE_LOG_RES ,
NULL ) ;
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
/*
* Hit the inode change time .
*/
if ( ! error & & ( flags & ATTR_KERNOTIME ) = = 0 ) {
xfs_ichgtime ( dp , XFS_ICHGTIME_CHG ) ;
}
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
2005-11-02 03:43:04 +03:00
xfs_attr_set ( bhv_desc_t * bdp , const char * name , char * value , int valuelen , int flags ,
2005-11-02 02:33:33 +03:00
struct cred * cred )
2005-04-17 02:20:36 +04:00
{
2005-11-02 02:33:33 +03:00
xfs_inode_t * dp ;
2006-01-10 07:52:00 +03:00
int namelen ;
2005-04-17 02:20:36 +04:00
namelen = strlen ( name ) ;
2005-11-02 02:33:33 +03:00
if ( namelen > = MAXNAMELEN )
return EFAULT ; /* match IRIX behaviour */
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
dp = XFS_BHVTOI ( bdp ) ;
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 ) ;
2005-11-02 02:33:33 +03:00
return xfs_attr_set_int ( dp , name , namelen , value , valuelen , flags ) ;
}
/*
* Generic handler routine to remove a name from an attribute list .
* Transitions attribute list from Btree to shortform as necessary .
*/
2006-09-28 05:01:37 +04:00
int
2005-11-02 03:43:04 +03:00
xfs_attr_remove_int ( xfs_inode_t * dp , const char * name , int namelen , 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 ) ) ;
args . name = name ;
args . namelen = namelen ;
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 ;
/*
* Attach the dquots to the inode .
*/
if ( ( error = XFS_QM_DQATTACH ( mp , dp , 0 ) ) )
return ( error ) ;
/*
* 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 ;
if ( ( error = xfs_trans_reserve ( args . trans ,
XFS_ATTRRM_SPACE_RES ( mp ) ,
XFS_ATTRRM_LOG_RES ( mp ) ,
0 , XFS_TRANS_PERM_LOG_RES ,
XFS_ATTRRM_LOG_COUNT ) ) ) {
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 .
*/
xfs_trans_ijoin ( args . trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args . trans , dp ) ;
/*
* Decide on what work routines to call based on the inode size .
*/
if ( XFS_IFORK_Q ( dp ) = = 0 | |
( dp - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
dp - > i_d . di_anextents = = 0 ) ) {
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 ) ;
}
/*
* Commit the last in the sequence of transactions .
*/
xfs_trans_log_inode ( args . trans , dp , XFS_ILOG_CORE ) ;
error = xfs_trans_commit ( args . trans , XFS_TRANS_RELEASE_LOG_RES ,
NULL ) ;
xfs_iunlock ( dp , XFS_ILOCK_EXCL ) ;
/*
* Hit the inode change time .
*/
if ( ! error & & ( flags & ATTR_KERNOTIME ) = = 0 ) {
xfs_ichgtime ( dp , XFS_ICHGTIME_CHG ) ;
}
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
2005-11-02 03:43:04 +03:00
xfs_attr_remove ( bhv_desc_t * bdp , const char * name , int flags , struct cred * cred )
2005-11-02 02:33:33 +03:00
{
xfs_inode_t * dp ;
2006-01-10 07:52:00 +03:00
int namelen ;
2005-11-02 02:33:33 +03:00
namelen = strlen ( name ) ;
if ( namelen > = MAXNAMELEN )
return EFAULT ; /* match IRIX behaviour */
XFS_STATS_INC ( xs_attr_remove ) ;
dp = XFS_BHVTOI ( bdp ) ;
if ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) )
return ( EIO ) ;
xfs_ilock ( dp , XFS_ILOCK_SHARED ) ;
2006-01-10 07:52:00 +03:00
if ( XFS_IFORK_Q ( dp ) = = 0 | |
2005-11-02 02:33:33 +03:00
( dp - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
dp - > i_d . di_anextents = = 0 ) ) {
xfs_iunlock ( dp , XFS_ILOCK_SHARED ) ;
return ( XFS_ERROR ( ENOATTR ) ) ;
}
xfs_iunlock ( dp , XFS_ILOCK_SHARED ) ;
return xfs_attr_remove_int ( dp , name , namelen , flags ) ;
}
2006-09-28 05:01:37 +04:00
int /* error */
xfs_attr_list_int ( xfs_attr_list_context_t * context )
{
int error ;
xfs_inode_t * dp = context - > dp ;
/*
* Decide on what work routines to call based on the inode size .
*/
if ( XFS_IFORK_Q ( dp ) = = 0 | |
( dp - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
dp - > i_d . di_anextents = = 0 ) ) {
error = 0 ;
} else if ( dp - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) {
error = xfs_attr_shortform_list ( context ) ;
} else if ( xfs_bmap_one_block ( dp , XFS_ATTR_FORK ) ) {
error = xfs_attr_leaf_list ( context ) ;
} else {
error = xfs_attr_node_list ( context ) ;
}
return error ;
}
# define ATTR_ENTBASESIZE /* minimum bytes used by an attr */ \
( ( ( struct attrlist_ent * ) 0 ) - > a_name - ( char * ) 0 )
# define ATTR_ENTSIZE(namelen) /* actual bytes used by an attr */ \
( ( ATTR_ENTBASESIZE + ( namelen ) + 1 + sizeof ( u_int32_t ) - 1 ) \
& ~ ( sizeof ( u_int32_t ) - 1 ) )
/*
* Format an attribute and copy it out to the user ' s buffer .
* Take care to check values and protect against them changing later ,
* we may be reading them directly out of a user buffer .
*/
/*ARGSUSED*/
STATIC int
xfs_attr_put_listent ( xfs_attr_list_context_t * context , attrnames_t * namesp ,
char * name , int namelen ,
int valuelen , char * value )
{
attrlist_ent_t * aep ;
int arraytop ;
ASSERT ( ! ( context - > flags & ATTR_KERNOVAL ) ) ;
ASSERT ( context - > count > = 0 ) ;
ASSERT ( context - > count < ( ATTR_MAX_VALUELEN / 8 ) ) ;
ASSERT ( context - > firstu > = sizeof ( * context - > alist ) ) ;
ASSERT ( context - > firstu < = context - > bufsize ) ;
arraytop = sizeof ( * context - > alist ) +
context - > count * sizeof ( context - > alist - > al_offset [ 0 ] ) ;
context - > firstu - = ATTR_ENTSIZE ( namelen ) ;
if ( context - > firstu < arraytop ) {
xfs_attr_trace_l_c ( " buffer full " , context ) ;
context - > alist - > al_more = 1 ;
context - > seen_enough = 1 ;
return 1 ;
}
aep = ( attrlist_ent_t * ) & ( ( ( char * ) context - > alist ) [ context - > firstu ] ) ;
aep - > a_valuelen = valuelen ;
memcpy ( aep - > a_name , name , namelen ) ;
aep - > a_name [ namelen ] = 0 ;
context - > alist - > al_offset [ context - > count + + ] = context - > firstu ;
context - > alist - > al_count = context - > count ;
xfs_attr_trace_l_c ( " add " , context ) ;
return 0 ;
}
STATIC int
xfs_attr_kern_list ( xfs_attr_list_context_t * context , attrnames_t * namesp ,
char * name , int namelen ,
int valuelen , char * value )
{
char * offset ;
int arraytop ;
ASSERT ( context - > count > = 0 ) ;
arraytop = context - > count + namesp - > attr_namelen + namelen + 1 ;
if ( arraytop > context - > firstu ) {
context - > count = - 1 ; /* insufficient space */
return 1 ;
}
offset = ( char * ) context - > alist + context - > count ;
strncpy ( offset , namesp - > attr_name , namesp - > attr_namelen ) ;
offset + = namesp - > attr_namelen ;
strncpy ( offset , name , namelen ) ; /* real name */
offset + = namelen ;
* offset = ' \0 ' ;
context - > count + = namesp - > attr_namelen + namelen + 1 ;
return 0 ;
}
/*ARGSUSED*/
STATIC int
xfs_attr_kern_list_sizes ( xfs_attr_list_context_t * context , attrnames_t * namesp ,
char * name , int namelen ,
int valuelen , char * value )
{
context - > count + = namesp - > attr_namelen + namelen + 1 ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Generate a list of extended attribute names and optionally
* also value lengths . Positive return value follows the XFS
* convention of being an error , zero or negative return code
* is the length of the buffer returned ( negated ) , indicating
* success .
*/
int
xfs_attr_list ( bhv_desc_t * bdp , char * buffer , int bufsize , int flags ,
attrlist_cursor_kern_t * cursor , struct cred * cred )
{
xfs_attr_list_context_t context ;
xfs_inode_t * dp ;
int error ;
XFS_STATS_INC ( xs_attr_list ) ;
/*
* Validate the cursor .
*/
if ( cursor - > pad1 | | cursor - > pad2 )
return ( XFS_ERROR ( EINVAL ) ) ;
if ( ( cursor - > initted = = 0 ) & &
( cursor - > hashval | | cursor - > blkno | | cursor - > offset ) )
2006-09-28 05:01:37 +04:00
return XFS_ERROR ( EINVAL ) ;
2005-04-17 02:20:36 +04:00
/*
* Check for a properly aligned buffer .
*/
if ( ( ( long ) buffer ) & ( sizeof ( int ) - 1 ) )
2006-09-28 05:01:37 +04:00
return XFS_ERROR ( EFAULT ) ;
2005-04-17 02:20:36 +04:00
if ( flags & ATTR_KERNOVAL )
bufsize = 0 ;
/*
* Initialize the output buffer .
*/
context . dp = dp = XFS_BHVTOI ( bdp ) ;
context . cursor = cursor ;
context . count = 0 ;
context . dupcnt = 0 ;
context . resynch = 1 ;
context . flags = flags ;
2006-09-28 05:01:37 +04:00
context . seen_enough = 0 ;
context . alist = ( attrlist_t * ) buffer ;
context . put_value = 0 ;
if ( flags & ATTR_KERNAMELS ) {
context . bufsize = bufsize ;
context . firstu = context . bufsize ;
if ( flags & ATTR_KERNOVAL )
context . put_listent = xfs_attr_kern_list_sizes ;
else
context . put_listent = xfs_attr_kern_list ;
} else {
2005-04-17 02:20:36 +04:00
context . bufsize = ( bufsize & ~ ( sizeof ( int ) - 1 ) ) ; /* align */
context . firstu = context . bufsize ;
context . alist - > al_count = 0 ;
context . alist - > al_more = 0 ;
context . alist - > al_offset [ 0 ] = context . bufsize ;
2006-09-28 05:01:37 +04:00
context . put_listent = xfs_attr_put_listent ;
2005-04-17 02:20:36 +04:00
}
if ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) )
2006-09-28 05:01:37 +04:00
return EIO ;
2005-04-17 02:20:36 +04:00
xfs_ilock ( dp , XFS_ILOCK_SHARED ) ;
xfs_attr_trace_l_c ( " syscall start " , & context ) ;
2006-09-28 05:01:37 +04:00
error = xfs_attr_list_int ( & context ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( dp , XFS_ILOCK_SHARED ) ;
xfs_attr_trace_l_c ( " syscall end " , & context ) ;
2006-09-28 05:01:37 +04:00
if ( context . flags & ( ATTR_KERNOVAL | ATTR_KERNAMELS ) ) {
/* must return negated buffer size or the error */
2005-04-17 02:20:36 +04:00
if ( context . count < 0 )
error = XFS_ERROR ( ERANGE ) ;
else
error = - context . count ;
2006-09-28 05:01:37 +04:00
} else
ASSERT ( error > = 0 ) ;
2005-04-17 02:20:36 +04:00
2006-09-28 05:01:37 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
int /* error */
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_IFORK_Q ( dp ) = = 0 ) | |
( dp - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) | |
( dp - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
dp - > i_d . di_anextents = = 0 ) ) {
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 ) ;
if ( ( error = xfs_trans_reserve ( trans , 0 , XFS_ATTRINVAL_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES ,
XFS_ATTRINVAL_LOG_COUNT ) ) ) {
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 , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( trans , dp ) ;
/*
* Decide on what work routines to call based on the inode size .
*/
if ( ( XFS_IFORK_Q ( dp ) = = 0 ) | |
( dp - > i_d . di_aformat = = XFS_DINODE_FMT_LOCAL ) | |
( dp - > i_d . di_aformat = = XFS_DINODE_FMT_EXTENTS & &
dp - > i_d . di_anextents = = 0 ) ) {
error = 0 ;
goto out ;
}
error = xfs_attr_root_inactive ( & trans , dp ) ;
if ( error )
goto out ;
/*
* signal synchronous inactive transactions unless this
* is a synchronous mount filesystem in which case we
* know that we ' re here because we ' ve been called out of
* xfs_inactive which means that the last reference is gone
* and the unlink transaction has already hit the disk so
* async inactive transactions are safe .
*/
if ( ( error = xfs_itruncate_finish ( & trans , dp , 0LL , XFS_ATTR_FORK ,
( ! ( mp - > m_flags & XFS_MOUNT_WSYNC )
? 1 : 0 ) ) ) )
goto out ;
/*
* Commit the last in the sequence of transactions .
*/
xfs_trans_log_inode ( trans , dp , XFS_ILOG_CORE ) ;
error = xfs_trans_commit ( trans , XFS_TRANS_RELEASE_LOG_RES ,
NULL ) ;
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 ) ;
}
/*========================================================================
* 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
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 ) .
*/
int
xfs_attr_leaf_addname ( xfs_da_args_t * args )
{
xfs_inode_t * dp ;
xfs_dabuf_t * bp ;
2005-11-02 02:34:53 +03:00
int retval , error , committed , forkoff ;
2005-04-17 02:20:36 +04:00
/*
* Read the ( only ) block in the attribute list in .
*/
dp = args - > dp ;
args - > blkno = 0 ;
error = xfs_da_read_buf ( args - > trans , args - > dp , args - > blkno , - 1 , & bp ,
XFS_ATTR_FORK ) ;
if ( error )
return ( error ) ;
ASSERT ( bp ! = NULL ) ;
/*
* Look up the given attribute in the leaf block . Figure out if
* the given flags produce an error or call for an atomic rename .
*/
retval = xfs_attr_leaf_lookup_int ( bp , args ) ;
if ( ( args - > flags & ATTR_REPLACE ) & & ( retval = = ENOATTR ) ) {
xfs_da_brelse ( args - > trans , bp ) ;
return ( retval ) ;
} else if ( retval = = EEXIST ) {
if ( args - > flags & ATTR_CREATE ) { /* pure create op */
xfs_da_brelse ( args - > trans , bp ) ;
return ( retval ) ;
}
args - > rename = 1 ; /* an atomic rename */
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 .
*/
retval = xfs_attr_leaf_add ( bp , args ) ;
xfs_da_buf_done ( bp ) ;
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 .
*/
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
error = xfs_attr_leaf_to_node ( args ) ;
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
* args - > firstblock , & 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
/*
* Commit the current trans ( including the inode ) and start
* a new one .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , dp ) ) )
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 .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , dp ) ) )
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 .
*/
if ( args - > rename ) {
/*
* In a separate transaction , set the incomplete flag on the
* " old " attr and clear the incomplete flag on the " new " attr .
*/
error = xfs_attr_leaf_flipflags ( args ) ;
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 ! )
*/
error = xfs_da_read_buf ( args - > trans , args - > dp , args - > blkno , - 1 ,
& bp , XFS_ATTR_FORK ) ;
if ( error )
return ( error ) ;
ASSERT ( bp ! = NULL ) ;
( void ) xfs_attr_leaf_remove ( bp , args ) ;
/*
* 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 ) ) ) {
2005-04-17 02:20:36 +04:00
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
2005-11-02 02:34:53 +03:00
error = xfs_attr_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 ,
* args - > firstblock ,
& 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
} else
xfs_da_buf_done ( bp ) ;
/*
* Commit the remove and start the next trans in series .
*/
error = xfs_attr_rolltrans ( & args - > trans , dp ) ;
} else if ( args - > rmtblkno > 0 ) {
/*
* Added a " remote " value , just clear the incomplete flag .
*/
error = xfs_attr_leaf_clearflag ( args ) ;
}
return ( error ) ;
}
/*
* 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 ;
xfs_dabuf_t * bp ;
2005-11-02 02:34:53 +03:00
int error , committed , forkoff ;
2005-04-17 02:20:36 +04:00
/*
* Remove the attribute .
*/
dp = args - > dp ;
args - > blkno = 0 ;
error = xfs_da_read_buf ( args - > trans , args - > dp , args - > blkno , - 1 , & bp ,
XFS_ATTR_FORK ) ;
if ( error ) {
return ( error ) ;
}
ASSERT ( bp ! = NULL ) ;
error = xfs_attr_leaf_lookup_int ( bp , args ) ;
if ( error = = ENOATTR ) {
xfs_da_brelse ( args - > trans , bp ) ;
return ( error ) ;
}
( void ) xfs_attr_leaf_remove ( bp , args ) ;
/*
* 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 ) ) ) {
2005-04-17 02:20:36 +04:00
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
2005-11-02 02:34:53 +03:00
error = xfs_attr_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 ,
* args - > firstblock , & 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
} else
xfs_da_buf_done ( bp ) ;
return ( 0 ) ;
}
/*
* 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 )
{
xfs_dabuf_t * bp ;
int error ;
args - > blkno = 0 ;
error = xfs_da_read_buf ( args - > trans , args - > dp , args - > blkno , - 1 , & bp ,
XFS_ATTR_FORK ) ;
if ( error )
return ( error ) ;
ASSERT ( bp ! = NULL ) ;
error = xfs_attr_leaf_lookup_int ( bp , args ) ;
if ( error ! = EEXIST ) {
xfs_da_brelse ( args - > trans , bp ) ;
return ( error ) ;
}
error = xfs_attr_leaf_getvalue ( bp , args ) ;
xfs_da_brelse ( args - > trans , bp ) ;
if ( ! error & & ( args - > rmtblkno > 0 ) & & ! ( args - > flags & ATTR_KERNOVAL ) ) {
error = xfs_attr_rmtval_get ( args ) ;
}
return ( error ) ;
}
/*
* Copy out attribute entries for attr_list ( ) , for leaf attribute lists .
*/
STATIC int
xfs_attr_leaf_list ( xfs_attr_list_context_t * context )
{
xfs_attr_leafblock_t * leaf ;
int error ;
xfs_dabuf_t * bp ;
context - > cursor - > blkno = 0 ;
error = xfs_da_read_buf ( NULL , context - > dp , 0 , - 1 , & bp , XFS_ATTR_FORK ) ;
if ( error )
2006-09-28 05:01:37 +04:00
return XFS_ERROR ( error ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( bp ! = NULL ) ;
leaf = bp - > data ;
2006-03-17 09:28:40 +03:00
if ( unlikely ( be16_to_cpu ( leaf - > hdr . info . magic ) ! = XFS_ATTR_LEAF_MAGIC ) ) {
2005-04-17 02:20:36 +04:00
XFS_CORRUPTION_ERROR ( " xfs_attr_leaf_list " , XFS_ERRLEVEL_LOW ,
context - > dp - > i_mount , leaf ) ;
xfs_da_brelse ( NULL , bp ) ;
2006-09-28 05:01:37 +04:00
return XFS_ERROR ( EFSCORRUPTED ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-28 05:01:37 +04:00
error = xfs_attr_leaf_list_int ( bp , context ) ;
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( NULL , bp ) ;
2006-09-28 05:01:37 +04:00
return XFS_ERROR ( 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 ;
/*
* 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 .
*/
error = xfs_da_node_lookup_int ( state , & retval ) ;
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 ;
args - > rename = 1 ; /* atomic rename op */
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 ;
}
retval = xfs_attr_leaf_add ( blk - > bp , state - > args ) ;
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 ) ;
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
error = xfs_attr_leaf_to_node ( args ) ;
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans ,
args - > flist ,
* args - > firstblock ,
& 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
/*
* Commit the node conversion and start the next
* trans in the chain .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , dp ) ) )
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 .
*/
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
error = xfs_da_split ( state ) ;
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
* args - > firstblock , & 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
} else {
/*
* Addition succeeded , update Btree hashvals .
*/
xfs_da_fixhashpath ( state , & state - > path ) ;
}
/*
* 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 .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , dp ) ) )
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 .
*/
if ( args - > rename ) {
/*
* In a separate transaction , set the incomplete flag on the
* " old " attr and clear the incomplete flag on the " new " attr .
*/
error = xfs_attr_leaf_flipflags ( args ) ;
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 ;
error = xfs_da_node_lookup_int ( state , & retval ) ;
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 ) ;
error = xfs_attr_leaf_remove ( blk - > bp , args ) ;
xfs_da_fixhashpath ( state , & state - > path ) ;
/*
* Check to see if the tree needs to be collapsed .
*/
if ( retval & & ( state - > path . active > 1 ) ) {
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
error = xfs_da_join ( state ) ;
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans ,
args - > flist ,
* args - > firstblock ,
& 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
}
/*
* Commit and start the next trans in the chain .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , dp ) ) )
goto out ;
} else if ( args - > rmtblkno > 0 ) {
/*
* Added a " remote " value , just clear the incomplete flag .
*/
error = xfs_attr_leaf_clearflag ( args ) ;
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 ;
xfs_dabuf_t * bp ;
2005-11-02 02:34:53 +03:00
int retval , error , committed , forkoff ;
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 .
*/
error = xfs_da_node_lookup_int ( state , & retval ) ;
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 .
*/
error = xfs_attr_leaf_setflag ( args ) ;
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 ) ;
retval = xfs_attr_leaf_remove ( blk - > bp , args ) ;
xfs_da_fixhashpath ( state , & state - > path ) ;
/*
* Check to see if the tree needs to be collapsed .
*/
if ( retval & & ( state - > path . active > 1 ) ) {
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
error = xfs_da_join ( state ) ;
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
* args - > firstblock , & 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
/*
* Commit the Btree join operation and start a new trans .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , dp ) ) )
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 ) ;
xfs_da_buf_done ( state - > path . blk [ 0 ] . bp ) ;
state - > path . blk [ 0 ] . bp = NULL ;
error = xfs_da_read_buf ( args - > trans , args - > dp , 0 , - 1 , & bp ,
XFS_ATTR_FORK ) ;
if ( error )
goto out ;
2006-03-17 09:28:40 +03:00
ASSERT ( be16_to_cpu ( ( ( xfs_attr_leafblock_t * )
bp - > data ) - > hdr . info . magic )
2005-04-17 02:20:36 +04:00
= = XFS_ATTR_LEAF_MAGIC ) ;
2005-11-02 02:34:53 +03:00
if ( ( forkoff = xfs_attr_shortform_allfit ( bp , dp ) ) ) {
2005-04-17 02:20:36 +04:00
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
2005-11-02 02:34:53 +03:00
error = xfs_attr_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 ,
* args - > firstblock ,
& 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
} else
xfs_da_brelse ( args - > trans , bp ) ;
}
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 ;
/*
* 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 ) {
blk - > disk_blkno = xfs_da_blkno ( blk - > bp ) ;
xfs_da_buf_done ( blk - > bp ) ;
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 ) {
blk - > disk_blkno = xfs_da_blkno ( blk - > bp ) ;
xfs_da_buf_done ( blk - > bp ) ;
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 ;
/*
* 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 ) {
error = xfs_da_read_buf ( state - > args - > trans ,
state - > args - > dp ,
blk - > blkno , blk - > disk_blkno ,
& blk - > bp , XFS_ATTR_FORK ) ;
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 ) {
error = xfs_da_read_buf ( state - > args - > trans ,
state - > args - > dp ,
blk - > blkno , blk - > disk_blkno ,
& blk - > bp , XFS_ATTR_FORK ) ;
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 ;
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 .
*/
error = xfs_da_node_lookup_int ( state , & retval ) ;
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 "
*/
retval = xfs_attr_leaf_getvalue ( blk - > bp , args ) ;
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 + + ) {
xfs_da_brelse ( args - > trans , state - > path . blk [ i ] . bp ) ;
state - > path . blk [ i ] . bp = NULL ;
}
xfs_da_state_free ( state ) ;
return ( retval ) ;
}
STATIC int /* error */
xfs_attr_node_list ( xfs_attr_list_context_t * context )
{
attrlist_cursor_kern_t * cursor ;
xfs_attr_leafblock_t * leaf ;
xfs_da_intnode_t * node ;
xfs_da_node_entry_t * btree ;
int error , i ;
xfs_dabuf_t * bp ;
cursor = context - > cursor ;
cursor - > initted = 1 ;
/*
* Do all sorts of validation on the passed - in cursor structure .
* If anything is amiss , ignore the cursor and look up the hashval
* starting from the btree root .
*/
bp = NULL ;
if ( cursor - > blkno > 0 ) {
error = xfs_da_read_buf ( NULL , context - > dp , cursor - > blkno , - 1 ,
& bp , XFS_ATTR_FORK ) ;
if ( ( error ! = 0 ) & & ( error ! = EFSCORRUPTED ) )
return ( error ) ;
if ( bp ) {
node = bp - > data ;
2006-03-17 09:28:40 +03:00
switch ( be16_to_cpu ( node - > hdr . info . magic ) ) {
2005-04-17 02:20:36 +04:00
case XFS_DA_NODE_MAGIC :
xfs_attr_trace_l_cn ( " wrong blk " , context , node ) ;
xfs_da_brelse ( NULL , bp ) ;
bp = NULL ;
break ;
case XFS_ATTR_LEAF_MAGIC :
leaf = bp - > data ;
2006-03-17 09:29:02 +03:00
if ( cursor - > hashval > be32_to_cpu ( leaf - > entries [
be16_to_cpu ( leaf - > hdr . count ) - 1 ] . hashval ) ) {
2005-04-17 02:20:36 +04:00
xfs_attr_trace_l_cl ( " wrong blk " ,
context , leaf ) ;
xfs_da_brelse ( NULL , bp ) ;
bp = NULL ;
} else if ( cursor - > hashval < =
2006-03-17 09:29:02 +03:00
be32_to_cpu ( leaf - > entries [ 0 ] . hashval ) ) {
2005-04-17 02:20:36 +04:00
xfs_attr_trace_l_cl ( " maybe wrong blk " ,
context , leaf ) ;
xfs_da_brelse ( NULL , bp ) ;
bp = NULL ;
}
break ;
default :
xfs_attr_trace_l_c ( " wrong blk - ?? " , context ) ;
xfs_da_brelse ( NULL , bp ) ;
bp = NULL ;
}
}
}
/*
* We did not find what we expected given the cursor ' s contents ,
* so we start from the top and work down based on the hash value .
* Note that start of node block is same as start of leaf block .
*/
if ( bp = = NULL ) {
cursor - > blkno = 0 ;
for ( ; ; ) {
error = xfs_da_read_buf ( NULL , context - > dp ,
cursor - > blkno , - 1 , & bp ,
XFS_ATTR_FORK ) ;
if ( error )
return ( error ) ;
if ( unlikely ( bp = = NULL ) ) {
XFS_ERROR_REPORT ( " xfs_attr_node_list(2) " ,
XFS_ERRLEVEL_LOW ,
context - > dp - > i_mount ) ;
return ( XFS_ERROR ( EFSCORRUPTED ) ) ;
}
node = bp - > data ;
2006-03-17 09:28:40 +03:00
if ( be16_to_cpu ( node - > hdr . info . magic )
2005-04-17 02:20:36 +04:00
= = XFS_ATTR_LEAF_MAGIC )
break ;
2006-03-17 09:28:40 +03:00
if ( unlikely ( be16_to_cpu ( node - > hdr . info . magic )
2005-04-17 02:20:36 +04:00
! = XFS_DA_NODE_MAGIC ) ) {
XFS_CORRUPTION_ERROR ( " xfs_attr_node_list(3) " ,
XFS_ERRLEVEL_LOW ,
context - > dp - > i_mount ,
node ) ;
xfs_da_brelse ( NULL , bp ) ;
return ( XFS_ERROR ( EFSCORRUPTED ) ) ;
}
btree = node - > btree ;
2006-03-17 09:29:56 +03:00
for ( i = 0 ; i < be16_to_cpu ( node - > hdr . count ) ;
2005-04-17 02:20:36 +04:00
btree + + , i + + ) {
if ( cursor - > hashval
2006-03-17 09:29:46 +03:00
< = be32_to_cpu ( btree - > hashval ) ) {
cursor - > blkno = be32_to_cpu ( btree - > before ) ;
2005-04-17 02:20:36 +04:00
xfs_attr_trace_l_cb ( " descending " ,
context , btree ) ;
break ;
}
}
2006-03-17 09:29:56 +03:00
if ( i = = be16_to_cpu ( node - > hdr . count ) ) {
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( NULL , bp ) ;
return ( 0 ) ;
}
xfs_da_brelse ( NULL , bp ) ;
}
}
ASSERT ( bp ! = NULL ) ;
/*
* Roll upward through the blocks , processing each leaf block in
* order . As long as there is space in the result buffer , keep
* adding the information .
*/
for ( ; ; ) {
leaf = bp - > data ;
2006-03-17 09:28:40 +03:00
if ( unlikely ( be16_to_cpu ( leaf - > hdr . info . magic )
2005-04-17 02:20:36 +04:00
! = XFS_ATTR_LEAF_MAGIC ) ) {
XFS_CORRUPTION_ERROR ( " xfs_attr_node_list(4) " ,
XFS_ERRLEVEL_LOW ,
context - > dp - > i_mount , leaf ) ;
xfs_da_brelse ( NULL , bp ) ;
return ( XFS_ERROR ( EFSCORRUPTED ) ) ;
}
error = xfs_attr_leaf_list_int ( bp , context ) ;
2006-09-28 05:01:37 +04:00
if ( error ) {
xfs_da_brelse ( NULL , bp ) ;
return error ;
}
if ( context - > seen_enough | | leaf - > hdr . info . forw = = 0 )
break ;
2006-03-17 09:28:40 +03:00
cursor - > blkno = be32_to_cpu ( leaf - > hdr . info . forw ) ;
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( NULL , bp ) ;
error = xfs_da_read_buf ( NULL , context - > dp , cursor - > blkno , - 1 ,
& bp , XFS_ATTR_FORK ) ;
if ( error )
return ( error ) ;
if ( unlikely ( ( bp = = NULL ) ) ) {
XFS_ERROR_REPORT ( " xfs_attr_node_list(5) " ,
XFS_ERRLEVEL_LOW ,
context - > dp - > i_mount ) ;
return ( XFS_ERROR ( EFSCORRUPTED ) ) ;
}
}
xfs_da_brelse ( NULL , bp ) ;
return ( 0 ) ;
}
/*========================================================================
* External routines for manipulating out - of - line attribute values .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
* Read the value associated with an attribute from the out - of - line buffer
* that we stored it in .
*/
2006-09-28 05:01:37 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_attr_rmtval_get ( xfs_da_args_t * args )
{
xfs_bmbt_irec_t map [ ATTR_RMTVALUE_MAPSIZE ] ;
xfs_mount_t * mp ;
xfs_daddr_t dblkno ;
xfs_caddr_t dst ;
xfs_buf_t * bp ;
int nmap , error , tmp , valuelen , blkcnt , i ;
xfs_dablk_t lblkno ;
ASSERT ( ! ( args - > flags & ATTR_KERNOVAL ) ) ;
mp = args - > dp - > i_mount ;
dst = args - > value ;
valuelen = args - > valuelen ;
lblkno = args - > rmtblkno ;
while ( valuelen > 0 ) {
nmap = ATTR_RMTVALUE_MAPSIZE ;
error = xfs_bmapi ( args - > trans , args - > dp , ( xfs_fileoff_t ) lblkno ,
args - > rmtblkcnt ,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA ,
2006-06-09 08:48:12 +04:00
NULL , 0 , map , & nmap , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return ( error ) ;
ASSERT ( nmap > = 1 ) ;
for ( i = 0 ; ( i < nmap ) & & ( valuelen > 0 ) ; i + + ) {
ASSERT ( ( map [ i ] . br_startblock ! = DELAYSTARTBLOCK ) & &
( map [ i ] . br_startblock ! = HOLESTARTBLOCK ) ) ;
dblkno = XFS_FSB_TO_DADDR ( mp , map [ i ] . br_startblock ) ;
blkcnt = XFS_FSB_TO_BB ( mp , map [ i ] . br_blockcount ) ;
error = xfs_read_buf ( mp , mp - > m_ddev_targp , dblkno ,
blkcnt , XFS_BUF_LOCK , & bp ) ;
if ( error )
return ( error ) ;
tmp = ( valuelen < XFS_BUF_SIZE ( bp ) )
? valuelen : XFS_BUF_SIZE ( bp ) ;
xfs_biomove ( bp , 0 , tmp , dst , XFS_B_READ ) ;
xfs_buf_relse ( bp ) ;
dst + = tmp ;
valuelen - = tmp ;
lblkno + = map [ i ] . br_blockcount ;
}
}
ASSERT ( valuelen = = 0 ) ;
return ( 0 ) ;
}
/*
* Write the value associated with an attribute into the out - of - line buffer
* that we have defined for it .
*/
STATIC int
xfs_attr_rmtval_set ( xfs_da_args_t * args )
{
xfs_mount_t * mp ;
xfs_fileoff_t lfileoff ;
xfs_inode_t * dp ;
xfs_bmbt_irec_t map ;
xfs_daddr_t dblkno ;
xfs_caddr_t src ;
xfs_buf_t * bp ;
xfs_dablk_t lblkno ;
int blkcnt , valuelen , nmap , error , tmp , committed ;
dp = args - > dp ;
mp = dp - > i_mount ;
src = args - > value ;
/*
* Find a " hole " in the attribute address space large enough for
* us to drop the new attribute ' s value into .
*/
blkcnt = XFS_B_TO_FSB ( mp , args - > valuelen ) ;
lfileoff = 0 ;
error = xfs_bmap_first_unused ( args - > trans , args - > dp , blkcnt , & lfileoff ,
XFS_ATTR_FORK ) ;
if ( error ) {
return ( error ) ;
}
args - > rmtblkno = lblkno = ( xfs_dablk_t ) lfileoff ;
args - > rmtblkcnt = blkcnt ;
/*
* Roll through the " value " , allocating blocks on disk as required .
*/
while ( blkcnt > 0 ) {
/*
* Allocate a single extent , up to the size of the value .
*/
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
nmap = 1 ;
error = xfs_bmapi ( args - > trans , dp , ( xfs_fileoff_t ) lblkno ,
blkcnt ,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA |
XFS_BMAPI_WRITE ,
args - > firstblock , args - > total , & map , & nmap ,
2006-06-09 08:48:12 +04:00
args - > flist , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
* args - > firstblock , & 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , dp ) ;
}
ASSERT ( nmap = = 1 ) ;
ASSERT ( ( map . br_startblock ! = DELAYSTARTBLOCK ) & &
( map . br_startblock ! = HOLESTARTBLOCK ) ) ;
lblkno + = map . br_blockcount ;
blkcnt - = map . br_blockcount ;
/*
* Start the next trans in the chain .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , dp ) ) )
return ( error ) ;
}
/*
* Roll through the " value " , copying the attribute value to the
* already - allocated blocks . Blocks are written synchronously
* so that we can know they are all on disk before we turn off
* the INCOMPLETE flag .
*/
lblkno = args - > rmtblkno ;
valuelen = args - > valuelen ;
while ( valuelen > 0 ) {
/*
* Try to remember where we decided to put the value .
*/
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
nmap = 1 ;
error = xfs_bmapi ( NULL , dp , ( xfs_fileoff_t ) lblkno ,
args - > rmtblkcnt ,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA ,
2006-06-09 08:48:12 +04:00
args - > firstblock , 0 , & map , & nmap ,
NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
return ( error ) ;
}
ASSERT ( nmap = = 1 ) ;
ASSERT ( ( map . br_startblock ! = DELAYSTARTBLOCK ) & &
( map . br_startblock ! = HOLESTARTBLOCK ) ) ;
dblkno = XFS_FSB_TO_DADDR ( mp , map . br_startblock ) ,
blkcnt = XFS_FSB_TO_BB ( mp , map . br_blockcount ) ;
bp = xfs_buf_get_flags ( mp - > m_ddev_targp , dblkno ,
blkcnt , XFS_BUF_LOCK ) ;
ASSERT ( bp ) ;
ASSERT ( ! XFS_BUF_GETERROR ( bp ) ) ;
tmp = ( valuelen < XFS_BUF_SIZE ( bp ) ) ? valuelen :
XFS_BUF_SIZE ( bp ) ;
xfs_biomove ( bp , 0 , tmp , src , XFS_B_WRITE ) ;
if ( tmp < XFS_BUF_SIZE ( bp ) )
xfs_biozero ( bp , tmp , XFS_BUF_SIZE ( bp ) - tmp ) ;
if ( ( error = xfs_bwrite ( mp , bp ) ) ) { /* GROT: NOTE: synchronous write */
return ( error ) ;
}
src + = tmp ;
valuelen - = tmp ;
lblkno + = map . br_blockcount ;
}
ASSERT ( valuelen = = 0 ) ;
return ( 0 ) ;
}
/*
* Remove the value associated with an attribute by deleting the
* out - of - line buffer that it is stored on .
*/
STATIC int
xfs_attr_rmtval_remove ( xfs_da_args_t * args )
{
xfs_mount_t * mp ;
xfs_bmbt_irec_t map ;
xfs_buf_t * bp ;
xfs_daddr_t dblkno ;
xfs_dablk_t lblkno ;
int valuelen , blkcnt , nmap , error , done , committed ;
mp = args - > dp - > i_mount ;
/*
* Roll through the " value " , invalidating the attribute value ' s
* blocks .
*/
lblkno = args - > rmtblkno ;
valuelen = args - > rmtblkcnt ;
while ( valuelen > 0 ) {
/*
* Try to remember where we decided to put the value .
*/
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
nmap = 1 ;
error = xfs_bmapi ( NULL , args - > dp , ( xfs_fileoff_t ) lblkno ,
args - > rmtblkcnt ,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA ,
args - > firstblock , 0 , & map , & nmap ,
2006-06-09 08:48:12 +04:00
args - > flist , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
return ( error ) ;
}
ASSERT ( nmap = = 1 ) ;
ASSERT ( ( map . br_startblock ! = DELAYSTARTBLOCK ) & &
( map . br_startblock ! = HOLESTARTBLOCK ) ) ;
dblkno = XFS_FSB_TO_DADDR ( mp , map . br_startblock ) ,
blkcnt = XFS_FSB_TO_BB ( mp , map . br_blockcount ) ;
/*
* If the " remote " value is in the cache , remove it .
*/
bp = xfs_incore ( mp - > m_ddev_targp , dblkno , blkcnt ,
XFS_INCORE_TRYLOCK ) ;
if ( bp ) {
XFS_BUF_STALE ( bp ) ;
XFS_BUF_UNDELAYWRITE ( bp ) ;
xfs_buf_relse ( bp ) ;
bp = NULL ;
}
valuelen - = map . br_blockcount ;
lblkno + = map . br_blockcount ;
}
/*
* Keep de - allocating extents until the remote - value region is gone .
*/
lblkno = args - > rmtblkno ;
blkcnt = args - > rmtblkcnt ;
done = 0 ;
while ( ! done ) {
XFS_BMAP_INIT ( args - > flist , args - > firstblock ) ;
error = xfs_bunmapi ( args - > trans , args - > dp , lblkno , blkcnt ,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA ,
2006-06-09 08:48:12 +04:00
1 , args - > firstblock , args - > flist ,
NULL , & done ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
error = xfs_bmap_finish ( & args - > trans , args - > flist ,
* args - > firstblock , & 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 .
*/
if ( committed ) {
xfs_trans_ijoin ( args - > trans , args - > dp , XFS_ILOCK_EXCL ) ;
xfs_trans_ihold ( args - > trans , args - > dp ) ;
}
/*
* Close out trans and start the next one in the chain .
*/
if ( ( error = xfs_attr_rolltrans ( & args - > trans , args - > dp ) ) )
return ( error ) ;
}
return ( 0 ) ;
}
# if defined(XFS_ATTR_TRACE)
/*
* Add a trace buffer entry for an attr_list context structure .
*/
void
xfs_attr_trace_l_c ( char * where , struct xfs_attr_list_context * context )
{
xfs_attr_trace_enter ( XFS_ATTR_KTRACE_L_C , where ,
( __psunsigned_t ) context - > dp ,
( __psunsigned_t ) context - > cursor - > hashval ,
( __psunsigned_t ) context - > cursor - > blkno ,
( __psunsigned_t ) context - > cursor - > offset ,
( __psunsigned_t ) context - > alist ,
( __psunsigned_t ) context - > bufsize ,
( __psunsigned_t ) context - > count ,
( __psunsigned_t ) context - > firstu ,
( __psunsigned_t )
( ( context - > count > 0 ) & &
! ( context - > flags & ( ATTR_KERNAMELS | ATTR_KERNOVAL ) ) )
? ( ATTR_ENTRY ( context - > alist ,
context - > count - 1 ) - > a_valuelen )
: 0 ,
( __psunsigned_t ) context - > dupcnt ,
( __psunsigned_t ) context - > flags ,
( __psunsigned_t ) NULL ,
( __psunsigned_t ) NULL ,
( __psunsigned_t ) NULL ) ;
}
/*
* Add a trace buffer entry for a context structure and a Btree node .
*/
void
xfs_attr_trace_l_cn ( char * where , struct xfs_attr_list_context * context ,
struct xfs_da_intnode * node )
{
xfs_attr_trace_enter ( XFS_ATTR_KTRACE_L_CN , where ,
( __psunsigned_t ) context - > dp ,
( __psunsigned_t ) context - > cursor - > hashval ,
( __psunsigned_t ) context - > cursor - > blkno ,
( __psunsigned_t ) context - > cursor - > offset ,
( __psunsigned_t ) context - > alist ,
( __psunsigned_t ) context - > bufsize ,
( __psunsigned_t ) context - > count ,
( __psunsigned_t ) context - > firstu ,
( __psunsigned_t )
( ( context - > count > 0 ) & &
! ( context - > flags & ( ATTR_KERNAMELS | ATTR_KERNOVAL ) ) )
? ( ATTR_ENTRY ( context - > alist ,
context - > count - 1 ) - > a_valuelen )
: 0 ,
( __psunsigned_t ) context - > dupcnt ,
( __psunsigned_t ) context - > flags ,
2006-03-17 09:29:56 +03:00
( __psunsigned_t ) be16_to_cpu ( node - > hdr . count ) ,
2006-03-17 09:29:46 +03:00
( __psunsigned_t ) be32_to_cpu ( node - > btree [ 0 ] . hashval ) ,
2006-03-17 09:29:56 +03:00
( __psunsigned_t ) be32_to_cpu ( node - > btree [
be16_to_cpu ( node - > hdr . count ) - 1 ] . hashval ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Add a trace buffer entry for a context structure and a Btree element .
*/
void
xfs_attr_trace_l_cb ( char * where , struct xfs_attr_list_context * context ,
struct xfs_da_node_entry * btree )
{
xfs_attr_trace_enter ( XFS_ATTR_KTRACE_L_CB , where ,
( __psunsigned_t ) context - > dp ,
( __psunsigned_t ) context - > cursor - > hashval ,
( __psunsigned_t ) context - > cursor - > blkno ,
( __psunsigned_t ) context - > cursor - > offset ,
( __psunsigned_t ) context - > alist ,
( __psunsigned_t ) context - > bufsize ,
( __psunsigned_t ) context - > count ,
( __psunsigned_t ) context - > firstu ,
( __psunsigned_t )
( ( context - > count > 0 ) & &
! ( context - > flags & ( ATTR_KERNAMELS | ATTR_KERNOVAL ) ) )
? ( ATTR_ENTRY ( context - > alist ,
context - > count - 1 ) - > a_valuelen )
: 0 ,
( __psunsigned_t ) context - > dupcnt ,
( __psunsigned_t ) context - > flags ,
2006-03-17 09:29:46 +03:00
( __psunsigned_t ) be32_to_cpu ( btree - > hashval ) ,
( __psunsigned_t ) be32_to_cpu ( btree - > before ) ,
2005-04-17 02:20:36 +04:00
( __psunsigned_t ) NULL ) ;
}
/*
* Add a trace buffer entry for a context structure and a leaf block .
*/
void
xfs_attr_trace_l_cl ( char * where , struct xfs_attr_list_context * context ,
struct xfs_attr_leafblock * leaf )
{
xfs_attr_trace_enter ( XFS_ATTR_KTRACE_L_CL , where ,
( __psunsigned_t ) context - > dp ,
( __psunsigned_t ) context - > cursor - > hashval ,
( __psunsigned_t ) context - > cursor - > blkno ,
( __psunsigned_t ) context - > cursor - > offset ,
( __psunsigned_t ) context - > alist ,
( __psunsigned_t ) context - > bufsize ,
( __psunsigned_t ) context - > count ,
( __psunsigned_t ) context - > firstu ,
( __psunsigned_t )
( ( context - > count > 0 ) & &
! ( context - > flags & ( ATTR_KERNAMELS | ATTR_KERNOVAL ) ) )
? ( ATTR_ENTRY ( context - > alist ,
context - > count - 1 ) - > a_valuelen )
: 0 ,
( __psunsigned_t ) context - > dupcnt ,
( __psunsigned_t ) context - > flags ,
2006-03-17 09:28:54 +03:00
( __psunsigned_t ) be16_to_cpu ( leaf - > hdr . count ) ,
2006-03-17 09:29:02 +03:00
( __psunsigned_t ) be32_to_cpu ( leaf - > entries [ 0 ] . hashval ) ,
( __psunsigned_t ) be32_to_cpu ( leaf - > entries [
be16_to_cpu ( leaf - > hdr . count ) - 1 ] . hashval ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Add a trace buffer entry for the arguments given to the routine ,
* generic form .
*/
void
xfs_attr_trace_enter ( int type , char * where ,
__psunsigned_t a2 , __psunsigned_t a3 ,
__psunsigned_t a4 , __psunsigned_t a5 ,
__psunsigned_t a6 , __psunsigned_t a7 ,
__psunsigned_t a8 , __psunsigned_t a9 ,
__psunsigned_t a10 , __psunsigned_t a11 ,
__psunsigned_t a12 , __psunsigned_t a13 ,
__psunsigned_t a14 , __psunsigned_t a15 )
{
ASSERT ( xfs_attr_trace_buf ) ;
ktrace_enter ( xfs_attr_trace_buf , ( void * ) ( ( __psunsigned_t ) type ) ,
( void * ) where ,
( void * ) a2 , ( void * ) a3 , ( void * ) a4 ,
( void * ) a5 , ( void * ) a6 , ( void * ) a7 ,
( void * ) a8 , ( void * ) a9 , ( void * ) a10 ,
( void * ) a11 , ( void * ) a12 , ( void * ) a13 ,
( void * ) a14 , ( void * ) a15 ) ;
}
# endif /* XFS_ATTR_TRACE */
/*========================================================================
* System ( pseudo ) namespace attribute interface routines .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
STATIC int
posix_acl_access_set (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vset ( vp , data , size , _ACL_TYPE_ACCESS ) ;
}
STATIC int
posix_acl_access_remove (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , int xflags )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vremove ( vp , _ACL_TYPE_ACCESS ) ;
}
STATIC int
posix_acl_access_get (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vget ( vp , data , size , _ACL_TYPE_ACCESS ) ;
}
STATIC int
posix_acl_access_exists (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vhasacl_access ( vp ) ;
}
STATIC int
posix_acl_default_set (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vset ( vp , data , size , _ACL_TYPE_DEFAULT ) ;
}
STATIC int
posix_acl_default_get (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vget ( vp , data , size , _ACL_TYPE_DEFAULT ) ;
}
STATIC int
posix_acl_default_remove (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , int xflags )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vremove ( vp , _ACL_TYPE_DEFAULT ) ;
}
STATIC int
posix_acl_default_exists (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp )
2005-04-17 02:20:36 +04:00
{
return xfs_acl_vhasacl_default ( vp ) ;
}
2005-06-21 09:36:52 +04:00
STATIC struct attrnames posix_acl_access = {
2005-04-17 02:20:36 +04:00
. attr_name = " posix_acl_access " ,
. attr_namelen = sizeof ( " posix_acl_access " ) - 1 ,
. attr_get = posix_acl_access_get ,
. attr_set = posix_acl_access_set ,
. attr_remove = posix_acl_access_remove ,
. attr_exists = posix_acl_access_exists ,
} ;
2005-06-21 09:36:52 +04:00
STATIC struct attrnames posix_acl_default = {
2005-04-17 02:20:36 +04:00
. attr_name = " posix_acl_default " ,
. attr_namelen = sizeof ( " posix_acl_default " ) - 1 ,
. attr_get = posix_acl_default_get ,
. attr_set = posix_acl_default_set ,
. attr_remove = posix_acl_default_remove ,
. attr_exists = posix_acl_default_exists ,
} ;
2005-06-21 09:36:52 +04:00
STATIC struct attrnames * attr_system_names [ ] =
2005-04-17 02:20:36 +04:00
{ & posix_acl_access , & posix_acl_default } ;
/*========================================================================
* Namespace - prefix - style attribute name interface routines .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
STATIC int
attr_generic_set (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
2006-06-09 11:00:52 +04:00
return - bhv_vop_attr_set ( vp , name , data , size , xflags , NULL ) ;
2005-04-17 02:20:36 +04:00
}
STATIC int
attr_generic_get (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
int error , asize = size ;
2006-06-09 11:00:52 +04:00
error = bhv_vop_attr_get ( vp , name , data , & asize , xflags , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! error )
return asize ;
return - error ;
}
STATIC int
attr_generic_remove (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , int xflags )
2005-04-17 02:20:36 +04:00
{
2006-06-09 11:00:52 +04:00
return - bhv_vop_attr_remove ( vp , name , xflags , NULL ) ;
2005-04-17 02:20:36 +04:00
}
STATIC int
attr_generic_listadd (
attrnames_t * prefix ,
attrnames_t * namesp ,
void * data ,
size_t size ,
ssize_t * result )
{
char * p = data + * result ;
* result + = prefix - > attr_namelen ;
* result + = namesp - > attr_namelen + 1 ;
if ( ! size )
return 0 ;
if ( * result > size )
return - ERANGE ;
strcpy ( p , prefix - > attr_name ) ;
p + = prefix - > attr_namelen ;
strcpy ( p , namesp - > attr_name ) ;
p + = namesp - > attr_namelen + 1 ;
return 0 ;
}
STATIC int
attr_system_list (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp ,
2005-04-17 02:20:36 +04:00
void * data ,
size_t size ,
ssize_t * result )
{
attrnames_t * namesp ;
int i , error = 0 ;
for ( i = 0 ; i < ATTR_SYSCOUNT ; i + + ) {
namesp = attr_system_names [ i ] ;
if ( ! namesp - > attr_exists | | ! namesp - > attr_exists ( vp ) )
continue ;
error = attr_generic_listadd ( & attr_system , namesp ,
data , size , result ) ;
if ( error )
break ;
}
return error ;
}
int
attr_generic_list (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , void * data , size_t size , int xflags , ssize_t * result )
2005-04-17 02:20:36 +04:00
{
attrlist_cursor_kern_t cursor = { 0 } ;
int error ;
2006-06-09 11:00:52 +04:00
error = bhv_vop_attr_list ( vp , data , size , xflags , & cursor , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( error > 0 )
return - error ;
* result = - error ;
return attr_system_list ( vp , data , size , result ) ;
}
attrnames_t *
attr_lookup_namespace (
char * name ,
struct attrnames * * names ,
int nnames )
{
int i ;
for ( i = 0 ; i < nnames ; i + + )
if ( ! strncmp ( name , names [ i ] - > attr_name , names [ i ] - > attr_namelen ) )
return names [ i ] ;
return NULL ;
}
/*
* Some checks to prevent people abusing EAs to get over quota :
* - Don ' t allow modifying user EAs on devices / symlinks ;
* - Don ' t allow modifying user EAs if sticky bit set ;
*/
STATIC int
attr_user_capable (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp ,
2005-04-17 02:20:36 +04:00
cred_t * cred )
{
2006-03-17 09:25:36 +03:00
struct inode * inode = vn_to_inode ( vp ) ;
2005-04-17 02:20:36 +04:00
if ( IS_IMMUTABLE ( inode ) | | IS_APPEND ( inode ) )
return - EPERM ;
if ( ! S_ISREG ( inode - > i_mode ) & & ! S_ISDIR ( inode - > i_mode ) & &
! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( S_ISDIR ( inode - > i_mode ) & & ( inode - > i_mode & S_ISVTX ) & &
( current_fsuid ( cred ) ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EPERM ;
return 0 ;
}
STATIC int
attr_trusted_capable (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp ,
2005-04-17 02:20:36 +04:00
cred_t * cred )
{
2006-03-17 09:25:36 +03:00
struct inode * inode = vn_to_inode ( vp ) ;
2005-04-17 02:20:36 +04:00
if ( IS_IMMUTABLE ( inode ) | | IS_APPEND ( inode ) )
return - EPERM ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
return 0 ;
}
STATIC int
attr_secure_capable (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp ,
2005-04-17 02:20:36 +04:00
cred_t * cred )
{
return - ENOSECURITY ;
}
STATIC int
attr_system_set (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
attrnames_t * namesp ;
int error ;
if ( xflags & ATTR_CREATE )
return - EINVAL ;
namesp = attr_lookup_namespace ( name , attr_system_names , ATTR_SYSCOUNT ) ;
if ( ! namesp )
return - EOPNOTSUPP ;
error = namesp - > attr_set ( vp , name , data , size , xflags ) ;
if ( ! error )
error = vn_revalidate ( vp ) ;
return error ;
}
STATIC int
attr_system_get (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , void * data , size_t size , int xflags )
2005-04-17 02:20:36 +04:00
{
attrnames_t * namesp ;
namesp = attr_lookup_namespace ( name , attr_system_names , ATTR_SYSCOUNT ) ;
if ( ! namesp )
return - EOPNOTSUPP ;
return namesp - > attr_get ( vp , name , data , size , xflags ) ;
}
STATIC int
attr_system_remove (
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp , char * name , int xflags )
2005-04-17 02:20:36 +04:00
{
attrnames_t * namesp ;
namesp = attr_lookup_namespace ( name , attr_system_names , ATTR_SYSCOUNT ) ;
if ( ! namesp )
return - EOPNOTSUPP ;
return namesp - > attr_remove ( vp , name , xflags ) ;
}
struct attrnames attr_system = {
. attr_name = " system. " ,
. attr_namelen = sizeof ( " system. " ) - 1 ,
. attr_flag = ATTR_SYSTEM ,
. attr_get = attr_system_get ,
. attr_set = attr_system_set ,
. attr_remove = attr_system_remove ,
. attr_capable = ( attrcapable_t ) fs_noerr ,
} ;
struct attrnames attr_trusted = {
. attr_name = " trusted. " ,
. attr_namelen = sizeof ( " trusted. " ) - 1 ,
. attr_flag = ATTR_ROOT ,
. attr_get = attr_generic_get ,
. attr_set = attr_generic_set ,
. attr_remove = attr_generic_remove ,
. attr_capable = attr_trusted_capable ,
} ;
struct attrnames attr_secure = {
. attr_name = " security. " ,
. attr_namelen = sizeof ( " security. " ) - 1 ,
. attr_flag = ATTR_SECURE ,
. attr_get = attr_generic_get ,
. attr_set = attr_generic_set ,
. attr_remove = attr_generic_remove ,
. attr_capable = attr_secure_capable ,
} ;
struct attrnames attr_user = {
. attr_name = " user. " ,
. attr_namelen = sizeof ( " user. " ) - 1 ,
. attr_get = attr_generic_get ,
. attr_set = attr_generic_set ,
. attr_remove = attr_generic_remove ,
. attr_capable = attr_user_capable ,
} ;
struct attrnames * attr_namespaces [ ] =
{ & attr_system , & attr_trusted , & attr_secure , & attr_user } ;