2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2002 , 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_fs.h"
2005-04-17 02:20:36 +04:00
# include "xfs_types.h"
# include "xfs_log.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inum.h"
2005-04-17 02:20:36 +04:00
# include "xfs_trans.h"
# include "xfs_sb.h"
2007-08-28 08:00:13 +04:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# 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_dir2_data.h"
# include "xfs_dir2_leaf.h"
# include "xfs_dir2_block.h"
# include "xfs_error.h"
2011-07-08 16:35:38 +04:00
STATIC xfs_dir2_data_free_t *
xfs_dir2_data_freefind ( xfs_dir2_data_hdr_t * hdr , xfs_dir2_data_unused_t * dup ) ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
/*
* Check the consistency of the data block .
* The input can also be a block - format directory .
* Pop an assert if we find anything bad .
*/
void
xfs_dir2_data_check (
xfs_inode_t * dp , /* incore inode pointer */
xfs_dabuf_t * bp ) /* data block's buffer */
{
xfs_dir2_dataptr_t addr ; /* addr for leaf lookup */
xfs_dir2_data_free_t * bf ; /* bestfree table */
xfs_dir2_block_tail_t * btp = NULL ; /* block tail */
int count ; /* count of entries found */
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr ; /* data block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_entry_t * dep ; /* data entry */
xfs_dir2_data_free_t * dfp ; /* bestfree entry */
xfs_dir2_data_unused_t * dup ; /* unused entry */
char * endp ; /* end of useful data */
int freeseen ; /* mask of bestfrees seen */
xfs_dahash_t hash ; /* hash of current name */
int i ; /* leaf index */
int lastfree ; /* last entry was unused */
xfs_dir2_leaf_entry_t * lep = NULL ; /* block leaf entries */
xfs_mount_t * mp ; /* filesystem mount point */
char * p ; /* current data position */
int stale ; /* count of stale leaves */
2008-05-21 10:41:01 +04:00
struct xfs_name name ;
2005-04-17 02:20:36 +04:00
mp = dp - > i_mount ;
2011-07-08 16:35:42 +04:00
hdr = bp - > data ;
2011-07-08 16:35:38 +04:00
bf = hdr - > bestfree ;
2011-07-08 16:35:42 +04:00
p = ( char * ) ( hdr + 1 ) ;
2011-07-08 16:35:38 +04:00
if ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) {
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
lep = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
endp = ( char * ) lep ;
2011-07-08 16:35:38 +04:00
} else {
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) ) ;
endp = ( char * ) hdr + mp - > m_dirblksize ;
}
2005-04-17 02:20:36 +04:00
count = lastfree = freeseen = 0 ;
/*
* Account for zero bestfree entries .
*/
if ( ! bf [ 0 ] . length ) {
ASSERT ( ! bf [ 0 ] . offset ) ;
freeseen | = 1 < < 0 ;
}
if ( ! bf [ 1 ] . length ) {
ASSERT ( ! bf [ 1 ] . offset ) ;
freeseen | = 1 < < 1 ;
}
if ( ! bf [ 2 ] . length ) {
ASSERT ( ! bf [ 2 ] . offset ) ;
freeseen | = 1 < < 2 ;
}
2006-03-17 09:26:52 +03:00
ASSERT ( be16_to_cpu ( bf [ 0 ] . length ) > = be16_to_cpu ( bf [ 1 ] . length ) ) ;
ASSERT ( be16_to_cpu ( bf [ 1 ] . length ) > = be16_to_cpu ( bf [ 2 ] . length ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Loop over the data / unused entries .
*/
while ( p < endp ) {
dup = ( xfs_dir2_data_unused_t * ) p ;
/*
* If it ' s unused , look for the space in the bestfree table .
* If we find it , account for that , else make sure it
* doesn ' t need to be there .
*/
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
2005-04-17 02:20:36 +04:00
ASSERT ( lastfree = = 0 ) ;
2007-06-28 10:43:50 +04:00
ASSERT ( be16_to_cpu ( * xfs_dir2_data_unused_tag_p ( dup ) ) = =
2011-07-08 16:35:38 +04:00
( char * ) dup - ( char * ) hdr ) ;
dfp = xfs_dir2_data_freefind ( hdr , dup ) ;
2005-04-17 02:20:36 +04:00
if ( dfp ) {
i = ( int ) ( dfp - bf ) ;
ASSERT ( ( freeseen & ( 1 < < i ) ) = = 0 ) ;
freeseen | = 1 < < i ;
2006-03-17 09:26:52 +03:00
} else {
2006-03-17 09:27:37 +03:00
ASSERT ( be16_to_cpu ( dup - > length ) < =
2006-03-17 09:26:52 +03:00
be16_to_cpu ( bf [ 2 ] . length ) ) ;
}
2006-03-17 09:27:37 +03:00
p + = be16_to_cpu ( dup - > length ) ;
2005-04-17 02:20:36 +04:00
lastfree = 1 ;
continue ;
}
/*
* It ' s a real entry . Validate the fields .
* If this is a block directory then make sure it ' s
* in the leaf section of the block .
* The linear search is crude but this is DEBUG code .
*/
dep = ( xfs_dir2_data_entry_t * ) p ;
ASSERT ( dep - > namelen ! = 0 ) ;
2006-06-09 08:48:37 +04:00
ASSERT ( xfs_dir_ino_validate ( mp , be64_to_cpu ( dep - > inumber ) ) = = 0 ) ;
2007-06-28 10:43:50 +04:00
ASSERT ( be16_to_cpu ( * xfs_dir2_data_entry_tag_p ( dep ) ) = =
2011-07-08 16:35:38 +04:00
( char * ) dep - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
count + + ;
lastfree = 0 ;
2011-07-08 16:35:38 +04:00
if ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) {
2007-06-28 10:43:50 +04:00
addr = xfs_dir2_db_off_to_dataptr ( mp , mp - > m_dirdatablk ,
2005-04-17 02:20:36 +04:00
( xfs_dir2_data_aoff_t )
2011-07-08 16:35:38 +04:00
( ( char * ) dep - ( char * ) hdr ) ) ;
2008-05-21 10:41:01 +04:00
name . name = dep - > name ;
name . len = dep - > namelen ;
hash = mp - > m_dirnameops - > hashname ( & name ) ;
2006-03-17 09:27:56 +03:00
for ( i = 0 ; i < be32_to_cpu ( btp - > count ) ; i + + ) {
2006-03-17 09:28:18 +03:00
if ( be32_to_cpu ( lep [ i ] . address ) = = addr & &
be32_to_cpu ( lep [ i ] . hashval ) = = hash )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-17 09:27:56 +03:00
ASSERT ( i < be32_to_cpu ( btp - > count ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-28 10:43:50 +04:00
p + = xfs_dir2_data_entsize ( dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Need to have seen all the entries and all the bestfree slots .
*/
ASSERT ( freeseen = = 7 ) ;
2011-07-08 16:35:38 +04:00
if ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) {
2006-03-17 09:27:56 +03:00
for ( i = stale = 0 ; i < be32_to_cpu ( btp - > count ) ; i + + ) {
2006-03-17 09:28:18 +03:00
if ( be32_to_cpu ( lep [ i ] . address ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-17 02:20:36 +04:00
stale + + ;
if ( i > 0 )
2006-03-17 09:28:18 +03:00
ASSERT ( be32_to_cpu ( lep [ i ] . hashval ) > = be32_to_cpu ( lep [ i - 1 ] . hashval ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-17 09:27:56 +03:00
ASSERT ( count = = be32_to_cpu ( btp - > count ) - be32_to_cpu ( btp - > stale ) ) ;
ASSERT ( stale = = be32_to_cpu ( btp - > stale ) ) ;
2005-04-17 02:20:36 +04:00
}
}
# endif
/*
* Given a data block and an unused entry from that block ,
* return the bestfree entry if any that corresponds to it .
*/
2011-07-08 16:35:38 +04:00
STATIC xfs_dir2_data_free_t *
2005-04-17 02:20:36 +04:00
xfs_dir2_data_freefind (
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr , /* data block */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_unused_t * dup ) /* data unused entry */
{
xfs_dir2_data_free_t * dfp ; /* bestfree entry */
xfs_dir2_data_aoff_t off ; /* offset value needed */
# if defined(DEBUG) && defined(__KERNEL__)
int matched ; /* matched the value */
int seenzero ; /* saw a 0 bestfree entry */
# endif
2011-07-08 16:35:38 +04:00
off = ( xfs_dir2_data_aoff_t ) ( ( char * ) dup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
# if defined(DEBUG) && defined(__KERNEL__)
/*
* Validate some consistency in the bestfree table .
* Check order , non - overlapping entries , and if we find the
* one we ' re looking for it has to be exact .
*/
2011-07-08 16:35:38 +04:00
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
for ( dfp = & hdr - > bestfree [ 0 ] , seenzero = matched = 0 ;
dfp < & hdr - > bestfree [ XFS_DIR2_DATA_FD_COUNT ] ;
2005-04-17 02:20:36 +04:00
dfp + + ) {
if ( ! dfp - > offset ) {
ASSERT ( ! dfp - > length ) ;
seenzero = 1 ;
continue ;
}
ASSERT ( seenzero = = 0 ) ;
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( dfp - > offset ) = = off ) {
2005-04-17 02:20:36 +04:00
matched = 1 ;
2006-03-17 09:27:37 +03:00
ASSERT ( dfp - > length = = dup - > length ) ;
2006-03-17 09:26:52 +03:00
} else if ( off < be16_to_cpu ( dfp - > offset ) )
2006-03-17 09:27:37 +03:00
ASSERT ( off + be16_to_cpu ( dup - > length ) < = be16_to_cpu ( dfp - > offset ) ) ;
2005-04-17 02:20:36 +04:00
else
2006-03-17 09:26:52 +03:00
ASSERT ( be16_to_cpu ( dfp - > offset ) + be16_to_cpu ( dfp - > length ) < = off ) ;
2006-03-17 09:27:37 +03:00
ASSERT ( matched | | be16_to_cpu ( dfp - > length ) > = be16_to_cpu ( dup - > length ) ) ;
2011-07-08 16:35:38 +04:00
if ( dfp > & hdr - > bestfree [ 0 ] )
2006-03-17 09:26:52 +03:00
ASSERT ( be16_to_cpu ( dfp [ - 1 ] . length ) > = be16_to_cpu ( dfp [ 0 ] . length ) ) ;
2005-04-17 02:20:36 +04:00
}
# endif
/*
* If this is smaller than the smallest bestfree entry ,
* it can ' t be there since they ' re sorted .
*/
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( dup - > length ) <
2011-07-08 16:35:38 +04:00
be16_to_cpu ( hdr - > bestfree [ XFS_DIR2_DATA_FD_COUNT - 1 ] . length ) )
2005-04-17 02:20:36 +04:00
return NULL ;
/*
* Look at the three bestfree entries for our guy .
*/
2011-07-08 16:35:38 +04:00
for ( dfp = & hdr - > bestfree [ 0 ] ;
dfp < & hdr - > bestfree [ XFS_DIR2_DATA_FD_COUNT ] ;
2005-04-17 02:20:36 +04:00
dfp + + ) {
if ( ! dfp - > offset )
return NULL ;
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( dfp - > offset ) = = off )
2005-04-17 02:20:36 +04:00
return dfp ;
}
/*
* Didn ' t find it . This only happens if there are duplicate lengths .
*/
return NULL ;
}
/*
* Insert an unused - space entry into the bestfree table .
*/
xfs_dir2_data_free_t * /* entry inserted */
xfs_dir2_data_freeinsert (
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr , /* data block pointer */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_unused_t * dup , /* unused space */
int * loghead ) /* log the data header (out) */
{
xfs_dir2_data_free_t * dfp ; /* bestfree table pointer */
xfs_dir2_data_free_t new ; /* new bestfree entry */
# ifdef __KERNEL__
2011-07-08 16:35:38 +04:00
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
2005-04-17 02:20:36 +04:00
# endif
2011-07-08 16:35:38 +04:00
dfp = hdr - > bestfree ;
2006-03-17 09:26:52 +03:00
new . length = dup - > length ;
2011-07-08 16:35:38 +04:00
new . offset = cpu_to_be16 ( ( char * ) dup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
/*
* Insert at position 0 , 1 , or 2 ; or not at all .
*/
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( new . length ) > be16_to_cpu ( dfp [ 0 ] . length ) ) {
2005-04-17 02:20:36 +04:00
dfp [ 2 ] = dfp [ 1 ] ;
dfp [ 1 ] = dfp [ 0 ] ;
dfp [ 0 ] = new ;
* loghead = 1 ;
return & dfp [ 0 ] ;
}
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( new . length ) > be16_to_cpu ( dfp [ 1 ] . length ) ) {
2005-04-17 02:20:36 +04:00
dfp [ 2 ] = dfp [ 1 ] ;
dfp [ 1 ] = new ;
* loghead = 1 ;
return & dfp [ 1 ] ;
}
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( new . length ) > be16_to_cpu ( dfp [ 2 ] . length ) ) {
2005-04-17 02:20:36 +04:00
dfp [ 2 ] = new ;
* loghead = 1 ;
return & dfp [ 2 ] ;
}
return NULL ;
}
/*
* Remove a bestfree entry from the table .
*/
2005-06-21 09:36:52 +04:00
STATIC void
2005-04-17 02:20:36 +04:00
xfs_dir2_data_freeremove (
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr , /* data block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_free_t * dfp , /* bestfree entry pointer */
int * loghead ) /* out: log data header */
{
# ifdef __KERNEL__
2011-07-08 16:35:38 +04:00
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
2005-04-17 02:20:36 +04:00
# endif
/*
* It ' s the first entry , slide the next 2 up .
*/
2011-07-08 16:35:38 +04:00
if ( dfp = = & hdr - > bestfree [ 0 ] ) {
hdr - > bestfree [ 0 ] = hdr - > bestfree [ 1 ] ;
hdr - > bestfree [ 1 ] = hdr - > bestfree [ 2 ] ;
2005-04-17 02:20:36 +04:00
}
/*
* It ' s the second entry , slide the 3 rd entry up .
*/
2011-07-08 16:35:38 +04:00
else if ( dfp = = & hdr - > bestfree [ 1 ] )
hdr - > bestfree [ 1 ] = hdr - > bestfree [ 2 ] ;
2005-04-17 02:20:36 +04:00
/*
* Must be the last entry .
*/
else
2011-07-08 16:35:38 +04:00
ASSERT ( dfp = = & hdr - > bestfree [ 2 ] ) ;
2005-04-17 02:20:36 +04:00
/*
* Clear the 3 rd entry , must be zero now .
*/
2011-07-08 16:35:38 +04:00
hdr - > bestfree [ 2 ] . length = 0 ;
hdr - > bestfree [ 2 ] . offset = 0 ;
2005-04-17 02:20:36 +04:00
* loghead = 1 ;
}
/*
* Given a data block , reconstruct its bestfree map .
*/
void
xfs_dir2_data_freescan (
xfs_mount_t * mp , /* filesystem mount point */
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr , /* data block header */
2007-05-08 07:48:49 +04:00
int * loghead ) /* out: log data header */
2005-04-17 02:20:36 +04:00
{
xfs_dir2_block_tail_t * btp ; /* block tail */
xfs_dir2_data_entry_t * dep ; /* active data entry */
xfs_dir2_data_unused_t * dup ; /* unused data entry */
char * endp ; /* end of block's data */
char * p ; /* current entry pointer */
# ifdef __KERNEL__
2011-07-08 16:35:38 +04:00
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
2005-04-17 02:20:36 +04:00
# endif
/*
* Start by clearing the table .
*/
2011-07-08 16:35:38 +04:00
memset ( hdr - > bestfree , 0 , sizeof ( hdr - > bestfree ) ) ;
2005-04-17 02:20:36 +04:00
* loghead = 1 ;
/*
* Set up pointers .
*/
2011-07-08 16:35:42 +04:00
p = ( char * ) ( hdr + 1 ) ;
2011-07-08 16:35:38 +04:00
if ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) {
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
endp = ( char * ) xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
} else
2011-07-08 16:35:38 +04:00
endp = ( char * ) hdr + mp - > m_dirblksize ;
2005-04-17 02:20:36 +04:00
/*
* Loop over the block ' s entries .
*/
while ( p < endp ) {
dup = ( xfs_dir2_data_unused_t * ) p ;
/*
* If it ' s a free entry , insert it .
*/
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
2011-07-08 16:35:38 +04:00
ASSERT ( ( char * ) dup - ( char * ) hdr = =
2007-06-28 10:43:50 +04:00
be16_to_cpu ( * xfs_dir2_data_unused_tag_p ( dup ) ) ) ;
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeinsert ( hdr , dup , loghead ) ;
2006-03-17 09:27:37 +03:00
p + = be16_to_cpu ( dup - > length ) ;
2005-04-17 02:20:36 +04:00
}
/*
* For active entries , check their tags and skip them .
*/
else {
dep = ( xfs_dir2_data_entry_t * ) p ;
2011-07-08 16:35:38 +04:00
ASSERT ( ( char * ) dep - ( char * ) hdr = =
2007-06-28 10:43:50 +04:00
be16_to_cpu ( * xfs_dir2_data_entry_tag_p ( dep ) ) ) ;
p + = xfs_dir2_data_entsize ( dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
}
}
}
/*
* Initialize a data block at the given block number in the directory .
* Give back the buffer for the created block .
*/
int /* error */
xfs_dir2_data_init (
xfs_da_args_t * args , /* directory operation args */
xfs_dir2_db_t blkno , /* logical dir block number */
xfs_dabuf_t * * bpp ) /* output block buffer */
{
xfs_dabuf_t * bp ; /* block buffer */
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr ; /* data block header */
2005-04-17 02:20:36 +04:00
xfs_inode_t * dp ; /* incore directory inode */
xfs_dir2_data_unused_t * dup ; /* unused entry pointer */
int error ; /* error return value */
int i ; /* bestfree index */
xfs_mount_t * mp ; /* filesystem mount point */
xfs_trans_t * tp ; /* transaction pointer */
int t ; /* temp */
dp = args - > dp ;
mp = dp - > i_mount ;
tp = args - > trans ;
/*
* Get the buffer set up for the block .
*/
2007-06-28 10:43:50 +04:00
error = xfs_da_get_buf ( tp , dp , xfs_dir2_db_to_da ( mp , blkno ) , - 1 , & bp ,
2005-04-17 02:20:36 +04:00
XFS_DATA_FORK ) ;
if ( error ) {
return error ;
}
ASSERT ( bp ! = NULL ) ;
2011-07-08 16:35:38 +04:00
2005-04-17 02:20:36 +04:00
/*
* Initialize the header .
*/
2011-07-08 16:35:42 +04:00
hdr = bp - > data ;
2011-07-08 16:35:38 +04:00
hdr - > magic = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) ;
hdr - > bestfree [ 0 ] . offset = cpu_to_be16 ( sizeof ( * hdr ) ) ;
2005-04-17 02:20:36 +04:00
for ( i = 1 ; i < XFS_DIR2_DATA_FD_COUNT ; i + + ) {
2011-07-08 16:35:38 +04:00
hdr - > bestfree [ i ] . length = 0 ;
hdr - > bestfree [ i ] . offset = 0 ;
2005-04-17 02:20:36 +04:00
}
2011-07-08 16:35:38 +04:00
2005-04-17 02:20:36 +04:00
/*
* Set up an unused entry for the block ' s body .
*/
2011-07-08 16:35:42 +04:00
dup = ( xfs_dir2_data_unused_t * ) ( hdr + 1 ) ;
2006-03-17 09:27:37 +03:00
dup - > freetag = cpu_to_be16 ( XFS_DIR2_DATA_FREE_TAG ) ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:38 +04:00
t = mp - > m_dirblksize - ( uint ) sizeof ( * hdr ) ;
hdr - > bestfree [ 0 ] . length = cpu_to_be16 ( t ) ;
2006-03-17 09:27:37 +03:00
dup - > length = cpu_to_be16 ( t ) ;
2011-07-08 16:35:38 +04:00
* xfs_dir2_data_unused_tag_p ( dup ) = cpu_to_be16 ( ( char * ) dup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
/*
* Log it and return it .
*/
xfs_dir2_data_log_header ( tp , bp ) ;
xfs_dir2_data_log_unused ( tp , bp , dup ) ;
* bpp = bp ;
return 0 ;
}
/*
* Log an active data entry from the block .
*/
void
xfs_dir2_data_log_entry (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp , /* block buffer */
xfs_dir2_data_entry_t * dep ) /* data entry pointer */
{
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr = bp - > data ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:38 +04:00
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
xfs_da_log_buf ( tp , bp , ( uint ) ( ( char * ) dep - ( char * ) hdr ) ,
2007-06-28 10:43:50 +04:00
( uint ) ( ( char * ) ( xfs_dir2_data_entry_tag_p ( dep ) + 1 ) -
2011-07-08 16:35:38 +04:00
( char * ) hdr - 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Log a data block header .
*/
void
xfs_dir2_data_log_header (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp ) /* block buffer */
{
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr = bp - > data ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:38 +04:00
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
xfs_da_log_buf ( tp , bp , 0 , sizeof ( * hdr ) - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Log a data unused entry .
*/
void
xfs_dir2_data_log_unused (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp , /* block buffer */
xfs_dir2_data_unused_t * dup ) /* data unused pointer */
{
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr = bp - > data ;
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
2005-04-17 02:20:36 +04:00
/*
* Log the first part of the unused entry .
*/
2011-07-08 16:35:38 +04:00
xfs_da_log_buf ( tp , bp , ( uint ) ( ( char * ) dup - ( char * ) hdr ) ,
2005-04-17 02:20:36 +04:00
( uint ) ( ( char * ) & dup - > length + sizeof ( dup - > length ) -
2011-07-08 16:35:38 +04:00
1 - ( char * ) hdr ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Log the end ( tag ) of the unused entry .
*/
xfs_da_log_buf ( tp , bp ,
2011-07-08 16:35:38 +04:00
( uint ) ( ( char * ) xfs_dir2_data_unused_tag_p ( dup ) - ( char * ) hdr ) ,
( uint ) ( ( char * ) xfs_dir2_data_unused_tag_p ( dup ) - ( char * ) hdr +
2005-04-17 02:20:36 +04:00
sizeof ( xfs_dir2_data_off_t ) - 1 ) ) ;
}
/*
* Make a byte range in the data block unused .
* Its current contents are unimportant .
*/
void
xfs_dir2_data_make_free (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp , /* block buffer */
xfs_dir2_data_aoff_t offset , /* starting byte offset */
xfs_dir2_data_aoff_t len , /* length in bytes */
int * needlogp , /* out: log header */
int * needscanp ) /* out: regen bestfree */
{
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr ; /* data block pointer */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_free_t * dfp ; /* bestfree pointer */
char * endptr ; /* end of data area */
xfs_mount_t * mp ; /* filesystem mount point */
int needscan ; /* need to regen bestfree */
xfs_dir2_data_unused_t * newdup ; /* new unused entry */
xfs_dir2_data_unused_t * postdup ; /* unused entry after us */
xfs_dir2_data_unused_t * prevdup ; /* unused entry before us */
mp = tp - > t_mountp ;
2011-07-08 16:35:38 +04:00
hdr = bp - > data ;
2005-04-17 02:20:36 +04:00
/*
* Figure out where the end of the data area is .
*/
2011-07-08 16:35:38 +04:00
if ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC )
endptr = ( char * ) hdr + mp - > m_dirblksize ;
2005-04-17 02:20:36 +04:00
else {
xfs_dir2_block_tail_t * btp ; /* block tail */
2011-07-08 16:35:38 +04:00
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
endptr = ( char * ) xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
}
/*
* If this isn ' t the start of the block , then back up to
* the previous entry and see if it ' s free .
*/
2011-07-08 16:35:38 +04:00
if ( offset > sizeof ( * hdr ) ) {
2006-03-17 09:28:27 +03:00
__be16 * tagp ; /* tag just before us */
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:38 +04:00
tagp = ( __be16 * ) ( ( char * ) hdr + offset ) - 1 ;
prevdup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + be16_to_cpu ( * tagp ) ) ;
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( prevdup - > freetag ) ! = XFS_DIR2_DATA_FREE_TAG )
2005-04-17 02:20:36 +04:00
prevdup = NULL ;
} else
prevdup = NULL ;
/*
* If this isn ' t the end of the block , see if the entry after
* us is free .
*/
2011-07-08 16:35:38 +04:00
if ( ( char * ) hdr + offset + len < endptr ) {
2005-04-17 02:20:36 +04:00
postdup =
2011-07-08 16:35:38 +04:00
( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + offset + len ) ;
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( postdup - > freetag ) ! = XFS_DIR2_DATA_FREE_TAG )
2005-04-17 02:20:36 +04:00
postdup = NULL ;
} else
postdup = NULL ;
ASSERT ( * needscanp = = 0 ) ;
needscan = 0 ;
/*
* Previous and following entries are both free ,
* merge everything into a single free entry .
*/
if ( prevdup & & postdup ) {
xfs_dir2_data_free_t * dfp2 ; /* another bestfree pointer */
/*
* See if prevdup and / or postdup are in bestfree table .
*/
2011-07-08 16:35:38 +04:00
dfp = xfs_dir2_data_freefind ( hdr , prevdup ) ;
dfp2 = xfs_dir2_data_freefind ( hdr , postdup ) ;
2005-04-17 02:20:36 +04:00
/*
* We need a rescan unless there are exactly 2 free entries
* namely our two . Then we know what ' s happening , otherwise
* since the third bestfree is there , there might be more
* entries .
*/
2011-07-08 16:35:38 +04:00
needscan = ( hdr - > bestfree [ 2 ] . length ! = 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Fix up the new big freespace .
*/
2008-02-14 02:03:29 +03:00
be16_add_cpu ( & prevdup - > length , len + be16_to_cpu ( postdup - > length ) ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( prevdup ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) prevdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , prevdup ) ;
if ( ! needscan ) {
/*
* Has to be the case that entries 0 and 1 are
* dfp and dfp2 ( don ' t know which is which ) , and
* entry 2 is empty .
* Remove entry 1 first then entry 0.
*/
ASSERT ( dfp & & dfp2 ) ;
2011-07-08 16:35:38 +04:00
if ( dfp = = & hdr - > bestfree [ 1 ] ) {
dfp = & hdr - > bestfree [ 0 ] ;
2005-04-17 02:20:36 +04:00
ASSERT ( dfp2 = = dfp ) ;
2011-07-08 16:35:38 +04:00
dfp2 = & hdr - > bestfree [ 1 ] ;
2005-04-17 02:20:36 +04:00
}
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeremove ( hdr , dfp2 , needlogp ) ;
xfs_dir2_data_freeremove ( hdr , dfp , needlogp ) ;
2005-04-17 02:20:36 +04:00
/*
* Now insert the new entry .
*/
2011-07-08 16:35:38 +04:00
dfp = xfs_dir2_data_freeinsert ( hdr , prevdup , needlogp ) ;
ASSERT ( dfp = = & hdr - > bestfree [ 0 ] ) ;
2006-03-17 09:27:37 +03:00
ASSERT ( dfp - > length = = prevdup - > length ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( ! dfp [ 1 ] . length ) ;
ASSERT ( ! dfp [ 2 ] . length ) ;
}
}
/*
* The entry before us is free , merge with it .
*/
else if ( prevdup ) {
2011-07-08 16:35:38 +04:00
dfp = xfs_dir2_data_freefind ( hdr , prevdup ) ;
2008-02-14 02:03:29 +03:00
be16_add_cpu ( & prevdup - > length , len ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( prevdup ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) prevdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , prevdup ) ;
/*
* If the previous entry was in the table , the new entry
* is longer , so it will be in the table too . Remove
* the old one and add the new one .
*/
if ( dfp ) {
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeremove ( hdr , dfp , needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , prevdup , needlogp ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Otherwise we need a scan if the new entry is big enough .
*/
2006-03-17 09:26:52 +03:00
else {
2006-03-17 09:27:37 +03:00
needscan = be16_to_cpu ( prevdup - > length ) >
2011-07-08 16:35:38 +04:00
be16_to_cpu ( hdr - > bestfree [ 2 ] . length ) ;
2006-03-17 09:26:52 +03:00
}
2005-04-17 02:20:36 +04:00
}
/*
* The following entry is free , merge with it .
*/
else if ( postdup ) {
2011-07-08 16:35:38 +04:00
dfp = xfs_dir2_data_freefind ( hdr , postdup ) ;
newdup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + offset ) ;
2006-03-17 09:27:37 +03:00
newdup - > freetag = cpu_to_be16 ( XFS_DIR2_DATA_FREE_TAG ) ;
newdup - > length = cpu_to_be16 ( len + be16_to_cpu ( postdup - > length ) ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( newdup ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) newdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , newdup ) ;
/*
* If the following entry was in the table , the new entry
* is longer , so it will be in the table too . Remove
* the old one and add the new one .
*/
if ( dfp ) {
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeremove ( hdr , dfp , needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , newdup , needlogp ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Otherwise we need a scan if the new entry is big enough .
*/
2006-03-17 09:26:52 +03:00
else {
2006-03-17 09:27:37 +03:00
needscan = be16_to_cpu ( newdup - > length ) >
2011-07-08 16:35:38 +04:00
be16_to_cpu ( hdr - > bestfree [ 2 ] . length ) ;
2006-03-17 09:26:52 +03:00
}
2005-04-17 02:20:36 +04:00
}
/*
* Neither neighbor is free . Make a new entry .
*/
else {
2011-07-08 16:35:38 +04:00
newdup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + offset ) ;
2006-03-17 09:27:37 +03:00
newdup - > freetag = cpu_to_be16 ( XFS_DIR2_DATA_FREE_TAG ) ;
newdup - > length = cpu_to_be16 ( len ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( newdup ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) newdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , newdup ) ;
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeinsert ( hdr , newdup , needlogp ) ;
2005-04-17 02:20:36 +04:00
}
* needscanp = needscan ;
}
/*
* Take a byte range out of an existing unused space and make it un - free .
*/
void
xfs_dir2_data_use_free (
xfs_trans_t * tp , /* transaction pointer */
xfs_dabuf_t * bp , /* data block buffer */
xfs_dir2_data_unused_t * dup , /* unused entry */
xfs_dir2_data_aoff_t offset , /* starting offset to use */
xfs_dir2_data_aoff_t len , /* length to use */
int * needlogp , /* out: need to log header */
int * needscanp ) /* out: need regen bestfree */
{
2011-07-08 16:35:38 +04:00
xfs_dir2_data_hdr_t * hdr ; /* data block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_data_free_t * dfp ; /* bestfree pointer */
int matchback ; /* matches end of freespace */
int matchfront ; /* matches start of freespace */
int needscan ; /* need to regen bestfree */
xfs_dir2_data_unused_t * newdup ; /* new unused entry */
xfs_dir2_data_unused_t * newdup2 ; /* another new unused entry */
int oldlen ; /* old unused entry's length */
2011-07-08 16:35:38 +04:00
hdr = bp - > data ;
ASSERT ( be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_DATA_MAGIC | |
be32_to_cpu ( hdr - > magic ) = = XFS_DIR2_BLOCK_MAGIC ) ;
2006-03-17 09:27:37 +03:00
ASSERT ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) ;
2011-07-08 16:35:38 +04:00
ASSERT ( offset > = ( char * ) dup - ( char * ) hdr ) ;
ASSERT ( offset + len < = ( char * ) dup + be16_to_cpu ( dup - > length ) - ( char * ) hdr ) ;
ASSERT ( ( char * ) dup - ( char * ) hdr = = be16_to_cpu ( * xfs_dir2_data_unused_tag_p ( dup ) ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Look up the entry in the bestfree table .
*/
2011-07-08 16:35:38 +04:00
dfp = xfs_dir2_data_freefind ( hdr , dup ) ;
2006-03-17 09:27:37 +03:00
oldlen = be16_to_cpu ( dup - > length ) ;
2011-07-08 16:35:38 +04:00
ASSERT ( dfp | | oldlen < = be16_to_cpu ( hdr - > bestfree [ 2 ] . length ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Check for alignment with front and back of the entry .
*/
2011-07-08 16:35:38 +04:00
matchfront = ( char * ) dup - ( char * ) hdr = = offset ;
matchback = ( char * ) dup + oldlen - ( char * ) hdr = = offset + len ;
2005-04-17 02:20:36 +04:00
ASSERT ( * needscanp = = 0 ) ;
needscan = 0 ;
/*
* If we matched it exactly we just need to get rid of it from
* the bestfree table .
*/
if ( matchfront & & matchback ) {
if ( dfp ) {
2011-07-08 16:35:38 +04:00
needscan = ( hdr - > bestfree [ 2 ] . offset ! = 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! needscan )
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeremove ( hdr , dfp , needlogp ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* We match the first part of the entry .
* Make a new entry with the remaining freespace .
*/
else if ( matchfront ) {
2011-07-08 16:35:38 +04:00
newdup = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + offset + len ) ;
2006-03-17 09:27:37 +03:00
newdup - > freetag = cpu_to_be16 ( XFS_DIR2_DATA_FREE_TAG ) ;
newdup - > length = cpu_to_be16 ( oldlen - len ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( newdup ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) newdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , newdup ) ;
/*
* If it was in the table , remove it and add the new one .
*/
if ( dfp ) {
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeremove ( hdr , dfp , needlogp ) ;
dfp = xfs_dir2_data_freeinsert ( hdr , newdup , needlogp ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( dfp ! = NULL ) ;
2006-03-17 09:27:37 +03:00
ASSERT ( dfp - > length = = newdup - > length ) ;
2011-07-08 16:35:38 +04:00
ASSERT ( be16_to_cpu ( dfp - > offset ) = = ( char * ) newdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
/*
* If we got inserted at the last slot ,
* that means we don ' t know if there was a better
* choice for the last slot , or not . Rescan .
*/
2011-07-08 16:35:38 +04:00
needscan = dfp = = & hdr - > bestfree [ 2 ] ;
2005-04-17 02:20:36 +04:00
}
}
/*
* We match the last part of the entry .
* Trim the allocated space off the tail of the entry .
*/
else if ( matchback ) {
newdup = dup ;
2011-07-08 16:35:38 +04:00
newdup - > length = cpu_to_be16 ( ( ( char * ) hdr + offset ) - ( char * ) newdup ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( newdup ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) newdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , newdup ) ;
/*
* If it was in the table , remove it and add the new one .
*/
if ( dfp ) {
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeremove ( hdr , dfp , needlogp ) ;
dfp = xfs_dir2_data_freeinsert ( hdr , newdup , needlogp ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( dfp ! = NULL ) ;
2006-03-17 09:27:37 +03:00
ASSERT ( dfp - > length = = newdup - > length ) ;
2011-07-08 16:35:38 +04:00
ASSERT ( be16_to_cpu ( dfp - > offset ) = = ( char * ) newdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
/*
* If we got inserted at the last slot ,
* that means we don ' t know if there was a better
* choice for the last slot , or not . Rescan .
*/
2011-07-08 16:35:38 +04:00
needscan = dfp = = & hdr - > bestfree [ 2 ] ;
2005-04-17 02:20:36 +04:00
}
}
/*
* Poking out the middle of an entry .
* Make two new entries .
*/
else {
newdup = dup ;
2011-07-08 16:35:38 +04:00
newdup - > length = cpu_to_be16 ( ( ( char * ) hdr + offset ) - ( char * ) newdup ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( newdup ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) newdup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , newdup ) ;
2011-07-08 16:35:38 +04:00
newdup2 = ( xfs_dir2_data_unused_t * ) ( ( char * ) hdr + offset + len ) ;
2006-03-17 09:27:37 +03:00
newdup2 - > freetag = cpu_to_be16 ( XFS_DIR2_DATA_FREE_TAG ) ;
newdup2 - > length = cpu_to_be16 ( oldlen - len - be16_to_cpu ( newdup - > length ) ) ;
2007-06-28 10:43:50 +04:00
* xfs_dir2_data_unused_tag_p ( newdup2 ) =
2011-07-08 16:35:38 +04:00
cpu_to_be16 ( ( char * ) newdup2 - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , newdup2 ) ;
/*
* If the old entry was in the table , we need to scan
* if the 3 rd entry was valid , since these entries
* are smaller than the old one .
* If we don ' t need to scan that means there were 1 or 2
* entries in the table , and removing the old and adding
* the 2 new will work .
*/
if ( dfp ) {
2011-07-08 16:35:38 +04:00
needscan = ( hdr - > bestfree [ 2 ] . length ! = 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! needscan ) {
2011-07-08 16:35:38 +04:00
xfs_dir2_data_freeremove ( hdr , dfp , needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , newdup , needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , newdup2 ,
needlogp ) ;
2005-04-17 02:20:36 +04:00
}
}
}
* needscanp = needscan ;
}