2018-06-06 05:42:14 +03:00
// SPDX-License-Identifier: GPL-2.0
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
*/
# include "xfs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_fs.h"
2019-06-29 05:25:35 +03:00
# include "xfs_shared.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_mount.h"
# include "xfs_inode.h"
2013-08-12 14:49:37 +04:00
# include "xfs_dir2.h"
2019-11-09 02:05:37 +03: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"
2015-10-12 07:59:25 +03:00
# include "xfs_log.h"
2005-04-17 02:20:36 +04:00
2018-06-04 02:10:13 +03:00
static xfs_failaddr_t xfs_dir2_data_freefind_verify (
struct xfs_dir2_data_hdr * hdr , struct xfs_dir2_data_free * bf ,
struct xfs_dir2_data_unused * dup ,
struct xfs_dir2_data_free * * bf_ent ) ;
2019-11-09 02:05:39 +03:00
struct xfs_dir2_data_free *
xfs_dir2_data_bestfree_p (
struct xfs_mount * mp ,
struct xfs_dir2_data_hdr * hdr )
{
2021-08-19 04:46:55 +03:00
if ( xfs_has_crc ( mp ) )
2019-11-09 02:05:39 +03:00
return ( ( struct xfs_dir3_data_hdr * ) hdr ) - > best_free ;
return hdr - > bestfree ;
}
2019-11-09 02:05:37 +03:00
/*
* Pointer to an entry ' s tag word .
*/
__be16 *
xfs_dir2_data_entry_tag_p (
struct xfs_mount * mp ,
struct xfs_dir2_data_entry * dep )
{
return ( __be16 * ) ( ( char * ) dep +
xfs_dir2_data_entsize ( mp , dep - > namelen ) - sizeof ( __be16 ) ) ;
}
2019-11-09 02:05:48 +03:00
uint8_t
xfs_dir2_data_get_ftype (
struct xfs_mount * mp ,
struct xfs_dir2_data_entry * dep )
{
2021-08-19 04:46:55 +03:00
if ( xfs_has_ftype ( mp ) ) {
2019-11-09 02:05:48 +03:00
uint8_t ftype = dep - > name [ dep - > namelen ] ;
if ( likely ( ftype < XFS_DIR3_FT_MAX ) )
return ftype ;
}
return XFS_DIR3_FT_UNKNOWN ;
}
void
xfs_dir2_data_put_ftype (
struct xfs_mount * mp ,
struct xfs_dir2_data_entry * dep ,
uint8_t ftype )
{
ASSERT ( ftype < XFS_DIR3_FT_MAX ) ;
ASSERT ( dep - > namelen ! = 0 ) ;
2021-08-19 04:46:55 +03:00
if ( xfs_has_ftype ( mp ) )
2019-11-09 02:05:48 +03:00
dep - > name [ dep - > namelen ] = ftype ;
}
2019-11-09 02:05:35 +03: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 .
*/
static inline unsigned int
xfs_dir2_data_max_leaf_entries (
struct xfs_da_geometry * geo )
{
return ( geo - > blksize - sizeof ( struct xfs_dir2_block_tail ) -
2019-11-09 02:05:38 +03:00
geo - > data_entry_offset ) /
2019-11-09 02:05:35 +03:00
sizeof ( struct xfs_dir2_leaf_entry ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Check the consistency of the data block .
* The input can also be a block - format directory .
2018-01-08 21:51:03 +03:00
* Return NULL if the buffer is good , otherwise the address of the error .
2005-04-17 02:20:36 +04:00
*/
2018-01-08 21:51:03 +03:00
xfs_failaddr_t
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_free_t * dfp ; /* bestfree entry */
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 */
2019-06-29 05:27:29 +03:00
struct xfs_mount * mp = bp - > b_mount ;
2005-04-17 02:20:36 +04:00
int stale ; /* count of stale leaves */
2008-05-21 10:41:01 +04:00
struct xfs_name name ;
2019-11-09 02:05:35 +03:00
unsigned int offset ;
unsigned int end ;
struct xfs_da_geometry * geo = mp - > m_dir_geo ;
2011-07-08 16:35:38 +04:00
2013-10-29 15:11:48 +04:00
/*
2019-11-09 02:05:48 +03:00
* If this isn ' t a directory , something is seriously wrong . Bail out .
2018-01-09 22:11:42 +03:00
*/
2019-11-09 02:05:48 +03:00
if ( dp & & ! S_ISDIR ( VFS_I ( dp ) - > i_mode ) )
2018-01-09 22:11:42 +03:00
return __this_address ;
2013-10-29 15:11:56 +04:00
hdr = bp - > b_addr ;
2019-11-09 02:05:38 +03:00
offset = geo - > data_entry_offset ;
2013-10-29 15:11:56 +04:00
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 ) :
2014-06-06 09:15:59 +04:00
btp = xfs_dir2_block_tail_p ( geo , hdr ) ;
2007-06-28 10:43:50 +04:00
lep = xfs_dir2_block_leaf_p ( btp ) ;
2013-10-29 15:11:56 +04:00
2018-01-08 21:51:01 +03:00
if ( be32_to_cpu ( btp - > count ) > =
2019-11-09 02:05:38 +03:00
xfs_dir2_data_max_leaf_entries ( geo ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
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 ) :
break ;
default :
2018-01-08 21:51:03 +03:00
return __this_address ;
2011-07-08 16:35:38 +04:00
}
2019-11-09 02:05:36 +03:00
end = xfs_dir3_data_end_offset ( geo , hdr ) ;
if ( ! end )
2018-01-17 05:54:12 +03:00
return __this_address ;
2011-07-08 16:35:38 +04:00
2005-04-17 02:20:36 +04:00
/*
* Account for zero bestfree entries .
*/
2019-11-09 02:05:39 +03:00
bf = xfs_dir2_data_bestfree_p ( mp , hdr ) ;
2013-10-29 15:11:56 +04:00
count = lastfree = freeseen = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! bf [ 0 ] . length ) {
2018-01-08 21:51:01 +03:00
if ( bf [ 0 ] . offset )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < 0 ;
}
if ( ! bf [ 1 ] . length ) {
2018-01-08 21:51:01 +03:00
if ( bf [ 1 ] . offset )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < 1 ;
}
if ( ! bf [ 2 ] . length ) {
2018-01-08 21:51:01 +03:00
if ( bf [ 2 ] . offset )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < 2 ;
}
2012-11-12 15:54:12 +04:00
2018-01-08 21:51:01 +03:00
if ( be16_to_cpu ( bf [ 0 ] . length ) < be16_to_cpu ( bf [ 1 ] . length ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2018-01-08 21:51:01 +03:00
if ( be16_to_cpu ( bf [ 1 ] . length ) < be16_to_cpu ( bf [ 2 ] . length ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
/*
* Loop over the data / unused entries .
*/
2019-11-09 02:05:35 +03:00
while ( offset < end ) {
struct xfs_dir2_data_unused * dup = bp - > b_addr + offset ;
struct xfs_dir2_data_entry * dep = bp - > b_addr + offset ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) {
2018-06-04 02:10:13 +03:00
xfs_failaddr_t fa ;
2018-01-08 21:51:01 +03:00
if ( lastfree ! = 0 )
2018-01-08 21:51:03 +03:00
return __this_address ;
2019-11-09 02:05:35 +03:00
if ( offset + be16_to_cpu ( dup - > length ) > end )
2018-01-08 21:51:03 +03:00
return __this_address ;
2018-01-08 21:51:01 +03:00
if ( be16_to_cpu ( * xfs_dir2_data_unused_tag_p ( dup ) ) ! =
2019-11-09 02:05:35 +03:00
offset )
2018-01-08 21:51:03 +03:00
return __this_address ;
2018-06-04 02:10:13 +03:00
fa = xfs_dir2_data_freefind_verify ( hdr , bf , dup , & dfp ) ;
if ( fa )
return fa ;
2005-04-17 02:20:36 +04:00
if ( dfp ) {
i = ( int ) ( dfp - bf ) ;
2018-01-08 21:51:01 +03:00
if ( ( freeseen & ( 1 < < i ) ) ! = 0 )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
freeseen | = 1 < < i ;
2006-03-17 09:26:52 +03:00
} else {
2018-01-08 21:51:01 +03:00
if ( be16_to_cpu ( dup - > length ) >
be16_to_cpu ( bf [ 2 ] . length ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2006-03-17 09:26:52 +03:00
}
2019-11-09 02:05:35 +03:00
offset + = 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 .
*/
2018-01-08 21:51:01 +03:00
if ( dep - > namelen = = 0 )
2018-01-08 21:51:03 +03:00
return __this_address ;
2021-03-22 19:52:05 +03:00
if ( ! xfs_verify_dir_ino ( mp , be64_to_cpu ( dep - > inumber ) ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2019-11-09 02:05:37 +03:00
if ( offset + xfs_dir2_data_entsize ( mp , dep - > namelen ) > end )
2018-01-08 21:51:03 +03:00
return __this_address ;
2019-11-09 02:05:37 +03:00
if ( be16_to_cpu ( * xfs_dir2_data_entry_tag_p ( mp , dep ) ) ! = offset )
2018-01-08 21:51:03 +03:00
return __this_address ;
2019-11-09 02:05:48 +03:00
if ( xfs_dir2_data_get_ftype ( mp , dep ) > = XFS_DIR3_FT_MAX )
2018-01-08 21:51:03 +03:00
return __this_address ;
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 ) ) {
2014-06-06 09:11:18 +04:00
addr = xfs_dir2_db_off_to_dataptr ( geo , geo - > datablk ,
( xfs_dir2_data_aoff_t )
( ( char * ) dep - ( char * ) hdr ) ) ;
2008-05-21 10:41:01 +04:00
name . name = dep - > name ;
name . len = dep - > namelen ;
2019-11-11 23:59:26 +03:00
hash = xfs_dir2_hashname ( mp , & 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 ;
}
2018-01-08 21:51:01 +03:00
if ( i > = be32_to_cpu ( btp - > count ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
}
2019-11-09 02:05:37 +03:00
offset + = xfs_dir2_data_entsize ( mp , dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Need to have seen all the entries and all the bestfree slots .
*/
2018-01-08 21:51:01 +03:00
if ( freeseen ! = 7 )
2018-01-08 21:51:03 +03:00
return __this_address ;
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 + + ;
2018-01-08 21:51:01 +03:00
if ( i > 0 & & be32_to_cpu ( lep [ i ] . hashval ) <
be32_to_cpu ( lep [ i - 1 ] . hashval ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
}
2018-01-08 21:51:01 +03:00
if ( count ! = be32_to_cpu ( btp - > count ) - be32_to_cpu ( btp - > stale ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2018-01-08 21:51:01 +03:00
if ( stale ! = be32_to_cpu ( btp - > stale ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2005-04-17 02:20:36 +04:00
}
2018-01-08 21:51:03 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2018-01-08 21:51:03 +03:00
# ifdef DEBUG
void
xfs_dir3_data_check (
struct xfs_inode * dp ,
struct xfs_buf * bp )
{
xfs_failaddr_t fa ;
fa = __xfs_dir3_data_check ( dp , bp ) ;
if ( ! fa )
return ;
xfs_corruption_error ( __func__ , XFS_ERRLEVEL_LOW , dp - > i_mount ,
2018-06-04 20:23:54 +03:00
bp - > b_addr , BBTOB ( bp - > b_length ) , __FILE__ , __LINE__ ,
fa ) ;
2018-01-08 21:51:03 +03:00
ASSERT ( 0 ) ;
}
# endif
static xfs_failaddr_t
2013-04-03 09:11:22 +04:00
xfs_dir3_data_verify (
2012-11-12 15:54:14 +04:00
struct xfs_buf * bp )
{
2019-06-29 05:27:29 +03:00
struct xfs_mount * mp = bp - > b_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
2019-02-07 21:45:48 +03:00
if ( ! xfs_verify_magic ( bp , hdr3 - > magic ) )
return __this_address ;
2021-08-19 04:46:55 +03:00
if ( xfs_has_crc ( mp ) ) {
2015-07-29 04:53:31 +03:00
if ( ! uuid_equal ( & hdr3 - > uuid , & mp - > m_sb . sb_meta_uuid ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2021-08-19 04:47:05 +03:00
if ( be64_to_cpu ( hdr3 - > blkno ) ! = xfs_buf_daddr ( bp ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2015-10-12 07:59:25 +03:00
if ( ! xfs_log_check_lsn ( mp , be64_to_cpu ( hdr3 - > lsn ) ) )
2018-01-08 21:51:03 +03:00
return __this_address ;
2012-11-12 15:54:14 +04:00
}
2018-01-08 21:51:01 +03:00
return __xfs_dir3_data_check ( NULL , bp ) ;
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_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 ) :
2015-08-19 03:33:58 +03:00
bp - > b_ops = & xfs_dir3_data_buf_ops ;
bp - > b_ops - > verify_read ( bp ) ;
2012-11-14 10:54:40 +04:00
return ;
default :
2018-01-08 21:51:03 +03:00
xfs_verifier_error ( bp , - EFSCORRUPTED , __this_address ) ;
2012-11-14 10:54:40 +04:00
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 )
{
2019-06-29 05:27:29 +03:00
struct xfs_mount * mp = bp - > b_mount ;
2018-01-08 21:51:03 +03:00
xfs_failaddr_t fa ;
2013-04-03 09:11:22 +04:00
2021-08-19 04:46:37 +03:00
if ( xfs_has_crc ( mp ) & &
2018-01-08 21:51:02 +03:00
! xfs_buf_verify_cksum ( bp , XFS_DIR3_DATA_CRC_OFF ) )
2018-01-08 21:51:03 +03:00
xfs_verifier_error ( bp , - EFSBADCRC , __this_address ) ;
else {
fa = xfs_dir3_data_verify ( bp ) ;
if ( fa )
xfs_verifier_error ( bp , - EFSCORRUPTED , fa ) ;
}
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 )
{
2019-06-29 05:27:29 +03:00
struct xfs_mount * mp = bp - > b_mount ;
2018-01-25 00:38:48 +03:00
struct xfs_buf_log_item * bip = bp - > b_log_item ;
2013-04-03 09:11:22 +04:00
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
2018-01-08 21:51:03 +03:00
xfs_failaddr_t fa ;
2013-04-03 09:11:22 +04:00
2018-01-08 21:51:03 +03:00
fa = xfs_dir3_data_verify ( bp ) ;
if ( fa ) {
xfs_verifier_error ( bp , - EFSCORRUPTED , fa ) ;
2013-04-03 09:11:22 +04:00
return ;
}
2021-08-19 04:46:37 +03:00
if ( ! xfs_has_crc ( mp ) )
2013-04-03 09:11:22 +04:00
return ;
if ( bip )
hdr3 - > lsn = cpu_to_be64 ( bip - > bli_item . li_lsn ) ;
2014-02-27 08:18:23 +04:00
xfs_buf_update_cksum ( bp , XFS_DIR3_DATA_CRC_OFF ) ;
2012-11-12 15:54:14 +04:00
}
2013-04-03 09:11:22 +04:00
const struct xfs_buf_ops xfs_dir3_data_buf_ops = {
2016-01-04 08:10:19 +03:00
. name = " xfs_dir3_data " ,
2019-02-07 21:45:48 +03:00
. magic = { cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) ,
cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) } ,
2013-04-03 09:11:22 +04:00
. verify_read = xfs_dir3_data_read_verify ,
. verify_write = xfs_dir3_data_write_verify ,
2018-01-08 21:51:08 +03:00
. verify_struct = xfs_dir3_data_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 = {
2016-01-04 08:10:19 +03:00
. name = " xfs_dir3_data_reada " ,
2019-02-07 21:45:48 +03:00
. magic = { cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) ,
cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) } ,
2013-04-03 09:11:22 +04:00
. verify_read = xfs_dir3_data_reada_verify ,
. verify_write = xfs_dir3_data_write_verify ,
2012-11-14 10:54:40 +04:00
} ;
2020-03-11 20:37:56 +03:00
static xfs_failaddr_t
xfs_dir3_data_header_check (
struct xfs_inode * dp ,
struct xfs_buf * bp )
{
struct xfs_mount * mp = dp - > i_mount ;
2021-08-19 04:46:55 +03:00
if ( xfs_has_crc ( mp ) ) {
2020-03-11 20:37:56 +03:00
struct xfs_dir3_data_hdr * hdr3 = bp - > b_addr ;
if ( be64_to_cpu ( hdr3 - > hdr . owner ) ! = dp - > i_ino )
return __this_address ;
}
return NULL ;
}
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 ,
2019-11-20 20:46:04 +03:00
unsigned int flags ,
2012-11-12 15:54:14 +04:00
struct xfs_buf * * bpp )
{
2020-03-11 20:37:56 +03:00
xfs_failaddr_t fa ;
2013-04-03 09:11:29 +04:00
int err ;
2019-11-20 20:46:04 +03:00
err = xfs_da_read_buf ( tp , dp , bno , flags , bpp , XFS_DATA_FORK ,
& xfs_dir3_data_buf_ops ) ;
2020-03-11 20:37:56 +03:00
if ( err | | ! * bpp )
return err ;
/* Check things that we can't do in the verifier. */
fa = xfs_dir3_data_header_check ( dp , * bpp ) ;
if ( fa ) {
__xfs_buf_mark_corrupt ( * bpp , fa ) ;
xfs_trans_brelse ( tp , * bpp ) ;
* bpp = NULL ;
return - EFSCORRUPTED ;
}
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_inode * dp ,
xfs_dablk_t bno ,
2019-11-20 20:46:02 +03:00
unsigned int flags )
2012-11-12 15:54:18 +04:00
{
2019-11-20 20:46:02 +03:00
return xfs_da_reada_buf ( dp , bno , flags , 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
/*
2018-06-04 02:10:13 +03:00
* Find the bestfree entry that exactly coincides with unused directory space
* or a verifier error because the bestfree data are bad .
2005-04-17 02:20:36 +04:00
*/
2018-06-04 02:10:13 +03:00
static xfs_failaddr_t
xfs_dir2_data_freefind_verify (
struct xfs_dir2_data_hdr * hdr ,
struct xfs_dir2_data_free * bf ,
struct xfs_dir2_data_unused * dup ,
struct xfs_dir2_data_free * * bf_ent )
2005-04-17 02:20:36 +04:00
{
2018-06-04 02:10:13 +03:00
struct xfs_dir2_data_free * dfp ;
xfs_dir2_data_aoff_t off ;
bool matched = false ;
bool seenzero = false ;
2005-04-17 02:20:36 +04:00
2018-06-04 02:10:13 +03:00
* bf_ent = NULL ;
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
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 .
*/
2018-06-04 02:10:13 +03:00
for ( dfp = & bf [ 0 ] ; dfp < & bf [ XFS_DIR2_DATA_FD_COUNT ] ; dfp + + ) {
2005-04-17 02:20:36 +04:00
if ( ! dfp - > offset ) {
2018-06-04 02:10:13 +03:00
if ( dfp - > length )
return __this_address ;
seenzero = true ;
2005-04-17 02:20:36 +04:00
continue ;
}
2018-06-04 02:10:13 +03:00
if ( seenzero )
return __this_address ;
2006-03-17 09:26:52 +03:00
if ( be16_to_cpu ( dfp - > offset ) = = off ) {
2018-06-04 02:10:13 +03:00
matched = true ;
if ( dfp - > length ! = dup - > length )
return __this_address ;
} else if ( be16_to_cpu ( dfp - > offset ) > off ) {
if ( off + be16_to_cpu ( dup - > length ) >
be16_to_cpu ( dfp - > offset ) )
return __this_address ;
} else {
if ( be16_to_cpu ( dfp - > offset ) +
be16_to_cpu ( dfp - > length ) > off )
return __this_address ;
}
if ( ! matched & &
be16_to_cpu ( dfp - > length ) < be16_to_cpu ( dup - > length ) )
return __this_address ;
if ( dfp > & bf [ 0 ] & &
be16_to_cpu ( dfp [ - 1 ] . length ) < be16_to_cpu ( dfp [ 0 ] . length ) )
return __this_address ;
2005-04-17 02:20:36 +04:00
}
2018-06-04 02:10:13 +03:00
/* Looks ok so far; now try to match up with a bestfree entry. */
* bf_ent = xfs_dir2_data_freefind ( hdr , bf , dup ) ;
return NULL ;
}
/*
* Given a data block and an unused entry from that block ,
* return the bestfree entry if any that corresponds to it .
*/
xfs_dir2_data_free_t *
xfs_dir2_data_freefind (
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 */
{
xfs_dir2_data_free_t * dfp ; /* bestfree entry */
xfs_dir2_data_aoff_t off ; /* offset value needed */
off = ( xfs_dir2_data_aoff_t ) ( ( char * ) dup - ( char * ) hdr ) ;
2005-04-17 02:20:36 +04:00
/*
* 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
2019-11-09 02:06:02 +03:00
xfs_dir2_data_freescan (
2019-11-09 02:05:37 +03:00
struct xfs_mount * mp ,
2019-11-09 02:05:34 +03:00
struct xfs_dir2_data_hdr * hdr ,
int * loghead )
2005-04-17 02:20:36 +04:00
{
2019-11-09 02:05:38 +03:00
struct xfs_da_geometry * geo = mp - > m_dir_geo ;
2019-11-09 02:05:39 +03:00
struct xfs_dir2_data_free * bf = xfs_dir2_data_bestfree_p ( mp , hdr ) ;
2019-11-09 02:05:34 +03:00
void * addr = hdr ;
2019-11-09 02:05:38 +03:00
unsigned int offset = geo - > data_entry_offset ;
2019-11-09 02:05:34 +03:00
unsigned int end ;
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 ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Start by clearing the table .
*/
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 ;
2019-11-09 02:05:34 +03:00
2019-11-09 02:05:38 +03:00
end = xfs_dir3_data_end_offset ( geo , addr ) ;
2019-11-09 02:05:34 +03:00
while ( offset < end ) {
struct xfs_dir2_data_unused * dup = addr + offset ;
struct xfs_dir2_data_entry * dep = addr + offset ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) {
2019-11-09 02:05:34 +03:00
ASSERT ( offset = =
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 ) ;
2019-11-09 02:05:34 +03:00
offset + = be16_to_cpu ( dup - > length ) ;
continue ;
2005-04-17 02:20:36 +04:00
}
2019-11-09 02:05:34 +03:00
2005-04-17 02:20:36 +04:00
/*
* For active entries , check their tags and skip them .
*/
2019-11-09 02:05:37 +03:00
ASSERT ( offset = =
be16_to_cpu ( * xfs_dir2_data_entry_tag_p ( mp , dep ) ) ) ;
2019-11-09 02:05:37 +03:00
offset + = xfs_dir2_data_entsize ( mp , 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 (
2019-11-09 02:05:31 +03:00
struct xfs_da_args * args , /* directory operation args */
xfs_dir2_db_t blkno , /* logical dir block number */
struct xfs_buf * * bpp ) /* output block buffer */
2005-04-17 02:20:36 +04:00
{
2019-11-09 02:05:31 +03:00
struct xfs_trans * tp = args - > trans ;
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2019-11-09 02:05:38 +03:00
struct xfs_da_geometry * geo = args - > geo ;
2019-11-09 02:05:31 +03:00
struct xfs_buf * bp ;
struct xfs_dir2_data_hdr * hdr ;
struct xfs_dir2_data_unused * dup ;
struct xfs_dir2_data_free * bf ;
int error ;
int i ;
2005-04-17 02:20:36 +04:00
/*
* Get the buffer set up for the block .
*/
2014-06-06 09:07:53 +04:00
error = xfs_da_get_buf ( tp , dp , xfs_dir2_db_to_da ( args - > geo , blkno ) ,
2019-11-20 20:46:05 +03:00
& bp , 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 ;
2021-08-19 04:46:37 +03:00
if ( xfs_has_crc ( mp ) ) {
2013-04-03 09:11:20 +04:00
struct xfs_dir3_blk_hdr * hdr3 = bp - > b_addr ;
memset ( hdr3 , 0 , sizeof ( * hdr3 ) ) ;
hdr3 - > magic = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) ;
2021-08-19 04:47:05 +03:00
hdr3 - > blkno = cpu_to_be64 ( xfs_buf_daddr ( bp ) ) ;
2013-04-03 09:11:20 +04:00
hdr3 - > owner = cpu_to_be64 ( dp - > i_ino ) ;
2015-07-29 04:53:31 +03:00
uuid_copy ( & hdr3 - > uuid , & mp - > m_sb . sb_meta_uuid ) ;
2013-04-03 09:11:20 +04:00
} else
hdr - > magic = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) ;
2019-11-09 02:05:39 +03:00
bf = xfs_dir2_data_bestfree_p ( mp , hdr ) ;
2019-11-09 02:05:38 +03:00
bf [ 0 ] . offset = cpu_to_be16 ( geo - > data_entry_offset ) ;
bf [ 0 ] . length = cpu_to_be16 ( geo - > blksize - geo - > 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 .
*/
2019-11-09 02:05:38 +03:00
dup = bp - > b_addr + geo - > data_entry_offset ;
2006-03-17 09:27:37 +03:00
dup - > freetag = cpu_to_be16 ( XFS_DIR2_DATA_FREE_TAG ) ;
2019-11-09 02:05:31 +03:00
dup - > length = bf [ 0 ] . length ;
2011-07-08 16:35:38 +04:00
* xfs_dir2_data_unused_tag_p ( dup ) = cpu_to_be16 ( ( char * ) dup - ( char * ) hdr ) ;
2019-11-09 02:05:31 +03:00
2005-04-17 02:20:36 +04:00
/*
* Log it and return it .
*/
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_header ( args , bp ) ;
xfs_dir2_data_log_unused ( args , bp , dup ) ;
2005-04-17 02:20:36 +04:00
* bpp = bp ;
return 0 ;
}
/*
* Log an active data entry from the block .
*/
void
xfs_dir2_data_log_entry (
2014-06-06 09:20:54 +04:00
struct xfs_da_args * args ,
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 */
{
2019-11-09 02:05:37 +03:00
struct xfs_mount * mp = bp - > b_mount ;
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
2014-06-06 09:20:54 +04:00
xfs_trans_log_buf ( args - > trans , bp , ( uint ) ( ( char * ) dep - ( char * ) hdr ) ,
2019-11-09 02:05:37 +03:00
( uint ) ( ( char * ) ( xfs_dir2_data_entry_tag_p ( mp , 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 (
2014-06-06 09:20:54 +04:00
struct xfs_da_args * args ,
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
2019-11-09 02:05:38 +03:00
xfs_trans_log_buf ( args - > trans , bp , 0 , args - > geo - > data_entry_offset - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Log a data unused entry .
*/
void
xfs_dir2_data_log_unused (
2014-06-06 09:20:54 +04:00
struct xfs_da_args * args ,
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 ) /* 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 .
*/
2014-06-06 09:20:54 +04:00
xfs_trans_log_buf ( args - > trans , 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 .
*/
2014-06-06 09:20:54 +04:00
xfs_trans_log_buf ( args - > trans , 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 (
2014-06-06 09:20:54 +04:00
struct xfs_da_args * args ,
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 */
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 */
2019-11-09 02:05:36 +03:00
unsigned int end ;
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:35:38 +04:00
2005-04-17 02:20:36 +04:00
/*
* Figure out where the end of the data area is .
*/
2019-11-09 02:05:36 +03:00
end = xfs_dir3_data_end_offset ( args - > geo , hdr ) ;
ASSERT ( end ! = 0 ) ;
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 .
*/
2019-11-09 02:05:38 +03:00
if ( offset > args - > geo - > 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 .
*/
2019-11-09 02:05:36 +03:00
if ( offset + len < end ) {
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 .
*/
2019-11-09 02:05:39 +03:00
bf = xfs_dir2_data_bestfree_p ( args - > dp - > i_mount , 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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , bp , prevdup ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , bp , prevdup ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , bp , newdup ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , 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 ;
}
2018-03-23 20:06:51 +03:00
/* Check our free data for obvious signs of corruption. */
static inline xfs_failaddr_t
xfs_dir2_data_check_free (
struct xfs_dir2_data_hdr * hdr ,
struct xfs_dir2_data_unused * dup ,
xfs_dir2_data_aoff_t offset ,
xfs_dir2_data_aoff_t len )
{
if ( hdr - > magic ! = cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) & &
hdr - > magic ! = cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) & &
hdr - > magic ! = cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) & &
hdr - > magic ! = cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) )
return __this_address ;
if ( be16_to_cpu ( dup - > freetag ) ! = XFS_DIR2_DATA_FREE_TAG )
return __this_address ;
if ( offset < ( char * ) dup - ( char * ) hdr )
return __this_address ;
if ( offset + len > ( char * ) dup + be16_to_cpu ( dup - > length ) - ( char * ) hdr )
return __this_address ;
if ( ( char * ) dup - ( char * ) hdr ! =
be16_to_cpu ( * xfs_dir2_data_unused_tag_p ( dup ) ) )
return __this_address ;
return NULL ;
}
/* Sanity-check a new bestfree entry. */
static inline xfs_failaddr_t
xfs_dir2_data_check_new_free (
struct xfs_dir2_data_hdr * hdr ,
struct xfs_dir2_data_free * dfp ,
struct xfs_dir2_data_unused * newdup )
{
if ( dfp = = NULL )
return __this_address ;
if ( dfp - > length ! = newdup - > length )
return __this_address ;
if ( be16_to_cpu ( dfp - > offset ) ! = ( char * ) newdup - ( char * ) hdr )
return __this_address ;
return NULL ;
}
2005-04-17 02:20:36 +04:00
/*
* Take a byte range out of an existing unused space and make it un - free .
*/
2018-03-23 20:06:51 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_dir2_data_use_free (
2014-06-06 09:20:54 +04:00
struct xfs_da_args * args ,
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 */
2018-03-23 20:06:51 +03:00
xfs_dir2_data_unused_t * newdup ; /* new unused entry */
xfs_dir2_data_unused_t * newdup2 ; /* another new unused entry */
struct xfs_dir2_data_free * bf ;
xfs_failaddr_t fa ;
2005-04-17 02:20:36 +04:00
int matchback ; /* matches end of freespace */
int matchfront ; /* matches start of freespace */
int needscan ; /* need to regen bestfree */
int oldlen ; /* old unused entry's length */
2012-06-22 12:50:14 +04:00
hdr = bp - > b_addr ;
2018-03-23 20:06:51 +03:00
fa = xfs_dir2_data_check_free ( hdr , dup , offset , len ) ;
if ( fa )
goto corrupt ;
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 ) ;
2019-11-09 02:05:39 +03:00
bf = xfs_dir2_data_bestfree_p ( args - > dp - > i_mount , hdr ) ;
2013-10-29 15:11:49 +04:00
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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , bp , newdup ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) ;
2018-03-23 20:06:51 +03:00
fa = xfs_dir2_data_check_new_free ( hdr , dfp , newdup ) ;
if ( fa )
goto corrupt ;
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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , bp , newdup ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) ;
2018-03-23 20:06:51 +03:00
fa = xfs_dir2_data_check_new_free ( hdr , dfp , newdup ) ;
if ( fa )
goto corrupt ;
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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , 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 ) ;
2014-06-06 09:20:54 +04:00
xfs_dir2_data_log_unused ( args , bp , newdup2 ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 ;
2018-03-23 20:06:51 +03:00
return 0 ;
corrupt :
xfs_corruption_error ( __func__ , XFS_ERRLEVEL_LOW , args - > dp - > i_mount ,
2018-06-04 20:23:54 +03:00
hdr , sizeof ( * hdr ) , __FILE__ , __LINE__ , fa ) ;
2018-03-23 20:06:51 +03:00
return - EFSCORRUPTED ;
2005-04-17 02:20:36 +04:00
}
2018-01-17 05:54:12 +03:00
/* Find the end of the entry data in a data/block format dir block. */
2019-11-09 02:05:36 +03:00
unsigned int
xfs_dir3_data_end_offset (
2018-01-17 05:54:12 +03:00
struct xfs_da_geometry * geo ,
struct xfs_dir2_data_hdr * hdr )
{
2019-11-09 02:05:36 +03:00
void * p ;
2018-01-17 05:54:12 +03:00
switch ( hdr - > magic ) {
case cpu_to_be32 ( XFS_DIR3_BLOCK_MAGIC ) :
case cpu_to_be32 ( XFS_DIR2_BLOCK_MAGIC ) :
2019-11-09 02:05:36 +03:00
p = xfs_dir2_block_leaf_p ( xfs_dir2_block_tail_p ( geo , hdr ) ) ;
return p - ( void * ) hdr ;
2018-01-17 05:54:12 +03:00
case cpu_to_be32 ( XFS_DIR3_DATA_MAGIC ) :
case cpu_to_be32 ( XFS_DIR2_DATA_MAGIC ) :
2019-11-09 02:05:36 +03:00
return geo - > blksize ;
2018-01-17 05:54:12 +03:00
default :
2019-11-09 02:05:36 +03:00
return 0 ;
2018-01-17 05:54:12 +03:00
}
}