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"
2013-10-23 03:51:50 +04:00
# include "xfs_format.h"
2013-10-23 03:50:10 +04:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inum.h"
2005-04-17 02:20:36 +04:00
# include "xfs_sb.h"
# include "xfs_ag.h"
# include "xfs_mount.h"
2013-10-15 02:17:51 +04:00
# include "xfs_da_format.h"
2005-11-02 06:38:42 +03:00
# include "xfs_da_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_inode.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inode_item.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
2013-08-12 14:49:37 +04:00
# include "xfs_dir2.h"
2011-07-13 15:43:48 +04:00
# include "xfs_dir2_priv.h"
2005-04-17 02:20:36 +04:00
# include "xfs_error.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2013-10-23 03:51:50 +04:00
# include "xfs_dinode.h"
2005-04-17 02:20:36 +04:00
2013-08-12 14:50:09 +04:00
struct xfs_name xfs_name_dotdot = { ( unsigned char * ) " .. " , 2 , XFS_DIR3_FT_DIR } ;
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 ,
} ;
2014-06-06 09:01:58 +04:00
int
xfs_da_mount (
struct xfs_mount * mp )
2005-04-17 02:20:36 +04:00
{
2014-06-06 09:01:58 +04:00
struct xfs_da_geometry * dageo ;
int nodehdr_size ;
2013-08-26 08:13:30 +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 ) ;
2013-10-29 15:11:51 +04:00
mp - > m_dir_inode_ops = xfs_dir_get_ops ( mp , NULL ) ;
mp - > m_nondir_inode_ops = xfs_nondir_get_ops ( mp , NULL ) ;
2013-10-30 02:15:02 +04:00
nodehdr_size = mp - > m_dir_inode_ops - > node_hdr_size ;
2014-06-06 09:01:58 +04:00
mp - > m_dir_geo = kmem_zalloc ( sizeof ( struct xfs_da_geometry ) ,
KM_SLEEP | KM_MAYFAIL ) ;
mp - > m_attr_geo = kmem_zalloc ( sizeof ( struct xfs_da_geometry ) ,
KM_SLEEP | KM_MAYFAIL ) ;
if ( ! mp - > m_dir_geo | | ! mp - > m_attr_geo ) {
kmem_free ( mp - > m_dir_geo ) ;
kmem_free ( mp - > m_attr_geo ) ;
return ENOMEM ;
}
/* set up directory geometry */
dageo = mp - > m_dir_geo ;
dageo - > blklog = mp - > m_sb . sb_blocklog + mp - > m_sb . sb_dirblklog ;
dageo - > fsblog = mp - > m_sb . sb_blocklog ;
dageo - > blksize = 1 < < dageo - > blklog ;
dageo - > fsbcount = 1 < < mp - > m_sb . sb_dirblklog ;
dageo - > datablk = xfs_dir2_byte_to_da ( mp , XFS_DIR2_DATA_OFFSET ) ;
dageo - > leafblk = xfs_dir2_byte_to_da ( mp , XFS_DIR2_LEAF_OFFSET ) ;
dageo - > freeblk = xfs_dir2_byte_to_da ( mp , XFS_DIR2_FREE_OFFSET ) ;
dageo - > node_ents = ( dageo - > blksize - nodehdr_size ) /
2013-08-26 08:13:30 +04:00
( uint ) sizeof ( xfs_da_node_entry_t ) ;
2014-06-06 09:01:58 +04:00
dageo - > magicpct = ( dageo - > blksize * 37 ) / 100 ;
/* set up attribute geometry - single fsb only */
dageo = mp - > m_attr_geo ;
dageo - > blklog = mp - > m_sb . sb_blocklog ;
dageo - > fsblog = mp - > m_sb . sb_blocklog ;
dageo - > blksize = 1 < < dageo - > blklog ;
dageo - > fsbcount = 1 ;
dageo - > node_ents = ( dageo - > blksize - nodehdr_size ) /
2013-08-26 08:13:30 +04:00
( uint ) sizeof ( xfs_da_node_entry_t ) ;
2014-06-06 09:01:58 +04:00
dageo - > magicpct = ( dageo - > blksize * 37 ) / 100 ;
2013-08-26 08:13:30 +04:00
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 ;
2013-10-29 15:11:46 +04:00
2014-06-06 09:01:58 +04:00
/* XXX: these are to be removed as code is converted to use geo */
mp - > m_dirblksize = mp - > m_dir_geo - > blksize ;
mp - > m_dirblkfsbs = mp - > m_dir_geo - > fsbcount ;
mp - > m_dirdatablk = mp - > m_dir_geo - > datablk ;
mp - > m_dirleafblk = mp - > m_dir_geo - > leafblk ;
mp - > m_dirfreeblk = mp - > m_dir_geo - > freeblk ;
mp - > m_dir_node_ents = mp - > m_dir_geo - > node_ents ;
mp - > m_dir_magicpct = mp - > m_dir_geo - > magicpct ;
mp - > m_attr_node_ents = mp - > m_attr_geo - > node_ents ;
mp - > m_attr_magicpct = mp - > m_attr_geo - > magicpct ;
return 0 ;
}
void
xfs_da_unmount (
struct xfs_mount * mp )
{
kmem_free ( mp - > m_dir_geo ) ;
kmem_free ( mp - > m_attr_geo ) ;
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
{
2014-02-27 09:51:26 +04:00
struct xfs_da_args * args ;
2006-06-20 07:04:51 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2011-07-26 10:31:30 +04:00
ASSERT ( S_ISDIR ( dp - > i_d . di_mode ) ) ;
2014-02-27 09:51:26 +04:00
error = xfs_dir_ino_validate ( tp - > t_mountp , pdp - > i_ino ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
2014-02-27 09:51:26 +04:00
args = kmem_zalloc ( sizeof ( * args ) , KM_SLEEP | KM_NOFS ) ;
if ( ! args )
return ENOMEM ;
2014-06-06 09:01:58 +04:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 09:51:26 +04:00
args - > dp = dp ;
args - > trans = tp ;
error = xfs_dir2_sf_create ( args , pdp - > i_ino ) ;
kmem_free ( args ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
/*
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 */
{
2014-02-27 09:51:26 +04:00
struct xfs_da_args * args ;
2006-06-20 07:04:51 +04:00
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 ) ) ;
2014-02-27 09:51:26 +04:00
rval = xfs_dir_ino_validate ( tp - > t_mountp , inum ) ;
if ( rval )
2005-04-17 02:20:36 +04:00
return rval ;
XFS_STATS_INC ( xs_dir_create ) ;
2006-06-20 07:04:51 +04:00
2014-02-27 09:51:26 +04:00
args = kmem_zalloc ( sizeof ( * args ) , KM_SLEEP | KM_NOFS ) ;
if ( ! args )
return ENOMEM ;
2014-06-06 09:01:58 +04:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 09:51:26 +04:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
args - > hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
args - > inumber = inum ;
args - > dp = dp ;
args - > firstblock = first ;
args - > flist = flist ;
args - > total = total ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
args - > op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT ;
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL ) {
rval = xfs_dir2_sf_addname ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isblock ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v ) {
rval = xfs_dir2_block_addname ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isleaf ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v )
rval = xfs_dir2_leaf_addname ( args ) ;
2005-04-17 02:20:36 +04:00
else
2014-02-27 09:51:26 +04:00
rval = xfs_dir2_node_addname ( args ) ;
out_free :
kmem_free ( args ) ;
2005-04-17 02:20:36 +04:00
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
{
2014-02-27 09:51:26 +04:00
struct xfs_da_args * args ;
2006-06-20 07:04:51 +04:00
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 ) ;
2014-02-27 09:51:26 +04:00
/*
* We need to use KM_NOFS here so that lockdep will not throw false
* positive deadlock warnings on a non - transactional lookup path . It is
* safe to recurse into inode recalim in that case , but lockdep can ' t
* easily be taught about it . Hence KM_NOFS avoids having to add more
* lockdep Doing this avoids having to add a bunch of lockdep class
* annotations into the reclaim path for the ilock .
*/
args = kmem_zalloc ( sizeof ( * args ) , KM_SLEEP | KM_NOFS ) ;
2014-06-06 09:01:58 +04:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 09:51:26 +04:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
args - > hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
args - > dp = dp ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
args - > op_flags = XFS_DA_OP_OKNOENT ;
2008-05-21 10:58:22 +04:00
if ( ci_name )
2014-02-27 09:51:26 +04:00
args - > op_flags | = XFS_DA_OP_CILOOKUP ;
2006-06-20 07:04:51 +04:00
2014-02-27 09:51:26 +04:00
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL ) {
rval = xfs_dir2_sf_lookup ( args ) ;
goto out_check_rval ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isblock ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v ) {
rval = xfs_dir2_block_lookup ( args ) ;
goto out_check_rval ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isleaf ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v )
rval = xfs_dir2_leaf_lookup ( args ) ;
2005-04-17 02:20:36 +04:00
else
2014-02-27 09:51:26 +04:00
rval = xfs_dir2_node_lookup ( args ) ;
out_check_rval :
2005-04-17 02:20:36 +04:00
if ( rval = = EEXIST )
rval = 0 ;
2008-05-21 10:58:22 +04:00
if ( ! rval ) {
2014-02-27 09:51:26 +04:00
* inum = args - > inumber ;
2008-05-21 10:58:22 +04:00
if ( ci_name ) {
2014-02-27 09:51:26 +04:00
ci_name - > name = args - > value ;
ci_name - > len = args - > valuelen ;
2008-05-21 10:58:22 +04:00
}
}
2014-02-27 09:51:26 +04:00
out_free :
kmem_free ( args ) ;
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 */
{
2014-02-27 09:51:26 +04:00
struct xfs_da_args * args ;
2006-06-20 07:04:51 +04:00
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
2014-02-27 09:51:26 +04:00
args = kmem_zalloc ( sizeof ( * args ) , KM_SLEEP | KM_NOFS ) ;
if ( ! args )
return ENOMEM ;
2014-06-06 09:01:58 +04:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 09:51:26 +04:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
args - > hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
args - > inumber = ino ;
args - > dp = dp ;
args - > firstblock = first ;
args - > flist = flist ;
args - > total = total ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL ) {
rval = xfs_dir2_sf_removename ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isblock ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v ) {
rval = xfs_dir2_block_removename ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isleaf ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v )
rval = xfs_dir2_leaf_removename ( args ) ;
2005-04-17 02:20:36 +04:00
else
2014-02-27 09:51:26 +04:00
rval = xfs_dir2_node_removename ( args ) ;
out_free :
kmem_free ( args ) ;
2005-04-17 02:20:36 +04:00
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 */
{
2014-02-27 09:51:26 +04:00
struct xfs_da_args * args ;
2006-06-20 07:04:51 +04:00
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
2014-02-27 09:51:26 +04:00
rval = xfs_dir_ino_validate ( tp - > t_mountp , inum ) ;
if ( rval )
2005-04-17 02:20:36 +04:00
return rval ;
2006-06-20 07:04:51 +04:00
2014-02-27 09:51:26 +04:00
args = kmem_zalloc ( sizeof ( * args ) , KM_SLEEP | KM_NOFS ) ;
if ( ! args )
return ENOMEM ;
2014-06-06 09:01:58 +04:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 09:51:26 +04:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
args - > hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
args - > inumber = inum ;
args - > dp = dp ;
args - > firstblock = first ;
args - > flist = flist ;
args - > total = total ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL ) {
rval = xfs_dir2_sf_replace ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isblock ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v ) {
rval = xfs_dir2_block_replace ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isleaf ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v )
rval = xfs_dir2_leaf_replace ( args ) ;
2005-04-17 02:20:36 +04:00
else
2014-02-27 09:51:26 +04:00
rval = xfs_dir2_node_replace ( args ) ;
out_free :
kmem_free ( args ) ;
2005-04-17 02:20:36 +04:00
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
{
2014-02-27 09:51:26 +04:00
struct xfs_da_args * args ;
2006-06-20 07:04:51 +04:00
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
2014-02-27 09:51:26 +04:00
args = kmem_zalloc ( sizeof ( * args ) , KM_SLEEP | KM_NOFS ) ;
if ( ! args )
return ENOMEM ;
2014-06-06 09:01:58 +04:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 09:51:26 +04:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
args - > hashval = dp - > i_mount - > m_dirnameops - > hashname ( name ) ;
args - > dp = dp ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
args - > op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME |
2008-05-21 10:42:05 +04:00
XFS_DA_OP_OKNOENT ;
2006-06-20 07:04:51 +04:00
2014-02-27 09:51:26 +04:00
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_LOCAL ) {
rval = xfs_dir2_sf_addname ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isblock ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v ) {
rval = xfs_dir2_block_addname ( args ) ;
goto out_free ;
}
2014-04-14 12:58:05 +04:00
rval = xfs_dir2_isleaf ( dp , & v ) ;
2014-02-27 09:51:26 +04:00
if ( rval )
goto out_free ;
if ( v )
rval = xfs_dir2_leaf_addname ( args ) ;
2005-04-17 02:20:36 +04:00
else
2014-02-27 09:51:26 +04:00
rval = xfs_dir2_node_addname ( args ) ;
out_free :
kmem_free ( args ) ;
2005-04-17 02:20:36 +04:00
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_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 ;
2014-04-14 12:58:05 +04:00
if ( ( rval = xfs_bmap_last_offset ( 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_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 ;
2014-04-14 12:58:05 +04:00
if ( ( rval = xfs_bmap_last_offset ( 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 .
*/
2014-06-06 09:04:41 +04:00
if ( db > = xfs_dir2_byte_to_db ( mp , XFS_DIR2_LEAF_OFFSET ) )
2005-04-17 02:20:36 +04:00
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 ;
}