2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2002 , 2005 Silicon Graphics , Inc .
2013-04-03 09:11:20 +04:00
* Copyright ( c ) 2013 Red Hat , Inc .
2005-11-02 06:58:39 +03:00
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_fs.h"
2013-10-23 03:51:50 +04:00
# include "xfs_format.h"
2013-10-23 03:50:10 +04:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2005-04-17 02:20:36 +04:00
# include "xfs_sb.h"
2007-08-28 08:00:13 +04:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# include "xfs_mount.h"
2013-10-15 02:17:51 +04:00
# include "xfs_da_format.h"
2005-11-02 06:38:42 +03:00
# include "xfs_da_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_inode.h"
2013-08-12 14:49:37 +04:00
# include "xfs_dir2.h"
2011-07-13 15:43:48 +04:00
# include "xfs_dir2_priv.h"
2005-04-17 02:20:36 +04:00
# include "xfs_error.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2013-04-03 09:11:22 +04:00
# include "xfs_buf_item.h"
# include "xfs_cksum.h"
2005-04-17 02:20:36 +04:00
/*
* Check the consistency of the data block .
* The input can also be a block - format directory .
2012-11-12 15:54:12 +04:00
* Return 0 is the buffer is good , otherwise an error .
2005-04-17 02:20:36 +04:00
*/
2012-11-12 15:54:12 +04:00
int
2013-04-03 09:11:22 +04:00
__xfs_dir3_data_check (
2012-06-22 12:50:14 +04:00
struct xfs_inode * dp , /* incore inode pointer */
struct xfs_buf * bp ) /* data block's buffer */
2005-04-17 02:20:36 +04:00
{
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 ;
2013-10-29 15:11:48 +04:00
const struct xfs_dir_ops * ops ;
2005-04-17 02:20:36 +04:00
2012-11-12 15:54:12 +04:00
mp = bp - > b_target - > bt_mount ;
2011-07-08 16:35:38 +04:00
2013-10-29 15:11:48 +04:00
/*
2013-10-29 15:11:50 +04:00
* We can be passed a null dp here from a verifier , so we need to go the
* hard way to get them .
2013-10-29 15:11:48 +04:00
*/
2013-10-29 15:11:50 +04:00
ops = xfs_dir_get_ops ( mp , dp ) ;
2013-10-29 15:11:48 +04:00
2013-10-29 15:11:56 +04:00
hdr = bp - > b_addr ;
p = ( char * ) ops - > data_entry_p ( hdr ) ;
2012-11-12 15:54:12 +04:00
switch ( hdr - > magic ) {
2013-04-03 09:11:20 +04:00
case cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) :
2012-11-12 15:54:12 +04:00
case cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) :
2011-07-08 16:35:38 +04:00
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 ;
2013-10-29 15:11:56 +04:00
/*
* The number of leaf entries is limited by the size of the
* block and the amount of space used by the data entries .
* We don ' t know how much space is used by the data entries yet ,
* so just ensure that the count falls somewhere inside the
* block right now .
*/
XFS_WANT_CORRUPTED_RETURN ( be32_to_cpu ( btp - > count ) <
( ( char * ) btp - p ) / sizeof ( struct xfs_dir2_leaf_entry ) ) ;
2012-11-12 15:54:12 +04:00
break ;
2013-04-03 09:11:22 +04:00
case cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) :
2012-11-12 15:54:12 +04:00
case cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) :
2011-07-08 16:35:38 +04:00
endp = ( char * ) hdr + mp - > m_dirblksize ;
2012-11-12 15:54:12 +04:00
break ;
default :
XFS_ERROR_REPORT ( " Bad Magic " , XFS_ERRLEVEL_LOW , mp ) ;
return EFSCORRUPTED ;
2011-07-08 16:35:38 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Account for zero bestfree entries .
*/
2013-10-29 15:11:56 +04:00
bf = ops - > data_bestfree_p ( hdr ) ;
count = lastfree = freeseen = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! bf [ 0 ] . length ) {
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( ! bf [ 0 ] . offset ) ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < 0 ;
}
if ( ! bf [ 1 ] . length ) {
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( ! bf [ 1 ] . offset ) ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < 1 ;
}
if ( ! bf [ 2 ] . length ) {
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( ! bf [ 2 ] . offset ) ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < 2 ;
}
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( be16_to_cpu ( bf [ 0 ] . length ) > =
be16_to_cpu ( bf [ 1 ] . length ) ) ;
XFS_WANT_CORRUPTED_RETURN ( 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 ) {
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( lastfree = = 0 ) ;
XFS_WANT_CORRUPTED_RETURN (
be16_to_cpu ( * xfs_dir2_data_unused_tag_p ( dup ) ) = =
( char * ) dup - ( char * ) hdr ) ;
2013-10-29 15:11:49 +04:00
dfp = xfs_dir2_data_freefind ( hdr , bf , dup ) ;
2005-04-17 02:20:36 +04:00
if ( dfp ) {
i = ( int ) ( dfp - bf ) ;
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN (
( freeseen & ( 1 < < i ) ) = = 0 ) ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < i ;
2006-03-17 09:26:52 +03:00
} else {
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN (
be16_to_cpu ( dup - > length ) < =
be16_to_cpu ( bf [ 2 ] . length ) ) ;
2006-03-17 09:26:52 +03:00
}
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 ;
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( dep - > namelen ! = 0 ) ;
XFS_WANT_CORRUPTED_RETURN (
! xfs_dir_ino_validate ( mp , be64_to_cpu ( dep - > inumber ) ) ) ;
XFS_WANT_CORRUPTED_RETURN (
2013-10-29 15:11:48 +04:00
be16_to_cpu ( * ops - > data_entry_tag_p ( dep ) ) = =
2012-11-12 15:54:12 +04:00
( char * ) dep - ( char * ) hdr ) ;
2013-08-12 14:50:10 +04:00
XFS_WANT_CORRUPTED_RETURN (
2013-10-29 15:11:48 +04:00
ops - > data_get_ftype ( dep ) < XFS_DIR3_FT_MAX ) ;
2005-04-17 02:20:36 +04:00
count + + ;
lastfree = 0 ;
2013-04-03 09:11:20 +04:00
if ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_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 ;
}
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( i < be32_to_cpu ( btp - > count ) ) ;
2005-04-17 02:20:36 +04:00
}
2013-10-29 15:11:48 +04:00
p + = ops - > data_entsize ( dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Need to have seen all the entries and all the bestfree slots .
*/
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( freeseen = = 7 ) ;
2013-04-03 09:11:20 +04:00
if ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) {
2006-03-17 09:27:56 +03:00
for ( i = stale = 0 ; i < be32_to_cpu ( btp - > count ) ; i + + ) {
2011-07-08 16:36:05 +04:00
if ( lep [ i ] . address = =
cpu_to_be32 ( XFS_DIR2_NULL_DATAPTR ) )
2005-04-17 02:20:36 +04:00
stale + + ;
if ( i > 0 )
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN (
be32_to_cpu ( lep [ i ] . hashval ) > =
be32_to_cpu ( lep [ i - 1 ] . hashval ) ) ;
2005-04-17 02:20:36 +04:00
}
2012-11-12 15:54:12 +04:00
XFS_WANT_CORRUPTED_RETURN ( count = =
be32_to_cpu ( btp - > count ) - be32_to_cpu ( btp - > stale ) ) ;
XFS_WANT_CORRUPTED_RETURN ( stale = = be32_to_cpu ( btp - > stale ) ) ;
2005-04-17 02:20:36 +04:00
}
2012-11-12 15:54:12 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2013-04-03 09:11:22 +04:00
static bool
xfs_dir3_data_verify (
2012-11-12 15:54:14 +04:00
struct xfs_buf * bp )
{
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
2013-04-03 09:11:22 +04:00
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
2012-11-12 15:54:14 +04:00
2013-04-03 09:11:22 +04:00
if ( xfs_sb_version_hascrc ( & mp - > m_sb ) ) {
if ( hdr3 - > magic ! = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) )
return false ;
if ( ! uuid_equal ( & hdr3 - > uuid , & mp - > m_sb . sb_uuid ) )
return false ;
if ( be64_to_cpu ( hdr3 - > blkno ) ! = bp - > b_bn )
return false ;
} else {
if ( hdr3 - > magic ! = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) )
return false ;
2012-11-12 15:54:14 +04:00
}
2013-04-03 09:11:22 +04:00
if ( __xfs_dir3_data_check ( NULL , bp ) )
return false ;
return true ;
2012-11-14 10:52:32 +04:00
}
2012-11-14 10:54:40 +04:00
/*
* Readahead of the first block of the directory when it is opened is completely
* oblivious to the format of the directory . Hence we can either get a block
* format buffer or a data format buffer on readahead .
*/
static void
2013-04-03 09:11:22 +04:00
xfs_dir3_data_reada_verify (
2012-11-14 10:54:40 +04:00
struct xfs_buf * bp )
{
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
struct xfs_dir2_data_hdr * hdr = bp - > b_addr ;
switch ( hdr - > magic ) {
case cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) :
2013-04-03 09:11:20 +04:00
case cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) :
bp - > b_ops = & xfs_dir3_block_buf_ops ;
2012-11-14 10:54:40 +04:00
bp - > b_ops - > verify_read ( bp ) ;
return ;
case cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) :
2013-04-03 09:11:22 +04:00
case cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) :
xfs_dir3_data_verify ( bp ) ;
2012-11-14 10:54:40 +04:00
return ;
default :
XFS_CORRUPTION_ERROR ( __func__ , XFS_ERRLEVEL_LOW , mp , hdr ) ;
xfs_buf_ioerror ( bp , EFSCORRUPTED ) ;
break ;
}
}
static void
2013-04-03 09:11:22 +04:00
xfs_dir3_data_read_verify (
2012-11-14 10:52:32 +04:00
struct xfs_buf * bp )
{
2013-04-03 09:11:22 +04:00
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
if ( ( xfs_sb_version_hascrc ( & mp - > m_sb ) & &
! xfs_verify_cksum ( bp - > b_addr , BBTOB ( bp - > b_length ) ,
XFS_DIR3_DATA_CRC_OFF ) ) | |
! xfs_dir3_data_verify ( bp ) ) {
XFS_CORRUPTION_ERROR ( __func__ , XFS_ERRLEVEL_LOW , mp , bp - > b_addr ) ;
xfs_buf_ioerror ( bp , EFSCORRUPTED ) ;
}
2012-11-14 10:52:32 +04:00
}
2012-11-12 15:54:14 +04:00
2012-11-14 10:53:49 +04:00
static void
2013-04-03 09:11:22 +04:00
xfs_dir3_data_write_verify (
2012-11-14 10:52:32 +04:00
struct xfs_buf * bp )
{
2013-04-03 09:11:22 +04:00
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
struct xfs_buf_log_item * bip = bp - > b_fspriv ;
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
if ( ! xfs_dir3_data_verify ( bp ) ) {
XFS_CORRUPTION_ERROR ( __func__ , XFS_ERRLEVEL_LOW , mp , bp - > b_addr ) ;
xfs_buf_ioerror ( bp , EFSCORRUPTED ) ;
return ;
}
if ( ! xfs_sb_version_hascrc ( & mp - > m_sb ) )
return ;
if ( bip )
hdr3 - > lsn = cpu_to_be64 ( bip - > bli_item . li_lsn ) ;
xfs_update_cksum ( bp - > b_addr , BBTOB ( bp - > b_length ) , XFS_DIR3_DATA_CRC_OFF ) ;
2012-11-12 15:54:14 +04:00
}
2013-04-03 09:11:22 +04:00
const struct xfs_buf_ops xfs_dir3_data_buf_ops = {
. verify_read = xfs_dir3_data_read_verify ,
. verify_write = xfs_dir3_data_write_verify ,
2012-11-14 10:54:40 +04:00
} ;
2013-04-03 09:11:22 +04:00
static const struct xfs_buf_ops xfs_dir3_data_reada_buf_ops = {
. verify_read = xfs_dir3_data_reada_verify ,
. verify_write = xfs_dir3_data_write_verify ,
2012-11-14 10:54:40 +04:00
} ;
2012-11-14 10:52:32 +04:00
2012-11-12 15:54:14 +04:00
int
2013-04-03 09:11:22 +04:00
xfs_dir3_data_read (
2012-11-12 15:54:14 +04:00
struct xfs_trans * tp ,
struct xfs_inode * dp ,
xfs_dablk_t bno ,
xfs_daddr_t mapped_bno ,
struct xfs_buf * * bpp )
{
2013-04-03 09:11:29 +04:00
int err ;
err = xfs_da_read_buf ( tp , dp , bno , mapped_bno , bpp ,
2013-04-03 09:11:22 +04:00
XFS_DATA_FORK , & xfs_dir3_data_buf_ops ) ;
2013-04-03 09:11:29 +04:00
if ( ! err & & tp )
2013-04-03 09:11:30 +04:00
xfs_trans_buf_set_type ( tp , * bpp , XFS_BLFT_DIR_DATA_BUF ) ;
2013-04-03 09:11:29 +04:00
return err ;
2012-11-12 15:54:14 +04:00
}
2012-11-12 15:54:18 +04:00
int
2013-04-03 09:11:22 +04:00
xfs_dir3_data_readahead (
2012-11-12 15:54:18 +04:00
struct xfs_trans * tp ,
struct xfs_inode * dp ,
xfs_dablk_t bno ,
xfs_daddr_t mapped_bno )
{
return xfs_da_reada_buf ( tp , dp , bno , mapped_bno ,
2013-04-03 09:11:22 +04:00
XFS_DATA_FORK , & xfs_dir3_data_reada_buf_ops ) ;
2012-11-12 15:54:18 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Given a data block and an unused entry from that block ,
* return the bestfree entry if any that corresponds to it .
*/
2013-08-12 14:49:37 +04:00
xfs_dir2_data_free_t *
2005-04-17 02:20:36 +04:00
xfs_dir2_data_freefind (
2013-10-29 15:11:49 +04:00
struct xfs_dir2_data_hdr * hdr , /* data block header */
struct xfs_dir2_data_free * bf , /* bestfree table pointer */
struct xfs_dir2_data_unused * dup ) /* unused space */
2005-04-17 02:20:36 +04:00
{
xfs_dir2_data_free_t * dfp ; /* bestfree entry */
xfs_dir2_data_aoff_t off ; /* offset value needed */
2013-08-12 14:49:51 +04:00
# ifdef DEBUG
2005-04-17 02:20:36 +04:00
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 ) ;
2013-04-03 09:11:20 +04:00
2013-08-12 14:49:51 +04:00
# ifdef DEBUG
2005-04-17 02:20:36 +04:00
/*
* 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:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:22 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
for ( dfp = & bf [ 0 ] , seenzero = matched = 0 ;
dfp < & bf [ 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 ) ) ;
2013-04-03 09:11:20 +04:00
if ( dfp > & bf [ 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 ) <
2013-04-03 09:11:20 +04:00
be16_to_cpu ( bf [ 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 .
*/
2013-04-03 09:11:20 +04:00
for ( dfp = & bf [ 0 ] ; dfp < & bf [ XFS_DIR2_DATA_FD_COUNT ] ; dfp + + ) {
2005-04-17 02:20:36 +04:00
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 (
2013-10-29 15:11:49 +04:00
struct xfs_dir2_data_hdr * hdr , /* data block pointer */
struct xfs_dir2_data_free * dfp , /* bestfree table pointer */
struct xfs_dir2_data_unused * dup , /* unused space */
2005-04-17 02:20:36 +04:00
int * loghead ) /* log the data header (out) */
{
xfs_dir2_data_free_t new ; /* new bestfree entry */
2011-07-08 16:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
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 (
2013-10-29 15:11:49 +04:00
struct xfs_dir2_data_hdr * hdr , /* data block header */
struct xfs_dir2_data_free * bf , /* bestfree table pointer */
struct xfs_dir2_data_free * dfp , /* bestfree entry pointer */
2005-04-17 02:20:36 +04:00
int * loghead ) /* out: log data header */
{
2013-04-03 09:11:20 +04:00
2011-07-08 16:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
2005-04-17 02:20:36 +04:00
/*
* It ' s the first entry , slide the next 2 up .
*/
2013-04-03 09:11:20 +04:00
if ( dfp = = & bf [ 0 ] ) {
bf [ 0 ] = bf [ 1 ] ;
bf [ 1 ] = bf [ 2 ] ;
2005-04-17 02:20:36 +04:00
}
/*
* It ' s the second entry , slide the 3 rd entry up .
*/
2013-04-03 09:11:20 +04:00
else if ( dfp = = & bf [ 1 ] )
bf [ 1 ] = bf [ 2 ] ;
2005-04-17 02:20:36 +04:00
/*
* Must be the last entry .
*/
else
2013-04-03 09:11:20 +04:00
ASSERT ( dfp = = & bf [ 2 ] ) ;
2005-04-17 02:20:36 +04:00
/*
* Clear the 3 rd entry , must be zero now .
*/
2013-04-03 09:11:20 +04:00
bf [ 2 ] . length = 0 ;
bf [ 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 (
2013-10-29 15:11:48 +04:00
struct xfs_inode * dp ,
struct xfs_dir2_data_hdr * hdr ,
int * loghead )
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 */
2013-04-03 09:11:20 +04:00
struct xfs_dir2_data_free * bf ;
2005-04-17 02:20:36 +04:00
char * endp ; /* end of block's data */
char * p ; /* current entry pointer */
2011-07-08 16:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:22 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Start by clearing the table .
*/
2013-10-29 15:11:49 +04:00
bf = dp - > d_ops - > data_bestfree_p ( hdr ) ;
2013-04-03 09:11:20 +04:00
memset ( bf , 0 , sizeof ( * bf ) * XFS_DIR2_DATA_FD_COUNT ) ;
2005-04-17 02:20:36 +04:00
* loghead = 1 ;
/*
* Set up pointers .
*/
2013-10-29 15:11:49 +04:00
p = ( char * ) dp - > d_ops - > data_entry_p ( hdr ) ;
2013-04-03 09:11:20 +04:00
if ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) {
2013-10-29 15:11:48 +04:00
btp = xfs_dir2_block_tail_p ( dp - > i_mount , 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
2013-10-29 15:11:48 +04:00
endp = ( char * ) hdr + dp - > i_mount - > 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 ) ) ) ;
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeinsert ( hdr , bf , 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 = =
2013-10-29 15:11:48 +04:00
be16_to_cpu ( * dp - > d_ops - > data_entry_tag_p ( dep ) ) ) ;
p + = dp - > d_ops - > 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 */
2013-04-03 09:11:20 +04:00
xfs_dir3_data_init (
2005-04-17 02:20:36 +04:00
xfs_da_args_t * args , /* directory operation args */
xfs_dir2_db_t blkno , /* logical dir block number */
2012-06-22 12:50:14 +04:00
struct xfs_buf * * bpp ) /* output block buffer */
2005-04-17 02:20:36 +04:00
{
2012-06-22 12:50:14 +04:00
struct xfs_buf * 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 */
2013-04-03 09:11:20 +04:00
struct xfs_dir2_data_free * bf ;
2005-04-17 02:20:36 +04:00
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 ) ;
2012-11-14 10:53:49 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
2013-04-03 09:11:22 +04:00
bp - > b_ops = & xfs_dir3_data_buf_ops ;
2013-04-03 09:11:30 +04:00
xfs_trans_buf_set_type ( tp , bp , XFS_BLFT_DIR_DATA_BUF ) ;
2011-07-08 16:35:38 +04:00
2005-04-17 02:20:36 +04:00
/*
* Initialize the header .
*/
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2013-04-03 09:11:20 +04:00
if ( xfs_sb_version_hascrc ( & mp - > m_sb ) ) {
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
memset ( hdr3 , 0 , sizeof ( * hdr3 ) ) ;
hdr3 - > magic = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) ;
hdr3 - > blkno = cpu_to_be64 ( bp - > b_bn ) ;
hdr3 - > owner = cpu_to_be64 ( dp - > i_ino ) ;
uuid_copy ( & hdr3 - > uuid , & mp - > m_sb . sb_uuid ) ;
} else
hdr - > magic = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) ;
2013-10-29 15:11:49 +04:00
bf = dp - > d_ops - > data_bestfree_p ( hdr ) ;
2013-10-30 02:15:02 +04:00
bf [ 0 ] . offset = cpu_to_be16 ( dp - > d_ops - > data_entry_offset ) ;
2005-04-17 02:20:36 +04:00
for ( i = 1 ; i < XFS_DIR2_DATA_FD_COUNT ; i + + ) {
2013-04-03 09:11:20 +04:00
bf [ i ] . length = 0 ;
bf [ 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 .
*/
2013-10-29 15:11:49 +04:00
dup = dp - > d_ops - > data_unused_p ( hdr ) ;
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
2013-10-30 02:15:02 +04:00
t = mp - > m_dirblksize - ( uint ) dp - > d_ops - > data_entry_offset ;
2013-04-03 09:11:20 +04:00
bf [ 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 .
*/
2013-10-29 15:11:49 +04:00
xfs_dir2_data_log_header ( tp , dp , bp ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_data_log_unused ( tp , bp , dup ) ;
* bpp = bp ;
return 0 ;
}
/*
* Log an active data entry from the block .
*/
void
xfs_dir2_data_log_entry (
2012-06-22 12:50:14 +04:00
struct xfs_trans * tp ,
2013-10-29 15:11:48 +04:00
struct xfs_inode * dp ,
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ,
2005-04-17 02:20:36 +04:00
xfs_dir2_data_entry_t * dep ) /* data entry pointer */
{
2013-08-12 14:50:09 +04:00
struct xfs_dir2_data_hdr * hdr = bp - > b_addr ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:22 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
2011-07-08 16:35:38 +04:00
2012-06-22 12:50:14 +04:00
xfs_trans_log_buf ( tp , bp , ( uint ) ( ( char * ) dep - ( char * ) hdr ) ,
2013-10-29 15:11:48 +04:00
( uint ) ( ( char * ) ( dp - > d_ops - > 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 (
2012-06-22 12:50:14 +04:00
struct xfs_trans * tp ,
2013-10-29 15:11:49 +04:00
struct xfs_inode * dp ,
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp )
2005-04-17 02:20:36 +04:00
{
2013-10-29 15:11:49 +04:00
# ifdef DEBUG
struct xfs_dir2_data_hdr * hdr = bp - > b_addr ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:22 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
2013-10-29 15:11:49 +04:00
# endif
2011-07-08 16:35:38 +04:00
2013-10-30 02:15:02 +04:00
xfs_trans_log_buf ( tp , bp , 0 , dp - > d_ops - > data_entry_offset - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Log a data unused entry .
*/
void
xfs_dir2_data_log_unused (
2012-06-22 12:50:14 +04:00
struct xfs_trans * tp ,
struct xfs_buf * bp ,
2005-04-17 02:20:36 +04:00
xfs_dir2_data_unused_t * dup ) /* data unused pointer */
{
2012-06-22 12:50:14 +04:00
xfs_dir2_data_hdr_t * hdr = bp - > b_addr ;
2011-07-08 16:35:38 +04:00
2011-07-08 16:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:22 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Log the first part of the unused entry .
*/
2012-06-22 12:50:14 +04:00
xfs_trans_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 .
*/
2012-06-22 12:50:14 +04:00
xfs_trans_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 (
2012-06-22 12:50:14 +04:00
struct xfs_trans * tp ,
2013-10-29 15:11:49 +04:00
struct xfs_inode * dp ,
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ,
2005-04-17 02:20:36 +04:00
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 */
2013-04-03 09:11:20 +04:00
struct xfs_dir2_data_free * bf ;
2005-04-17 02:20:36 +04:00
mp = tp - > t_mountp ;
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2011-07-08 16:35:38 +04:00
2005-04-17 02:20:36 +04:00
/*
* Figure out where the end of the data area is .
*/
2013-04-03 09:11:22 +04:00
if ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) )
2011-07-08 16:35:38 +04:00
endptr = ( char * ) hdr + mp - > m_dirblksize ;
2005-04-17 02:20:36 +04:00
else {
xfs_dir2_block_tail_t * btp ; /* block tail */
2013-04-03 09:11:20 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) ) ;
2011-07-08 16:35:38 +04:00
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 .
*/
2013-10-30 02:15:02 +04:00
if ( offset > dp - > d_ops - > data_entry_offset ) {
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 .
*/
2013-10-29 15:11:49 +04:00
bf = dp - > d_ops - > data_bestfree_p ( hdr ) ;
2005-04-17 02:20:36 +04:00
if ( prevdup & & postdup ) {
xfs_dir2_data_free_t * dfp2 ; /* another bestfree pointer */
/*
* See if prevdup and / or postdup are in bestfree table .
*/
2013-10-29 15:11:49 +04:00
dfp = xfs_dir2_data_freefind ( hdr , bf , prevdup ) ;
dfp2 = xfs_dir2_data_freefind ( hdr , bf , 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 .
*/
2013-04-03 09:11:20 +04:00
needscan = ( bf [ 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 ) ;
2013-04-03 09:11:20 +04:00
if ( dfp = = & bf [ 1 ] ) {
dfp = & bf [ 0 ] ;
2005-04-17 02:20:36 +04:00
ASSERT ( dfp2 = = dfp ) ;
2013-04-03 09:11:20 +04:00
dfp2 = & bf [ 1 ] ;
2005-04-17 02:20:36 +04:00
}
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeremove ( hdr , bf , dfp2 , needlogp ) ;
xfs_dir2_data_freeremove ( hdr , bf , dfp , needlogp ) ;
2005-04-17 02:20:36 +04:00
/*
* Now insert the new entry .
*/
2013-10-29 15:11:49 +04:00
dfp = xfs_dir2_data_freeinsert ( hdr , bf , prevdup ,
needlogp ) ;
2013-04-03 09:11:20 +04:00
ASSERT ( dfp = = & bf [ 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 ) {
2013-10-29 15:11:49 +04:00
dfp = xfs_dir2_data_freefind ( hdr , bf , 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 ) {
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeremove ( hdr , bf , dfp , needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , bf , 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 ) >
2013-04-03 09:11:20 +04:00
be16_to_cpu ( bf [ 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 ) {
2013-10-29 15:11:49 +04:00
dfp = xfs_dir2_data_freefind ( hdr , bf , postdup ) ;
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 + 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 ) {
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeremove ( hdr , bf , dfp , needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , bf , 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 ) >
2013-04-03 09:11:20 +04:00
be16_to_cpu ( bf [ 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 ) ;
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeinsert ( hdr , bf , 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 (
2012-06-22 12:50:14 +04:00
struct xfs_trans * tp ,
2013-10-29 15:11:49 +04:00
struct xfs_inode * dp ,
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ,
2005-04-17 02:20:36 +04:00
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 */
2013-04-03 09:11:20 +04:00
struct xfs_dir2_data_free * bf ;
2005-04-17 02:20:36 +04:00
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2011-07-08 16:36:05 +04:00
ASSERT ( hdr - > magic = = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) | |
2013-04-03 09:11:22 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) | |
2013-04-03 09:11:20 +04:00
hdr - > magic = = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) | |
hdr - > magic = = cpu_to_be32 ( XFS_DIR3_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 .
*/
2006-03-17 09:27:37 +03:00
oldlen = be16_to_cpu ( dup - > length ) ;
2013-10-29 15:11:49 +04:00
bf = dp - > d_ops - > data_bestfree_p ( hdr ) ;
dfp = xfs_dir2_data_freefind ( hdr , bf , dup ) ;
2013-04-03 09:11:20 +04:00
ASSERT ( dfp | | oldlen < = be16_to_cpu ( bf [ 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 ) {
2013-04-03 09:11:20 +04:00
needscan = ( bf [ 2 ] . offset ! = 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! needscan )
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeremove ( hdr , bf , 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 ) {
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeremove ( hdr , bf , dfp , needlogp ) ;
dfp = xfs_dir2_data_freeinsert ( hdr , bf , 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 .
*/
2013-04-03 09:11:20 +04:00
needscan = dfp = = & bf [ 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 ) {
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeremove ( hdr , bf , dfp , needlogp ) ;
dfp = xfs_dir2_data_freeinsert ( hdr , bf , 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 .
*/
2013-04-03 09:11:20 +04:00
needscan = dfp = = & bf [ 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 ) {
2013-04-03 09:11:20 +04:00
needscan = ( bf [ 2 ] . length ! = 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! needscan ) {
2013-10-29 15:11:49 +04:00
xfs_dir2_data_freeremove ( hdr , bf , dfp ,
needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , bf , newdup ,
needlogp ) ;
xfs_dir2_data_freeinsert ( hdr , bf , newdup2 ,
2011-07-08 16:35:38 +04:00
needlogp ) ;
2005-04-17 02:20:36 +04:00
}
}
}
* needscanp = needscan ;
}