2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2001 , 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"
2005-04-17 02:20:36 +04:00
# include "xfs_types.h"
# 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_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_dinode.h"
# include "xfs_inode.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"
2011-07-13 15:43:48 +04:00
# include "xfs_dir2.h"
# include "xfs_dir2_format.h"
# include "xfs_dir2_priv.h"
2005-04-17 02:20:36 +04:00
# include "xfs_error.h"
2007-11-23 08:28:09 +03:00
# include "xfs_vnodeops.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2005-04-17 02:20:36 +04:00
2010-01-20 02:48:05 +03:00
struct xfs_name xfs_name_dotdot = { ( unsigned char * ) " .. " , 2 } ;
2005-04-17 02:20:36 +04:00
2008-05-21 10:58:55 +04:00
/*
* ASCII case - insensitive ( ie . A - Z ) support for directories that was
* used in IRIX .
*/
STATIC xfs_dahash_t
xfs_ascii_ci_hashname (
struct xfs_name * name )
{
xfs_dahash_t hash ;
int i ;
for ( i = 0 , hash = 0 ; i < name - > len ; i + + )
hash = tolower ( name - > name [ i ] ) ^ rol32 ( hash , 7 ) ;
return hash ;
}
STATIC enum xfs_dacmp
xfs_ascii_ci_compname (
struct xfs_da_args * args ,
2010-01-20 02:47:17 +03:00
const unsigned char * name ,
int len )
2008-05-21 10:58:55 +04:00
{
enum xfs_dacmp result ;
int i ;
if ( args - > namelen ! = len )
return XFS_CMP_DIFFERENT ;
result = XFS_CMP_EXACT ;
for ( i = 0 ; i < len ; i + + ) {
if ( args - > name [ i ] = = name [ i ] )
continue ;
if ( tolower ( args - > name [ i ] ) ! = tolower ( name [ i ] ) )
return XFS_CMP_DIFFERENT ;
result = XFS_CMP_CASE ;
}
return result ;
}
static struct xfs_nameops xfs_ascii_ci_nameops = {
. hashname = xfs_ascii_ci_hashname ,
. compname = xfs_ascii_ci_compname ,
} ;
2006-06-20 07:04:51 +04:00
void
xfs_dir_mount (
xfs_mount_t * mp )
2005-04-17 02:20:36 +04:00
{
2008-03-06 05:44:28 +03:00
ASSERT ( xfs_sb_version_hasdirv2 ( & mp - > m_sb ) ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( ( 1 < < ( mp - > m_sb . sb_blocklog + mp - > m_sb . sb_dirblklog ) ) < =
XFS_MAX_BLOCKSIZE ) ;
mp - > m_dirblksize = 1 < < ( mp - > m_sb . sb_blocklog + mp - > m_sb . sb_dirblklog ) ;
mp - > m_dirblkfsbs = 1 < < mp - > m_sb . sb_dirblklog ;
2007-06-28 10:43:50 +04:00
mp - > m_dirdatablk = xfs_dir2_db_to_da ( mp , XFS_DIR2_DATA_FIRSTDB ( mp ) ) ;
mp - > m_dirleafblk = xfs_dir2_db_to_da ( mp , XFS_DIR2_LEAF_FIRSTDB ( mp ) ) ;
mp - > m_dirfreeblk = xfs_dir2_db_to_da ( mp , XFS_DIR2_FREE_FIRSTDB ( mp ) ) ;
2005-04-17 02:20:36 +04:00
mp - > m_attr_node_ents =
( mp - > m_sb . sb_blocksize - ( uint ) sizeof ( xfs_da_node_hdr_t ) ) /
( uint ) sizeof ( xfs_da_node_entry_t ) ;
mp - > m_dir_node_ents =
( mp - > m_dirblksize - ( uint ) sizeof ( xfs_da_node_hdr_t ) ) /
( uint ) sizeof ( xfs_da_node_entry_t ) ;
mp - > m_dir_magicpct = ( mp - > m_dirblksize * 37 ) / 100 ;
2008-05-21 10:58:55 +04:00
if ( xfs_sb_version_hasasciici ( & mp - > m_sb ) )
mp - > m_dirnameops = & xfs_ascii_ci_nameops ;
else
mp - > m_dirnameops = & xfs_default_nameops ;
2005-04-17 02:20:36 +04:00
}
/*
* Return 1 if directory contains only " . " and " .. " .
*/
2006-06-20 07:04:51 +04:00
int
xfs_dir_isempty (
xfs_inode_t * dp )
2005-04-17 02:20:36 +04:00
{
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ;
2005-04-17 02:20:36 +04:00
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2006-06-20 07:04:51 +04:00
if ( dp - > i_d . di_size = = 0 ) /* might happen during shutdown. */
2005-04-17 02:20:36 +04:00
return 1 ;
if ( dp - > i_d . di_size > XFS_IFORK_DSIZE ( dp ) )
return 0 ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
return ! sfp - > count ;
2005-04-17 02:20:36 +04:00
}
2006-06-20 07:04:51 +04:00
/*
* Validate a given inode number .
*/
int
xfs_dir_ino_validate (
xfs_mount_t * mp ,
xfs_ino_t ino )
{
xfs_agblock_t agblkno ;
xfs_agino_t agino ;
xfs_agnumber_t agno ;
int ino_ok ;
int ioff ;
agno = XFS_INO_TO_AGNO ( mp , ino ) ;
agblkno = XFS_INO_TO_AGBNO ( mp , ino ) ;
ioff = XFS_INO_TO_OFFSET ( mp , ino ) ;
agino = XFS_OFFBNO_TO_AGINO ( mp , agblkno , ioff ) ;
ino_ok =
agno < mp - > m_sb . sb_agcount & &
agblkno < mp - > m_sb . sb_agblocks & &
agblkno ! = 0 & &
ioff < ( 1 < < mp - > m_sb . sb_inopblog ) & &
XFS_AGINO_TO_INO ( mp , agno , agino ) = = ino ;
if ( unlikely ( XFS_TEST_ERROR ( ! ino_ok , mp , XFS_ERRTAG_DIR_INO_VALIDATE ,
XFS_RANDOM_DIR_INO_VALIDATE ) ) ) {
2011-03-07 02:05:35 +03:00
xfs_warn ( mp , " Invalid inode number 0x%Lx " ,
2006-06-20 07:04:51 +04:00
( unsigned long long ) ino ) ;
XFS_ERROR_REPORT ( " xfs_dir_ino_validate " , XFS_ERRLEVEL_LOW , mp ) ;
return XFS_ERROR ( EFSCORRUPTED ) ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Initialize a directory with its " . " and " .. " entries .
*/
2006-06-20 07:04:51 +04:00
int
xfs_dir_init (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
xfs_inode_t * pdp )
2005-04-17 02:20:36 +04:00
{
2006-06-20 07:04:51 +04:00
xfs_da_args_t args ;
int error ;
2005-04-17 02:20:36 +04:00
memset ( ( char * ) & args , 0 , sizeof ( args ) ) ;
args . dp = dp ;
args . trans = tp ;
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2006-06-20 07:04:51 +04:00
if ( ( error = xfs_dir_ino_validate ( tp - > t_mountp , pdp - > i_ino ) ) )
2005-04-17 02:20:36 +04:00
return error ;
return xfs_dir2_sf_create ( & args , pdp - > i_ino ) ;
}
/*
Enter a name in a directory .
*/
2006-06-20 07:04:51 +04:00
int
xfs_dir_createname (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name ,
2005-04-17 02:20:36 +04:00
xfs_ino_t inum , /* new entry inode number */
xfs_fsblock_t * first , /* bmap's firstblock */
xfs_bmap_free_t * flist , /* bmap's freeblock list */
xfs_extlen_t total ) /* bmap's total block count */
{
2006-06-20 07:04:51 +04:00
xfs_da_args_t args ;
int rval ;
2005-04-17 02:20:36 +04:00
int v ; /* type-checking value */
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2006-06-20 07:04:51 +04:00
if ( ( rval = xfs_dir_ino_validate ( tp - > t_mountp , inum ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
XFS_STATS_INC ( xs_dir_create ) ;
2006-06-20 07:04:51 +04:00
2008-06-03 05:59:18 +04:00
memset ( & args , 0 , sizeof ( xfs_da_args_t ) ) ;
2008-04-10 06:22:07 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2008-05-21 10:41:01 +04:00
args . hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
2005-04-17 02:20:36 +04:00
args . inumber = inum ;
args . dp = dp ;
args . firstblock = first ;
args . flist = flist ;
args . total = total ;
args . whichfork = XFS_DATA_FORK ;
args . trans = tp ;
2008-05-21 10:42:05 +04:00
args . op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT ;
2006-06-20 07:04:51 +04:00
2005-04-17 02:20:36 +04:00
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL )
rval = xfs_dir2_sf_addname ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isblock ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_block_addname ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isleaf ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_leaf_addname ( & args ) ;
else
rval = xfs_dir2_node_addname ( & args ) ;
return rval ;
}
2008-05-21 10:58:22 +04:00
/*
* If doing a CI lookup and case - insensitive match , dup actual name into
* args . value . Return EEXIST for success ( ie . name found ) or an error .
*/
int
xfs_dir_cilookup_result (
struct xfs_da_args * args ,
2010-01-20 02:47:25 +03:00
const unsigned char * name ,
2008-05-21 10:58:22 +04:00
int len )
{
if ( args - > cmpresult = = XFS_CMP_DIFFERENT )
return ENOENT ;
if ( args - > cmpresult ! = XFS_CMP_CASE | |
! ( args - > op_flags & XFS_DA_OP_CILOOKUP ) )
return EEXIST ;
2009-07-19 02:14:57 +04:00
args - > value = kmem_alloc ( len , KM_NOFS | KM_MAYFAIL ) ;
2008-05-21 10:58:22 +04:00
if ( ! args - > value )
return ENOMEM ;
memcpy ( args - > value , name , len ) ;
args - > valuelen = len ;
return EEXIST ;
}
2005-04-17 02:20:36 +04:00
/*
* Lookup a name in a directory , give back the inode number .
2008-05-21 10:58:22 +04:00
* If ci_name is not NULL , returns the actual name in ci_name if it differs
* to name , or ci_name - > name is set to NULL for an exact match .
2005-04-17 02:20:36 +04:00
*/
2008-05-21 10:58:22 +04:00
2006-06-20 07:04:51 +04:00
int
xfs_dir_lookup (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name ,
2008-05-21 10:58:22 +04:00
xfs_ino_t * inum , /* out: inode number */
struct xfs_name * ci_name ) /* out: actual name if CI match */
2005-04-17 02:20:36 +04:00
{
2006-06-20 07:04:51 +04:00
xfs_da_args_t args ;
int rval ;
2005-04-17 02:20:36 +04:00
int v ; /* type-checking value */
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2005-04-17 02:20:36 +04:00
XFS_STATS_INC ( xs_dir_lookup ) ;
2008-06-03 05:59:18 +04:00
memset ( & args , 0 , sizeof ( xfs_da_args_t ) ) ;
2008-04-10 06:22:07 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2008-05-21 10:41:01 +04:00
args . hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
2005-04-17 02:20:36 +04:00
args . dp = dp ;
args . whichfork = XFS_DATA_FORK ;
args . trans = tp ;
2008-05-21 10:42:05 +04:00
args . op_flags = XFS_DA_OP_OKNOENT ;
2008-05-21 10:58:22 +04:00
if ( ci_name )
args . op_flags | = XFS_DA_OP_CILOOKUP ;
2006-06-20 07:04:51 +04:00
2005-04-17 02:20:36 +04:00
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL )
rval = xfs_dir2_sf_lookup ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isblock ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_block_lookup ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isleaf ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_leaf_lookup ( & args ) ;
else
rval = xfs_dir2_node_lookup ( & args ) ;
if ( rval = = EEXIST )
rval = 0 ;
2008-05-21 10:58:22 +04:00
if ( ! rval ) {
2005-04-17 02:20:36 +04:00
* inum = args . inumber ;
2008-05-21 10:58:22 +04:00
if ( ci_name ) {
ci_name - > name = args . value ;
ci_name - > len = args . valuelen ;
}
}
2005-04-17 02:20:36 +04:00
return rval ;
}
/*
* Remove an entry from a directory .
*/
2006-06-20 07:04:51 +04:00
int
xfs_dir_removename (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name ,
2006-06-20 07:04:51 +04:00
xfs_ino_t ino ,
2005-04-17 02:20:36 +04:00
xfs_fsblock_t * first , /* bmap's firstblock */
xfs_bmap_free_t * flist , /* bmap's freeblock list */
xfs_extlen_t total ) /* bmap's total block count */
{
2006-06-20 07:04:51 +04:00
xfs_da_args_t args ;
int rval ;
2005-04-17 02:20:36 +04:00
int v ; /* type-checking value */
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2005-04-17 02:20:36 +04:00
XFS_STATS_INC ( xs_dir_remove ) ;
2006-06-20 07:04:51 +04:00
2008-06-03 05:59:18 +04:00
memset ( & args , 0 , sizeof ( xfs_da_args_t ) ) ;
2008-04-10 06:22:07 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2008-05-21 10:41:01 +04:00
args . hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
2005-04-17 02:20:36 +04:00
args . inumber = ino ;
args . dp = dp ;
args . firstblock = first ;
args . flist = flist ;
args . total = total ;
args . whichfork = XFS_DATA_FORK ;
args . trans = tp ;
2006-06-20 07:04:51 +04:00
2005-04-17 02:20:36 +04:00
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL )
rval = xfs_dir2_sf_removename ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isblock ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_block_removename ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isleaf ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_leaf_removename ( & args ) ;
else
rval = xfs_dir2_node_removename ( & args ) ;
return rval ;
}
/*
* Replace the inode number of a directory entry .
*/
2006-06-20 07:04:51 +04:00
int
xfs_dir_replace (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name , /* name of entry to replace */
2005-04-17 02:20:36 +04:00
xfs_ino_t inum , /* new inode number */
xfs_fsblock_t * first , /* bmap's firstblock */
xfs_bmap_free_t * flist , /* bmap's freeblock list */
xfs_extlen_t total ) /* bmap's total block count */
{
2006-06-20 07:04:51 +04:00
xfs_da_args_t args ;
int rval ;
2005-04-17 02:20:36 +04:00
int v ; /* type-checking value */
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2005-04-17 02:20:36 +04:00
2006-06-20 07:04:51 +04:00
if ( ( rval = xfs_dir_ino_validate ( tp - > t_mountp , inum ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
2008-06-03 05:59:18 +04:00
memset ( & args , 0 , sizeof ( xfs_da_args_t ) ) ;
2008-04-10 06:22:07 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2008-05-21 10:41:01 +04:00
args . hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
2005-04-17 02:20:36 +04:00
args . inumber = inum ;
args . dp = dp ;
args . firstblock = first ;
args . flist = flist ;
args . total = total ;
args . whichfork = XFS_DATA_FORK ;
args . trans = tp ;
2006-06-20 07:04:51 +04:00
2005-04-17 02:20:36 +04:00
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL )
rval = xfs_dir2_sf_replace ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isblock ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_block_replace ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isleaf ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_leaf_replace ( & args ) ;
else
rval = xfs_dir2_node_replace ( & args ) ;
return rval ;
}
/*
* See if this entry can be added to the directory without allocating space .
2008-04-10 06:22:07 +04:00
* First checks that the caller couldn ' t reserve enough space ( resblks = 0 ) .
2005-04-17 02:20:36 +04:00
*/
2006-06-20 07:04:51 +04:00
int
xfs_dir_canenter (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2008-04-10 06:22:07 +04:00
struct xfs_name * name , /* name of entry to add */
uint resblks )
2005-04-17 02:20:36 +04:00
{
2006-06-20 07:04:51 +04:00
xfs_da_args_t args ;
int rval ;
2005-04-17 02:20:36 +04:00
int v ; /* type-checking value */
2008-04-10 06:22:07 +04:00
if ( resblks )
return 0 ;
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2006-06-20 07:04:51 +04:00
2008-06-03 05:59:18 +04:00
memset ( & args , 0 , sizeof ( xfs_da_args_t ) ) ;
2008-04-10 06:22:07 +04:00
args . name = name - > name ;
args . namelen = name - > len ;
2008-05-21 10:41:01 +04:00
args . hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
2005-04-17 02:20:36 +04:00
args . dp = dp ;
args . whichfork = XFS_DATA_FORK ;
args . trans = tp ;
2008-05-21 10:42:05 +04:00
args . op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME |
XFS_DA_OP_OKNOENT ;
2006-06-20 07:04:51 +04:00
2005-04-17 02:20:36 +04:00
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL )
rval = xfs_dir2_sf_addname ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isblock ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_block_addname ( & args ) ;
2006-06-20 07:04:51 +04:00
else if ( ( rval = xfs_dir2_isleaf ( tp , dp , & v ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
else if ( v )
2005-04-17 02:20:36 +04:00
rval = xfs_dir2_leaf_addname ( & args ) ;
else
rval = xfs_dir2_node_addname ( & args ) ;
return rval ;
}
/*
* Utility routines .
*/
/*
* Add a block to the directory .
2011-07-13 15:43:49 +04:00
*
* This routine is for data and free blocks , not leaf / node blocks which are
* handled by xfs_da_grow_inode .
2005-04-17 02:20:36 +04:00
*/
2006-06-20 07:04:51 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_dir2_grow_inode (
2011-07-13 15:43:49 +04:00
struct xfs_da_args * args ,
int space , /* v2 dir's space XFS_DIR2_xxx_SPACE */
xfs_dir2_db_t * dbp ) /* out: block number added */
2005-04-17 02:20:36 +04:00
{
2011-07-13 15:43:49 +04:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
xfs_fileoff_t bno ; /* directory offset of new block */
int count ; /* count of filesystem blocks */
int error ;
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_grow_inode ( args , space ) ;
2005-04-17 02:20:36 +04:00
/*
* Set lowest possible block in the space requested .
*/
bno = XFS_B_TO_FSBT ( mp , space * XFS_DIR2_SPACE_SIZE ) ;
count = mp - > m_dirblkfsbs ;
2011-07-13 15:43:49 +04:00
error = xfs_da_grow_inode_int ( args , & bno , count ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
2008-10-30 09:38:12 +03:00
2007-06-28 10:43:50 +04:00
* dbp = xfs_dir2_da_to_db ( mp , ( xfs_dablk_t ) bno ) ;
2008-10-30 09:38:12 +03:00
2005-04-17 02:20:36 +04:00
/*
* Update file ' s size if this is the data space and it grew .
*/
if ( space = = XFS_DIR2_DATA_SPACE ) {
xfs_fsize_t size ; /* directory file (data) size */
size = XFS_FSB_TO_B ( mp , bno + count ) ;
if ( size > dp - > i_d . di_size ) {
dp - > i_d . di_size = size ;
2011-07-13 15:43:49 +04:00
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
/*
* See if the directory is a single - block form directory .
*/
2006-06-20 07:04:51 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_dir2_isblock (
2006-06-20 07:04:51 +04:00
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2005-04-17 02:20:36 +04:00
int * vp ) /* out: 1 is block, 0 is not block */
{
xfs_fileoff_t last ; /* last file offset */
2006-06-20 07:04:51 +04:00
xfs_mount_t * mp ;
int rval ;
2005-04-17 02:20:36 +04:00
mp = dp - > i_mount ;
2006-06-20 07:04:51 +04:00
if ( ( rval = xfs_bmap_last_offset ( tp , dp , & last , XFS_DATA_FORK ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
rval = XFS_FSB_TO_B ( mp , last ) = = mp - > m_dirblksize ;
ASSERT ( rval = = 0 | | dp - > i_d . di_size = = mp - > m_dirblksize ) ;
* vp = rval ;
return 0 ;
}
/*
* See if the directory is a single - leaf form directory .
*/
2006-06-20 07:04:51 +04:00
int
2005-04-17 02:20:36 +04:00
xfs_dir2_isleaf (
2006-06-20 07:04:51 +04:00
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2005-04-17 02:20:36 +04:00
int * vp ) /* out: 1 is leaf, 0 is not leaf */
{
xfs_fileoff_t last ; /* last file offset */
2006-06-20 07:04:51 +04:00
xfs_mount_t * mp ;
int rval ;
2005-04-17 02:20:36 +04:00
mp = dp - > i_mount ;
2006-06-20 07:04:51 +04:00
if ( ( rval = xfs_bmap_last_offset ( tp , dp , & last , XFS_DATA_FORK ) ) )
2005-04-17 02:20:36 +04:00
return rval ;
* vp = last = = mp - > m_dirleafblk + ( 1 < < mp - > m_sb . sb_dirblklog ) ;
return 0 ;
}
/*
* Remove the given block from the directory .
* This routine is used for data and free blocks , leaf / node are done
* by xfs_da_shrink_inode .
*/
int
xfs_dir2_shrink_inode (
2006-06-20 07:04:51 +04:00
xfs_da_args_t * args ,
xfs_dir2_db_t db ,
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp )
2005-04-17 02:20:36 +04:00
{
xfs_fileoff_t bno ; /* directory file offset */
xfs_dablk_t da ; /* directory file offset */
int done ; /* bunmap is finished */
2006-06-20 07:04:51 +04:00
xfs_inode_t * dp ;
int error ;
xfs_mount_t * mp ;
xfs_trans_t * tp ;
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_shrink_inode ( args , db ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
mp = dp - > i_mount ;
tp = args - > trans ;
2007-06-28 10:43:50 +04:00
da = xfs_dir2_db_to_da ( mp , db ) ;
2005-04-17 02:20:36 +04:00
/*
* Unmap the fsblock ( s ) .
*/
if ( ( error = xfs_bunmapi ( tp , dp , da , mp - > m_dirblkfsbs ,
XFS_BMAPI_METADATA , 0 , args - > firstblock , args - > flist ,
2010-06-23 12:11:15 +04:00
& done ) ) ) {
2005-04-17 02:20:36 +04:00
/*
* ENOSPC actually can happen if we ' re in a removename with
* no space reservation , and the resulting block removal
* would cause a bmap btree split or conversion from extents
* to btree . This can only happen for un - fragmented
* directory blocks , since you need to be punching out
* the middle of an extent .
* In this case we need to leave the block in the file ,
* and not binval it .
* So the block has to be in a consistent empty state
* and appropriately logged .
* We don ' t free up the buffer , the caller can tell it
* hasn ' t happened since it got an error back .
*/
return error ;
}
ASSERT ( done ) ;
/*
* Invalidate the buffer from the transaction .
*/
2012-06-22 12:50:14 +04:00
xfs_trans_binval ( tp , bp ) ;
2005-04-17 02:20:36 +04:00
/*
* If it ' s not a data block , we ' re done .
*/
if ( db > = XFS_DIR2_LEAF_FIRSTDB ( mp ) )
return 0 ;
/*
* If the block isn ' t the last one in the directory , we ' re done .
*/
2007-06-28 10:43:50 +04:00
if ( dp - > i_d . di_size > xfs_dir2_db_off_to_byte ( mp , db + 1 , 0 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
bno = da ;
if ( ( error = xfs_bmap_last_before ( tp , dp , & bno , XFS_DATA_FORK ) ) ) {
/*
* This can ' t really happen unless there ' s kernel corruption .
*/
return error ;
}
if ( db = = mp - > m_dirdatablk )
ASSERT ( bno = = 0 ) ;
else
ASSERT ( bno > 0 ) ;
/*
* Set the size to the new last block .
*/
dp - > i_d . di_size = XFS_FSB_TO_B ( mp , bno ) ;
xfs_trans_log_inode ( tp , dp , XFS_ILOG_CORE ) ;
return 0 ;
}