2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2003 , 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"
2005-11-02 06:38:42 +03:00
# include "xfs_bit.h"
2005-04-17 02:20:36 +04:00
# include "xfs_log.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inum.h"
2005-04-17 02:20:36 +04:00
# include "xfs_trans.h"
# include "xfs_sb.h"
# include "xfs_ag.h"
# include "xfs_dir2.h"
# include "xfs_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"
# include "xfs_dir2_sf.h"
# include "xfs_dinode.h"
# include "xfs_inode.h"
# include "xfs_bmap.h"
# include "xfs_dir2_data.h"
# include "xfs_dir2_leaf.h"
# include "xfs_dir2_block.h"
# include "xfs_dir2_node.h"
# include "xfs_error.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2005-04-17 02:20:36 +04:00
/*
* Local function declarations .
*/
# ifdef DEBUG
static void xfs_dir2_leaf_check ( xfs_inode_t * dp , xfs_dabuf_t * bp ) ;
# else
# define xfs_dir2_leaf_check(dp, bp)
# endif
static int xfs_dir2_leaf_lookup_int ( xfs_da_args_t * args , xfs_dabuf_t * * lbpp ,
int * indexp , xfs_dabuf_t * * dbpp ) ;
2005-06-21 09:36:52 +04:00
static void xfs_dir2_leaf_log_bests ( struct xfs_trans * tp , struct xfs_dabuf * bp ,
int first , int last ) ;
2005-06-21 09:39:44 +04:00
static void xfs_dir2_leaf_log_tail ( struct xfs_trans * tp , struct xfs_dabuf * bp ) ;
2005-06-21 09:36:52 +04:00
2005-04-17 02:20:36 +04:00
/*
* Convert a block form directory to a leaf form directory .
*/
int /* error */
xfs_dir2_block_to_leaf (
xfs_da_args_t * args , /* operation arguments */
xfs_dabuf_t * dbp ) /* input block's buffer */
{
2006-03-17 09:27:19 +03:00
__be16 * bestsp ; /* leaf's bestsp entries */
2005-04-17 02:20:36 +04:00
xfs_dablk_t blkno ; /* leaf block's bno */
xfs_dir2_block_t * block ; /* block structure */
xfs_dir2_leaf_entry_t * blp ; /* block's leaf entries */
xfs_dir2_block_tail_t * btp ; /* block's tail */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return code */
xfs_dabuf_t * lbp ; /* leaf block's buffer */
xfs_dir2_db_t ldb ; /* leaf block's bno */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_tail_t * ltp ; /* leaf's tail */
xfs_mount_t * mp ; /* filesystem mount point */
int needlog ; /* need to log block header */
int needscan ; /* need to rescan bestfree */
xfs_trans_t * tp ; /* transaction pointer */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_block_to_leaf ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
mp = dp - > i_mount ;
tp = args - > trans ;
/*
* Add the leaf block to the inode .
* This interface will only put blocks in the leaf / node range .
* Since that ' s empty now , we ' ll get the root ( block 0 in range ) .
*/
if ( ( error = xfs_da_grow_inode ( args , & blkno ) ) ) {
return error ;
}
2007-06-28 10:43:50 +04:00
ldb = xfs_dir2_da_to_db ( mp , blkno ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( ldb = = XFS_DIR2_LEAF_FIRSTDB ( mp ) ) ;
/*
* Initialize the leaf block , get a buffer for it .
*/
if ( ( error = xfs_dir2_leaf_init ( args , ldb , & lbp , XFS_DIR2_LEAF1_MAGIC ) ) ) {
return error ;
}
ASSERT ( lbp ! = NULL ) ;
leaf = lbp - > data ;
block = dbp - > data ;
xfs_dir2_data_check ( dp , dbp ) ;
2007-06-28 10:43:50 +04:00
btp = xfs_dir2_block_tail_p ( mp , block ) ;
blp = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
/*
* Set the counts in the leaf header .
*/
2006-03-17 09:28:07 +03:00
leaf - > hdr . count = cpu_to_be16 ( be32_to_cpu ( btp - > count ) ) ;
leaf - > hdr . stale = cpu_to_be16 ( be32_to_cpu ( btp - > stale ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Could compact these but I think we always do the conversion
* after squeezing out stale entries .
*/
2006-03-17 09:27:56 +03:00
memcpy ( leaf - > ents , blp , be32_to_cpu ( btp - > count ) * sizeof ( xfs_dir2_leaf_entry_t ) ) ;
2006-03-17 09:28:07 +03:00
xfs_dir2_leaf_log_ents ( tp , lbp , 0 , be16_to_cpu ( leaf - > hdr . count ) - 1 ) ;
2005-04-17 02:20:36 +04:00
needscan = 0 ;
needlog = 1 ;
/*
* Make the space formerly occupied by the leaf entries and block
* tail be free .
*/
xfs_dir2_data_make_free ( tp , dbp ,
( xfs_dir2_data_aoff_t ) ( ( char * ) blp - ( char * ) block ) ,
( xfs_dir2_data_aoff_t ) ( ( char * ) block + mp - > m_dirblksize -
( char * ) blp ) ,
& needlog , & needscan ) ;
/*
* Fix up the block header , make it a data block .
*/
2006-03-17 09:26:52 +03:00
block - > hdr . magic = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) ;
2005-04-17 02:20:36 +04:00
if ( needscan )
2007-05-08 07:48:49 +04:00
xfs_dir2_data_freescan ( mp , ( xfs_dir2_data_t * ) block , & needlog ) ;
2005-04-17 02:20:36 +04:00
/*
* Set up leaf tail and bests table .
*/
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
2006-03-17 09:27:28 +03:00
ltp - > bestcount = cpu_to_be32 ( 1 ) ;
2007-06-28 10:43:50 +04:00
bestsp = xfs_dir2_leaf_bests_p ( ltp ) ;
2006-03-17 09:26:52 +03:00
bestsp [ 0 ] = block - > hdr . bestfree [ 0 ] . length ;
2005-04-17 02:20:36 +04:00
/*
* Log the data header and leaf bests table .
*/
if ( needlog )
xfs_dir2_data_log_header ( tp , dbp ) ;
xfs_dir2_leaf_check ( dp , lbp ) ;
xfs_dir2_data_check ( dp , dbp ) ;
xfs_dir2_leaf_log_bests ( tp , lbp , 0 , 0 ) ;
xfs_da_buf_done ( lbp ) ;
return 0 ;
}
2011-07-08 16:34:59 +04:00
struct xfs_dir2_leaf_entry *
xfs_dir2_leaf_find_entry (
xfs_dir2_leaf_t * leaf , /* leaf structure */
int index , /* leaf table position */
int compact , /* need to compact leaves */
int lowstale , /* index of prev stale leaf */
int highstale , /* index of next stale leaf */
int * lfloglow , /* low leaf logging index */
int * lfloghigh ) /* high leaf logging index */
{
if ( ! leaf - > hdr . stale ) {
xfs_dir2_leaf_entry_t * lep ; /* leaf entry table pointer */
/*
* Now we need to make room to insert the leaf entry .
*
* If there are no stale entries , just insert a hole at index .
*/
lep = & leaf - > ents [ index ] ;
if ( index < be16_to_cpu ( leaf - > hdr . count ) )
memmove ( lep + 1 , lep ,
( be16_to_cpu ( leaf - > hdr . count ) - index ) *
sizeof ( * lep ) ) ;
/*
* Record low and high logging indices for the leaf .
*/
* lfloglow = index ;
* lfloghigh = be16_to_cpu ( leaf - > hdr . count ) ;
be16_add_cpu ( & leaf - > hdr . count , 1 ) ;
return lep ;
}
/*
* There are stale entries .
*
* We will use one of them for the new entry . It ' s probably not at
* the right location , so we ' ll have to shift some up or down first .
*
* If we didn ' t compact before , we need to find the nearest stale
* entries before and after our insertion point .
*/
if ( compact = = 0 ) {
/*
* Find the first stale entry before the insertion point ,
* if any .
*/
for ( lowstale = index - 1 ;
lowstale > = 0 & &
be32_to_cpu ( leaf - > ents [ lowstale ] . address ) ! =
XFS_DIR2_NULL_DATAPTR ;
lowstale - - )
continue ;
/*
* Find the next stale entry at or after the insertion point ,
* if any . Stop if we go so far that the lowstale entry
* would be better .
*/
for ( highstale = index ;
highstale < be16_to_cpu ( leaf - > hdr . count ) & &
be32_to_cpu ( leaf - > ents [ highstale ] . address ) ! =
XFS_DIR2_NULL_DATAPTR & &
( lowstale < 0 | |
index - lowstale - 1 > = highstale - index ) ;
highstale + + )
continue ;
}
/*
* If the low one is better , use it .
*/
if ( lowstale > = 0 & &
( highstale = = be16_to_cpu ( leaf - > hdr . count ) | |
index - lowstale - 1 < highstale - index ) ) {
ASSERT ( index - lowstale - 1 > = 0 ) ;
ASSERT ( be32_to_cpu ( leaf - > ents [ lowstale ] . address ) = =
XFS_DIR2_NULL_DATAPTR ) ;
/*
* Copy entries up to cover the stale entry and make room
* for the new entry .
*/
if ( index - lowstale - 1 > 0 ) {
memmove ( & leaf - > ents [ lowstale ] ,
& leaf - > ents [ lowstale + 1 ] ,
( index - lowstale - 1 ) *
sizeof ( xfs_dir2_leaf_entry_t ) ) ;
}
* lfloglow = MIN ( lowstale , * lfloglow ) ;
* lfloghigh = MAX ( index - 1 , * lfloghigh ) ;
be16_add_cpu ( & leaf - > hdr . stale , - 1 ) ;
return & leaf - > ents [ index - 1 ] ;
}
/*
* The high one is better , so use that one .
*/
ASSERT ( highstale - index > = 0 ) ;
ASSERT ( be32_to_cpu ( leaf - > ents [ highstale ] . address ) = =
XFS_DIR2_NULL_DATAPTR ) ;
/*
* Copy entries down to cover the stale entry and make room for the
* new entry .
*/
if ( highstale - index > 0 ) {
memmove ( & leaf - > ents [ index + 1 ] ,
& leaf - > ents [ index ] ,
( highstale - index ) * sizeof ( xfs_dir2_leaf_entry_t ) ) ;
}
* lfloglow = MIN ( index , * lfloglow ) ;
* lfloghigh = MAX ( highstale , * lfloghigh ) ;
be16_add_cpu ( & leaf - > hdr . stale , - 1 ) ;
return & leaf - > ents [ index ] ;
}
2005-04-17 02:20:36 +04:00
/*
* Add an entry to a leaf form directory .
*/
int /* error */
xfs_dir2_leaf_addname (
xfs_da_args_t * args ) /* operation arguments */
{
2006-03-17 09:27:19 +03:00
__be16 * bestsp ; /* freespace table in leaf */
2005-04-17 02:20:36 +04:00
int compact ; /* need to compact leaves */
xfs_dir2_data_t * data ; /* data block structure */
xfs_dabuf_t * dbp ; /* data block buffer */
xfs_dir2_data_entry_t * dep ; /* data block entry */
xfs_inode_t * dp ; /* incore directory inode */
xfs_dir2_data_unused_t * dup ; /* data unused entry */
int error ; /* error return value */
int grown ; /* allocated new data block */
int highstale ; /* index of next stale leaf */
int i ; /* temporary, index */
int index ; /* leaf table position */
xfs_dabuf_t * lbp ; /* leaf's buffer */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
int length ; /* length of new entry */
xfs_dir2_leaf_entry_t * lep ; /* leaf entry table pointer */
int lfloglow ; /* low leaf logging index */
int lfloghigh ; /* high leaf logging index */
int lowstale ; /* index of prev stale leaf */
xfs_dir2_leaf_tail_t * ltp ; /* leaf tail pointer */
xfs_mount_t * mp ; /* filesystem mount point */
int needbytes ; /* leaf block bytes needed */
int needlog ; /* need to log data header */
int needscan ; /* need to rescan data free */
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 */
xfs_dir2_db_t use_block ; /* data block number */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_leaf_addname ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
/*
* Read the leaf block .
*/
error = xfs_da_read_buf ( tp , dp , mp - > m_dirleafblk , - 1 , & lbp ,
XFS_DATA_FORK ) ;
if ( error ) {
return error ;
}
ASSERT ( lbp ! = NULL ) ;
/*
* Look up the entry by hash value and name .
* We know it ' s not there , our caller has already done a lookup .
* So the index is of the entry to insert in front of .
* But if there are dup hash values the index is of the first of those .
*/
index = xfs_dir2_leaf_search_hash ( args , lbp ) ;
leaf = lbp - > data ;
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
bestsp = xfs_dir2_leaf_bests_p ( ltp ) ;
length = xfs_dir2_data_entsize ( args - > namelen ) ;
2005-04-17 02:20:36 +04:00
/*
* See if there are any entries with the same hash value
* and space in their block for the new entry .
* This is good because it puts multiple same - hash value entries
* in a data block , improving the lookup of those entries .
*/
for ( use_block = - 1 , lep = & leaf - > ents [ index ] ;
2006-03-17 09:28:18 +03:00
index < be16_to_cpu ( leaf - > hdr . count ) & & be32_to_cpu ( lep - > hashval ) = = args - > hashval ;
2005-04-17 02:20:36 +04:00
index + + , lep + + ) {
2006-03-17 09:28:18 +03:00
if ( be32_to_cpu ( lep - > address ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-17 02:20:36 +04:00
continue ;
2007-06-28 10:43:50 +04:00
i = xfs_dir2_dataptr_to_db ( mp , be32_to_cpu ( lep - > address ) ) ;
2006-03-17 09:27:28 +03:00
ASSERT ( i < be32_to_cpu ( ltp - > bestcount ) ) ;
2006-03-17 09:27:19 +03:00
ASSERT ( be16_to_cpu ( bestsp [ i ] ) ! = NULLDATAOFF ) ;
if ( be16_to_cpu ( bestsp [ i ] ) > = length ) {
2005-04-17 02:20:36 +04:00
use_block = i ;
break ;
}
}
/*
* Didn ' t find a block yet , linear search all the data blocks .
*/
if ( use_block = = - 1 ) {
2006-03-17 09:27:28 +03:00
for ( i = 0 ; i < be32_to_cpu ( ltp - > bestcount ) ; i + + ) {
2005-04-17 02:20:36 +04:00
/*
* Remember a block we see that ' s missing .
*/
2006-03-17 09:27:19 +03:00
if ( be16_to_cpu ( bestsp [ i ] ) = = NULLDATAOFF & & use_block = = - 1 )
2005-04-17 02:20:36 +04:00
use_block = i ;
2006-03-17 09:27:19 +03:00
else if ( be16_to_cpu ( bestsp [ i ] ) > = length ) {
2005-04-17 02:20:36 +04:00
use_block = i ;
break ;
}
}
}
/*
* How many bytes do we need in the leaf block ?
*/
needbytes =
( leaf - > hdr . stale ? 0 : ( uint ) sizeof ( leaf - > ents [ 0 ] ) ) +
( use_block ! = - 1 ? 0 : ( uint ) sizeof ( leaf - > bests [ 0 ] ) ) ;
/*
* Now kill use_block if it refers to a missing block , so we
* can use it as an indication of allocation needed .
*/
2006-03-17 09:27:19 +03:00
if ( use_block ! = - 1 & & be16_to_cpu ( bestsp [ use_block ] ) = = NULLDATAOFF )
2005-04-17 02:20:36 +04:00
use_block = - 1 ;
/*
* If we don ' t have enough free bytes but we can make enough
* by compacting out stale entries , we ' ll do that .
*/
2008-05-21 10:42:05 +04:00
if ( ( char * ) bestsp - ( char * ) & leaf - > ents [ be16_to_cpu ( leaf - > hdr . count ) ] <
needbytes & & be16_to_cpu ( leaf - > hdr . stale ) > 1 ) {
2005-04-17 02:20:36 +04:00
compact = 1 ;
}
/*
* Otherwise if we don ' t have enough free bytes we need to
* convert to node form .
*/
2008-05-21 10:42:05 +04:00
else if ( ( char * ) bestsp - ( char * ) & leaf - > ents [ be16_to_cpu (
leaf - > hdr . count ) ] < needbytes ) {
2005-04-17 02:20:36 +04:00
/*
* Just checking or no space reservation , give up .
*/
2008-05-21 10:42:05 +04:00
if ( ( args - > op_flags & XFS_DA_OP_JUSTCHECK ) | |
args - > total = = 0 ) {
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( tp , lbp ) ;
return XFS_ERROR ( ENOSPC ) ;
}
/*
* Convert to node form .
*/
error = xfs_dir2_leaf_to_node ( args , lbp ) ;
xfs_da_buf_done ( lbp ) ;
if ( error )
return error ;
/*
* Then add the new entry .
*/
return xfs_dir2_node_addname ( args ) ;
}
/*
* Otherwise it will fit without compaction .
*/
else
compact = 0 ;
/*
* If just checking , then it will fit unless we needed to allocate
* a new data block .
*/
2008-05-21 10:42:05 +04:00
if ( args - > op_flags & XFS_DA_OP_JUSTCHECK ) {
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( tp , lbp ) ;
return use_block = = - 1 ? XFS_ERROR ( ENOSPC ) : 0 ;
}
/*
* If no allocations are allowed , return now before we ' ve
* changed anything .
*/
if ( args - > total = = 0 & & use_block = = - 1 ) {
xfs_da_brelse ( tp , lbp ) ;
return XFS_ERROR ( ENOSPC ) ;
}
/*
* Need to compact the leaf entries , removing stale ones .
* Leave one stale entry behind - the one closest to our
* insertion index - and we ' ll shift that one to our insertion
* point later .
*/
if ( compact ) {
xfs_dir2_leaf_compact_x1 ( lbp , & index , & lowstale , & highstale ,
& lfloglow , & lfloghigh ) ;
}
/*
* There are stale entries , so we ' ll need log - low and log - high
* impossibly bad values later .
*/
2006-03-17 09:28:07 +03:00
else if ( be16_to_cpu ( leaf - > hdr . stale ) ) {
lfloglow = be16_to_cpu ( leaf - > hdr . count ) ;
2005-04-17 02:20:36 +04:00
lfloghigh = - 1 ;
}
/*
* If there was no data block space found , we need to allocate
* a new one .
*/
if ( use_block = = - 1 ) {
/*
* Add the new data block .
*/
if ( ( error = xfs_dir2_grow_inode ( args , XFS_DIR2_DATA_SPACE ,
& use_block ) ) ) {
xfs_da_brelse ( tp , lbp ) ;
return error ;
}
/*
* Initialize the block .
*/
if ( ( error = xfs_dir2_data_init ( args , use_block , & dbp ) ) ) {
xfs_da_brelse ( tp , lbp ) ;
return error ;
}
/*
* If we ' re adding a new data block on the end we need to
* extend the bests table . Copy it up one entry .
*/
2006-03-17 09:27:28 +03:00
if ( use_block > = be32_to_cpu ( ltp - > bestcount ) ) {
2005-04-17 02:20:36 +04:00
bestsp - - ;
memmove ( & bestsp [ 0 ] , & bestsp [ 1 ] ,
2006-03-17 09:27:28 +03:00
be32_to_cpu ( ltp - > bestcount ) * sizeof ( bestsp [ 0 ] ) ) ;
2008-02-14 02:03:29 +03:00
be32_add_cpu ( & ltp - > bestcount , 1 ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_tail ( tp , lbp ) ;
2006-03-17 09:27:28 +03:00
xfs_dir2_leaf_log_bests ( tp , lbp , 0 , be32_to_cpu ( ltp - > bestcount ) - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* If we ' re filling in a previously empty block just log it .
*/
else
xfs_dir2_leaf_log_bests ( tp , lbp , use_block , use_block ) ;
data = dbp - > data ;
2006-03-17 09:26:52 +03:00
bestsp [ use_block ] = data - > hdr . bestfree [ 0 ] . length ;
2005-04-17 02:20:36 +04:00
grown = 1 ;
}
/*
* Already had space in some data block .
* Just read that one in .
*/
else {
if ( ( error =
2007-06-28 10:43:50 +04:00
xfs_da_read_buf ( tp , dp , xfs_dir2_db_to_da ( mp , use_block ) ,
2005-04-17 02:20:36 +04:00
- 1 , & dbp , XFS_DATA_FORK ) ) ) {
xfs_da_brelse ( tp , lbp ) ;
return error ;
}
data = dbp - > data ;
grown = 0 ;
}
xfs_dir2_data_check ( dp , dbp ) ;
/*
* Point to the biggest freespace in our data block .
*/
dup = ( xfs_dir2_data_unused_t * )
2006-03-17 09:26:52 +03:00
( ( char * ) data + be16_to_cpu ( data - > hdr . bestfree [ 0 ] . offset ) ) ;
2006-03-17 09:27:37 +03:00
ASSERT ( be16_to_cpu ( dup - > length ) > = length ) ;
2005-04-17 02:20:36 +04:00
needscan = needlog = 0 ;
/*
* Mark the initial part of our freespace in use for the new entry .
*/
xfs_dir2_data_use_free ( tp , dbp , dup ,
( xfs_dir2_data_aoff_t ) ( ( char * ) dup - ( char * ) data ) , length ,
& needlog , & needscan ) ;
/*
* Initialize our new entry ( at last ) .
*/
dep = ( xfs_dir2_data_entry_t * ) dup ;
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 , dep - > namelen ) ;
2007-06-28 10:43:50 +04:00
tagp = xfs_dir2_data_entry_tag_p ( dep ) ;
2006-03-17 09:28:27 +03:00
* tagp = cpu_to_be16 ( ( char * ) dep - ( char * ) data ) ;
2005-04-17 02:20:36 +04:00
/*
* Need to scan fix up the bestfree table .
*/
if ( needscan )
2007-05-08 07:48:49 +04:00
xfs_dir2_data_freescan ( mp , data , & needlog ) ;
2005-04-17 02:20:36 +04:00
/*
* Need to log the data block ' s header .
*/
if ( needlog )
xfs_dir2_data_log_header ( tp , dbp ) ;
xfs_dir2_data_log_entry ( tp , dbp , dep ) ;
/*
* If the bests table needs to be changed , do it .
* Log the change unless we ' ve already done that .
*/
2006-03-17 09:27:19 +03:00
if ( be16_to_cpu ( bestsp [ use_block ] ) ! = be16_to_cpu ( data - > hdr . bestfree [ 0 ] . length ) ) {
2006-03-17 09:26:52 +03:00
bestsp [ use_block ] = data - > hdr . bestfree [ 0 ] . length ;
2005-04-17 02:20:36 +04:00
if ( ! grown )
xfs_dir2_leaf_log_bests ( tp , lbp , use_block , use_block ) ;
}
2011-07-08 16:34:59 +04:00
lep = xfs_dir2_leaf_find_entry ( leaf , index , compact , lowstale ,
highstale , & lfloglow , & lfloghigh ) ;
2005-04-17 02:20:36 +04:00
/*
* Fill in the new leaf entry .
*/
2006-03-17 09:28:18 +03:00
lep - > hashval = cpu_to_be32 ( args - > hashval ) ;
2007-06-28 10:43:50 +04:00
lep - > address = cpu_to_be32 ( xfs_dir2_db_off_to_dataptr ( mp , use_block ,
2006-03-17 09:28:27 +03:00
be16_to_cpu ( * tagp ) ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Log the leaf fields and give up the buffers .
*/
xfs_dir2_leaf_log_header ( tp , lbp ) ;
xfs_dir2_leaf_log_ents ( tp , lbp , lfloglow , lfloghigh ) ;
xfs_dir2_leaf_check ( dp , lbp ) ;
xfs_da_buf_done ( lbp ) ;
xfs_dir2_data_check ( dp , dbp ) ;
xfs_da_buf_done ( dbp ) ;
return 0 ;
}
# ifdef DEBUG
/*
* Check the internal consistency of a leaf1 block .
* Pop an assert if something is wrong .
*/
2009-03-04 21:34:10 +03:00
STATIC void
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_check (
xfs_inode_t * dp , /* incore directory inode */
xfs_dabuf_t * bp ) /* leaf's buffer */
{
int i ; /* leaf index */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_tail_t * ltp ; /* leaf tail pointer */
xfs_mount_t * mp ; /* filesystem mount point */
int stale ; /* count of stale leaves */
leaf = bp - > data ;
mp = dp - > i_mount ;
2006-03-17 09:28:40 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAF1_MAGIC ) ;
2005-04-17 02:20:36 +04:00
/*
* This value is not restrictive enough .
* Should factor in the size of the bests table as well .
* We can deduce a value for that from di_size .
*/
2007-06-28 10:43:50 +04:00
ASSERT ( be16_to_cpu ( leaf - > hdr . count ) < = xfs_dir2_max_leaf_ents ( mp ) ) ;
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
2005-04-17 02:20:36 +04:00
/*
* Leaves and bests don ' t overlap .
*/
2006-03-17 09:28:07 +03:00
ASSERT ( ( char * ) & leaf - > ents [ be16_to_cpu ( leaf - > hdr . count ) ] < =
2007-06-28 10:43:50 +04:00
( char * ) xfs_dir2_leaf_bests_p ( ltp ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Check hash value order , count stale entries .
*/
2006-03-17 09:28:07 +03:00
for ( i = stale = 0 ; i < be16_to_cpu ( leaf - > hdr . count ) ; i + + ) {
if ( i + 1 < be16_to_cpu ( leaf - > hdr . count ) )
2006-03-17 09:28:18 +03:00
ASSERT ( be32_to_cpu ( leaf - > ents [ i ] . hashval ) < =
be32_to_cpu ( leaf - > ents [ i + 1 ] . hashval ) ) ;
if ( be32_to_cpu ( leaf - > ents [ i ] . address ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-17 02:20:36 +04:00
stale + + ;
}
2006-03-17 09:28:07 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . stale ) = = stale ) ;
2005-04-17 02:20:36 +04:00
}
# endif /* DEBUG */
/*
* Compact out any stale entries in the leaf .
* Log the header and changed leaf entries , if any .
*/
void
xfs_dir2_leaf_compact (
xfs_da_args_t * args , /* operation arguments */
xfs_dabuf_t * bp ) /* leaf buffer */
{
int from ; /* source leaf index */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
int loglow ; /* first leaf entry to log */
int to ; /* target leaf index */
leaf = bp - > data ;
if ( ! leaf - > hdr . stale ) {
return ;
}
/*
* Compress out the stale entries in place .
*/
2006-03-17 09:28:07 +03:00
for ( from = to = 0 , loglow = - 1 ; from < be16_to_cpu ( leaf - > hdr . count ) ; from + + ) {
2006-03-17 09:28:18 +03:00
if ( be32_to_cpu ( leaf - > ents [ from ] . address ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-17 02:20:36 +04:00
continue ;
/*
* Only actually copy the entries that are different .
*/
if ( from > to ) {
if ( loglow = = - 1 )
loglow = to ;
leaf - > ents [ to ] = leaf - > ents [ from ] ;
}
to + + ;
}
/*
* Update and log the header , log the leaf entries .
*/
2006-03-17 09:28:07 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . stale ) = = from - to ) ;
2008-02-14 02:03:29 +03:00
be16_add_cpu ( & leaf - > hdr . count , - ( be16_to_cpu ( leaf - > hdr . stale ) ) ) ;
2005-04-17 02:20:36 +04:00
leaf - > hdr . stale = 0 ;
xfs_dir2_leaf_log_header ( args - > trans , bp ) ;
if ( loglow ! = - 1 )
xfs_dir2_leaf_log_ents ( args - > trans , bp , loglow , to - 1 ) ;
}
/*
* Compact the leaf entries , removing stale ones .
* Leave one stale entry behind - the one closest to our
* insertion index - and the caller will shift that one to our insertion
* point later .
* Return new insertion index , where the remaining stale entry is ,
* and leaf logging indices .
*/
void
xfs_dir2_leaf_compact_x1 (
xfs_dabuf_t * bp , /* leaf buffer */
int * indexp , /* insertion index */
int * lowstalep , /* out: stale entry before us */
int * highstalep , /* out: stale entry after us */
int * lowlogp , /* out: low log index */
int * highlogp ) /* out: high log index */
{
int from ; /* source copy index */
int highstale ; /* stale entry at/after index */
int index ; /* insertion index */
int keepstale ; /* source index of kept stale */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
int lowstale ; /* stale entry before index */
int newindex = 0 ; /* new insertion index */
int to ; /* destination copy index */
leaf = bp - > data ;
2006-03-17 09:28:07 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . stale ) > 1 ) ;
2005-04-17 02:20:36 +04:00
index = * indexp ;
/*
* Find the first stale entry before our index , if any .
*/
for ( lowstale = index - 1 ;
lowstale > = 0 & &
2006-03-17 09:28:18 +03:00
be32_to_cpu ( leaf - > ents [ lowstale ] . address ) ! = XFS_DIR2_NULL_DATAPTR ;
2005-04-17 02:20:36 +04:00
lowstale - - )
continue ;
/*
* Find the first stale entry at or after our index , if any .
* Stop if the answer would be worse than lowstale .
*/
for ( highstale = index ;
2006-03-17 09:28:07 +03:00
highstale < be16_to_cpu ( leaf - > hdr . count ) & &
2006-03-17 09:28:18 +03:00
be32_to_cpu ( leaf - > ents [ highstale ] . address ) ! = XFS_DIR2_NULL_DATAPTR & &
2005-04-17 02:20:36 +04:00
( lowstale < 0 | | index - lowstale > highstale - index ) ;
highstale + + )
continue ;
/*
* Pick the better of lowstale and highstale .
*/
if ( lowstale > = 0 & &
2006-03-17 09:28:07 +03:00
( highstale = = be16_to_cpu ( leaf - > hdr . count ) | |
2005-04-17 02:20:36 +04:00
index - lowstale < = highstale - index ) )
keepstale = lowstale ;
else
keepstale = highstale ;
/*
* Copy the entries in place , removing all the stale entries
* except keepstale .
*/
2006-03-17 09:28:07 +03:00
for ( from = to = 0 ; from < be16_to_cpu ( leaf - > hdr . count ) ; from + + ) {
2005-04-17 02:20:36 +04:00
/*
* Notice the new value of index .
*/
if ( index = = from )
newindex = to ;
if ( from ! = keepstale & &
2006-03-17 09:28:18 +03:00
be32_to_cpu ( leaf - > ents [ from ] . address ) = = XFS_DIR2_NULL_DATAPTR ) {
2005-04-17 02:20:36 +04:00
if ( from = = to )
* lowlogp = to ;
continue ;
}
/*
* Record the new keepstale value for the insertion .
*/
if ( from = = keepstale )
lowstale = highstale = to ;
/*
* Copy only the entries that have moved .
*/
if ( from > to )
leaf - > ents [ to ] = leaf - > ents [ from ] ;
to + + ;
}
ASSERT ( from > to ) ;
/*
* If the insertion point was past the last entry ,
* set the new insertion point accordingly .
*/
if ( index = = from )
newindex = to ;
* indexp = newindex ;
/*
* Adjust the leaf header values .
*/
2008-02-14 02:03:29 +03:00
be16_add_cpu ( & leaf - > hdr . count , - ( from - to ) ) ;
2006-03-17 09:28:07 +03:00
leaf - > hdr . stale = cpu_to_be16 ( 1 ) ;
2005-04-17 02:20:36 +04:00
/*
* Remember the low / high stale value only in the " right "
* direction .
*/
if ( lowstale > = newindex )
lowstale = - 1 ;
else
2006-03-17 09:28:07 +03:00
highstale = be16_to_cpu ( leaf - > hdr . count ) ;
* highlogp = be16_to_cpu ( leaf - > hdr . count ) - 1 ;
2005-04-17 02:20:36 +04:00
* lowstalep = lowstale ;
* highstalep = highstale ;
}
/*
* Getdents ( readdir ) for leaf and node directories .
* This reads the data blocks only , so is the same for both forms .
*/
int /* error */
xfs_dir2_leaf_getdents (
xfs_inode_t * dp , /* incore directory inode */
2007-08-28 07:58:24 +04:00
void * dirent ,
size_t bufsize ,
xfs_off_t * offset ,
filldir_t filldir )
2005-04-17 02:20:36 +04:00
{
xfs_dabuf_t * bp ; /* data block buffer */
int byteoff ; /* offset in current block */
xfs_dir2_db_t curdb ; /* db for current block */
xfs_dir2_off_t curoff ; /* current overall offset */
xfs_dir2_data_t * data ; /* data block structure */
xfs_dir2_data_entry_t * dep ; /* data entry */
xfs_dir2_data_unused_t * dup ; /* unused entry */
2006-03-14 05:32:24 +03:00
int error = 0 ; /* error return value */
2005-04-17 02:20:36 +04:00
int i ; /* temporary loop index */
int j ; /* temporary loop index */
int length ; /* temporary length value */
xfs_bmbt_irec_t * map ; /* map vector for blocks */
xfs_extlen_t map_blocks ; /* number of fsbs in map */
xfs_dablk_t map_off ; /* last mapped file offset */
int map_size ; /* total entries in *map */
int map_valid ; /* valid entries in *map */
xfs_mount_t * mp ; /* filesystem mount point */
xfs_dir2_off_t newoff ; /* new curoff after new blk */
int nmap ; /* mappings to ask xfs_bmapi */
2006-03-14 05:32:24 +03:00
char * ptr = NULL ; /* pointer to current data */
2005-04-17 02:20:36 +04:00
int ra_current ; /* number of read-ahead blks */
int ra_index ; /* *map index for read-ahead */
int ra_offset ; /* map entry offset for ra */
int ra_want ; /* readahead count wanted */
/*
* If the offset is at or past the largest allowed value ,
2007-08-28 07:58:24 +04:00
* give up right away .
2005-04-17 02:20:36 +04:00
*/
2007-08-28 07:58:24 +04:00
if ( * offset > = XFS_DIR2_MAX_DATAPTR )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-08-28 07:58:24 +04:00
2005-04-17 02:20:36 +04:00
mp = dp - > i_mount ;
2007-08-28 07:58:24 +04:00
2005-04-17 02:20:36 +04:00
/*
* Set up to bmap a number of blocks based on the caller ' s
* buffer size , the directory block size , and the filesystem
* block size .
*/
2007-08-28 07:58:24 +04:00
map_size = howmany ( bufsize + mp - > m_dirblksize , mp - > m_sb . sb_blocksize ) ;
2005-04-17 02:20:36 +04:00
map = kmem_alloc ( map_size * sizeof ( * map ) , KM_SLEEP ) ;
map_valid = ra_index = ra_offset = ra_current = map_blocks = 0 ;
bp = NULL ;
2007-08-28 07:58:24 +04:00
2005-04-17 02:20:36 +04:00
/*
* Inside the loop we keep the main offset value as a byte offset
* in the directory file .
*/
2007-08-28 07:58:24 +04:00
curoff = xfs_dir2_dataptr_to_byte ( mp , * offset ) ;
2005-04-17 02:20:36 +04:00
/*
* Force this conversion through db so we truncate the offset
* down to get the start of the data block .
*/
2007-06-28 10:43:50 +04:00
map_off = xfs_dir2_db_to_da ( mp , xfs_dir2_byte_to_db ( mp , curoff ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Loop over directory entries until we reach the end offset .
* Get more blocks and readahead as necessary .
*/
while ( curoff < XFS_DIR2_LEAF_OFFSET ) {
/*
* If we have no buffer , or we ' re off the end of the
* current buffer , need to get another one .
*/
if ( ! bp | | ptr > = ( char * ) bp - > data + mp - > m_dirblksize ) {
/*
* If we have a buffer , we need to release it and
* take it out of the mapping .
*/
if ( bp ) {
2007-08-28 07:58:24 +04:00
xfs_da_brelse ( NULL , bp ) ;
2005-04-17 02:20:36 +04:00
bp = NULL ;
map_blocks - = mp - > m_dirblkfsbs ;
/*
* Loop to get rid of the extents for the
* directory block .
*/
for ( i = mp - > m_dirblkfsbs ; i > 0 ; ) {
j = MIN ( ( int ) map - > br_blockcount , i ) ;
map - > br_blockcount - = j ;
map - > br_startblock + = j ;
map - > br_startoff + = j ;
/*
* If mapping is done , pitch it from
* the table .
*/
if ( ! map - > br_blockcount & & - - map_valid )
memmove ( & map [ 0 ] , & map [ 1 ] ,
sizeof ( map [ 0 ] ) *
map_valid ) ;
i - = j ;
}
}
/*
* Recalculate the readahead blocks wanted .
*/
2007-08-28 07:58:24 +04:00
ra_want = howmany ( bufsize + mp - > m_dirblksize ,
2005-04-17 02:20:36 +04:00
mp - > m_sb . sb_blocksize ) - 1 ;
fix readahead calculations in xfs_dir2_leaf_getdents()
This is for bug #850,
http://oss.sgi.com/bugzilla/show_bug.cgi?id=850
XFS file system segfaults , repeatedly and 100% reproducable in 2.6.30 , 2.6.31
The above only showed up on a CONFIG_XFS_DEBUG=y kernel, because
xfs_bmapi() ASSERTs that it has been asked for at least one map,
and it was getting 0.
The root cause is that our guesstimated "bufsize" from xfs_file_readdir
was fairly small, and the
bufsize -= length;
in the loop was going negative - except bufsize is a size_t, so it
was wrapping to a very large number.
Then when we did
ra_want = howmany(bufsize + mp->m_dirblksize,
mp->m_sb.sb_blocksize) - 1;
with that very large number, the (int) ra_want was coming out
negative, and a subsequent compare:
if (1 + ra_want > map_blocks ...
was coming out -true- (negative int compare w/ uint) and we went
back to xfs_bmapi() for more, even though we did not need more,
and asked for 0 maps, and hit the ASSERT.
We have kind of a type mess here, but just keeping bufsize from
going negative is probably sufficient to avoid the problem.
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Alex Elder <aelder@sgi.com>
Signed-off-by: Alex Elder <aelder@sgi.com>
2009-09-25 23:42:26 +04:00
ASSERT ( ra_want > = 0 ) ;
2007-08-28 07:58:24 +04:00
2005-04-17 02:20:36 +04:00
/*
* If we don ' t have as many as we want , and we haven ' t
* run out of data blocks , get some more mappings .
*/
if ( 1 + ra_want > map_blocks & &
map_off <
2007-06-28 10:43:50 +04:00
xfs_dir2_byte_to_da ( mp , XFS_DIR2_LEAF_OFFSET ) ) {
2005-04-17 02:20:36 +04:00
/*
* Get more bmaps , fill in after the ones
* we already have in the table .
*/
nmap = map_size - map_valid ;
2007-08-28 07:58:24 +04:00
error = xfs_bmapi ( NULL , dp ,
2005-04-17 02:20:36 +04:00
map_off ,
2007-06-28 10:43:50 +04:00
xfs_dir2_byte_to_da ( mp ,
2005-04-17 02:20:36 +04:00
XFS_DIR2_LEAF_OFFSET ) - map_off ,
XFS_BMAPI_METADATA , NULL , 0 ,
2010-06-23 12:11:15 +04:00
& map [ map_valid ] , & nmap , NULL ) ;
2005-04-17 02:20:36 +04:00
/*
* Don ' t know if we should ignore this or
* try to return an error .
* The trouble with returning errors
* is that readdir will just stop without
* actually passing the error through .
*/
if ( error )
break ; /* XXX */
/*
* If we got all the mappings we asked for ,
* set the final map offset based on the
* last bmap value received .
* Otherwise , we ' ve reached the end .
*/
if ( nmap = = map_size - map_valid )
map_off =
map [ map_valid + nmap - 1 ] . br_startoff +
map [ map_valid + nmap - 1 ] . br_blockcount ;
else
map_off =
2007-06-28 10:43:50 +04:00
xfs_dir2_byte_to_da ( mp ,
2005-04-17 02:20:36 +04:00
XFS_DIR2_LEAF_OFFSET ) ;
/*
* Look for holes in the mapping , and
* eliminate them . Count up the valid blocks .
*/
for ( i = map_valid ; i < map_valid + nmap ; ) {
if ( map [ i ] . br_startblock = =
HOLESTARTBLOCK ) {
nmap - - ;
length = map_valid + nmap - i ;
if ( length )
memmove ( & map [ i ] ,
& map [ i + 1 ] ,
sizeof ( map [ i ] ) *
length ) ;
} else {
map_blocks + =
map [ i ] . br_blockcount ;
i + + ;
}
}
map_valid + = nmap ;
}
/*
* No valid mappings , so no more data blocks .
*/
if ( ! map_valid ) {
2007-06-28 10:43:50 +04:00
curoff = xfs_dir2_da_to_byte ( mp , map_off ) ;
2005-04-17 02:20:36 +04:00
break ;
}
/*
* Read the directory block starting at the first
* mapping .
*/
2007-06-28 10:43:50 +04:00
curdb = xfs_dir2_da_to_db ( mp , map - > br_startoff ) ;
2007-08-28 07:58:24 +04:00
error = xfs_da_read_buf ( NULL , dp , map - > br_startoff ,
2005-04-17 02:20:36 +04:00
map - > br_blockcount > = mp - > m_dirblkfsbs ?
XFS_FSB_TO_DADDR ( mp , map - > br_startblock ) :
- 1 ,
& bp , XFS_DATA_FORK ) ;
/*
* Should just skip over the data block instead
* of giving up .
*/
if ( error )
break ; /* XXX */
/*
* Adjust the current amount of read - ahead : we just
* read a block that was previously ra .
*/
if ( ra_current )
ra_current - = mp - > m_dirblkfsbs ;
/*
* Do we need more readahead ?
*/
for ( ra_index = ra_offset = i = 0 ;
ra_want > ra_current & & i < map_blocks ;
i + = mp - > m_dirblkfsbs ) {
ASSERT ( ra_index < map_valid ) ;
/*
* Read - ahead a contiguous directory block .
*/
if ( i > ra_current & &
map [ ra_index ] . br_blockcount > =
mp - > m_dirblkfsbs ) {
2010-10-06 22:41:18 +04:00
xfs_buf_readahead ( mp - > m_ddev_targp ,
2005-04-17 02:20:36 +04:00
XFS_FSB_TO_DADDR ( mp ,
map [ ra_index ] . br_startblock +
ra_offset ) ,
( int ) BTOBB ( mp - > m_dirblksize ) ) ;
ra_current = i ;
}
/*
* Read - ahead a non - contiguous directory block .
* This doesn ' t use our mapping , but this
* is a very rare case .
*/
else if ( i > ra_current ) {
2007-08-28 07:58:24 +04:00
( void ) xfs_da_reada_buf ( NULL , dp ,
2005-04-17 02:20:36 +04:00
map [ ra_index ] . br_startoff +
ra_offset , XFS_DATA_FORK ) ;
ra_current = i ;
}
/*
* Advance offset through the mapping table .
*/
for ( j = 0 ; j < mp - > m_dirblkfsbs ; j + + ) {
/*
* The rest of this extent but not
* more than a dir block .
*/
length = MIN ( mp - > m_dirblkfsbs ,
( int ) ( map [ ra_index ] . br_blockcount -
ra_offset ) ) ;
j + = length ;
ra_offset + = length ;
/*
* Advance to the next mapping if
* this one is used up .
*/
if ( ra_offset = =
map [ ra_index ] . br_blockcount ) {
ra_offset = 0 ;
ra_index + + ;
}
}
}
/*
* Having done a read , we need to set a new offset .
*/
2007-06-28 10:43:50 +04:00
newoff = xfs_dir2_db_off_to_byte ( mp , curdb , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Start of the current block .
*/
if ( curoff < newoff )
curoff = newoff ;
/*
* Make sure we ' re in the right block .
*/
else if ( curoff > newoff )
2007-06-28 10:43:50 +04:00
ASSERT ( xfs_dir2_byte_to_db ( mp , curoff ) = =
2005-04-17 02:20:36 +04:00
curdb ) ;
data = bp - > data ;
xfs_dir2_data_check ( dp , bp ) ;
/*
* Find our position in the block .
*/
ptr = ( char * ) & data - > u ;
2007-06-28 10:43:50 +04:00
byteoff = xfs_dir2_byte_to_off ( mp , curoff ) ;
2005-04-17 02:20:36 +04:00
/*
* Skip past the header .
*/
if ( byteoff = = 0 )
curoff + = ( uint ) sizeof ( data - > hdr ) ;
/*
* Skip past entries until we reach our offset .
*/
else {
while ( ( char * ) ptr - ( char * ) data < byteoff ) {
dup = ( xfs_dir2_data_unused_t * ) ptr ;
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( dup - > freetag )
2005-04-17 02:20:36 +04:00
= = XFS_DIR2_DATA_FREE_TAG ) {
2006-03-17 09:27:37 +03:00
length = be16_to_cpu ( dup - > length ) ;
2005-04-17 02:20:36 +04:00
ptr + = length ;
continue ;
}
dep = ( xfs_dir2_data_entry_t * ) ptr ;
length =
2007-06-28 10:43:50 +04:00
xfs_dir2_data_entsize ( dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
ptr + = length ;
}
/*
* Now set our real offset .
*/
curoff =
2007-06-28 10:43:50 +04:00
xfs_dir2_db_off_to_byte ( mp ,
xfs_dir2_byte_to_db ( mp , curoff ) ,
2005-04-17 02:20:36 +04:00
( char * ) ptr - ( char * ) data ) ;
if ( ptr > = ( char * ) data + mp - > m_dirblksize ) {
continue ;
}
}
}
/*
* We have a pointer to an entry .
* Is it a live one ?
*/
dup = ( xfs_dir2_data_unused_t * ) ptr ;
/*
* No , it ' s unused , skip over it .
*/
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
length = be16_to_cpu ( dup - > length ) ;
2005-04-17 02:20:36 +04:00
ptr + = length ;
curoff + = length ;
continue ;
}
dep = ( xfs_dir2_data_entry_t * ) ptr ;
2007-08-28 07:58:24 +04:00
length = xfs_dir2_data_entsize ( dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
2010-01-20 02:48:05 +03:00
if ( filldir ( dirent , ( char * ) dep - > name , dep - > namelen ,
2009-01-08 22:00:00 +03:00
xfs_dir2_byte_to_dataptr ( mp , curoff ) & 0x7fffffff ,
2009-03-29 11:51:08 +04:00
be64_to_cpu ( dep - > inumber ) , DT_UNKNOWN ) )
2005-04-17 02:20:36 +04:00
break ;
2007-08-28 07:58:24 +04:00
2005-04-17 02:20:36 +04:00
/*
* Advance to next entry in the block .
*/
ptr + = length ;
curoff + = length ;
fix readahead calculations in xfs_dir2_leaf_getdents()
This is for bug #850,
http://oss.sgi.com/bugzilla/show_bug.cgi?id=850
XFS file system segfaults , repeatedly and 100% reproducable in 2.6.30 , 2.6.31
The above only showed up on a CONFIG_XFS_DEBUG=y kernel, because
xfs_bmapi() ASSERTs that it has been asked for at least one map,
and it was getting 0.
The root cause is that our guesstimated "bufsize" from xfs_file_readdir
was fairly small, and the
bufsize -= length;
in the loop was going negative - except bufsize is a size_t, so it
was wrapping to a very large number.
Then when we did
ra_want = howmany(bufsize + mp->m_dirblksize,
mp->m_sb.sb_blocksize) - 1;
with that very large number, the (int) ra_want was coming out
negative, and a subsequent compare:
if (1 + ra_want > map_blocks ...
was coming out -true- (negative int compare w/ uint) and we went
back to xfs_bmapi() for more, even though we did not need more,
and asked for 0 maps, and hit the ASSERT.
We have kind of a type mess here, but just keeping bufsize from
going negative is probably sufficient to avoid the problem.
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Alex Elder <aelder@sgi.com>
Signed-off-by: Alex Elder <aelder@sgi.com>
2009-09-25 23:42:26 +04:00
/* bufsize may have just been a guess; don't go negative */
bufsize = bufsize > length ? bufsize - length : 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* All done . Set output offset value to current offset .
*/
2007-06-28 10:43:50 +04:00
if ( curoff > xfs_dir2_dataptr_to_byte ( mp , XFS_DIR2_MAX_DATAPTR ) )
2009-01-08 22:00:00 +03:00
* offset = XFS_DIR2_MAX_DATAPTR & 0x7fffffff ;
2005-04-17 02:20:36 +04:00
else
2009-01-08 22:00:00 +03:00
* offset = xfs_dir2_byte_to_dataptr ( mp , curoff ) & 0x7fffffff ;
2008-05-19 10:31:57 +04:00
kmem_free ( map ) ;
2005-04-17 02:20:36 +04:00
if ( bp )
2007-08-28 07:58:24 +04:00
xfs_da_brelse ( NULL , bp ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/*
* Initialize a new leaf block , leaf1 or leafn magic accepted .
*/
int
xfs_dir2_leaf_init (
xfs_da_args_t * args , /* operation arguments */
xfs_dir2_db_t bno , /* directory block number */
xfs_dabuf_t * * bpp , /* out: leaf buffer */
int magic ) /* magic number for block */
{
xfs_dabuf_t * bp ; /* leaf buffer */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return code */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_tail_t * ltp ; /* leaf tail structure */
xfs_mount_t * mp ; /* filesystem mount point */
xfs_trans_t * tp ; /* transaction pointer */
dp = args - > dp ;
ASSERT ( dp ! = NULL ) ;
tp = args - > trans ;
mp = dp - > i_mount ;
ASSERT ( bno > = XFS_DIR2_LEAF_FIRSTDB ( mp ) & &
bno < XFS_DIR2_FREE_FIRSTDB ( mp ) ) ;
/*
* Get the buffer for the block .
*/
2007-06-28 10:43:50 +04:00
error = xfs_da_get_buf ( tp , dp , xfs_dir2_db_to_da ( mp , bno ) , - 1 , & bp ,
2005-04-17 02:20:36 +04:00
XFS_DATA_FORK ) ;
if ( error ) {
return error ;
}
ASSERT ( bp ! = NULL ) ;
leaf = bp - > data ;
/*
* Initialize the header .
*/
2006-03-17 09:28:40 +03:00
leaf - > hdr . info . magic = cpu_to_be16 ( magic ) ;
2005-04-17 02:20:36 +04:00
leaf - > hdr . info . forw = 0 ;
leaf - > hdr . info . back = 0 ;
leaf - > hdr . count = 0 ;
leaf - > hdr . stale = 0 ;
xfs_dir2_leaf_log_header ( tp , bp ) ;
/*
* If it ' s a leaf - format directory initialize the tail .
* In this case our caller has the real bests table to copy into
* the block .
*/
if ( magic = = XFS_DIR2_LEAF1_MAGIC ) {
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
2005-04-17 02:20:36 +04:00
ltp - > bestcount = 0 ;
xfs_dir2_leaf_log_tail ( tp , bp ) ;
}
* bpp = bp ;
return 0 ;
}
/*
* Log the bests entries indicated from a leaf1 block .
*/
2005-06-21 09:36:52 +04:00
static void
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_bests (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp , /* leaf buffer */
int first , /* first entry to log */
int last ) /* last entry to log */
{
2006-03-17 09:27:19 +03:00
__be16 * firstb ; /* pointer to first entry */
__be16 * lastb ; /* pointer to last entry */
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_tail_t * ltp ; /* leaf tail structure */
leaf = bp - > data ;
2006-03-17 09:28:40 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAF1_MAGIC ) ;
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( tp - > t_mountp , leaf ) ;
firstb = xfs_dir2_leaf_bests_p ( ltp ) + first ;
lastb = xfs_dir2_leaf_bests_p ( ltp ) + last ;
2005-04-17 02:20:36 +04:00
xfs_da_log_buf ( tp , bp , ( uint ) ( ( char * ) firstb - ( char * ) leaf ) ,
( uint ) ( ( char * ) lastb - ( char * ) leaf + sizeof ( * lastb ) - 1 ) ) ;
}
/*
* Log the leaf entries indicated from a leaf1 or leafn block .
*/
void
xfs_dir2_leaf_log_ents (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp , /* leaf buffer */
int first , /* first entry to log */
int last ) /* last entry to log */
{
xfs_dir2_leaf_entry_t * firstlep ; /* pointer to first entry */
xfs_dir2_leaf_entry_t * lastlep ; /* pointer to last entry */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
leaf = bp - > data ;
2006-03-17 09:28:40 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAF1_MAGIC | |
be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAFN_MAGIC ) ;
2005-04-17 02:20:36 +04:00
firstlep = & leaf - > ents [ first ] ;
lastlep = & leaf - > ents [ last ] ;
xfs_da_log_buf ( tp , bp , ( uint ) ( ( char * ) firstlep - ( char * ) leaf ) ,
( uint ) ( ( char * ) lastlep - ( char * ) leaf + sizeof ( * lastlep ) - 1 ) ) ;
}
/*
* Log the header of the leaf1 or leafn block .
*/
void
xfs_dir2_leaf_log_header (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp ) /* leaf buffer */
{
xfs_dir2_leaf_t * leaf ; /* leaf structure */
leaf = bp - > data ;
2006-03-17 09:28:40 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAF1_MAGIC | |
be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAFN_MAGIC ) ;
2005-04-17 02:20:36 +04:00
xfs_da_log_buf ( tp , bp , ( uint ) ( ( char * ) & leaf - > hdr - ( char * ) leaf ) ,
( uint ) ( sizeof ( leaf - > hdr ) - 1 ) ) ;
}
/*
* Log the tail of the leaf1 block .
*/
2005-06-21 09:36:52 +04:00
STATIC void
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_tail (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp ) /* leaf buffer */
{
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_tail_t * ltp ; /* leaf tail structure */
xfs_mount_t * mp ; /* filesystem mount point */
mp = tp - > t_mountp ;
leaf = bp - > data ;
2006-03-17 09:28:40 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAF1_MAGIC ) ;
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
2005-04-17 02:20:36 +04:00
xfs_da_log_buf ( tp , bp , ( uint ) ( ( char * ) ltp - ( char * ) leaf ) ,
( uint ) ( mp - > m_dirblksize - 1 ) ) ;
}
/*
* Look up the entry referred to by args in the leaf format directory .
* Most of the work is done by the xfs_dir2_leaf_lookup_int routine which
* is also used by the node - format code .
*/
int
xfs_dir2_leaf_lookup (
xfs_da_args_t * args ) /* operation arguments */
{
xfs_dabuf_t * dbp ; /* data block buffer */
xfs_dir2_data_entry_t * dep ; /* data block entry */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return code */
int index ; /* found entry index */
xfs_dabuf_t * lbp ; /* leaf buffer */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_entry_t * lep ; /* leaf entry */
xfs_trans_t * tp ; /* transaction pointer */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_leaf_lookup ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Look up name in the leaf block , returning both buffers and index .
*/
if ( ( error = xfs_dir2_leaf_lookup_int ( args , & lbp , & index , & dbp ) ) ) {
return error ;
}
tp = args - > trans ;
dp = args - > dp ;
xfs_dir2_leaf_check ( dp , lbp ) ;
leaf = lbp - > data ;
/*
* Get to the leaf entry and contained data entry address .
*/
lep = & leaf - > ents [ index ] ;
/*
* Point to the data entry .
*/
dep = ( xfs_dir2_data_entry_t * )
( ( char * ) dbp - > data +
2007-06-28 10:43:50 +04:00
xfs_dir2_dataptr_to_off ( dp - > i_mount , be32_to_cpu ( lep - > address ) ) ) ;
2005-04-17 02:20:36 +04:00
/*
2008-05-21 10:58:22 +04:00
* Return the found inode number & CI name if appropriate
2005-04-17 02:20:36 +04:00
*/
2006-06-09 08:48:37 +04:00
args - > inumber = be64_to_cpu ( dep - > inumber ) ;
2008-05-21 10:58:22 +04:00
error = xfs_dir_cilookup_result ( args , dep - > name , dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( tp , dbp ) ;
xfs_da_brelse ( tp , lbp ) ;
2008-05-21 10:58:22 +04:00
return XFS_ERROR ( error ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Look up name / hash in the leaf block .
* Fill in indexp with the found index , and dbpp with the data buffer .
* If not found dbpp will be NULL , and ENOENT comes back .
* lbpp will always be filled in with the leaf buffer unless there ' s an error .
*/
static int /* error */
xfs_dir2_leaf_lookup_int (
xfs_da_args_t * args , /* operation arguments */
xfs_dabuf_t * * lbpp , /* out: leaf buffer */
int * indexp , /* out: index in leaf block */
xfs_dabuf_t * * dbpp ) /* out: data buffer */
{
2008-06-27 07:32:11 +04:00
xfs_dir2_db_t curdb = - 1 ; /* current data block number */
xfs_dabuf_t * dbp = NULL ; /* data buffer */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_entry_t * dep ; /* data entry */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return code */
int index ; /* index in leaf block */
xfs_dabuf_t * lbp ; /* leaf buffer */
xfs_dir2_leaf_entry_t * lep ; /* leaf entry */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_mount_t * mp ; /* filesystem mount point */
xfs_dir2_db_t newdb ; /* new data block number */
xfs_trans_t * tp ; /* transaction pointer */
2008-06-27 07:32:11 +04:00
xfs_dir2_db_t cidb = - 1 ; /* case match data block no. */
2008-05-21 10:41:01 +04:00
enum xfs_dacmp cmp ; /* name compare result */
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
/*
* Read the leaf block into the buffer .
*/
2008-06-27 07:32:11 +04:00
error = xfs_da_read_buf ( tp , dp , mp - > m_dirleafblk , - 1 , & lbp ,
XFS_DATA_FORK ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
* lbpp = lbp ;
leaf = lbp - > data ;
xfs_dir2_leaf_check ( dp , lbp ) ;
/*
* Look for the first leaf entry with our hash value .
*/
index = xfs_dir2_leaf_search_hash ( args , lbp ) ;
/*
* Loop over all the entries with the right hash value
* looking to match the name .
*/
2008-06-27 07:32:11 +04:00
for ( lep = & leaf - > ents [ index ] ; index < be16_to_cpu ( leaf - > hdr . count ) & &
2008-05-21 10:41:01 +04:00
be32_to_cpu ( lep - > hashval ) = = args - > hashval ;
lep + + , index + + ) {
2005-04-17 02:20:36 +04:00
/*
* Skip over stale leaf entries .
*/
2006-03-17 09:28:18 +03:00
if ( be32_to_cpu ( lep - > address ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-17 02:20:36 +04:00
continue ;
/*
* Get the new data block number .
*/
2007-06-28 10:43:50 +04:00
newdb = xfs_dir2_dataptr_to_db ( mp , be32_to_cpu ( lep - > address ) ) ;
2005-04-17 02:20:36 +04:00
/*
* If it ' s not the same as the old data block number ,
* need to pitch the old one and read the new one .
*/
if ( newdb ! = curdb ) {
2008-06-27 07:32:11 +04:00
if ( dbp )
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( tp , dbp ) ;
2008-05-21 10:41:01 +04:00
error = xfs_da_read_buf ( tp , dp ,
xfs_dir2_db_to_da ( mp , newdb ) ,
- 1 , & dbp , XFS_DATA_FORK ) ;
if ( error ) {
2005-04-17 02:20:36 +04:00
xfs_da_brelse ( tp , lbp ) ;
return error ;
}
xfs_dir2_data_check ( dp , dbp ) ;
curdb = newdb ;
}
/*
* Point to the data entry .
*/
2008-05-21 10:41:01 +04:00
dep = ( xfs_dir2_data_entry_t * ) ( ( char * ) dbp - > data +
xfs_dir2_dataptr_to_off ( mp , be32_to_cpu ( lep - > address ) ) ) ;
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
* indexp = index ;
2008-06-27 07:32:11 +04:00
/* case exact match: return the current buffer. */
2008-05-21 10:41:01 +04:00
if ( cmp = = XFS_CMP_EXACT ) {
* dbpp = dbp ;
return 0 ;
}
2008-06-27 07:32:11 +04:00
cidb = curdb ;
2005-04-17 02:20:36 +04:00
}
}
2008-05-21 10:42:05 +04:00
ASSERT ( args - > op_flags & XFS_DA_OP_OKNOENT ) ;
2008-05-21 10:41:01 +04:00
/*
2008-06-27 07:32:11 +04:00
* Here , we can only be doing a lookup ( not a rename or remove ) .
* If a case - insensitive match was found earlier , re - read the
* appropriate data block if required and return it .
2008-05-21 10:41:01 +04:00
*/
if ( args - > cmpresult = = XFS_CMP_CASE ) {
2008-06-27 07:32:11 +04:00
ASSERT ( cidb ! = - 1 ) ;
if ( cidb ! = curdb ) {
2008-05-21 10:41:01 +04:00
xfs_da_brelse ( tp , dbp ) ;
2008-06-27 07:32:11 +04:00
error = xfs_da_read_buf ( tp , dp ,
xfs_dir2_db_to_da ( mp , cidb ) ,
- 1 , & dbp , XFS_DATA_FORK ) ;
if ( error ) {
xfs_da_brelse ( tp , lbp ) ;
return error ;
}
}
* dbpp = dbp ;
2008-05-21 10:41:01 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* No match found , return ENOENT .
*/
2008-06-27 07:32:11 +04:00
ASSERT ( cidb = = - 1 ) ;
2005-04-17 02:20:36 +04:00
if ( dbp )
xfs_da_brelse ( tp , dbp ) ;
xfs_da_brelse ( tp , lbp ) ;
return XFS_ERROR ( ENOENT ) ;
}
/*
* Remove an entry from a leaf format directory .
*/
int /* error */
xfs_dir2_leaf_removename (
xfs_da_args_t * args ) /* operation arguments */
{
2006-03-17 09:27:19 +03:00
__be16 * bestsp ; /* leaf block best freespace */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_t * data ; /* data block structure */
xfs_dir2_db_t db ; /* data block number */
xfs_dabuf_t * dbp ; /* data block buffer */
xfs_dir2_data_entry_t * dep ; /* data entry structure */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return code */
xfs_dir2_db_t i ; /* temporary data block # */
int index ; /* index into leaf entries */
xfs_dabuf_t * lbp ; /* leaf buffer */
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 ; /* filesystem mount point */
int needlog ; /* need to log data header */
int needscan ; /* need to rescan data frees */
xfs_dir2_data_off_t oldbest ; /* old value of best free */
xfs_trans_t * tp ; /* transaction pointer */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_leaf_removename ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Lookup the leaf entry , get the leaf and data blocks read in .
*/
if ( ( error = xfs_dir2_leaf_lookup_int ( args , & lbp , & index , & dbp ) ) ) {
return error ;
}
dp = args - > dp ;
tp = args - > trans ;
mp = dp - > i_mount ;
leaf = lbp - > data ;
data = dbp - > data ;
xfs_dir2_data_check ( dp , dbp ) ;
/*
* Point to the leaf entry , use that to point to the data entry .
*/
lep = & leaf - > ents [ index ] ;
2007-06-28 10:43:50 +04:00
db = xfs_dir2_dataptr_to_db ( mp , be32_to_cpu ( lep - > address ) ) ;
2005-04-17 02:20:36 +04:00
dep = ( xfs_dir2_data_entry_t * )
2007-06-28 10:43:50 +04:00
( ( char * ) data + xfs_dir2_dataptr_to_off ( mp , be32_to_cpu ( lep - > address ) ) ) ;
2005-04-17 02:20:36 +04:00
needscan = needlog = 0 ;
2006-03-17 09:26:52 +03:00
oldbest = be16_to_cpu ( data - > hdr . bestfree [ 0 ] . length ) ;
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
bestsp = xfs_dir2_leaf_bests_p ( ltp ) ;
2006-03-17 09:27:19 +03:00
ASSERT ( be16_to_cpu ( bestsp [ db ] ) = = oldbest ) ;
2005-04-17 02:20:36 +04:00
/*
* Mark the former data entry unused .
*/
xfs_dir2_data_make_free ( tp , dbp ,
( xfs_dir2_data_aoff_t ) ( ( char * ) dep - ( char * ) data ) ,
2007-06-28 10:43:50 +04:00
xfs_dir2_data_entsize ( dep - > namelen ) , & needlog , & needscan ) ;
2005-04-17 02:20:36 +04:00
/*
* We just mark the leaf entry stale by putting a null in it .
*/
2008-02-14 02:03:29 +03:00
be16_add_cpu ( & leaf - > hdr . stale , 1 ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_header ( tp , lbp ) ;
2006-03-17 09:28:18 +03:00
lep - > address = cpu_to_be32 ( XFS_DIR2_NULL_DATAPTR ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_ents ( tp , lbp , index , index ) ;
/*
* Scan the freespace in the data block again if necessary ,
* log the data block header if necessary .
*/
if ( needscan )
2007-05-08 07:48:49 +04:00
xfs_dir2_data_freescan ( mp , data , & needlog ) ;
2005-04-17 02:20:36 +04:00
if ( needlog )
xfs_dir2_data_log_header ( tp , dbp ) ;
/*
* If the longest freespace in the data block has changed ,
* put the new value in the bests table and log that .
*/
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( data - > hdr . bestfree [ 0 ] . length ) ! = oldbest ) {
bestsp [ db ] = data - > hdr . bestfree [ 0 ] . length ;
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_bests ( tp , lbp , db , db ) ;
}
xfs_dir2_data_check ( dp , dbp ) ;
/*
* If the data block is now empty then get rid of the data block .
*/
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( data - > hdr . bestfree [ 0 ] . length ) = =
2005-04-17 02:20:36 +04:00
mp - > m_dirblksize - ( uint ) sizeof ( data - > hdr ) ) {
ASSERT ( db ! = mp - > m_dirdatablk ) ;
if ( ( error = xfs_dir2_shrink_inode ( args , db , dbp ) ) ) {
/*
* Nope , can ' t get rid of it because it caused
* allocation of a bmap btree block to do so .
* Just go on , returning success , leaving the
* empty block in place .
*/
if ( error = = ENOSPC & & args - > total = = 0 ) {
xfs_da_buf_done ( dbp ) ;
error = 0 ;
}
xfs_dir2_leaf_check ( dp , lbp ) ;
xfs_da_buf_done ( lbp ) ;
return error ;
}
dbp = NULL ;
/*
* If this is the last data block then compact the
* bests table by getting rid of entries .
*/
2006-03-17 09:27:28 +03:00
if ( db = = be32_to_cpu ( ltp - > bestcount ) - 1 ) {
2005-04-17 02:20:36 +04:00
/*
* Look for the last active entry ( i ) .
*/
for ( i = db - 1 ; i > 0 ; i - - ) {
2006-03-17 09:27:19 +03:00
if ( be16_to_cpu ( bestsp [ i ] ) ! = NULLDATAOFF )
2005-04-17 02:20:36 +04:00
break ;
}
/*
* Copy the table down so inactive entries at the
* end are removed .
*/
memmove ( & bestsp [ db - i ] , bestsp ,
2006-03-17 09:27:28 +03:00
( be32_to_cpu ( ltp - > bestcount ) - ( db - i ) ) * sizeof ( * bestsp ) ) ;
2008-02-14 02:03:29 +03:00
be32_add_cpu ( & ltp - > bestcount , - ( db - i ) ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_tail ( tp , lbp ) ;
2006-03-17 09:27:28 +03:00
xfs_dir2_leaf_log_bests ( tp , lbp , 0 , be32_to_cpu ( ltp - > bestcount ) - 1 ) ;
2005-04-17 02:20:36 +04:00
} else
2006-03-17 09:27:19 +03:00
bestsp [ db ] = cpu_to_be16 ( NULLDATAOFF ) ;
2005-04-17 02:20:36 +04:00
}
/*
* If the data block was not the first one , drop it .
*/
else if ( db ! = mp - > m_dirdatablk & & dbp ! = NULL ) {
xfs_da_buf_done ( dbp ) ;
dbp = NULL ;
}
xfs_dir2_leaf_check ( dp , lbp ) ;
/*
* See if we can convert to block form .
*/
return xfs_dir2_leaf_to_block ( args , lbp , dbp ) ;
}
/*
* Replace the inode number in a leaf format directory entry .
*/
int /* error */
xfs_dir2_leaf_replace (
xfs_da_args_t * args ) /* operation arguments */
{
xfs_dabuf_t * dbp ; /* data block buffer */
xfs_dir2_data_entry_t * dep ; /* data block entry */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return code */
int index ; /* index of leaf entry */
xfs_dabuf_t * lbp ; /* leaf buffer */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_entry_t * lep ; /* leaf entry */
xfs_trans_t * tp ; /* transaction pointer */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_leaf_replace ( args ) ;
2005-04-17 02:20:36 +04:00
/*
* Look up the entry .
*/
if ( ( error = xfs_dir2_leaf_lookup_int ( args , & lbp , & index , & dbp ) ) ) {
return error ;
}
dp = args - > dp ;
leaf = lbp - > data ;
/*
* Point to the leaf entry , get data address from it .
*/
lep = & leaf - > ents [ index ] ;
/*
* Point to the data entry .
*/
dep = ( xfs_dir2_data_entry_t * )
( ( char * ) dbp - > data +
2007-06-28 10:43:50 +04:00
xfs_dir2_dataptr_to_off ( dp - > i_mount , be32_to_cpu ( lep - > address ) ) ) ;
2006-06-09 08:48:37 +04:00
ASSERT ( args - > inumber ! = be64_to_cpu ( dep - > inumber ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Put the new inode number in , log it .
*/
2006-06-09 08:48:37 +04:00
dep - > inumber = cpu_to_be64 ( args - > inumber ) ;
2005-04-17 02:20:36 +04:00
tp = args - > trans ;
xfs_dir2_data_log_entry ( tp , dbp , dep ) ;
xfs_da_buf_done ( dbp ) ;
xfs_dir2_leaf_check ( dp , lbp ) ;
xfs_da_brelse ( tp , lbp ) ;
return 0 ;
}
/*
* Return index in the leaf block ( lbp ) which is either the first
* one with this hash value , or if there are none , the insert point
* for that hash value .
*/
int /* index value */
xfs_dir2_leaf_search_hash (
xfs_da_args_t * args , /* operation arguments */
xfs_dabuf_t * lbp ) /* leaf buffer */
{
xfs_dahash_t hash = 0 ; /* hash from this entry */
xfs_dahash_t hashwant ; /* hash value looking for */
int high ; /* high leaf index */
int low ; /* low leaf index */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_entry_t * lep ; /* leaf entry */
int mid = 0 ; /* current leaf index */
leaf = lbp - > data ;
# ifndef __KERNEL__
if ( ! leaf - > hdr . count )
return 0 ;
# endif
/*
* Note , the table cannot be empty , so we have to go through the loop .
* Binary search the leaf entries looking for our hash value .
*/
2006-03-17 09:28:07 +03:00
for ( lep = leaf - > ents , low = 0 , high = be16_to_cpu ( leaf - > hdr . count ) - 1 ,
2005-04-17 02:20:36 +04:00
hashwant = args - > hashval ;
low < = high ; ) {
mid = ( low + high ) > > 1 ;
2006-03-17 09:28:18 +03:00
if ( ( hash = be32_to_cpu ( lep [ mid ] . hashval ) ) = = hashwant )
2005-04-17 02:20:36 +04:00
break ;
if ( hash < hashwant )
low = mid + 1 ;
else
high = mid - 1 ;
}
/*
* Found one , back up through all the equal hash values .
*/
if ( hash = = hashwant ) {
2006-03-17 09:28:18 +03:00
while ( mid > 0 & & be32_to_cpu ( lep [ mid - 1 ] . hashval ) = = hashwant ) {
2005-04-17 02:20:36 +04:00
mid - - ;
}
}
/*
* Need to point to an entry higher than ours .
*/
else if ( hash < hashwant )
mid + + ;
return mid ;
}
/*
* Trim off a trailing data block . We know it ' s empty since the leaf
* freespace table says so .
*/
int /* error */
xfs_dir2_leaf_trim_data (
xfs_da_args_t * args , /* operation arguments */
xfs_dabuf_t * lbp , /* leaf buffer */
xfs_dir2_db_t db ) /* data block number */
{
2006-03-17 09:27:19 +03:00
__be16 * bestsp ; /* leaf bests table */
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
xfs_dir2_data_t * data ; /* data block structure */
# endif
xfs_dabuf_t * dbp ; /* data block buffer */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return value */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_dir2_leaf_tail_t * ltp ; /* leaf tail structure */
xfs_mount_t * mp ; /* filesystem mount point */
xfs_trans_t * tp ; /* transaction pointer */
dp = args - > dp ;
mp = dp - > i_mount ;
tp = args - > trans ;
/*
* Read the offending data block . We need its buffer .
*/
2007-06-28 10:43:50 +04:00
if ( ( error = xfs_da_read_buf ( tp , dp , xfs_dir2_db_to_da ( mp , db ) , - 1 , & dbp ,
2005-04-17 02:20:36 +04:00
XFS_DATA_FORK ) ) ) {
return error ;
}
# ifdef DEBUG
data = dbp - > data ;
2006-03-17 09:26:52 +03:00
ASSERT ( be32_to_cpu ( data - > hdr . magic ) = = XFS_DIR2_DATA_MAGIC ) ;
2005-04-17 02:20:36 +04:00
# endif
/* this seems to be an error
* data is only valid if DEBUG is defined ?
* RMC 09 / 08 / 1999
*/
leaf = lbp - > data ;
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
2006-03-17 09:26:52 +03:00
ASSERT ( be16_to_cpu ( data - > hdr . bestfree [ 0 ] . length ) = =
2005-04-17 02:20:36 +04:00
mp - > m_dirblksize - ( uint ) sizeof ( data - > hdr ) ) ;
2006-03-17 09:27:28 +03:00
ASSERT ( db = = be32_to_cpu ( ltp - > bestcount ) - 1 ) ;
2005-04-17 02:20:36 +04:00
/*
* Get rid of the data block .
*/
if ( ( error = xfs_dir2_shrink_inode ( args , db , dbp ) ) ) {
ASSERT ( error ! = ENOSPC ) ;
xfs_da_brelse ( tp , dbp ) ;
return error ;
}
/*
* Eliminate the last bests entry from the table .
*/
2007-06-28 10:43:50 +04:00
bestsp = xfs_dir2_leaf_bests_p ( ltp ) ;
2008-02-14 02:03:29 +03:00
be32_add_cpu ( & ltp - > bestcount , - 1 ) ;
2006-03-17 09:27:28 +03:00
memmove ( & bestsp [ 1 ] , & bestsp [ 0 ] , be32_to_cpu ( ltp - > bestcount ) * sizeof ( * bestsp ) ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_tail ( tp , lbp ) ;
2006-03-17 09:27:28 +03:00
xfs_dir2_leaf_log_bests ( tp , lbp , 0 , be32_to_cpu ( ltp - > bestcount ) - 1 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Convert node form directory to leaf form directory .
* The root of the node form dir needs to already be a LEAFN block .
* Just return if we can ' t do anything .
*/
int /* error */
xfs_dir2_node_to_leaf (
xfs_da_state_t * state ) /* directory operation state */
{
xfs_da_args_t * args ; /* operation arguments */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return code */
xfs_dabuf_t * fbp ; /* buffer for freespace block */
xfs_fileoff_t fo ; /* freespace file offset */
xfs_dir2_free_t * free ; /* freespace structure */
xfs_dabuf_t * lbp ; /* buffer for leaf block */
xfs_dir2_leaf_tail_t * ltp ; /* tail of leaf structure */
xfs_dir2_leaf_t * leaf ; /* leaf structure */
xfs_mount_t * mp ; /* filesystem mount point */
int rval ; /* successful free trim? */
xfs_trans_t * tp ; /* transaction pointer */
/*
* There ' s more than a leaf level in the btree , so there must
* be multiple leafn blocks . Give up .
*/
if ( state - > path . active > 1 )
return 0 ;
args = state - > args ;
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_node_to_leaf ( args ) ;
2005-04-17 02:20:36 +04:00
mp = state - > mp ;
dp = args - > dp ;
tp = args - > trans ;
/*
* Get the last offset in the file .
*/
if ( ( error = xfs_bmap_last_offset ( tp , dp , & fo , XFS_DATA_FORK ) ) ) {
return error ;
}
fo - = mp - > m_dirblkfsbs ;
/*
* If there are freespace blocks other than the first one ,
* take this opportunity to remove trailing empty freespace blocks
* that may have been left behind during no - space - reservation
* operations .
*/
while ( fo > mp - > m_dirfreeblk ) {
if ( ( error = xfs_dir2_node_trim_free ( args , fo , & rval ) ) ) {
return error ;
}
if ( rval )
fo - = mp - > m_dirblkfsbs ;
else
return 0 ;
}
/*
* Now find the block just before the freespace block .
*/
if ( ( error = xfs_bmap_last_before ( tp , dp , & fo , XFS_DATA_FORK ) ) ) {
return error ;
}
/*
* If it ' s not the single leaf block , give up .
*/
if ( XFS_FSB_TO_B ( mp , fo ) > XFS_DIR2_LEAF_OFFSET + mp - > m_dirblksize )
return 0 ;
lbp = state - > path . blk [ 0 ] . bp ;
leaf = lbp - > data ;
2006-03-17 09:28:40 +03:00
ASSERT ( be16_to_cpu ( leaf - > hdr . info . magic ) = = XFS_DIR2_LEAFN_MAGIC ) ;
2005-04-17 02:20:36 +04:00
/*
* Read the freespace block .
*/
if ( ( error = xfs_da_read_buf ( tp , dp , mp - > m_dirfreeblk , - 1 , & fbp ,
XFS_DATA_FORK ) ) ) {
return error ;
}
free = fbp - > data ;
2006-03-17 09:27:07 +03:00
ASSERT ( be32_to_cpu ( free - > hdr . magic ) = = XFS_DIR2_FREE_MAGIC ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( ! free - > hdr . firstdb ) ;
/*
* Now see if the leafn and free data will fit in a leaf1 .
* If not , release the buffer and give up .
*/
if ( ( uint ) sizeof ( leaf - > hdr ) +
2006-03-17 09:28:07 +03:00
( be16_to_cpu ( leaf - > hdr . count ) - be16_to_cpu ( leaf - > hdr . stale ) ) * ( uint ) sizeof ( leaf - > ents [ 0 ] ) +
2006-03-17 09:27:07 +03:00
be32_to_cpu ( free - > hdr . nvalid ) * ( uint ) sizeof ( leaf - > bests [ 0 ] ) +
2005-04-17 02:20:36 +04:00
( uint ) sizeof ( leaf - > tail ) >
mp - > m_dirblksize ) {
xfs_da_brelse ( tp , fbp ) ;
return 0 ;
}
/*
* If the leaf has any stale entries in it , compress them out .
* The compact routine will log the header .
*/
2006-03-17 09:28:07 +03:00
if ( be16_to_cpu ( leaf - > hdr . stale ) )
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_compact ( args , lbp ) ;
else
xfs_dir2_leaf_log_header ( tp , lbp ) ;
2006-03-17 09:28:40 +03:00
leaf - > hdr . info . magic = cpu_to_be16 ( XFS_DIR2_LEAF1_MAGIC ) ;
2005-04-17 02:20:36 +04:00
/*
* Set up the leaf tail from the freespace block .
*/
2007-06-28 10:43:50 +04:00
ltp = xfs_dir2_leaf_tail_p ( mp , leaf ) ;
2006-03-17 09:27:07 +03:00
ltp - > bestcount = free - > hdr . nvalid ;
2005-04-17 02:20:36 +04:00
/*
* Set up the leaf bests table .
*/
2007-06-28 10:43:50 +04:00
memcpy ( xfs_dir2_leaf_bests_p ( ltp ) , free - > bests ,
2006-03-17 09:27:28 +03:00
be32_to_cpu ( ltp - > bestcount ) * sizeof ( leaf - > bests [ 0 ] ) ) ;
xfs_dir2_leaf_log_bests ( tp , lbp , 0 , be32_to_cpu ( ltp - > bestcount ) - 1 ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_leaf_log_tail ( tp , lbp ) ;
xfs_dir2_leaf_check ( dp , lbp ) ;
/*
* Get rid of the freespace block .
*/
error = xfs_dir2_shrink_inode ( args , XFS_DIR2_FREE_FIRSTDB ( mp ) , fbp ) ;
if ( error ) {
/*
* This can ' t fail here because it can only happen when
* punching out the middle of an extent , and this is an
* isolated block .
*/
ASSERT ( error ! = ENOSPC ) ;
return error ;
}
fbp = NULL ;
/*
* Now see if we can convert the single - leaf directory
* down to a block form directory .
* This routine always kills the dabuf for the leaf , so
* eliminate it from the path .
*/
error = xfs_dir2_leaf_to_block ( args , lbp , NULL ) ;
state - > path . blk [ 0 ] . bp = NULL ;
return error ;
}