2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2003 , 2005 Silicon Graphics , Inc .
2013-04-03 09:11:20 +04:00
* Copyright ( c ) 2013 Red Hat , Inc .
2005-11-02 06:58:39 +03:00
* 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-04-17 02:20:36 +04:00
# include "xfs_sb.h"
2007-08-28 08:00:13 +04:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# 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"
2013-07-10 01:04:00 +04:00
# include "xfs_bmap.h"
2013-04-03 09:11:20 +04:00
# include "xfs_buf_item.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-04-03 09:11:20 +04:00
# include "xfs_cksum.h"
2013-10-23 03:51:50 +04:00
# include "xfs_dinode.h"
2005-04-17 02:20:36 +04:00
/*
* Local function prototypes .
*/
2012-06-22 12:50:14 +04:00
static void xfs_dir2_block_log_leaf ( xfs_trans_t * tp , struct xfs_buf * bp ,
int first , int last ) ;
static void xfs_dir2_block_log_tail ( xfs_trans_t * tp , struct xfs_buf * bp ) ;
static int xfs_dir2_block_lookup_int ( xfs_da_args_t * args , struct xfs_buf * * bpp ,
2005-04-17 02:20:36 +04:00
int * entno ) ;
static int xfs_dir2_block_sort ( const void * a , const void * b ) ;
2006-06-20 07:04:51 +04:00
static xfs_dahash_t xfs_dir_hash_dot , xfs_dir_hash_dotdot ;
/*
* One - time startup routine called from xfs_init ( ) .
*/
void
xfs_dir_startup ( void )
{
2010-01-20 02:48:05 +03:00
xfs_dir_hash_dot = xfs_da_hashname ( ( unsigned char * ) " . " , 1 ) ;
xfs_dir_hash_dotdot = xfs_da_hashname ( ( unsigned char * ) " .. " , 2 ) ;
2006-06-20 07:04:51 +04:00
}
2013-04-03 09:11:20 +04:00
static bool
xfs_dir3_block_verify (
2012-11-12 15:54:12 +04:00
struct xfs_buf * bp )
{
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
2013-04-03 09:11:20 +04:00
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
if ( xfs_sb_version_hascrc ( & mp - > m_sb ) ) {
if ( hdr3 - > magic ! = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) )
return false ;
if ( ! uuid_equal ( & hdr3 - > uuid , & mp - > m_sb . sb_uuid ) )
return false ;
if ( be64_to_cpu ( hdr3 - > blkno ) ! = bp - > b_bn )
return false ;
} else {
if ( hdr3 - > magic ! = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) )
return false ;
2012-11-12 15:54:12 +04:00
}
2013-04-03 09:11:22 +04:00
if ( __xfs_dir3_data_check ( NULL , bp ) )
2013-04-03 09:11:20 +04:00
return false ;
return true ;
2012-11-14 10:52:32 +04:00
}
2012-11-12 15:54:12 +04:00
2012-11-14 10:52:32 +04:00
static void
2013-04-03 09:11:20 +04:00
xfs_dir3_block_read_verify (
2012-11-14 10:52:32 +04:00
struct xfs_buf * bp )
{
2013-04-03 09:11:20 +04:00
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
2014-02-27 08:23:10 +04:00
if ( xfs_sb_version_hascrc ( & mp - > m_sb ) & &
! xfs_buf_verify_cksum ( bp , XFS_DIR3_DATA_CRC_OFF ) )
xfs_buf_ioerror ( bp , EFSBADCRC ) ;
else if ( ! xfs_dir3_block_verify ( bp ) )
2013-04-03 09:11:20 +04:00
xfs_buf_ioerror ( bp , EFSCORRUPTED ) ;
2014-02-27 08:23:10 +04:00
if ( bp - > b_error )
xfs_verifier_error ( bp ) ;
2012-11-14 10:52:32 +04:00
}
2012-11-14 10:54:40 +04:00
static void
2013-04-03 09:11:20 +04:00
xfs_dir3_block_write_verify (
2012-11-14 10:52:32 +04:00
struct xfs_buf * bp )
{
2013-04-03 09:11:20 +04:00
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
struct xfs_buf_log_item * bip = bp - > b_fspriv ;
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
if ( ! xfs_dir3_block_verify ( bp ) ) {
xfs_buf_ioerror ( bp , EFSCORRUPTED ) ;
2014-02-27 08:23:10 +04:00
xfs_verifier_error ( bp ) ;
2013-04-03 09:11:20 +04:00
return ;
}
if ( ! xfs_sb_version_hascrc ( & mp - > m_sb ) )
return ;
if ( bip )
hdr3 - > lsn = cpu_to_be64 ( bip - > bli_item . li_lsn ) ;
2014-02-27 08:18:23 +04:00
xfs_buf_update_cksum ( bp , XFS_DIR3_DATA_CRC_OFF ) ;
2012-11-12 15:54:12 +04:00
}
2013-04-03 09:11:20 +04:00
const struct xfs_buf_ops xfs_dir3_block_buf_ops = {
. verify_read = xfs_dir3_block_read_verify ,
. verify_write = xfs_dir3_block_write_verify ,
2012-11-14 10:54:40 +04:00
} ;
2013-08-12 14:49:36 +04:00
int
2013-04-03 09:11:20 +04:00
xfs_dir3_block_read (
2012-11-12 15:54:11 +04:00
struct xfs_trans * tp ,
struct xfs_inode * dp ,
struct xfs_buf * * bpp )
{
struct xfs_mount * mp = dp - > i_mount ;
2013-04-03 09:11:29 +04:00
int err ;
2012-11-12 15:54:11 +04:00
2013-04-03 09:11:29 +04:00
err = xfs_da_read_buf ( tp , dp , mp - > m_dirdatablk , - 1 , bpp ,
2013-04-03 09:11:20 +04:00
XFS_DATA_FORK , & xfs_dir3_block_buf_ops ) ;
2013-04-03 09:11:29 +04:00
if ( ! err & & tp )
2013-04-03 09:11:30 +04:00
xfs_trans_buf_set_type ( tp , * bpp , XFS_BLFT_DIR_BLOCK_BUF ) ;
2013-04-03 09:11:29 +04:00
return err ;
2013-04-03 09:11:20 +04:00
}
static void
xfs_dir3_block_init (
struct xfs_mount * mp ,
2013-04-03 09:11:29 +04:00
struct xfs_trans * tp ,
2013-04-03 09:11:20 +04:00
struct xfs_buf * bp ,
struct xfs_inode * dp )
{
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
bp - > b_ops = & xfs_dir3_block_buf_ops ;
2013-04-03 09:11:30 +04:00
xfs_trans_buf_set_type ( tp , bp , XFS_BLFT_DIR_BLOCK_BUF ) ;
2013-04-03 09:11:20 +04:00
if ( xfs_sb_version_hascrc ( & mp - > m_sb ) ) {
memset ( hdr3 , 0 , sizeof ( * hdr3 ) ) ;
hdr3 - > magic = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ;
hdr3 - > blkno = cpu_to_be64 ( bp - > b_bn ) ;
hdr3 - > owner = cpu_to_be64 ( dp - > i_ino ) ;
uuid_copy ( & hdr3 - > uuid , & mp - > m_sb . sb_uuid ) ;
return ;
}
hdr3 - > magic = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) ;
2012-11-12 15:54:11 +04:00
}
static void
xfs_dir2_block_need_space (
2013-10-29 15:11:49 +04:00
struct xfs_inode * dp ,
2012-11-12 15:54:11 +04:00
struct xfs_dir2_data_hdr * hdr ,
struct xfs_dir2_block_tail * btp ,
struct xfs_dir2_leaf_entry * blp ,
__be16 * * tagpp ,
struct xfs_dir2_data_unused * * dupp ,
struct xfs_dir2_data_unused * * enddupp ,
int * compact ,
int len )
{
struct xfs_dir2_data_free * bf ;
__be16 * tagp = NULL ;
struct xfs_dir2_data_unused * dup = NULL ;
struct xfs_dir2_data_unused * enddup = NULL ;
* compact = 0 ;
2013-10-29 15:11:49 +04:00
bf = dp - > d_ops - > data_bestfree_p ( hdr ) ;
2012-11-12 15:54:11 +04:00
/*
* If there are stale entries we ' ll use one for the leaf .
*/
if ( btp - > stale ) {
if ( be16_to_cpu ( bf [ 0 ] . length ) > = len ) {
/*
* The biggest entry enough to avoid compaction .
*/
dup = ( xfs_dir2_data_unused_t * )
( ( char * ) hdr + be16_to_cpu ( bf [ 0 ] . offset ) ) ;
goto out ;
}
/*
* Will need to compact to make this work .
* Tag just before the first leaf entry .
*/
* compact = 1 ;
tagp = ( __be16 * ) blp - 1 ;
/* Data object just before the first leaf entry. */
dup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + be16_to_cpu ( * tagp ) ) ;
/*
* If it ' s not free then the data will go where the
* leaf data starts now , if it works at all .
*/
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
if ( be16_to_cpu ( dup - > length ) + ( be32_to_cpu ( btp - > stale ) - 1 ) *
( uint ) sizeof ( * blp ) < len )
dup = NULL ;
} else if ( ( be32_to_cpu ( btp - > stale ) - 1 ) * ( uint ) sizeof ( * blp ) < len )
dup = NULL ;
else
dup = ( xfs_dir2_data_unused_t * ) blp ;
goto out ;
}
/*
* no stale entries , so just use free space .
* Tag just before the first leaf entry .
*/
tagp = ( __be16 * ) blp - 1 ;
/* Data object just before the first leaf entry. */
enddup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + be16_to_cpu ( * tagp ) ) ;
/*
* If it ' s not free then can ' t do this add without cleaning up :
* the space before the first leaf entry needs to be free so it
* can be expanded to hold the pointer to the new entry .
*/
if ( be16_to_cpu ( enddup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
/*
* Check out the biggest freespace and see if it ' s the same one .
*/
dup = ( xfs_dir2_data_unused_t * )
( ( char * ) hdr + be16_to_cpu ( bf [ 0 ] . offset ) ) ;
if ( dup ! = enddup ) {
/*
* Not the same free entry , just check its length .
*/
if ( be16_to_cpu ( dup - > length ) < len )
dup = NULL ;
goto out ;
}
/*
* It is the biggest freespace , can it hold the leaf too ?
*/
if ( be16_to_cpu ( dup - > length ) < len + ( uint ) sizeof ( * blp ) ) {
/*
* Yes , use the second - largest entry instead if it works .
*/
if ( be16_to_cpu ( bf [ 1 ] . length ) > = len )
dup = ( xfs_dir2_data_unused_t * )
( ( char * ) hdr + be16_to_cpu ( bf [ 1 ] . offset ) ) ;
else
dup = NULL ;
}
}
out :
* tagpp = tagp ;
* dupp = dup ;
* enddupp = enddup ;
}
/*
* compact the leaf entries .
* Leave the highest - numbered stale entry stale .
* XXX should be the one closest to mid but mid is not yet computed .
*/
static void
xfs_dir2_block_compact (
struct xfs_trans * tp ,
2013-10-29 15:11:48 +04:00
struct xfs_inode * dp ,
2012-11-12 15:54:11 +04:00
struct xfs_buf * bp ,
struct xfs_dir2_data_hdr * hdr ,
struct xfs_dir2_block_tail * btp ,
struct xfs_dir2_leaf_entry * blp ,
int * needlog ,
int * lfloghigh ,
int * lfloglow )
{
int fromidx ; /* source leaf index */
int toidx ; /* target leaf index */
int needscan = 0 ;
int highstale ; /* high stale index */
fromidx = toidx = be32_to_cpu ( btp - > count ) - 1 ;
highstale = * lfloghigh = - 1 ;
for ( ; fromidx > = 0 ; fromidx - - ) {
if ( blp [ fromidx ] . address = = cpu_to_be32 ( XFS_DIR2_NULL_DATAPTR ) ) {
if ( highstale = = - 1 )
highstale = toidx ;
else {
if ( * lfloghigh = = - 1 )
* lfloghigh = toidx ;
continue ;
}
}
if ( fromidx < toidx )
blp [ toidx ] = blp [ fromidx ] ;
toidx - - ;
}
* lfloglow = toidx + 1 - ( be32_to_cpu ( btp - > stale ) - 1 ) ;
* lfloghigh - = be32_to_cpu ( btp - > stale ) - 1 ;
be32_add_cpu ( & btp - > count , - ( be32_to_cpu ( btp - > stale ) - 1 ) ) ;
2013-10-29 15:11:49 +04:00
xfs_dir2_data_make_free ( tp , dp , bp ,
2012-11-12 15:54:11 +04:00
( xfs_dir2_data_aoff_t ) ( ( char * ) blp - ( char * ) hdr ) ,
( xfs_dir2_data_aoff_t ) ( ( be32_to_cpu ( btp - > stale ) - 1 ) * sizeof ( * blp ) ) ,
needlog , & needscan ) ;
blp + = be32_to_cpu ( btp - > stale ) - 1 ;
btp - > stale = cpu_to_be32 ( 1 ) ;
/*
* If we now need to rebuild the bestfree map , do so .
* This needs to happen before the next call to use_free .
*/
if ( needscan )
2013-10-29 15:11:48 +04:00
xfs_dir2_data_freescan ( dp , hdr , needlog ) ;
2012-11-12 15:54:11 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Add an entry to a block directory .
*/
int /* error */
xfs_dir2_block_addname (
xfs_da_args_t * args ) /* directory op arguments */
{
2011-07-08 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_entry_t * blp ; /* block leaf entries */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ; /* buffer for block */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail */
int compact ; /* need to compact leaf ents */
xfs_dir2_data_entry_t * dep ; /* block data entry */
xfs_inode_t * dp ; /* directory inode */
xfs_dir2_data_unused_t * dup ; /* block unused entry */
int error ; /* error return value */
xfs_dir2_data_unused_t * enddup = NULL ; /* unused at end of data */
xfs_dahash_t hash ; /* hash value of found entry */
int high ; /* high index for binary srch */
int highstale ; /* high stale index */
int lfloghigh = 0 ; /* last final leaf to log */
int lfloglow = 0 ; /* first final leaf to log */
int len ; /* length of the new entry */
int low ; /* low index for binary srch */
int lowstale ; /* low stale index */
int mid = 0 ; /* midpoint for binary srch */
xfs_mount_t * mp ; /* filesystem mount point */
int needlog ; /* need to log header */
int needscan ; /* need to rescan freespace */
2006-03-17 09:28:27 +03:00
__be16 * tagp ; /* pointer to tag value */
2005-04-17 02:20:36 +04:00
xfs_trans_t * tp ; /* transaction structure */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_block_addname ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
2012-11-12 15:54:11 +04:00
/* Read the (one and only) directory block into bp. */
2013-04-03 09:11:20 +04:00
error = xfs_dir3_block_read ( tp , dp , & bp ) ;
2012-11-12 15:54:10 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
2012-11-12 15:54:11 +04:00
2013-10-29 15:11:48 +04:00
len = dp - > d_ops - > data_entsize ( args - > namelen ) ;
2012-11-12 15:54:11 +04:00
2005-04-17 02:20:36 +04:00
/*
* Set up pointers to parts of the block .
*/
2012-11-12 15:54:11 +04:00
hdr = bp - > b_addr ;
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2012-11-12 15:54:11 +04:00
2005-04-17 02:20:36 +04:00
/*
2012-11-12 15:54:11 +04:00
* Find out if we can reuse stale entries or whether we need extra
* space for entry and new leaf .
2005-04-17 02:20:36 +04:00
*/
2013-10-29 15:11:49 +04:00
xfs_dir2_block_need_space ( dp , hdr , btp , blp , & tagp , & dup ,
2012-11-12 15:54:11 +04:00
& enddup , & compact , len ) ;
2005-04-17 02:20:36 +04:00
/*
2012-11-12 15:54:11 +04:00
* Done everything we need for a space check now .
2005-04-17 02:20:36 +04:00
*/
2012-11-12 15:54:11 +04:00
if ( args - > op_flags & XFS_DA_OP_JUSTCHECK ) {
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( tp , bp ) ;
2012-11-12 15:54:11 +04:00
if ( ! dup )
return XFS_ERROR ( ENOSPC ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* If we don ' t have space for the new entry & leaf . . .
*/
if ( ! dup ) {
2012-11-12 15:54:11 +04:00
/* Don't have a space reservation: return no-space. */
if ( args - > total = = 0 )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( ENOSPC ) ;
/*
* Convert to the next larger format .
* Then add the new entry in that format .
*/
error = xfs_dir2_block_to_leaf ( args , bp ) ;
if ( error )
return error ;
return xfs_dir2_leaf_addname ( args ) ;
}
2012-11-12 15:54:11 +04:00
2005-04-17 02:20:36 +04:00
needlog = needscan = 0 ;
2012-11-12 15:54:11 +04:00
2005-04-17 02:20:36 +04:00
/*
* If need to compact the leaf entries , do it now .
*/
2013-01-10 20:41:48 +04:00
if ( compact ) {
2013-10-29 15:11:48 +04:00
xfs_dir2_block_compact ( tp , dp , bp , hdr , btp , blp , & needlog ,
2012-11-12 15:54:11 +04:00
& lfloghigh , & lfloglow ) ;
2013-01-10 20:41:48 +04:00
/* recalculate blp post-compaction */
blp = xfs_dir2_block_leaf_p ( btp ) ;
} else if ( btp - > stale ) {
2005-04-17 02:20:36 +04:00
/*
2012-11-12 15:54:11 +04:00
* Set leaf logging boundaries to impossible state .
* For the no - stale case they ' re set explicitly .
2005-04-17 02:20:36 +04:00
*/
2006-03-17 09:27:56 +03:00
lfloglow = be32_to_cpu ( btp - > count ) ;
2005-04-17 02:20:36 +04:00
lfloghigh = - 1 ;
}
2012-11-12 15:54:11 +04:00
2005-04-17 02:20:36 +04:00
/*
* Find the slot that ' s first lower than our hash value , - 1 if none .
*/
2006-03-17 09:27:56 +03:00
for ( low = 0 , high = be32_to_cpu ( btp - > count ) - 1 ; low < = high ; ) {
2005-04-17 02:20:36 +04:00
mid = ( low + high ) > > 1 ;
2006-03-17 09:28:18 +03:00
if ( ( hash = be32_to_cpu ( blp [ mid ] . hashval ) ) = = args - > hashval )
2005-04-17 02:20:36 +04:00
break ;
if ( hash < args - > hashval )
low = mid + 1 ;
else
high = mid - 1 ;
}
2006-03-17 09:28:18 +03:00
while ( mid > = 0 & & be32_to_cpu ( blp [ mid ] . hashval ) > = args - > hashval ) {
2005-04-17 02:20:36 +04:00
mid - - ;
}
/*
* No stale entries , will use enddup space to hold new leaf .
*/
if ( ! btp - > stale ) {
/*
* Mark the space needed for the new leaf entry , now in use .
*/
2013-10-29 15:11:49 +04:00
xfs_dir2_data_use_free ( tp , dp , bp , enddup ,
2005-04-17 02:20:36 +04:00
( xfs_dir2_data_aoff_t )
2011-07-08 16:35:27 +04:00
( ( char * ) enddup - ( char * ) hdr + be16_to_cpu ( enddup - > length ) -
2005-04-17 02:20:36 +04:00
sizeof ( * blp ) ) ,
( xfs_dir2_data_aoff_t ) sizeof ( * blp ) ,
& needlog , & needscan ) ;
/*
* Update the tail ( entry count ) .
*/
2008-02-14 02:03:29 +03:00
be32_add_cpu ( & btp - > count , 1 ) ;
2005-04-17 02:20:36 +04:00
/*
* If we now need to rebuild the bestfree map , do so .
* This needs to happen before the next call to use_free .
*/
if ( needscan ) {
2013-10-29 15:11:48 +04:00
xfs_dir2_data_freescan ( dp , hdr , & needlog ) ;
2005-04-17 02:20:36 +04:00
needscan = 0 ;
}
/*
* Adjust pointer to the first leaf entry , we ' re about to move
* the table up one to open up space for the new leaf entry .
* Then adjust our index to match .
*/
blp - - ;
mid + + ;
if ( mid )
memmove ( blp , & blp [ 1 ] , mid * sizeof ( * blp ) ) ;
lfloglow = 0 ;
lfloghigh = mid ;
}
/*
* Use a stale leaf for our new entry .
*/
else {
for ( lowstale = mid ;
lowstale > = 0 & &
2011-07-08 16:36:05 +04:00
blp [ lowstale ] . address ! =
cpu_to_be32 ( XFS_DIR2_NULL_DATAPTR ) ;
2005-04-17 02:20:36 +04:00
lowstale - - )
continue ;
for ( highstale = mid + 1 ;
2006-03-17 09:27:56 +03:00
highstale < be32_to_cpu ( btp - > count ) & &
2011-07-08 16:36:05 +04:00
blp [ highstale ] . address ! =
cpu_to_be32 ( XFS_DIR2_NULL_DATAPTR ) & &
2005-04-17 02:20:36 +04:00
( lowstale < 0 | | mid - lowstale > highstale - mid ) ;
highstale + + )
continue ;
/*
* Move entries toward the low - numbered stale entry .
*/
if ( lowstale > = 0 & &
2006-03-17 09:27:56 +03:00
( highstale = = be32_to_cpu ( btp - > count ) | |
2005-04-17 02:20:36 +04:00
mid - lowstale < = highstale - mid ) ) {
if ( mid - lowstale )
memmove ( & blp [ lowstale ] , & blp [ lowstale + 1 ] ,
( mid - lowstale ) * sizeof ( * blp ) ) ;
lfloglow = MIN ( lowstale , lfloglow ) ;
lfloghigh = MAX ( mid , lfloghigh ) ;
}
/*
* Move entries toward the high - numbered stale entry .
*/
else {
2006-03-17 09:27:56 +03:00
ASSERT ( highstale < be32_to_cpu ( btp - > count ) ) ;
2005-04-17 02:20:36 +04:00
mid + + ;
if ( highstale - mid )
memmove ( & blp [ mid + 1 ] , & blp [ mid ] ,
( highstale - mid ) * sizeof ( * blp ) ) ;
lfloglow = MIN ( mid , lfloglow ) ;
lfloghigh = MAX ( highstale , lfloghigh ) ;
}
2008-02-14 02:03:29 +03:00
be32_add_cpu ( & btp - > stale , - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Point to the new data entry .
*/
dep = ( xfs_dir2_data_entry_t * ) dup ;
/*
* Fill in the leaf entry .
*/
2006-03-17 09:28:18 +03:00
blp [ mid ] . hashval = cpu_to_be32 ( args - > hashval ) ;
2007-06-28 10:43:50 +04:00
blp [ mid ] . address = cpu_to_be32 ( xfs_dir2_byte_to_dataptr ( mp ,
2011-07-08 16:35:27 +04:00
( char * ) dep - ( char * ) hdr ) ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_block_log_leaf ( tp , bp , lfloglow , lfloghigh ) ;
/*
* Mark space for the data entry used .
*/
2013-10-29 15:11:49 +04:00
xfs_dir2_data_use_free ( tp , dp , bp , dup ,
2011-07-08 16:35:27 +04:00
( xfs_dir2_data_aoff_t ) ( ( char * ) dup - ( char * ) hdr ) ,
2005-04-17 02:20:36 +04:00
( xfs_dir2_data_aoff_t ) len , & needlog , & needscan ) ;
/*
* Create the new data entry .
*/
2006-06-09 08:48:37 +04:00
dep - > inumber = cpu_to_be64 ( args - > inumber ) ;
2005-04-17 02:20:36 +04:00
dep - > namelen = args - > namelen ;
memcpy ( dep - > name , args - > name , args - > namelen ) ;
2013-10-29 15:11:48 +04:00
dp - > d_ops - > data_put_ftype ( dep , args - > filetype ) ;
tagp = dp - > d_ops - > data_entry_tag_p ( dep ) ;
2011-07-08 16:35:27 +04:00
* tagp = cpu_to_be16 ( ( char * ) dep - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
/*
* Clean up the bestfree array and log the header , tail , and entry .
*/
if ( needscan )
2013-10-29 15:11:48 +04:00
xfs_dir2_data_freescan ( dp , hdr , & needlog ) ;
2005-04-17 02:20:36 +04:00
if ( needlog )
2013-10-29 15:11:49 +04:00
xfs_dir2_data_log_header ( tp , dp , bp ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_block_log_tail ( tp , bp ) ;
2013-10-29 15:11:48 +04:00
xfs_dir2_data_log_entry ( tp , dp , bp , dep ) ;
2013-04-03 09:11:22 +04:00
xfs_dir3_data_check ( dp , bp ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Log leaf entries from the block .
*/
static void
xfs_dir2_block_log_leaf (
xfs_trans_t * tp , /* transaction structure */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp , /* block buffer */
2005-04-17 02:20:36 +04:00
int first , /* index of first logged leaf */
int last ) /* index of last logged leaf */
{
2012-06-22 12:50:14 +04:00
xfs_dir2_data_hdr_t * hdr = bp - > b_addr ;
2011-07-08 16:35:27 +04:00
xfs_dir2_leaf_entry_t * blp ;
xfs_dir2_block_tail_t * btp ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( tp - > t_mountp , hdr ) ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2012-06-22 12:50:14 +04:00
xfs_trans_log_buf ( tp , bp , ( uint ) ( ( char * ) & blp [ first ] - ( char * ) hdr ) ,
2011-07-08 16:35:27 +04:00
( uint ) ( ( char * ) & blp [ last + 1 ] - ( char * ) hdr - 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Log the block tail .
*/
static void
xfs_dir2_block_log_tail (
xfs_trans_t * tp , /* transaction structure */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ) /* block buffer */
2005-04-17 02:20:36 +04:00
{
2012-06-22 12:50:14 +04:00
xfs_dir2_data_hdr_t * hdr = bp - > b_addr ;
2011-07-08 16:35:27 +04:00
xfs_dir2_block_tail_t * btp ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( tp - > t_mountp , hdr ) ;
2012-06-22 12:50:14 +04:00
xfs_trans_log_buf ( tp , bp , ( uint ) ( ( char * ) btp - ( char * ) hdr ) ,
2011-07-08 16:35:27 +04:00
( uint ) ( ( char * ) ( btp + 1 ) - ( char * ) hdr - 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Look up an entry in the block . This is the external routine ,
* xfs_dir2_block_lookup_int does the real work .
*/
int /* error */
xfs_dir2_block_lookup (
xfs_da_args_t * args ) /* dir lookup arguments */
{
2011-07-08 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_entry_t * blp ; /* block leaf entries */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ; /* block buffer */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail */
xfs_dir2_data_entry_t * dep ; /* block data entry */
xfs_inode_t * dp ; /* incore inode */
int ent ; /* entry index */
int error ; /* error return value */
xfs_mount_t * mp ; /* filesystem mount point */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_block_lookup ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Get the buffer , look up the entry .
* If not found ( ENOENT ) then return , have no buffer .
*/
if ( ( error = xfs_dir2_block_lookup_int ( args , & bp , & ent ) ) )
return error ;
dp = args - > dp ;
mp = dp - > i_mount ;
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2013-04-03 09:11:22 +04:00
xfs_dir3_data_check ( dp , bp ) ;
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
/*
* Get the offset from the leaf entry , to point to the data .
*/
2011-07-08 16:35:27 +04:00
dep = ( xfs_dir2_data_entry_t * ) ( ( char * ) hdr +
2008-05-21 10:58:22 +04:00
xfs_dir2_dataptr_to_off ( mp , be32_to_cpu ( blp [ ent ] . address ) ) ) ;
2005-04-17 02:20:36 +04:00
/*
2008-05-21 10:58:22 +04:00
* Fill in inode number , CI name if appropriate , release the block .
2005-04-17 02:20:36 +04:00
*/
2006-06-09 08:48:37 +04:00
args - > inumber = be64_to_cpu ( dep - > inumber ) ;
2013-10-29 15:11:48 +04:00
args - > filetype = dp - > d_ops - > data_get_ftype ( dep ) ;
2008-05-21 10:58:22 +04:00
error = xfs_dir_cilookup_result ( args , dep - > name , dep - > namelen ) ;
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( args - > trans , bp ) ;
2008-05-21 10:58:22 +04:00
return XFS_ERROR ( error ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Internal block lookup routine .
*/
static int /* error */
xfs_dir2_block_lookup_int (
xfs_da_args_t * args , /* dir lookup arguments */
2012-06-22 12:50:14 +04:00
struct xfs_buf * * bpp , /* returned block buffer */
2005-04-17 02:20:36 +04:00
int * entno ) /* returned entry number */
{
xfs_dir2_dataptr_t addr ; /* data entry address */
2011-07-08 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_entry_t * blp ; /* block leaf entries */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ; /* block buffer */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail */
xfs_dir2_data_entry_t * dep ; /* block data entry */
xfs_inode_t * dp ; /* incore inode */
int error ; /* error return value */
xfs_dahash_t hash ; /* found hash value */
int high ; /* binary search high index */
int low ; /* binary search low index */
int mid ; /* binary search current idx */
xfs_mount_t * mp ; /* filesystem mount point */
xfs_trans_t * tp ; /* transaction pointer */
2008-05-21 10:41:01 +04:00
enum xfs_dacmp cmp ; /* comparison result */
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
2012-11-12 15:54:11 +04:00
2013-04-03 09:11:20 +04:00
error = xfs_dir3_block_read ( tp , dp , & bp ) ;
2012-11-12 15:54:10 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
2012-11-12 15:54:11 +04:00
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2013-04-03 09:11:22 +04:00
xfs_dir3_data_check ( dp , bp ) ;
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
/*
* Loop doing a binary search for our hash value .
* Find our entry , ENOENT if it ' s not there .
*/
2006-03-17 09:27:56 +03:00
for ( low = 0 , high = be32_to_cpu ( btp - > count ) - 1 ; ; ) {
2005-04-17 02:20:36 +04:00
ASSERT ( low < = high ) ;
mid = ( low + high ) > > 1 ;
2006-03-17 09:28:18 +03:00
if ( ( hash = be32_to_cpu ( blp [ mid ] . hashval ) ) = = args - > hashval )
2005-04-17 02:20:36 +04:00
break ;
if ( hash < args - > hashval )
low = mid + 1 ;
else
high = mid - 1 ;
if ( low > high ) {
2008-05-21 10:42:05 +04:00
ASSERT ( args - > op_flags & XFS_DA_OP_OKNOENT ) ;
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( tp , bp ) ;
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( ENOENT ) ;
}
}
/*
* Back up to the first one with the right hash value .
*/
2006-03-17 09:28:18 +03:00
while ( mid > 0 & & be32_to_cpu ( blp [ mid - 1 ] . hashval ) = = args - > hashval ) {
2005-04-17 02:20:36 +04:00
mid - - ;
}
/*
* Now loop forward through all the entries with the
* right hash value looking for our name .
*/
do {
2006-03-17 09:28:18 +03:00
if ( ( addr = be32_to_cpu ( blp [ mid ] . address ) ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-17 02:20:36 +04:00
continue ;
/*
* Get pointer to the entry from the leaf .
*/
dep = ( xfs_dir2_data_entry_t * )
2011-07-08 16:35:27 +04:00
( ( char * ) hdr + xfs_dir2_dataptr_to_off ( mp , addr ) ) ;
2005-04-17 02:20:36 +04:00
/*
2008-05-21 10:41:01 +04:00
* Compare name and if it ' s an exact match , return the index
* and buffer . If it ' s the first case - insensitive match , store
* the index and buffer and continue looking for an exact match .
2005-04-17 02:20:36 +04:00
*/
2008-05-21 10:41:01 +04:00
cmp = mp - > m_dirnameops - > compname ( args , dep - > name , dep - > namelen ) ;
if ( cmp ! = XFS_CMP_DIFFERENT & & cmp ! = args - > cmpresult ) {
args - > cmpresult = cmp ;
2005-04-17 02:20:36 +04:00
* bpp = bp ;
* entno = mid ;
2008-05-21 10:41:01 +04:00
if ( cmp = = XFS_CMP_EXACT )
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-05-21 10:41:01 +04:00
} while ( + + mid < be32_to_cpu ( btp - > count ) & &
be32_to_cpu ( blp [ mid ] . hashval ) = = hash ) ;
2008-05-21 10:42:05 +04:00
ASSERT ( args - > op_flags & XFS_DA_OP_OKNOENT ) ;
2008-05-21 10:41:01 +04:00
/*
* Here , we can only be doing a lookup ( not a rename or replace ) .
* If a case - insensitive match was found earlier , return success .
*/
if ( args - > cmpresult = = XFS_CMP_CASE )
return 0 ;
2005-04-17 02:20:36 +04:00
/*
* No match , release the buffer and return ENOENT .
*/
2012-06-22 12:50:14 +04:00
xfs_trans_brelse ( tp , bp ) ;
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( ENOENT ) ;
}
/*
* Remove an entry from a block format directory .
* If that makes the block small enough to fit in shortform , transform it .
*/
int /* error */
xfs_dir2_block_removename (
xfs_da_args_t * args ) /* directory operation args */
{
2011-07-08 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_entry_t * blp ; /* block leaf pointer */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ; /* block buffer */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail */
xfs_dir2_data_entry_t * dep ; /* block data entry */
xfs_inode_t * dp ; /* incore inode */
int ent ; /* block leaf entry index */
int error ; /* error return value */
xfs_mount_t * mp ; /* filesystem mount point */
int needlog ; /* need to log block header */
int needscan ; /* need to fixup bestfree */
xfs_dir2_sf_hdr_t sfh ; /* shortform header */
int size ; /* shortform size */
xfs_trans_t * tp ; /* transaction pointer */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_block_removename ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Look up the entry in the block . Gets the buffer and entry index .
* It will always be there , the vnodeops level does a lookup first .
*/
if ( ( error = xfs_dir2_block_lookup_int ( args , & bp , & ent ) ) ) {
return error ;
}
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
/*
* Point to the data entry using the leaf entry .
*/
dep = ( xfs_dir2_data_entry_t * )
2011-07-08 16:35:27 +04:00
( ( char * ) hdr + xfs_dir2_dataptr_to_off ( mp , be32_to_cpu ( blp [ ent ] . address ) ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Mark the data entry ' s space free .
*/
needlog = needscan = 0 ;
2013-10-29 15:11:49 +04:00
xfs_dir2_data_make_free ( tp , dp , bp ,
2011-07-08 16:35:27 +04:00
( xfs_dir2_data_aoff_t ) ( ( char * ) dep - ( char * ) hdr ) ,
2013-10-29 15:11:48 +04:00
dp - > d_ops - > data_entsize ( dep - > namelen ) , & needlog , & needscan ) ;
2005-04-17 02:20:36 +04:00
/*
* Fix up the block tail .
*/
2008-02-14 02:03:29 +03:00
be32_add_cpu ( & btp - > stale , 1 ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_block_log_tail ( tp , bp ) ;
/*
* Remove the leaf entry by marking it stale .
*/
2006-03-17 09:28:18 +03:00
blp [ ent ] . address = cpu_to_be32 ( XFS_DIR2_NULL_DATAPTR ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_block_log_leaf ( tp , bp , ent , ent ) ;
/*
* Fix up bestfree , log the header if necessary .
*/
if ( needscan )
2013-10-29 15:11:48 +04:00
xfs_dir2_data_freescan ( dp , hdr , & needlog ) ;
2005-04-17 02:20:36 +04:00
if ( needlog )
2013-10-29 15:11:49 +04:00
xfs_dir2_data_log_header ( tp , dp , bp ) ;
2013-04-03 09:11:22 +04:00
xfs_dir3_data_check ( dp , bp ) ;
2005-04-17 02:20:36 +04:00
/*
* See if the size as a shortform is good enough .
*/
2011-07-08 16:35:27 +04:00
size = xfs_dir2_block_sfsize ( dp , hdr , & sfh ) ;
2012-06-22 12:50:14 +04:00
if ( size > XFS_IFORK_DSIZE ( dp ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2012-06-22 12:50:14 +04:00
2005-04-17 02:20:36 +04:00
/*
* If it works , do the conversion .
*/
return xfs_dir2_block_to_sf ( args , bp , size , & sfh ) ;
}
/*
* Replace an entry in a V2 block directory .
* Change the inode number to the new value .
*/
int /* error */
xfs_dir2_block_replace (
xfs_da_args_t * args ) /* directory operation args */
{
2011-07-08 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_entry_t * blp ; /* block leaf entries */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ; /* block buffer */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail */
xfs_dir2_data_entry_t * dep ; /* block data entry */
xfs_inode_t * dp ; /* incore inode */
int ent ; /* leaf entry index */
int error ; /* error return value */
xfs_mount_t * mp ; /* filesystem mount point */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_block_replace ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Lookup the entry in the directory . Get buffer and entry index .
* This will always succeed since the caller has already done a lookup .
*/
if ( ( error = xfs_dir2_block_lookup_int ( args , & bp , & ent ) ) ) {
return error ;
}
dp = args - > dp ;
mp = dp - > i_mount ;
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
/*
* Point to the data entry we need to change .
*/
dep = ( xfs_dir2_data_entry_t * )
2011-07-08 16:35:27 +04:00
( ( char * ) hdr + xfs_dir2_dataptr_to_off ( mp , be32_to_cpu ( blp [ ent ] . address ) ) ) ;
2006-06-09 08:48:37 +04:00
ASSERT ( be64_to_cpu ( dep - > inumber ) ! = args - > inumber ) ;
2005-04-17 02:20:36 +04:00
/*
* Change the inode number to the new value .
*/
2006-06-09 08:48:37 +04:00
dep - > inumber = cpu_to_be64 ( args - > inumber ) ;
2013-10-29 15:11:48 +04:00
dp - > d_ops - > data_put_ftype ( dep , args - > filetype ) ;
xfs_dir2_data_log_entry ( args - > trans , dp , bp , dep ) ;
2013-04-03 09:11:22 +04:00
xfs_dir3_data_check ( dp , bp ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Qsort comparison routine for the block leaf entries .
*/
static int /* sort order */
xfs_dir2_block_sort (
const void * a , /* first leaf entry */
const void * b ) /* second leaf entry */
{
const xfs_dir2_leaf_entry_t * la ; /* first leaf entry */
const xfs_dir2_leaf_entry_t * lb ; /* second leaf entry */
la = a ;
lb = b ;
2006-03-17 09:28:18 +03:00
return be32_to_cpu ( la - > hashval ) < be32_to_cpu ( lb - > hashval ) ? - 1 :
( be32_to_cpu ( la - > hashval ) > be32_to_cpu ( lb - > hashval ) ? 1 : 0 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Convert a V2 leaf directory to a V2 block directory if possible .
*/
int /* error */
xfs_dir2_leaf_to_block (
xfs_da_args_t * args , /* operation arguments */
2012-06-22 12:50:14 +04:00
struct xfs_buf * lbp , /* leaf buffer */
struct xfs_buf * dbp ) /* data buffer */
2005-04-17 02:20:36 +04:00
{
2006-03-17 09:27:19 +03:00
__be16 * bestsp ; /* leaf bests table */
2011-07-08 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail */
xfs_inode_t * dp ; /* incore directory inode */
xfs_dir2_data_unused_t * dup ; /* unused data entry */
int error ; /* error return value */
int from ; /* leaf from index */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_entry_t * lep ; /* leaf entry */
xfs_dir2_leaf_tail_t * ltp ; /* leaf tail structure */
xfs_mount_t * mp ; /* file system mount point */
int needlog ; /* need to log data header */
int needscan ; /* need to scan for bestfree */
xfs_dir2_sf_hdr_t sfh ; /* shortform header */
int size ; /* bytes used */
2006-03-17 09:28:27 +03:00
__be16 * tagp ; /* end of entry (tag) */
2005-04-17 02:20:36 +04:00
int to ; /* block/leaf to index */
xfs_trans_t * tp ; /* transaction pointer */
2013-04-12 01:30:21 +04:00
struct xfs_dir2_leaf_entry * ents ;
struct xfs_dir3_icleaf_hdr leafhdr ;
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_leaf_to_block ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
2012-06-22 12:50:14 +04:00
leaf = lbp - > b_addr ;
2013-10-29 15:11:52 +04:00
dp - > d_ops - > leaf_hdr_from_disk ( & leafhdr , leaf ) ;
2013-10-29 15:11:50 +04:00
ents = dp - > d_ops - > leaf_ents_p ( leaf ) ;
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
2013-04-12 01:30:21 +04:00
ASSERT ( leafhdr . magic = = XFS_DIR2_LEAF1_MAGIC | |
leafhdr . magic = = XFS_DIR3_LEAF1_MAGIC ) ;
2005-04-17 02:20:36 +04:00
/*
* If there are data blocks other than the first one , take this
* opportunity to remove trailing empty data blocks that may have
* been left behind during no - space - reservation operations .
* These will show up in the leaf bests table .
*/
while ( dp - > i_d . di_size > mp - > m_dirblksize ) {
2013-04-03 09:11:20 +04:00
int hdrsz ;
2013-10-30 02:15:02 +04:00
hdrsz = dp - > d_ops - > data_entry_offset ;
2007-06-28 10:43:50 +04:00
bestsp = xfs_dir2_leaf_bests_p ( ltp ) ;
2006-03-17 09:27:28 +03:00
if ( be16_to_cpu ( bestsp [ be32_to_cpu ( ltp - > bestcount ) - 1 ] ) = =
2013-04-03 09:11:20 +04:00
mp - > m_dirblksize - hdrsz ) {
2005-04-17 02:20:36 +04:00
if ( ( error =
xfs_dir2_leaf_trim_data ( args , lbp ,
2006-03-17 09:27:28 +03:00
( xfs_dir2_db_t ) ( be32_to_cpu ( ltp - > bestcount ) - 1 ) ) ) )
2012-06-22 12:50:14 +04:00
return error ;
} else
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Read the data block if we don ' t already have it , give up if it fails .
*/
2012-11-12 15:54:10 +04:00
if ( ! dbp ) {
2013-04-03 09:11:22 +04:00
error = xfs_dir3_data_read ( tp , dp , mp - > m_dirdatablk , - 1 , & dbp ) ;
2012-11-12 15:54:10 +04:00
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
}
2012-06-22 12:50:14 +04:00
hdr = dbp - > b_addr ;
2013-04-03 09:11:22 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Size of the " leaf " area in the block .
*/
2011-07-08 16:35:27 +04:00
size = ( uint ) sizeof ( xfs_dir2_block_tail_t ) +
2013-04-12 01:30:21 +04:00
( uint ) sizeof ( * lep ) * ( leafhdr . count - leafhdr . stale ) ;
2005-04-17 02:20:36 +04:00
/*
* Look at the last data entry .
*/
2011-07-08 16:35:27 +04:00
tagp = ( __be16 * ) ( ( char * ) hdr + mp - > m_dirblksize ) - 1 ;
dup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + be16_to_cpu ( * tagp ) ) ;
2005-04-17 02:20:36 +04:00
/*
* If it ' s not free or is too short we can ' t do it .
*/
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( dup - > freetag ) ! = XFS_DIR2_DATA_FREE_TAG | |
2012-06-22 12:50:14 +04:00
be16_to_cpu ( dup - > length ) < size )
return 0 ;
2005-04-17 02:20:36 +04:00
/*
* Start converting it to block form .
*/
2013-04-03 09:11:29 +04:00
xfs_dir3_block_init ( mp , tp , dbp , dp ) ;
2013-04-03 09:11:20 +04:00
2005-04-17 02:20:36 +04:00
needlog = 1 ;
needscan = 0 ;
/*
* Use up the space at the end of the block ( blp / btp ) .
*/
2013-10-29 15:11:49 +04:00
xfs_dir2_data_use_free ( tp , dp , dbp , dup , mp - > m_dirblksize - size , size ,
2005-04-17 02:20:36 +04:00
& needlog , & needscan ) ;
/*
* Initialize the block tail .
*/
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2013-04-12 01:30:21 +04:00
btp - > count = cpu_to_be32 ( leafhdr . count - leafhdr . stale ) ;
2005-04-17 02:20:36 +04:00
btp - > stale = 0 ;
xfs_dir2_block_log_tail ( tp , dbp ) ;
/*
* Initialize the block leaf area . We compact out stale entries .
*/
2007-06-28 10:43:50 +04:00
lep = xfs_dir2_block_leaf_p ( btp ) ;
2013-04-12 01:30:21 +04:00
for ( from = to = 0 ; from < leafhdr . count ; from + + ) {
if ( ents [ from ] . address = = cpu_to_be32 ( XFS_DIR2_NULL_DATAPTR ) )
2005-04-17 02:20:36 +04:00
continue ;
2013-04-12 01:30:21 +04:00
lep [ to + + ] = ents [ from ] ;
2005-04-17 02:20:36 +04:00
}
2006-03-17 09:27:56 +03:00
ASSERT ( to = = be32_to_cpu ( btp - > count ) ) ;
xfs_dir2_block_log_leaf ( tp , dbp , 0 , be32_to_cpu ( btp - > count ) - 1 ) ;
2005-04-17 02:20:36 +04:00
/*
* Scan the bestfree if we need it and log the data block header .
*/
if ( needscan )
2013-10-29 15:11:48 +04:00
xfs_dir2_data_freescan ( dp , hdr , & needlog ) ;
2005-04-17 02:20:36 +04:00
if ( needlog )
2013-10-29 15:11:49 +04:00
xfs_dir2_data_log_header ( tp , dp , dbp ) ;
2005-04-17 02:20:36 +04:00
/*
* Pitch the old leaf block .
*/
error = xfs_da_shrink_inode ( args , mp - > m_dirleafblk , lbp ) ;
2012-06-22 12:50:14 +04:00
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
/*
* Now see if the resulting block can be shrunken to shortform .
*/
2011-07-08 16:35:27 +04:00
size = xfs_dir2_block_sfsize ( dp , hdr , & sfh ) ;
2012-06-22 12:50:14 +04:00
if ( size > XFS_IFORK_DSIZE ( dp ) )
return 0 ;
2005-04-17 02:20:36 +04:00
return xfs_dir2_block_to_sf ( args , dbp , size , & sfh ) ;
}
/*
* Convert the shortform directory to block form .
*/
int /* error */
xfs_dir2_sf_to_block (
xfs_da_args_t * args ) /* operation arguments */
{
xfs_dir2_db_t blkno ; /* dir-relative block # (0) */
2011-07-08 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_entry_t * blp ; /* block leaf entries */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ; /* block buffer */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail pointer */
xfs_dir2_data_entry_t * dep ; /* data entry pointer */
xfs_inode_t * dp ; /* incore directory inode */
int dummy ; /* trash */
xfs_dir2_data_unused_t * dup ; /* unused entry pointer */
int endoffset ; /* end of data objects */
int error ; /* error return value */
int i ; /* index */
xfs_mount_t * mp ; /* filesystem mount point */
int needlog ; /* need to log block header */
int needscan ; /* need to scan block freespc */
int newoffset ; /* offset from current entry */
int offset ; /* target block offset */
xfs_dir2_sf_entry_t * sfep ; /* sf entry pointer */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * oldsfp ; /* old shortform header */
xfs_dir2_sf_hdr_t * sfp ; /* shortform header */
2006-03-17 09:28:27 +03:00
__be16 * tagp ; /* end of data entry */
2005-04-17 02:20:36 +04:00
xfs_trans_t * tp ; /* transaction pointer */
2008-05-21 10:41:01 +04:00
struct xfs_name name ;
2013-07-10 01:04:00 +04:00
struct xfs_ifork * ifp ;
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_to_block ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
2013-07-10 01:04:00 +04:00
ifp = XFS_IFORK_PTR ( dp , XFS_DATA_FORK ) ;
ASSERT ( ifp - > if_flags & XFS_IFINLINE ) ;
2005-04-17 02:20:36 +04:00
/*
* Bomb out if the shortform directory is way too short .
*/
if ( dp - > i_d . di_size < offsetof ( xfs_dir2_sf_hdr_t , parent ) ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( mp ) ) ;
return XFS_ERROR ( EIO ) ;
}
2011-07-08 16:35:13 +04:00
2013-07-10 01:04:00 +04:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) ifp - > if_u1 . if_data ;
2011-07-08 16:35:13 +04:00
2013-07-10 01:04:00 +04:00
ASSERT ( ifp - > if_bytes = = dp - > i_d . di_size ) ;
ASSERT ( ifp - > if_u1 . if_data ! = NULL ) ;
2011-07-08 16:35:13 +04:00
ASSERT ( dp - > i_d . di_size > = xfs_dir2_sf_hdr_size ( oldsfp - > i8count ) ) ;
2013-07-10 01:04:00 +04:00
ASSERT ( dp - > i_d . di_nextents = = 0 ) ;
2011-07-08 16:35:13 +04:00
2005-04-17 02:20:36 +04:00
/*
2011-07-08 16:35:13 +04:00
* Copy the directory into a temporary buffer .
2005-04-17 02:20:36 +04:00
* Then pitch the incore inode data so we can make extents .
*/
2013-07-10 01:04:00 +04:00
sfp = kmem_alloc ( ifp - > if_bytes , KM_SLEEP ) ;
memcpy ( sfp , oldsfp , ifp - > if_bytes ) ;
2005-04-17 02:20:36 +04:00
2013-07-10 01:04:00 +04:00
xfs_idata_realloc ( dp , - ifp - > if_bytes , XFS_DATA_FORK ) ;
xfs_bmap_local_to_extents_empty ( dp , XFS_DATA_FORK ) ;
2005-04-17 02:20:36 +04:00
dp - > i_d . di_size = 0 ;
2011-07-08 16:35:13 +04:00
2005-04-17 02:20:36 +04:00
/*
* Add block 0 to the inode .
*/
error = xfs_dir2_grow_inode ( args , XFS_DIR2_DATA_SPACE , & blkno ) ;
if ( error ) {
2011-07-08 16:35:13 +04:00
kmem_free ( sfp ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/*
2013-04-03 09:11:20 +04:00
* Initialize the data block , then convert it to block format .
2005-04-17 02:20:36 +04:00
*/
2013-04-03 09:11:20 +04:00
error = xfs_dir3_data_init ( args , blkno , & bp ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
2011-07-08 16:35:13 +04:00
kmem_free ( sfp ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2013-04-03 09:11:29 +04:00
xfs_dir3_block_init ( mp , tp , bp , dp ) ;
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2013-04-03 09:11:20 +04:00
2005-04-17 02:20:36 +04:00
/*
* Compute size of block " tail " area .
*/
i = ( uint ) sizeof ( * btp ) +
2011-07-08 16:35:13 +04:00
( sfp - > count + 2 ) * ( uint ) sizeof ( xfs_dir2_leaf_entry_t ) ;
2005-04-17 02:20:36 +04:00
/*
* The whole thing is initialized to free by the init routine .
* Say we ' re using the leaf and tail area .
*/
2013-10-29 15:11:49 +04:00
dup = dp - > d_ops - > data_unused_p ( hdr ) ;
2005-04-17 02:20:36 +04:00
needlog = needscan = 0 ;
2013-10-29 15:11:49 +04:00
xfs_dir2_data_use_free ( tp , dp , bp , dup , mp - > m_dirblksize - i , i , & needlog ,
2005-04-17 02:20:36 +04:00
& needscan ) ;
ASSERT ( needscan = = 0 ) ;
/*
* Fill in the tail .
*/
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2011-07-08 16:35:13 +04:00
btp - > count = cpu_to_be32 ( sfp - > count + 2 ) ; /* ., .. */
2005-04-17 02:20:36 +04:00
btp - > stale = 0 ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2011-07-08 16:35:27 +04:00
endoffset = ( uint ) ( ( char * ) blp - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
/*
* Remove the freespace , we ' ll manage it .
*/
2013-10-29 15:11:49 +04:00
xfs_dir2_data_use_free ( tp , dp , bp , dup ,
2011-07-08 16:35:27 +04:00
( xfs_dir2_data_aoff_t ) ( ( char * ) dup - ( char * ) hdr ) ,
2006-03-17 09:27:37 +03:00
be16_to_cpu ( dup - > length ) , & needlog , & needscan ) ;
2005-04-17 02:20:36 +04:00
/*
* Create entry for .
*/
2013-10-29 15:11:48 +04:00
dep = dp - > d_ops - > data_dot_entry_p ( hdr ) ;
2006-06-09 08:48:37 +04:00
dep - > inumber = cpu_to_be64 ( dp - > i_ino ) ;
2005-04-17 02:20:36 +04:00
dep - > namelen = 1 ;
dep - > name [ 0 ] = ' . ' ;
2013-10-29 15:11:48 +04:00
dp - > d_ops - > data_put_ftype ( dep , XFS_DIR3_FT_DIR ) ;
tagp = dp - > d_ops - > data_entry_tag_p ( dep ) ;
2011-07-08 16:35:27 +04:00
* tagp = cpu_to_be16 ( ( char * ) dep - ( char * ) hdr ) ;
2013-10-29 15:11:48 +04:00
xfs_dir2_data_log_entry ( tp , dp , bp , dep ) ;
2006-03-17 09:28:18 +03:00
blp [ 0 ] . hashval = cpu_to_be32 ( xfs_dir_hash_dot ) ;
2007-06-28 10:43:50 +04:00
blp [ 0 ] . address = cpu_to_be32 ( xfs_dir2_byte_to_dataptr ( mp ,
2011-07-08 16:35:27 +04:00
( char * ) dep - ( char * ) hdr ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Create entry for . .
*/
2013-10-29 15:11:48 +04:00
dep = dp - > d_ops - > data_dotdot_entry_p ( hdr ) ;
2013-10-29 15:11:47 +04:00
dep - > inumber = cpu_to_be64 ( dp - > d_ops - > sf_get_parent_ino ( sfp ) ) ;
2005-04-17 02:20:36 +04:00
dep - > namelen = 2 ;
dep - > name [ 0 ] = dep - > name [ 1 ] = ' . ' ;
2013-10-29 15:11:48 +04:00
dp - > d_ops - > data_put_ftype ( dep , XFS_DIR3_FT_DIR ) ;
tagp = dp - > d_ops - > data_entry_tag_p ( dep ) ;
2011-07-08 16:35:27 +04:00
* tagp = cpu_to_be16 ( ( char * ) dep - ( char * ) hdr ) ;
2013-10-29 15:11:48 +04:00
xfs_dir2_data_log_entry ( tp , dp , bp , dep ) ;
2006-03-17 09:28:18 +03:00
blp [ 1 ] . hashval = cpu_to_be32 ( xfs_dir_hash_dotdot ) ;
2007-06-28 10:43:50 +04:00
blp [ 1 ] . address = cpu_to_be32 ( xfs_dir2_byte_to_dataptr ( mp ,
2011-07-08 16:35:27 +04:00
( char * ) dep - ( char * ) hdr ) ) ;
2013-10-30 02:15:02 +04:00
offset = dp - > d_ops - > data_first_offset ;
2005-04-17 02:20:36 +04:00
/*
* Loop over existing entries , stuff them in .
*/
2011-07-08 16:35:13 +04:00
i = 0 ;
if ( ! sfp - > count )
2005-04-17 02:20:36 +04:00
sfep = NULL ;
else
2007-06-28 10:43:50 +04:00
sfep = xfs_dir2_sf_firstentry ( sfp ) ;
2005-04-17 02:20:36 +04:00
/*
* Need to preserve the existing offset values in the sf directory .
* Insert holes ( unused entries ) where necessary .
*/
while ( offset < endoffset ) {
/*
* sfep is null when we reach the end of the list .
*/
if ( sfep = = NULL )
newoffset = endoffset ;
else
2007-06-28 10:43:50 +04:00
newoffset = xfs_dir2_sf_get_offset ( sfep ) ;
2005-04-17 02:20:36 +04:00
/*
* There should be a hole here , make one .
*/
if ( offset < newoffset ) {
2011-07-08 16:35:27 +04:00
dup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + offset ) ;
2006-03-17 09:27:37 +03:00
dup - > freetag = cpu_to_be16 ( XFS_DIR2_DATA_FREE_TAG ) ;
dup - > length = cpu_to_be16 ( newoffset - offset ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( dup ) = cpu_to_be16 (
2011-07-08 16:35:27 +04:00
( ( char * ) dup - ( char * ) hdr ) ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , dup ) ;
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeinsert ( hdr ,
dp - > d_ops - > data_bestfree_p ( hdr ) ,
dup , & dummy ) ;
2006-03-17 09:27:37 +03:00
offset + = be16_to_cpu ( dup - > length ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
/*
* Copy a real entry .
*/
2011-07-08 16:35:27 +04:00
dep = ( xfs_dir2_data_entry_t * ) ( ( char * ) hdr + newoffset ) ;
2013-10-29 15:11:47 +04:00
dep - > inumber = cpu_to_be64 ( dp - > d_ops - > sf_get_ino ( sfp , sfep ) ) ;
2005-04-17 02:20:36 +04:00
dep - > namelen = sfep - > namelen ;
2013-10-29 15:11:48 +04:00
dp - > d_ops - > data_put_ftype ( dep , dp - > d_ops - > sf_get_ftype ( sfep ) ) ;
2005-04-17 02:20:36 +04:00
memcpy ( dep - > name , sfep - > name , dep - > namelen ) ;
2013-10-29 15:11:48 +04:00
tagp = dp - > d_ops - > data_entry_tag_p ( dep ) ;
2011-07-08 16:35:27 +04:00
* tagp = cpu_to_be16 ( ( char * ) dep - ( char * ) hdr ) ;
2013-10-29 15:11:48 +04:00
xfs_dir2_data_log_entry ( tp , dp , bp , dep ) ;
2008-05-21 10:41:01 +04:00
name . name = sfep - > name ;
name . len = sfep - > namelen ;
blp [ 2 + i ] . hashval = cpu_to_be32 ( mp - > m_dirnameops - >
hashname ( & name ) ) ;
2007-06-28 10:43:50 +04:00
blp [ 2 + i ] . address = cpu_to_be32 ( xfs_dir2_byte_to_dataptr ( mp ,
2011-07-08 16:35:27 +04:00
( char * ) dep - ( char * ) hdr ) ) ;
offset = ( int ) ( ( char * ) ( tagp + 1 ) - ( char * ) hdr ) ;
2011-07-08 16:35:13 +04:00
if ( + + i = = sfp - > count )
2005-04-17 02:20:36 +04:00
sfep = NULL ;
else
2013-10-29 15:11:46 +04:00
sfep = dp - > d_ops - > sf_nextentry ( sfp , sfep ) ;
2005-04-17 02:20:36 +04:00
}
/* Done with the temporary buffer */
2011-07-08 16:35:13 +04:00
kmem_free ( sfp ) ;
2005-04-17 02:20:36 +04:00
/*
* Sort the leaf entries by hash value .
*/
2006-03-17 09:27:56 +03:00
xfs_sort ( blp , be32_to_cpu ( btp - > count ) , sizeof ( * blp ) , xfs_dir2_block_sort ) ;
2005-04-17 02:20:36 +04:00
/*
* Log the leaf entry area and tail .
* Already logged the header in data_init , ignore needlog .
*/
ASSERT ( needscan = = 0 ) ;
2006-03-17 09:27:56 +03:00
xfs_dir2_block_log_leaf ( tp , bp , 0 , be32_to_cpu ( btp - > count ) - 1 ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_block_log_tail ( tp , bp ) ;
2013-04-03 09:11:22 +04:00
xfs_dir3_data_check ( dp , bp ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}