162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2005 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * Copyright (c) 2013 Red Hat, Inc. 562306a36Sopenharmony_ci * All Rights Reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include "xfs.h" 862306a36Sopenharmony_ci#include "xfs_fs.h" 962306a36Sopenharmony_ci#include "xfs_shared.h" 1062306a36Sopenharmony_ci#include "xfs_format.h" 1162306a36Sopenharmony_ci#include "xfs_log_format.h" 1262306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1362306a36Sopenharmony_ci#include "xfs_bit.h" 1462306a36Sopenharmony_ci#include "xfs_mount.h" 1562306a36Sopenharmony_ci#include "xfs_inode.h" 1662306a36Sopenharmony_ci#include "xfs_dir2.h" 1762306a36Sopenharmony_ci#include "xfs_dir2_priv.h" 1862306a36Sopenharmony_ci#include "xfs_trans.h" 1962306a36Sopenharmony_ci#include "xfs_bmap.h" 2062306a36Sopenharmony_ci#include "xfs_attr_leaf.h" 2162306a36Sopenharmony_ci#include "xfs_error.h" 2262306a36Sopenharmony_ci#include "xfs_trace.h" 2362306a36Sopenharmony_ci#include "xfs_buf_item.h" 2462306a36Sopenharmony_ci#include "xfs_log.h" 2562306a36Sopenharmony_ci#include "xfs_errortag.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * xfs_da_btree.c 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Routines to implement directories as Btrees of hashed names. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/*======================================================================== 3462306a36Sopenharmony_ci * Function prototypes for the kernel. 3562306a36Sopenharmony_ci *========================================================================*/ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Routines used for growing the Btree. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ciSTATIC int xfs_da3_root_split(xfs_da_state_t *state, 4162306a36Sopenharmony_ci xfs_da_state_blk_t *existing_root, 4262306a36Sopenharmony_ci xfs_da_state_blk_t *new_child); 4362306a36Sopenharmony_ciSTATIC int xfs_da3_node_split(xfs_da_state_t *state, 4462306a36Sopenharmony_ci xfs_da_state_blk_t *existing_blk, 4562306a36Sopenharmony_ci xfs_da_state_blk_t *split_blk, 4662306a36Sopenharmony_ci xfs_da_state_blk_t *blk_to_add, 4762306a36Sopenharmony_ci int treelevel, 4862306a36Sopenharmony_ci int *result); 4962306a36Sopenharmony_ciSTATIC void xfs_da3_node_rebalance(xfs_da_state_t *state, 5062306a36Sopenharmony_ci xfs_da_state_blk_t *node_blk_1, 5162306a36Sopenharmony_ci xfs_da_state_blk_t *node_blk_2); 5262306a36Sopenharmony_ciSTATIC void xfs_da3_node_add(xfs_da_state_t *state, 5362306a36Sopenharmony_ci xfs_da_state_blk_t *old_node_blk, 5462306a36Sopenharmony_ci xfs_da_state_blk_t *new_node_blk); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Routines used for shrinking the Btree. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ciSTATIC int xfs_da3_root_join(xfs_da_state_t *state, 6062306a36Sopenharmony_ci xfs_da_state_blk_t *root_blk); 6162306a36Sopenharmony_ciSTATIC int xfs_da3_node_toosmall(xfs_da_state_t *state, int *retval); 6262306a36Sopenharmony_ciSTATIC void xfs_da3_node_remove(xfs_da_state_t *state, 6362306a36Sopenharmony_ci xfs_da_state_blk_t *drop_blk); 6462306a36Sopenharmony_ciSTATIC void xfs_da3_node_unbalance(xfs_da_state_t *state, 6562306a36Sopenharmony_ci xfs_da_state_blk_t *src_node_blk, 6662306a36Sopenharmony_ci xfs_da_state_blk_t *dst_node_blk); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * Utility routines. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ciSTATIC int xfs_da3_blk_unlink(xfs_da_state_t *state, 7262306a36Sopenharmony_ci xfs_da_state_blk_t *drop_blk, 7362306a36Sopenharmony_ci xfs_da_state_blk_t *save_blk); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct kmem_cache *xfs_da_state_cache; /* anchor for dir/attr state */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Allocate a dir-state structure. 8062306a36Sopenharmony_ci * We don't put them on the stack since they're large. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistruct xfs_da_state * 8362306a36Sopenharmony_cixfs_da_state_alloc( 8462306a36Sopenharmony_ci struct xfs_da_args *args) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct xfs_da_state *state; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci state = kmem_cache_zalloc(xfs_da_state_cache, GFP_NOFS | __GFP_NOFAIL); 8962306a36Sopenharmony_ci state->args = args; 9062306a36Sopenharmony_ci state->mp = args->dp->i_mount; 9162306a36Sopenharmony_ci return state; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Kill the altpath contents of a da-state structure. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ciSTATIC void 9862306a36Sopenharmony_cixfs_da_state_kill_altpath(xfs_da_state_t *state) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci int i; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (i = 0; i < state->altpath.active; i++) 10362306a36Sopenharmony_ci state->altpath.blk[i].bp = NULL; 10462306a36Sopenharmony_ci state->altpath.active = 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * Free a da-state structure. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_civoid 11162306a36Sopenharmony_cixfs_da_state_free(xfs_da_state_t *state) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci xfs_da_state_kill_altpath(state); 11462306a36Sopenharmony_ci#ifdef DEBUG 11562306a36Sopenharmony_ci memset((char *)state, 0, sizeof(*state)); 11662306a36Sopenharmony_ci#endif /* DEBUG */ 11762306a36Sopenharmony_ci kmem_cache_free(xfs_da_state_cache, state); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_civoid 12162306a36Sopenharmony_cixfs_da_state_reset( 12262306a36Sopenharmony_ci struct xfs_da_state *state, 12362306a36Sopenharmony_ci struct xfs_da_args *args) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci xfs_da_state_kill_altpath(state); 12662306a36Sopenharmony_ci memset(state, 0, sizeof(struct xfs_da_state)); 12762306a36Sopenharmony_ci state->args = args; 12862306a36Sopenharmony_ci state->mp = state->args->dp->i_mount; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic inline int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci if (whichfork == XFS_DATA_FORK) 13462306a36Sopenharmony_ci return mp->m_dir_geo->fsbcount; 13562306a36Sopenharmony_ci return mp->m_attr_geo->fsbcount; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_civoid 13962306a36Sopenharmony_cixfs_da3_node_hdr_from_disk( 14062306a36Sopenharmony_ci struct xfs_mount *mp, 14162306a36Sopenharmony_ci struct xfs_da3_icnode_hdr *to, 14262306a36Sopenharmony_ci struct xfs_da_intnode *from) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 14562306a36Sopenharmony_ci struct xfs_da3_intnode *from3 = (struct xfs_da3_intnode *)from; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci to->forw = be32_to_cpu(from3->hdr.info.hdr.forw); 14862306a36Sopenharmony_ci to->back = be32_to_cpu(from3->hdr.info.hdr.back); 14962306a36Sopenharmony_ci to->magic = be16_to_cpu(from3->hdr.info.hdr.magic); 15062306a36Sopenharmony_ci to->count = be16_to_cpu(from3->hdr.__count); 15162306a36Sopenharmony_ci to->level = be16_to_cpu(from3->hdr.__level); 15262306a36Sopenharmony_ci to->btree = from3->__btree; 15362306a36Sopenharmony_ci ASSERT(to->magic == XFS_DA3_NODE_MAGIC); 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci to->forw = be32_to_cpu(from->hdr.info.forw); 15662306a36Sopenharmony_ci to->back = be32_to_cpu(from->hdr.info.back); 15762306a36Sopenharmony_ci to->magic = be16_to_cpu(from->hdr.info.magic); 15862306a36Sopenharmony_ci to->count = be16_to_cpu(from->hdr.__count); 15962306a36Sopenharmony_ci to->level = be16_to_cpu(from->hdr.__level); 16062306a36Sopenharmony_ci to->btree = from->__btree; 16162306a36Sopenharmony_ci ASSERT(to->magic == XFS_DA_NODE_MAGIC); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_civoid 16662306a36Sopenharmony_cixfs_da3_node_hdr_to_disk( 16762306a36Sopenharmony_ci struct xfs_mount *mp, 16862306a36Sopenharmony_ci struct xfs_da_intnode *to, 16962306a36Sopenharmony_ci struct xfs_da3_icnode_hdr *from) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 17262306a36Sopenharmony_ci struct xfs_da3_intnode *to3 = (struct xfs_da3_intnode *)to; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ASSERT(from->magic == XFS_DA3_NODE_MAGIC); 17562306a36Sopenharmony_ci to3->hdr.info.hdr.forw = cpu_to_be32(from->forw); 17662306a36Sopenharmony_ci to3->hdr.info.hdr.back = cpu_to_be32(from->back); 17762306a36Sopenharmony_ci to3->hdr.info.hdr.magic = cpu_to_be16(from->magic); 17862306a36Sopenharmony_ci to3->hdr.__count = cpu_to_be16(from->count); 17962306a36Sopenharmony_ci to3->hdr.__level = cpu_to_be16(from->level); 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci ASSERT(from->magic == XFS_DA_NODE_MAGIC); 18262306a36Sopenharmony_ci to->hdr.info.forw = cpu_to_be32(from->forw); 18362306a36Sopenharmony_ci to->hdr.info.back = cpu_to_be32(from->back); 18462306a36Sopenharmony_ci to->hdr.info.magic = cpu_to_be16(from->magic); 18562306a36Sopenharmony_ci to->hdr.__count = cpu_to_be16(from->count); 18662306a36Sopenharmony_ci to->hdr.__level = cpu_to_be16(from->level); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Verify an xfs_da3_blkinfo structure. Note that the da3 fields are only 19262306a36Sopenharmony_ci * accessible on v5 filesystems. This header format is common across da node, 19362306a36Sopenharmony_ci * attr leaf and dir leaf blocks. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cixfs_failaddr_t 19662306a36Sopenharmony_cixfs_da3_blkinfo_verify( 19762306a36Sopenharmony_ci struct xfs_buf *bp, 19862306a36Sopenharmony_ci struct xfs_da3_blkinfo *hdr3) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 20162306a36Sopenharmony_ci struct xfs_da_blkinfo *hdr = &hdr3->hdr; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!xfs_verify_magic16(bp, hdr->magic)) 20462306a36Sopenharmony_ci return __this_address; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 20762306a36Sopenharmony_ci if (!uuid_equal(&hdr3->uuid, &mp->m_sb.sb_meta_uuid)) 20862306a36Sopenharmony_ci return __this_address; 20962306a36Sopenharmony_ci if (be64_to_cpu(hdr3->blkno) != xfs_buf_daddr(bp)) 21062306a36Sopenharmony_ci return __this_address; 21162306a36Sopenharmony_ci if (!xfs_log_check_lsn(mp, be64_to_cpu(hdr3->lsn))) 21262306a36Sopenharmony_ci return __this_address; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return NULL; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic xfs_failaddr_t 21962306a36Sopenharmony_cixfs_da3_node_verify( 22062306a36Sopenharmony_ci struct xfs_buf *bp) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 22362306a36Sopenharmony_ci struct xfs_da_intnode *hdr = bp->b_addr; 22462306a36Sopenharmony_ci struct xfs_da3_icnode_hdr ichdr; 22562306a36Sopenharmony_ci xfs_failaddr_t fa; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(mp, &ichdr, hdr); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci fa = xfs_da3_blkinfo_verify(bp, bp->b_addr); 23062306a36Sopenharmony_ci if (fa) 23162306a36Sopenharmony_ci return fa; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (ichdr.level == 0) 23462306a36Sopenharmony_ci return __this_address; 23562306a36Sopenharmony_ci if (ichdr.level > XFS_DA_NODE_MAXDEPTH) 23662306a36Sopenharmony_ci return __this_address; 23762306a36Sopenharmony_ci if (ichdr.count == 0) 23862306a36Sopenharmony_ci return __this_address; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * we don't know if the node is for and attribute or directory tree, 24262306a36Sopenharmony_ci * so only fail if the count is outside both bounds 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci if (ichdr.count > mp->m_dir_geo->node_ents && 24562306a36Sopenharmony_ci ichdr.count > mp->m_attr_geo->node_ents) 24662306a36Sopenharmony_ci return __this_address; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* XXX: hash order check? */ 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return NULL; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void 25462306a36Sopenharmony_cixfs_da3_node_write_verify( 25562306a36Sopenharmony_ci struct xfs_buf *bp) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 25862306a36Sopenharmony_ci struct xfs_buf_log_item *bip = bp->b_log_item; 25962306a36Sopenharmony_ci struct xfs_da3_node_hdr *hdr3 = bp->b_addr; 26062306a36Sopenharmony_ci xfs_failaddr_t fa; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci fa = xfs_da3_node_verify(bp); 26362306a36Sopenharmony_ci if (fa) { 26462306a36Sopenharmony_ci xfs_verifier_error(bp, -EFSCORRUPTED, fa); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (!xfs_has_crc(mp)) 26962306a36Sopenharmony_ci return; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (bip) 27262306a36Sopenharmony_ci hdr3->info.lsn = cpu_to_be64(bip->bli_item.li_lsn); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci xfs_buf_update_cksum(bp, XFS_DA3_NODE_CRC_OFF); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * leaf/node format detection on trees is sketchy, so a node read can be done on 27962306a36Sopenharmony_ci * leaf level blocks when detection identifies the tree as a node format tree 28062306a36Sopenharmony_ci * incorrectly. In this case, we need to swap the verifier to match the correct 28162306a36Sopenharmony_ci * format of the block being read. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cistatic void 28462306a36Sopenharmony_cixfs_da3_node_read_verify( 28562306a36Sopenharmony_ci struct xfs_buf *bp) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct xfs_da_blkinfo *info = bp->b_addr; 28862306a36Sopenharmony_ci xfs_failaddr_t fa; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci switch (be16_to_cpu(info->magic)) { 29162306a36Sopenharmony_ci case XFS_DA3_NODE_MAGIC: 29262306a36Sopenharmony_ci if (!xfs_buf_verify_cksum(bp, XFS_DA3_NODE_CRC_OFF)) { 29362306a36Sopenharmony_ci xfs_verifier_error(bp, -EFSBADCRC, 29462306a36Sopenharmony_ci __this_address); 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci fallthrough; 29862306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 29962306a36Sopenharmony_ci fa = xfs_da3_node_verify(bp); 30062306a36Sopenharmony_ci if (fa) 30162306a36Sopenharmony_ci xfs_verifier_error(bp, -EFSCORRUPTED, fa); 30262306a36Sopenharmony_ci return; 30362306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 30462306a36Sopenharmony_ci case XFS_ATTR3_LEAF_MAGIC: 30562306a36Sopenharmony_ci bp->b_ops = &xfs_attr3_leaf_buf_ops; 30662306a36Sopenharmony_ci bp->b_ops->verify_read(bp); 30762306a36Sopenharmony_ci return; 30862306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 30962306a36Sopenharmony_ci case XFS_DIR3_LEAFN_MAGIC: 31062306a36Sopenharmony_ci bp->b_ops = &xfs_dir3_leafn_buf_ops; 31162306a36Sopenharmony_ci bp->b_ops->verify_read(bp); 31262306a36Sopenharmony_ci return; 31362306a36Sopenharmony_ci default: 31462306a36Sopenharmony_ci xfs_verifier_error(bp, -EFSCORRUPTED, __this_address); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* Verify the structure of a da3 block. */ 32062306a36Sopenharmony_cistatic xfs_failaddr_t 32162306a36Sopenharmony_cixfs_da3_node_verify_struct( 32262306a36Sopenharmony_ci struct xfs_buf *bp) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct xfs_da_blkinfo *info = bp->b_addr; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci switch (be16_to_cpu(info->magic)) { 32762306a36Sopenharmony_ci case XFS_DA3_NODE_MAGIC: 32862306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 32962306a36Sopenharmony_ci return xfs_da3_node_verify(bp); 33062306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 33162306a36Sopenharmony_ci case XFS_ATTR3_LEAF_MAGIC: 33262306a36Sopenharmony_ci bp->b_ops = &xfs_attr3_leaf_buf_ops; 33362306a36Sopenharmony_ci return bp->b_ops->verify_struct(bp); 33462306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 33562306a36Sopenharmony_ci case XFS_DIR3_LEAFN_MAGIC: 33662306a36Sopenharmony_ci bp->b_ops = &xfs_dir3_leafn_buf_ops; 33762306a36Sopenharmony_ci return bp->b_ops->verify_struct(bp); 33862306a36Sopenharmony_ci default: 33962306a36Sopenharmony_ci return __this_address; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ciconst struct xfs_buf_ops xfs_da3_node_buf_ops = { 34462306a36Sopenharmony_ci .name = "xfs_da3_node", 34562306a36Sopenharmony_ci .magic16 = { cpu_to_be16(XFS_DA_NODE_MAGIC), 34662306a36Sopenharmony_ci cpu_to_be16(XFS_DA3_NODE_MAGIC) }, 34762306a36Sopenharmony_ci .verify_read = xfs_da3_node_read_verify, 34862306a36Sopenharmony_ci .verify_write = xfs_da3_node_write_verify, 34962306a36Sopenharmony_ci .verify_struct = xfs_da3_node_verify_struct, 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int 35362306a36Sopenharmony_cixfs_da3_node_set_type( 35462306a36Sopenharmony_ci struct xfs_trans *tp, 35562306a36Sopenharmony_ci struct xfs_buf *bp) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct xfs_da_blkinfo *info = bp->b_addr; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci switch (be16_to_cpu(info->magic)) { 36062306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 36162306a36Sopenharmony_ci case XFS_DA3_NODE_MAGIC: 36262306a36Sopenharmony_ci xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DA_NODE_BUF); 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 36562306a36Sopenharmony_ci case XFS_ATTR3_LEAF_MAGIC: 36662306a36Sopenharmony_ci xfs_trans_buf_set_type(tp, bp, XFS_BLFT_ATTR_LEAF_BUF); 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 36962306a36Sopenharmony_ci case XFS_DIR3_LEAFN_MAGIC: 37062306a36Sopenharmony_ci xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DIR_LEAFN_BUF); 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, tp->t_mountp, 37462306a36Sopenharmony_ci info, sizeof(*info)); 37562306a36Sopenharmony_ci xfs_trans_brelse(tp, bp); 37662306a36Sopenharmony_ci return -EFSCORRUPTED; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ciint 38162306a36Sopenharmony_cixfs_da3_node_read( 38262306a36Sopenharmony_ci struct xfs_trans *tp, 38362306a36Sopenharmony_ci struct xfs_inode *dp, 38462306a36Sopenharmony_ci xfs_dablk_t bno, 38562306a36Sopenharmony_ci struct xfs_buf **bpp, 38662306a36Sopenharmony_ci int whichfork) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci int error; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci error = xfs_da_read_buf(tp, dp, bno, 0, bpp, whichfork, 39162306a36Sopenharmony_ci &xfs_da3_node_buf_ops); 39262306a36Sopenharmony_ci if (error || !*bpp || !tp) 39362306a36Sopenharmony_ci return error; 39462306a36Sopenharmony_ci return xfs_da3_node_set_type(tp, *bpp); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ciint 39862306a36Sopenharmony_cixfs_da3_node_read_mapped( 39962306a36Sopenharmony_ci struct xfs_trans *tp, 40062306a36Sopenharmony_ci struct xfs_inode *dp, 40162306a36Sopenharmony_ci xfs_daddr_t mappedbno, 40262306a36Sopenharmony_ci struct xfs_buf **bpp, 40362306a36Sopenharmony_ci int whichfork) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 40662306a36Sopenharmony_ci int error; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, mappedbno, 40962306a36Sopenharmony_ci XFS_FSB_TO_BB(mp, xfs_dabuf_nfsb(mp, whichfork)), 0, 41062306a36Sopenharmony_ci bpp, &xfs_da3_node_buf_ops); 41162306a36Sopenharmony_ci if (error || !*bpp) 41262306a36Sopenharmony_ci return error; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (whichfork == XFS_ATTR_FORK) 41562306a36Sopenharmony_ci xfs_buf_set_ref(*bpp, XFS_ATTR_BTREE_REF); 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci xfs_buf_set_ref(*bpp, XFS_DIR_BTREE_REF); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!tp) 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci return xfs_da3_node_set_type(tp, *bpp); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/*======================================================================== 42562306a36Sopenharmony_ci * Routines used for growing the Btree. 42662306a36Sopenharmony_ci *========================================================================*/ 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci/* 42962306a36Sopenharmony_ci * Create the initial contents of an intermediate node. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ciint 43262306a36Sopenharmony_cixfs_da3_node_create( 43362306a36Sopenharmony_ci struct xfs_da_args *args, 43462306a36Sopenharmony_ci xfs_dablk_t blkno, 43562306a36Sopenharmony_ci int level, 43662306a36Sopenharmony_ci struct xfs_buf **bpp, 43762306a36Sopenharmony_ci int whichfork) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct xfs_da_intnode *node; 44062306a36Sopenharmony_ci struct xfs_trans *tp = args->trans; 44162306a36Sopenharmony_ci struct xfs_mount *mp = tp->t_mountp; 44262306a36Sopenharmony_ci struct xfs_da3_icnode_hdr ichdr = {0}; 44362306a36Sopenharmony_ci struct xfs_buf *bp; 44462306a36Sopenharmony_ci int error; 44562306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci trace_xfs_da_node_create(args); 44862306a36Sopenharmony_ci ASSERT(level <= XFS_DA_NODE_MAXDEPTH); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci error = xfs_da_get_buf(tp, dp, blkno, &bp, whichfork); 45162306a36Sopenharmony_ci if (error) 45262306a36Sopenharmony_ci return error; 45362306a36Sopenharmony_ci bp->b_ops = &xfs_da3_node_buf_ops; 45462306a36Sopenharmony_ci xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DA_NODE_BUF); 45562306a36Sopenharmony_ci node = bp->b_addr; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 45862306a36Sopenharmony_ci struct xfs_da3_node_hdr *hdr3 = bp->b_addr; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci memset(hdr3, 0, sizeof(struct xfs_da3_node_hdr)); 46162306a36Sopenharmony_ci ichdr.magic = XFS_DA3_NODE_MAGIC; 46262306a36Sopenharmony_ci hdr3->info.blkno = cpu_to_be64(xfs_buf_daddr(bp)); 46362306a36Sopenharmony_ci hdr3->info.owner = cpu_to_be64(args->dp->i_ino); 46462306a36Sopenharmony_ci uuid_copy(&hdr3->info.uuid, &mp->m_sb.sb_meta_uuid); 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci ichdr.magic = XFS_DA_NODE_MAGIC; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci ichdr.level = level; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, node, &ichdr); 47162306a36Sopenharmony_ci xfs_trans_log_buf(tp, bp, 47262306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &node->hdr, args->geo->node_hdr_size)); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci *bpp = bp; 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* 47962306a36Sopenharmony_ci * Split a leaf node, rebalance, then possibly split 48062306a36Sopenharmony_ci * intermediate nodes, rebalance, etc. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ciint /* error */ 48362306a36Sopenharmony_cixfs_da3_split( 48462306a36Sopenharmony_ci struct xfs_da_state *state) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct xfs_da_state_blk *oldblk; 48762306a36Sopenharmony_ci struct xfs_da_state_blk *newblk; 48862306a36Sopenharmony_ci struct xfs_da_state_blk *addblk; 48962306a36Sopenharmony_ci struct xfs_da_intnode *node; 49062306a36Sopenharmony_ci int max; 49162306a36Sopenharmony_ci int action = 0; 49262306a36Sopenharmony_ci int error; 49362306a36Sopenharmony_ci int i; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci trace_xfs_da_split(state->args); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (XFS_TEST_ERROR(false, state->mp, XFS_ERRTAG_DA_LEAF_SPLIT)) 49862306a36Sopenharmony_ci return -EIO; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* 50162306a36Sopenharmony_ci * Walk back up the tree splitting/inserting/adjusting as necessary. 50262306a36Sopenharmony_ci * If we need to insert and there isn't room, split the node, then 50362306a36Sopenharmony_ci * decide which fragment to insert the new block from below into. 50462306a36Sopenharmony_ci * Note that we may split the root this way, but we need more fixup. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci max = state->path.active - 1; 50762306a36Sopenharmony_ci ASSERT((max >= 0) && (max < XFS_DA_NODE_MAXDEPTH)); 50862306a36Sopenharmony_ci ASSERT(state->path.blk[max].magic == XFS_ATTR_LEAF_MAGIC || 50962306a36Sopenharmony_ci state->path.blk[max].magic == XFS_DIR2_LEAFN_MAGIC); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci addblk = &state->path.blk[max]; /* initial dummy value */ 51262306a36Sopenharmony_ci for (i = max; (i >= 0) && addblk; state->path.active--, i--) { 51362306a36Sopenharmony_ci oldblk = &state->path.blk[i]; 51462306a36Sopenharmony_ci newblk = &state->altpath.blk[i]; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci * If a leaf node then 51862306a36Sopenharmony_ci * Allocate a new leaf node, then rebalance across them. 51962306a36Sopenharmony_ci * else if an intermediate node then 52062306a36Sopenharmony_ci * We split on the last layer, must we split the node? 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci switch (oldblk->magic) { 52362306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 52462306a36Sopenharmony_ci error = xfs_attr3_leaf_split(state, oldblk, newblk); 52562306a36Sopenharmony_ci if ((error != 0) && (error != -ENOSPC)) { 52662306a36Sopenharmony_ci return error; /* GROT: attr is inconsistent */ 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci if (!error) { 52962306a36Sopenharmony_ci addblk = newblk; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * Entry wouldn't fit, split the leaf again. The new 53462306a36Sopenharmony_ci * extrablk will be consumed by xfs_da3_node_split if 53562306a36Sopenharmony_ci * the node is split. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci state->extravalid = 1; 53862306a36Sopenharmony_ci if (state->inleaf) { 53962306a36Sopenharmony_ci state->extraafter = 0; /* before newblk */ 54062306a36Sopenharmony_ci trace_xfs_attr_leaf_split_before(state->args); 54162306a36Sopenharmony_ci error = xfs_attr3_leaf_split(state, oldblk, 54262306a36Sopenharmony_ci &state->extrablk); 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci state->extraafter = 1; /* after newblk */ 54562306a36Sopenharmony_ci trace_xfs_attr_leaf_split_after(state->args); 54662306a36Sopenharmony_ci error = xfs_attr3_leaf_split(state, newblk, 54762306a36Sopenharmony_ci &state->extrablk); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci if (error) 55062306a36Sopenharmony_ci return error; /* GROT: attr inconsistent */ 55162306a36Sopenharmony_ci addblk = newblk; 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 55462306a36Sopenharmony_ci error = xfs_dir2_leafn_split(state, oldblk, newblk); 55562306a36Sopenharmony_ci if (error) 55662306a36Sopenharmony_ci return error; 55762306a36Sopenharmony_ci addblk = newblk; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 56062306a36Sopenharmony_ci error = xfs_da3_node_split(state, oldblk, newblk, addblk, 56162306a36Sopenharmony_ci max - i, &action); 56262306a36Sopenharmony_ci addblk->bp = NULL; 56362306a36Sopenharmony_ci if (error) 56462306a36Sopenharmony_ci return error; /* GROT: dir is inconsistent */ 56562306a36Sopenharmony_ci /* 56662306a36Sopenharmony_ci * Record the newly split block for the next time thru? 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci if (action) 56962306a36Sopenharmony_ci addblk = newblk; 57062306a36Sopenharmony_ci else 57162306a36Sopenharmony_ci addblk = NULL; 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * Update the btree to show the new hashval for this child. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci xfs_da3_fixhashpath(state, &state->path); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci if (!addblk) 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * xfs_da3_node_split() should have consumed any extra blocks we added 58562306a36Sopenharmony_ci * during a double leaf split in the attr fork. This is guaranteed as 58662306a36Sopenharmony_ci * we can't be here if the attr fork only has a single leaf block. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci ASSERT(state->extravalid == 0 || 58962306a36Sopenharmony_ci state->path.blk[max].magic == XFS_DIR2_LEAFN_MAGIC); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * Split the root node. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci ASSERT(state->path.active == 0); 59562306a36Sopenharmony_ci oldblk = &state->path.blk[0]; 59662306a36Sopenharmony_ci error = xfs_da3_root_split(state, oldblk, addblk); 59762306a36Sopenharmony_ci if (error) 59862306a36Sopenharmony_ci goto out; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * Update pointers to the node which used to be block 0 and just got 60262306a36Sopenharmony_ci * bumped because of the addition of a new root node. Note that the 60362306a36Sopenharmony_ci * original block 0 could be at any position in the list of blocks in 60462306a36Sopenharmony_ci * the tree. 60562306a36Sopenharmony_ci * 60662306a36Sopenharmony_ci * Note: the magic numbers and sibling pointers are in the same physical 60762306a36Sopenharmony_ci * place for both v2 and v3 headers (by design). Hence it doesn't matter 60862306a36Sopenharmony_ci * which version of the xfs_da_intnode structure we use here as the 60962306a36Sopenharmony_ci * result will be the same using either structure. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ci node = oldblk->bp->b_addr; 61262306a36Sopenharmony_ci if (node->hdr.info.forw) { 61362306a36Sopenharmony_ci if (be32_to_cpu(node->hdr.info.forw) != addblk->blkno) { 61462306a36Sopenharmony_ci xfs_buf_mark_corrupt(oldblk->bp); 61562306a36Sopenharmony_ci error = -EFSCORRUPTED; 61662306a36Sopenharmony_ci goto out; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci node = addblk->bp->b_addr; 61962306a36Sopenharmony_ci node->hdr.info.back = cpu_to_be32(oldblk->blkno); 62062306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, addblk->bp, 62162306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &node->hdr.info, 62262306a36Sopenharmony_ci sizeof(node->hdr.info))); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci node = oldblk->bp->b_addr; 62562306a36Sopenharmony_ci if (node->hdr.info.back) { 62662306a36Sopenharmony_ci if (be32_to_cpu(node->hdr.info.back) != addblk->blkno) { 62762306a36Sopenharmony_ci xfs_buf_mark_corrupt(oldblk->bp); 62862306a36Sopenharmony_ci error = -EFSCORRUPTED; 62962306a36Sopenharmony_ci goto out; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci node = addblk->bp->b_addr; 63262306a36Sopenharmony_ci node->hdr.info.forw = cpu_to_be32(oldblk->blkno); 63362306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, addblk->bp, 63462306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &node->hdr.info, 63562306a36Sopenharmony_ci sizeof(node->hdr.info))); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ciout: 63862306a36Sopenharmony_ci addblk->bp = NULL; 63962306a36Sopenharmony_ci return error; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* 64362306a36Sopenharmony_ci * Split the root. We have to create a new root and point to the two 64462306a36Sopenharmony_ci * parts (the split old root) that we just created. Copy block zero to 64562306a36Sopenharmony_ci * the EOF, extending the inode in process. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ciSTATIC int /* error */ 64862306a36Sopenharmony_cixfs_da3_root_split( 64962306a36Sopenharmony_ci struct xfs_da_state *state, 65062306a36Sopenharmony_ci struct xfs_da_state_blk *blk1, 65162306a36Sopenharmony_ci struct xfs_da_state_blk *blk2) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct xfs_da_intnode *node; 65462306a36Sopenharmony_ci struct xfs_da_intnode *oldroot; 65562306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 65662306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 65762306a36Sopenharmony_ci struct xfs_da_args *args; 65862306a36Sopenharmony_ci struct xfs_buf *bp; 65962306a36Sopenharmony_ci struct xfs_inode *dp; 66062306a36Sopenharmony_ci struct xfs_trans *tp; 66162306a36Sopenharmony_ci struct xfs_dir2_leaf *leaf; 66262306a36Sopenharmony_ci xfs_dablk_t blkno; 66362306a36Sopenharmony_ci int level; 66462306a36Sopenharmony_ci int error; 66562306a36Sopenharmony_ci int size; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci trace_xfs_da_root_split(state->args); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* 67062306a36Sopenharmony_ci * Copy the existing (incorrect) block from the root node position 67162306a36Sopenharmony_ci * to a free space somewhere. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci args = state->args; 67462306a36Sopenharmony_ci error = xfs_da_grow_inode(args, &blkno); 67562306a36Sopenharmony_ci if (error) 67662306a36Sopenharmony_ci return error; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci dp = args->dp; 67962306a36Sopenharmony_ci tp = args->trans; 68062306a36Sopenharmony_ci error = xfs_da_get_buf(tp, dp, blkno, &bp, args->whichfork); 68162306a36Sopenharmony_ci if (error) 68262306a36Sopenharmony_ci return error; 68362306a36Sopenharmony_ci node = bp->b_addr; 68462306a36Sopenharmony_ci oldroot = blk1->bp->b_addr; 68562306a36Sopenharmony_ci if (oldroot->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC) || 68662306a36Sopenharmony_ci oldroot->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC)) { 68762306a36Sopenharmony_ci struct xfs_da3_icnode_hdr icnodehdr; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &icnodehdr, oldroot); 69062306a36Sopenharmony_ci btree = icnodehdr.btree; 69162306a36Sopenharmony_ci size = (int)((char *)&btree[icnodehdr.count] - (char *)oldroot); 69262306a36Sopenharmony_ci level = icnodehdr.level; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * we are about to copy oldroot to bp, so set up the type 69662306a36Sopenharmony_ci * of bp while we know exactly what it will be. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DA_NODE_BUF); 69962306a36Sopenharmony_ci } else { 70062306a36Sopenharmony_ci struct xfs_dir3_icleaf_hdr leafhdr; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci leaf = (xfs_dir2_leaf_t *)oldroot; 70362306a36Sopenharmony_ci xfs_dir2_leaf_hdr_from_disk(dp->i_mount, &leafhdr, leaf); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ASSERT(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC || 70662306a36Sopenharmony_ci leafhdr.magic == XFS_DIR3_LEAFN_MAGIC); 70762306a36Sopenharmony_ci size = (int)((char *)&leafhdr.ents[leafhdr.count] - 70862306a36Sopenharmony_ci (char *)leaf); 70962306a36Sopenharmony_ci level = 0; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * we are about to copy oldroot to bp, so set up the type 71362306a36Sopenharmony_ci * of bp while we know exactly what it will be. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DIR_LEAFN_BUF); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* 71962306a36Sopenharmony_ci * we can copy most of the information in the node from one block to 72062306a36Sopenharmony_ci * another, but for CRC enabled headers we have to make sure that the 72162306a36Sopenharmony_ci * block specific identifiers are kept intact. We update the buffer 72262306a36Sopenharmony_ci * directly for this. 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_ci memcpy(node, oldroot, size); 72562306a36Sopenharmony_ci if (oldroot->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC) || 72662306a36Sopenharmony_ci oldroot->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC)) { 72762306a36Sopenharmony_ci struct xfs_da3_intnode *node3 = (struct xfs_da3_intnode *)node; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci node3->hdr.info.blkno = cpu_to_be64(xfs_buf_daddr(bp)); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci xfs_trans_log_buf(tp, bp, 0, size - 1); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci bp->b_ops = blk1->bp->b_ops; 73462306a36Sopenharmony_ci xfs_trans_buf_copy_type(bp, blk1->bp); 73562306a36Sopenharmony_ci blk1->bp = bp; 73662306a36Sopenharmony_ci blk1->blkno = blkno; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* 73962306a36Sopenharmony_ci * Set up the new root node. 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ci error = xfs_da3_node_create(args, 74262306a36Sopenharmony_ci (args->whichfork == XFS_DATA_FORK) ? args->geo->leafblk : 0, 74362306a36Sopenharmony_ci level + 1, &bp, args->whichfork); 74462306a36Sopenharmony_ci if (error) 74562306a36Sopenharmony_ci return error; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci node = bp->b_addr; 74862306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 74962306a36Sopenharmony_ci btree = nodehdr.btree; 75062306a36Sopenharmony_ci btree[0].hashval = cpu_to_be32(blk1->hashval); 75162306a36Sopenharmony_ci btree[0].before = cpu_to_be32(blk1->blkno); 75262306a36Sopenharmony_ci btree[1].hashval = cpu_to_be32(blk2->hashval); 75362306a36Sopenharmony_ci btree[1].before = cpu_to_be32(blk2->blkno); 75462306a36Sopenharmony_ci nodehdr.count = 2; 75562306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, node, &nodehdr); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci#ifdef DEBUG 75862306a36Sopenharmony_ci if (oldroot->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) || 75962306a36Sopenharmony_ci oldroot->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC)) { 76062306a36Sopenharmony_ci ASSERT(blk1->blkno >= args->geo->leafblk && 76162306a36Sopenharmony_ci blk1->blkno < args->geo->freeblk); 76262306a36Sopenharmony_ci ASSERT(blk2->blkno >= args->geo->leafblk && 76362306a36Sopenharmony_ci blk2->blkno < args->geo->freeblk); 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci#endif 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* Header is already logged by xfs_da_node_create */ 76862306a36Sopenharmony_ci xfs_trans_log_buf(tp, bp, 76962306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, btree, sizeof(xfs_da_node_entry_t) * 2)); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/* 77562306a36Sopenharmony_ci * Split the node, rebalance, then add the new entry. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ciSTATIC int /* error */ 77862306a36Sopenharmony_cixfs_da3_node_split( 77962306a36Sopenharmony_ci struct xfs_da_state *state, 78062306a36Sopenharmony_ci struct xfs_da_state_blk *oldblk, 78162306a36Sopenharmony_ci struct xfs_da_state_blk *newblk, 78262306a36Sopenharmony_ci struct xfs_da_state_blk *addblk, 78362306a36Sopenharmony_ci int treelevel, 78462306a36Sopenharmony_ci int *result) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct xfs_da_intnode *node; 78762306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 78862306a36Sopenharmony_ci xfs_dablk_t blkno; 78962306a36Sopenharmony_ci int newcount; 79062306a36Sopenharmony_ci int error; 79162306a36Sopenharmony_ci int useextra; 79262306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci trace_xfs_da_node_split(state->args); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci node = oldblk->bp->b_addr; 79762306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * With V2 dirs the extra block is data or freespace. 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci useextra = state->extravalid && state->args->whichfork == XFS_ATTR_FORK; 80362306a36Sopenharmony_ci newcount = 1 + useextra; 80462306a36Sopenharmony_ci /* 80562306a36Sopenharmony_ci * Do we have to split the node? 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_ci if (nodehdr.count + newcount > state->args->geo->node_ents) { 80862306a36Sopenharmony_ci /* 80962306a36Sopenharmony_ci * Allocate a new node, add to the doubly linked chain of 81062306a36Sopenharmony_ci * nodes, then move some of our excess entries into it. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci error = xfs_da_grow_inode(state->args, &blkno); 81362306a36Sopenharmony_ci if (error) 81462306a36Sopenharmony_ci return error; /* GROT: dir is inconsistent */ 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci error = xfs_da3_node_create(state->args, blkno, treelevel, 81762306a36Sopenharmony_ci &newblk->bp, state->args->whichfork); 81862306a36Sopenharmony_ci if (error) 81962306a36Sopenharmony_ci return error; /* GROT: dir is inconsistent */ 82062306a36Sopenharmony_ci newblk->blkno = blkno; 82162306a36Sopenharmony_ci newblk->magic = XFS_DA_NODE_MAGIC; 82262306a36Sopenharmony_ci xfs_da3_node_rebalance(state, oldblk, newblk); 82362306a36Sopenharmony_ci error = xfs_da3_blk_link(state, oldblk, newblk); 82462306a36Sopenharmony_ci if (error) 82562306a36Sopenharmony_ci return error; 82662306a36Sopenharmony_ci *result = 1; 82762306a36Sopenharmony_ci } else { 82862306a36Sopenharmony_ci *result = 0; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* 83262306a36Sopenharmony_ci * Insert the new entry(s) into the correct block 83362306a36Sopenharmony_ci * (updating last hashval in the process). 83462306a36Sopenharmony_ci * 83562306a36Sopenharmony_ci * xfs_da3_node_add() inserts BEFORE the given index, 83662306a36Sopenharmony_ci * and as a result of using node_lookup_int() we always 83762306a36Sopenharmony_ci * point to a valid entry (not after one), but a split 83862306a36Sopenharmony_ci * operation always results in a new block whose hashvals 83962306a36Sopenharmony_ci * FOLLOW the current block. 84062306a36Sopenharmony_ci * 84162306a36Sopenharmony_ci * If we had double-split op below us, then add the extra block too. 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_ci node = oldblk->bp->b_addr; 84462306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 84562306a36Sopenharmony_ci if (oldblk->index <= nodehdr.count) { 84662306a36Sopenharmony_ci oldblk->index++; 84762306a36Sopenharmony_ci xfs_da3_node_add(state, oldblk, addblk); 84862306a36Sopenharmony_ci if (useextra) { 84962306a36Sopenharmony_ci if (state->extraafter) 85062306a36Sopenharmony_ci oldblk->index++; 85162306a36Sopenharmony_ci xfs_da3_node_add(state, oldblk, &state->extrablk); 85262306a36Sopenharmony_ci state->extravalid = 0; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci } else { 85562306a36Sopenharmony_ci newblk->index++; 85662306a36Sopenharmony_ci xfs_da3_node_add(state, newblk, addblk); 85762306a36Sopenharmony_ci if (useextra) { 85862306a36Sopenharmony_ci if (state->extraafter) 85962306a36Sopenharmony_ci newblk->index++; 86062306a36Sopenharmony_ci xfs_da3_node_add(state, newblk, &state->extrablk); 86162306a36Sopenharmony_ci state->extravalid = 0; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci/* 86962306a36Sopenharmony_ci * Balance the btree elements between two intermediate nodes, 87062306a36Sopenharmony_ci * usually one full and one empty. 87162306a36Sopenharmony_ci * 87262306a36Sopenharmony_ci * NOTE: if blk2 is empty, then it will get the upper half of blk1. 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_ciSTATIC void 87562306a36Sopenharmony_cixfs_da3_node_rebalance( 87662306a36Sopenharmony_ci struct xfs_da_state *state, 87762306a36Sopenharmony_ci struct xfs_da_state_blk *blk1, 87862306a36Sopenharmony_ci struct xfs_da_state_blk *blk2) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct xfs_da_intnode *node1; 88162306a36Sopenharmony_ci struct xfs_da_intnode *node2; 88262306a36Sopenharmony_ci struct xfs_da_node_entry *btree1; 88362306a36Sopenharmony_ci struct xfs_da_node_entry *btree2; 88462306a36Sopenharmony_ci struct xfs_da_node_entry *btree_s; 88562306a36Sopenharmony_ci struct xfs_da_node_entry *btree_d; 88662306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr1; 88762306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr2; 88862306a36Sopenharmony_ci struct xfs_trans *tp; 88962306a36Sopenharmony_ci int count; 89062306a36Sopenharmony_ci int tmp; 89162306a36Sopenharmony_ci int swap = 0; 89262306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci trace_xfs_da_node_rebalance(state->args); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci node1 = blk1->bp->b_addr; 89762306a36Sopenharmony_ci node2 = blk2->bp->b_addr; 89862306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr1, node1); 89962306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr2, node2); 90062306a36Sopenharmony_ci btree1 = nodehdr1.btree; 90162306a36Sopenharmony_ci btree2 = nodehdr2.btree; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* 90462306a36Sopenharmony_ci * Figure out how many entries need to move, and in which direction. 90562306a36Sopenharmony_ci * Swap the nodes around if that makes it simpler. 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_ci if (nodehdr1.count > 0 && nodehdr2.count > 0 && 90862306a36Sopenharmony_ci ((be32_to_cpu(btree2[0].hashval) < be32_to_cpu(btree1[0].hashval)) || 90962306a36Sopenharmony_ci (be32_to_cpu(btree2[nodehdr2.count - 1].hashval) < 91062306a36Sopenharmony_ci be32_to_cpu(btree1[nodehdr1.count - 1].hashval)))) { 91162306a36Sopenharmony_ci swap(node1, node2); 91262306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr1, node1); 91362306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr2, node2); 91462306a36Sopenharmony_ci btree1 = nodehdr1.btree; 91562306a36Sopenharmony_ci btree2 = nodehdr2.btree; 91662306a36Sopenharmony_ci swap = 1; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci count = (nodehdr1.count - nodehdr2.count) / 2; 92062306a36Sopenharmony_ci if (count == 0) 92162306a36Sopenharmony_ci return; 92262306a36Sopenharmony_ci tp = state->args->trans; 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * Two cases: high-to-low and low-to-high. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci if (count > 0) { 92762306a36Sopenharmony_ci /* 92862306a36Sopenharmony_ci * Move elements in node2 up to make a hole. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_ci tmp = nodehdr2.count; 93162306a36Sopenharmony_ci if (tmp > 0) { 93262306a36Sopenharmony_ci tmp *= (uint)sizeof(xfs_da_node_entry_t); 93362306a36Sopenharmony_ci btree_s = &btree2[0]; 93462306a36Sopenharmony_ci btree_d = &btree2[count]; 93562306a36Sopenharmony_ci memmove(btree_d, btree_s, tmp); 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* 93962306a36Sopenharmony_ci * Move the req'd B-tree elements from high in node1 to 94062306a36Sopenharmony_ci * low in node2. 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_ci nodehdr2.count += count; 94362306a36Sopenharmony_ci tmp = count * (uint)sizeof(xfs_da_node_entry_t); 94462306a36Sopenharmony_ci btree_s = &btree1[nodehdr1.count - count]; 94562306a36Sopenharmony_ci btree_d = &btree2[0]; 94662306a36Sopenharmony_ci memcpy(btree_d, btree_s, tmp); 94762306a36Sopenharmony_ci nodehdr1.count -= count; 94862306a36Sopenharmony_ci } else { 94962306a36Sopenharmony_ci /* 95062306a36Sopenharmony_ci * Move the req'd B-tree elements from low in node2 to 95162306a36Sopenharmony_ci * high in node1. 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ci count = -count; 95462306a36Sopenharmony_ci tmp = count * (uint)sizeof(xfs_da_node_entry_t); 95562306a36Sopenharmony_ci btree_s = &btree2[0]; 95662306a36Sopenharmony_ci btree_d = &btree1[nodehdr1.count]; 95762306a36Sopenharmony_ci memcpy(btree_d, btree_s, tmp); 95862306a36Sopenharmony_ci nodehdr1.count += count; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci xfs_trans_log_buf(tp, blk1->bp, 96162306a36Sopenharmony_ci XFS_DA_LOGRANGE(node1, btree_d, tmp)); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* 96462306a36Sopenharmony_ci * Move elements in node2 down to fill the hole. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ci tmp = nodehdr2.count - count; 96762306a36Sopenharmony_ci tmp *= (uint)sizeof(xfs_da_node_entry_t); 96862306a36Sopenharmony_ci btree_s = &btree2[count]; 96962306a36Sopenharmony_ci btree_d = &btree2[0]; 97062306a36Sopenharmony_ci memmove(btree_d, btree_s, tmp); 97162306a36Sopenharmony_ci nodehdr2.count -= count; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* 97562306a36Sopenharmony_ci * Log header of node 1 and all current bits of node 2. 97662306a36Sopenharmony_ci */ 97762306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, node1, &nodehdr1); 97862306a36Sopenharmony_ci xfs_trans_log_buf(tp, blk1->bp, 97962306a36Sopenharmony_ci XFS_DA_LOGRANGE(node1, &node1->hdr, 98062306a36Sopenharmony_ci state->args->geo->node_hdr_size)); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, node2, &nodehdr2); 98362306a36Sopenharmony_ci xfs_trans_log_buf(tp, blk2->bp, 98462306a36Sopenharmony_ci XFS_DA_LOGRANGE(node2, &node2->hdr, 98562306a36Sopenharmony_ci state->args->geo->node_hdr_size + 98662306a36Sopenharmony_ci (sizeof(btree2[0]) * nodehdr2.count))); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* 98962306a36Sopenharmony_ci * Record the last hashval from each block for upward propagation. 99062306a36Sopenharmony_ci * (note: don't use the swapped node pointers) 99162306a36Sopenharmony_ci */ 99262306a36Sopenharmony_ci if (swap) { 99362306a36Sopenharmony_ci node1 = blk1->bp->b_addr; 99462306a36Sopenharmony_ci node2 = blk2->bp->b_addr; 99562306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr1, node1); 99662306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr2, node2); 99762306a36Sopenharmony_ci btree1 = nodehdr1.btree; 99862306a36Sopenharmony_ci btree2 = nodehdr2.btree; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci blk1->hashval = be32_to_cpu(btree1[nodehdr1.count - 1].hashval); 100162306a36Sopenharmony_ci blk2->hashval = be32_to_cpu(btree2[nodehdr2.count - 1].hashval); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* 100462306a36Sopenharmony_ci * Adjust the expected index for insertion. 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_ci if (blk1->index >= nodehdr1.count) { 100762306a36Sopenharmony_ci blk2->index = blk1->index - nodehdr1.count; 100862306a36Sopenharmony_ci blk1->index = nodehdr1.count + 1; /* make it invalid */ 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/* 101362306a36Sopenharmony_ci * Add a new entry to an intermediate node. 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ciSTATIC void 101662306a36Sopenharmony_cixfs_da3_node_add( 101762306a36Sopenharmony_ci struct xfs_da_state *state, 101862306a36Sopenharmony_ci struct xfs_da_state_blk *oldblk, 101962306a36Sopenharmony_ci struct xfs_da_state_blk *newblk) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci struct xfs_da_intnode *node; 102262306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 102362306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 102462306a36Sopenharmony_ci int tmp; 102562306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci trace_xfs_da_node_add(state->args); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci node = oldblk->bp->b_addr; 103062306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 103162306a36Sopenharmony_ci btree = nodehdr.btree; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci ASSERT(oldblk->index >= 0 && oldblk->index <= nodehdr.count); 103462306a36Sopenharmony_ci ASSERT(newblk->blkno != 0); 103562306a36Sopenharmony_ci if (state->args->whichfork == XFS_DATA_FORK) 103662306a36Sopenharmony_ci ASSERT(newblk->blkno >= state->args->geo->leafblk && 103762306a36Sopenharmony_ci newblk->blkno < state->args->geo->freeblk); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci /* 104062306a36Sopenharmony_ci * We may need to make some room before we insert the new node. 104162306a36Sopenharmony_ci */ 104262306a36Sopenharmony_ci tmp = 0; 104362306a36Sopenharmony_ci if (oldblk->index < nodehdr.count) { 104462306a36Sopenharmony_ci tmp = (nodehdr.count - oldblk->index) * (uint)sizeof(*btree); 104562306a36Sopenharmony_ci memmove(&btree[oldblk->index + 1], &btree[oldblk->index], tmp); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci btree[oldblk->index].hashval = cpu_to_be32(newblk->hashval); 104862306a36Sopenharmony_ci btree[oldblk->index].before = cpu_to_be32(newblk->blkno); 104962306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, oldblk->bp, 105062306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &btree[oldblk->index], 105162306a36Sopenharmony_ci tmp + sizeof(*btree))); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci nodehdr.count += 1; 105462306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, node, &nodehdr); 105562306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, oldblk->bp, 105662306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &node->hdr, 105762306a36Sopenharmony_ci state->args->geo->node_hdr_size)); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* 106062306a36Sopenharmony_ci * Copy the last hash value from the oldblk to propagate upwards. 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_ci oldblk->hashval = be32_to_cpu(btree[nodehdr.count - 1].hashval); 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci/*======================================================================== 106662306a36Sopenharmony_ci * Routines used for shrinking the Btree. 106762306a36Sopenharmony_ci *========================================================================*/ 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci/* 107062306a36Sopenharmony_ci * Deallocate an empty leaf node, remove it from its parent, 107162306a36Sopenharmony_ci * possibly deallocating that block, etc... 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ciint 107462306a36Sopenharmony_cixfs_da3_join( 107562306a36Sopenharmony_ci struct xfs_da_state *state) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct xfs_da_state_blk *drop_blk; 107862306a36Sopenharmony_ci struct xfs_da_state_blk *save_blk; 107962306a36Sopenharmony_ci int action = 0; 108062306a36Sopenharmony_ci int error; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci trace_xfs_da_join(state->args); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci drop_blk = &state->path.blk[ state->path.active-1 ]; 108562306a36Sopenharmony_ci save_blk = &state->altpath.blk[ state->path.active-1 ]; 108662306a36Sopenharmony_ci ASSERT(state->path.blk[0].magic == XFS_DA_NODE_MAGIC); 108762306a36Sopenharmony_ci ASSERT(drop_blk->magic == XFS_ATTR_LEAF_MAGIC || 108862306a36Sopenharmony_ci drop_blk->magic == XFS_DIR2_LEAFN_MAGIC); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* 109162306a36Sopenharmony_ci * Walk back up the tree joining/deallocating as necessary. 109262306a36Sopenharmony_ci * When we stop dropping blocks, break out. 109362306a36Sopenharmony_ci */ 109462306a36Sopenharmony_ci for ( ; state->path.active >= 2; drop_blk--, save_blk--, 109562306a36Sopenharmony_ci state->path.active--) { 109662306a36Sopenharmony_ci /* 109762306a36Sopenharmony_ci * See if we can combine the block with a neighbor. 109862306a36Sopenharmony_ci * (action == 0) => no options, just leave 109962306a36Sopenharmony_ci * (action == 1) => coalesce, then unlink 110062306a36Sopenharmony_ci * (action == 2) => block empty, unlink it 110162306a36Sopenharmony_ci */ 110262306a36Sopenharmony_ci switch (drop_blk->magic) { 110362306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 110462306a36Sopenharmony_ci error = xfs_attr3_leaf_toosmall(state, &action); 110562306a36Sopenharmony_ci if (error) 110662306a36Sopenharmony_ci return error; 110762306a36Sopenharmony_ci if (action == 0) 110862306a36Sopenharmony_ci return 0; 110962306a36Sopenharmony_ci xfs_attr3_leaf_unbalance(state, drop_blk, save_blk); 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 111262306a36Sopenharmony_ci error = xfs_dir2_leafn_toosmall(state, &action); 111362306a36Sopenharmony_ci if (error) 111462306a36Sopenharmony_ci return error; 111562306a36Sopenharmony_ci if (action == 0) 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci xfs_dir2_leafn_unbalance(state, drop_blk, save_blk); 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 112062306a36Sopenharmony_ci /* 112162306a36Sopenharmony_ci * Remove the offending node, fixup hashvals, 112262306a36Sopenharmony_ci * check for a toosmall neighbor. 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_ci xfs_da3_node_remove(state, drop_blk); 112562306a36Sopenharmony_ci xfs_da3_fixhashpath(state, &state->path); 112662306a36Sopenharmony_ci error = xfs_da3_node_toosmall(state, &action); 112762306a36Sopenharmony_ci if (error) 112862306a36Sopenharmony_ci return error; 112962306a36Sopenharmony_ci if (action == 0) 113062306a36Sopenharmony_ci return 0; 113162306a36Sopenharmony_ci xfs_da3_node_unbalance(state, drop_blk, save_blk); 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci xfs_da3_fixhashpath(state, &state->altpath); 113562306a36Sopenharmony_ci error = xfs_da3_blk_unlink(state, drop_blk, save_blk); 113662306a36Sopenharmony_ci xfs_da_state_kill_altpath(state); 113762306a36Sopenharmony_ci if (error) 113862306a36Sopenharmony_ci return error; 113962306a36Sopenharmony_ci error = xfs_da_shrink_inode(state->args, drop_blk->blkno, 114062306a36Sopenharmony_ci drop_blk->bp); 114162306a36Sopenharmony_ci drop_blk->bp = NULL; 114262306a36Sopenharmony_ci if (error) 114362306a36Sopenharmony_ci return error; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci /* 114662306a36Sopenharmony_ci * We joined all the way to the top. If it turns out that 114762306a36Sopenharmony_ci * we only have one entry in the root, make the child block 114862306a36Sopenharmony_ci * the new root. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci xfs_da3_node_remove(state, drop_blk); 115162306a36Sopenharmony_ci xfs_da3_fixhashpath(state, &state->path); 115262306a36Sopenharmony_ci error = xfs_da3_root_join(state, &state->path.blk[0]); 115362306a36Sopenharmony_ci return error; 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci#ifdef DEBUG 115762306a36Sopenharmony_cistatic void 115862306a36Sopenharmony_cixfs_da_blkinfo_onlychild_validate(struct xfs_da_blkinfo *blkinfo, __u16 level) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci __be16 magic = blkinfo->magic; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (level == 1) { 116362306a36Sopenharmony_ci ASSERT(magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) || 116462306a36Sopenharmony_ci magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC) || 116562306a36Sopenharmony_ci magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC) || 116662306a36Sopenharmony_ci magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)); 116762306a36Sopenharmony_ci } else { 116862306a36Sopenharmony_ci ASSERT(magic == cpu_to_be16(XFS_DA_NODE_MAGIC) || 116962306a36Sopenharmony_ci magic == cpu_to_be16(XFS_DA3_NODE_MAGIC)); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci ASSERT(!blkinfo->forw); 117262306a36Sopenharmony_ci ASSERT(!blkinfo->back); 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci#else /* !DEBUG */ 117562306a36Sopenharmony_ci#define xfs_da_blkinfo_onlychild_validate(blkinfo, level) 117662306a36Sopenharmony_ci#endif /* !DEBUG */ 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci/* 117962306a36Sopenharmony_ci * We have only one entry in the root. Copy the only remaining child of 118062306a36Sopenharmony_ci * the old root to block 0 as the new root node. 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ciSTATIC int 118362306a36Sopenharmony_cixfs_da3_root_join( 118462306a36Sopenharmony_ci struct xfs_da_state *state, 118562306a36Sopenharmony_ci struct xfs_da_state_blk *root_blk) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci struct xfs_da_intnode *oldroot; 118862306a36Sopenharmony_ci struct xfs_da_args *args; 118962306a36Sopenharmony_ci xfs_dablk_t child; 119062306a36Sopenharmony_ci struct xfs_buf *bp; 119162306a36Sopenharmony_ci struct xfs_da3_icnode_hdr oldroothdr; 119262306a36Sopenharmony_ci int error; 119362306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci trace_xfs_da_root_join(state->args); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci ASSERT(root_blk->magic == XFS_DA_NODE_MAGIC); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci args = state->args; 120062306a36Sopenharmony_ci oldroot = root_blk->bp->b_addr; 120162306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &oldroothdr, oldroot); 120262306a36Sopenharmony_ci ASSERT(oldroothdr.forw == 0); 120362306a36Sopenharmony_ci ASSERT(oldroothdr.back == 0); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* 120662306a36Sopenharmony_ci * If the root has more than one child, then don't do anything. 120762306a36Sopenharmony_ci */ 120862306a36Sopenharmony_ci if (oldroothdr.count > 1) 120962306a36Sopenharmony_ci return 0; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * Read in the (only) child block, then copy those bytes into 121362306a36Sopenharmony_ci * the root block's buffer and free the original child block. 121462306a36Sopenharmony_ci */ 121562306a36Sopenharmony_ci child = be32_to_cpu(oldroothdr.btree[0].before); 121662306a36Sopenharmony_ci ASSERT(child != 0); 121762306a36Sopenharmony_ci error = xfs_da3_node_read(args->trans, dp, child, &bp, args->whichfork); 121862306a36Sopenharmony_ci if (error) 121962306a36Sopenharmony_ci return error; 122062306a36Sopenharmony_ci xfs_da_blkinfo_onlychild_validate(bp->b_addr, oldroothdr.level); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci /* 122362306a36Sopenharmony_ci * This could be copying a leaf back into the root block in the case of 122462306a36Sopenharmony_ci * there only being a single leaf block left in the tree. Hence we have 122562306a36Sopenharmony_ci * to update the b_ops pointer as well to match the buffer type change 122662306a36Sopenharmony_ci * that could occur. For dir3 blocks we also need to update the block 122762306a36Sopenharmony_ci * number in the buffer header. 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci memcpy(root_blk->bp->b_addr, bp->b_addr, args->geo->blksize); 123062306a36Sopenharmony_ci root_blk->bp->b_ops = bp->b_ops; 123162306a36Sopenharmony_ci xfs_trans_buf_copy_type(root_blk->bp, bp); 123262306a36Sopenharmony_ci if (oldroothdr.magic == XFS_DA3_NODE_MAGIC) { 123362306a36Sopenharmony_ci struct xfs_da3_blkinfo *da3 = root_blk->bp->b_addr; 123462306a36Sopenharmony_ci da3->blkno = cpu_to_be64(xfs_buf_daddr(root_blk->bp)); 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, root_blk->bp, 0, 123762306a36Sopenharmony_ci args->geo->blksize - 1); 123862306a36Sopenharmony_ci error = xfs_da_shrink_inode(args, child, bp); 123962306a36Sopenharmony_ci return error; 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci/* 124362306a36Sopenharmony_ci * Check a node block and its neighbors to see if the block should be 124462306a36Sopenharmony_ci * collapsed into one or the other neighbor. Always keep the block 124562306a36Sopenharmony_ci * with the smaller block number. 124662306a36Sopenharmony_ci * If the current block is over 50% full, don't try to join it, return 0. 124762306a36Sopenharmony_ci * If the block is empty, fill in the state structure and return 2. 124862306a36Sopenharmony_ci * If it can be collapsed, fill in the state structure and return 1. 124962306a36Sopenharmony_ci * If nothing can be done, return 0. 125062306a36Sopenharmony_ci */ 125162306a36Sopenharmony_ciSTATIC int 125262306a36Sopenharmony_cixfs_da3_node_toosmall( 125362306a36Sopenharmony_ci struct xfs_da_state *state, 125462306a36Sopenharmony_ci int *action) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct xfs_da_intnode *node; 125762306a36Sopenharmony_ci struct xfs_da_state_blk *blk; 125862306a36Sopenharmony_ci struct xfs_da_blkinfo *info; 125962306a36Sopenharmony_ci xfs_dablk_t blkno; 126062306a36Sopenharmony_ci struct xfs_buf *bp; 126162306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 126262306a36Sopenharmony_ci int count; 126362306a36Sopenharmony_ci int forward; 126462306a36Sopenharmony_ci int error; 126562306a36Sopenharmony_ci int retval; 126662306a36Sopenharmony_ci int i; 126762306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci trace_xfs_da_node_toosmall(state->args); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci /* 127262306a36Sopenharmony_ci * Check for the degenerate case of the block being over 50% full. 127362306a36Sopenharmony_ci * If so, it's not worth even looking to see if we might be able 127462306a36Sopenharmony_ci * to coalesce with a sibling. 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_ci blk = &state->path.blk[ state->path.active-1 ]; 127762306a36Sopenharmony_ci info = blk->bp->b_addr; 127862306a36Sopenharmony_ci node = (xfs_da_intnode_t *)info; 127962306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 128062306a36Sopenharmony_ci if (nodehdr.count > (state->args->geo->node_ents >> 1)) { 128162306a36Sopenharmony_ci *action = 0; /* blk over 50%, don't try to join */ 128262306a36Sopenharmony_ci return 0; /* blk over 50%, don't try to join */ 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci /* 128662306a36Sopenharmony_ci * Check for the degenerate case of the block being empty. 128762306a36Sopenharmony_ci * If the block is empty, we'll simply delete it, no need to 128862306a36Sopenharmony_ci * coalesce it with a sibling block. We choose (arbitrarily) 128962306a36Sopenharmony_ci * to merge with the forward block unless it is NULL. 129062306a36Sopenharmony_ci */ 129162306a36Sopenharmony_ci if (nodehdr.count == 0) { 129262306a36Sopenharmony_ci /* 129362306a36Sopenharmony_ci * Make altpath point to the block we want to keep and 129462306a36Sopenharmony_ci * path point to the block we want to drop (this one). 129562306a36Sopenharmony_ci */ 129662306a36Sopenharmony_ci forward = (info->forw != 0); 129762306a36Sopenharmony_ci memcpy(&state->altpath, &state->path, sizeof(state->path)); 129862306a36Sopenharmony_ci error = xfs_da3_path_shift(state, &state->altpath, forward, 129962306a36Sopenharmony_ci 0, &retval); 130062306a36Sopenharmony_ci if (error) 130162306a36Sopenharmony_ci return error; 130262306a36Sopenharmony_ci if (retval) { 130362306a36Sopenharmony_ci *action = 0; 130462306a36Sopenharmony_ci } else { 130562306a36Sopenharmony_ci *action = 2; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci return 0; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* 131162306a36Sopenharmony_ci * Examine each sibling block to see if we can coalesce with 131262306a36Sopenharmony_ci * at least 25% free space to spare. We need to figure out 131362306a36Sopenharmony_ci * whether to merge with the forward or the backward block. 131462306a36Sopenharmony_ci * We prefer coalescing with the lower numbered sibling so as 131562306a36Sopenharmony_ci * to shrink a directory over time. 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_ci count = state->args->geo->node_ents; 131862306a36Sopenharmony_ci count -= state->args->geo->node_ents >> 2; 131962306a36Sopenharmony_ci count -= nodehdr.count; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* start with smaller blk num */ 132262306a36Sopenharmony_ci forward = nodehdr.forw < nodehdr.back; 132362306a36Sopenharmony_ci for (i = 0; i < 2; forward = !forward, i++) { 132462306a36Sopenharmony_ci struct xfs_da3_icnode_hdr thdr; 132562306a36Sopenharmony_ci if (forward) 132662306a36Sopenharmony_ci blkno = nodehdr.forw; 132762306a36Sopenharmony_ci else 132862306a36Sopenharmony_ci blkno = nodehdr.back; 132962306a36Sopenharmony_ci if (blkno == 0) 133062306a36Sopenharmony_ci continue; 133162306a36Sopenharmony_ci error = xfs_da3_node_read(state->args->trans, dp, blkno, &bp, 133262306a36Sopenharmony_ci state->args->whichfork); 133362306a36Sopenharmony_ci if (error) 133462306a36Sopenharmony_ci return error; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci node = bp->b_addr; 133762306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &thdr, node); 133862306a36Sopenharmony_ci xfs_trans_brelse(state->args->trans, bp); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci if (count - thdr.count >= 0) 134162306a36Sopenharmony_ci break; /* fits with at least 25% to spare */ 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci if (i >= 2) { 134462306a36Sopenharmony_ci *action = 0; 134562306a36Sopenharmony_ci return 0; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* 134962306a36Sopenharmony_ci * Make altpath point to the block we want to keep (the lower 135062306a36Sopenharmony_ci * numbered block) and path point to the block we want to drop. 135162306a36Sopenharmony_ci */ 135262306a36Sopenharmony_ci memcpy(&state->altpath, &state->path, sizeof(state->path)); 135362306a36Sopenharmony_ci if (blkno < blk->blkno) { 135462306a36Sopenharmony_ci error = xfs_da3_path_shift(state, &state->altpath, forward, 135562306a36Sopenharmony_ci 0, &retval); 135662306a36Sopenharmony_ci } else { 135762306a36Sopenharmony_ci error = xfs_da3_path_shift(state, &state->path, forward, 135862306a36Sopenharmony_ci 0, &retval); 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci if (error) 136162306a36Sopenharmony_ci return error; 136262306a36Sopenharmony_ci if (retval) { 136362306a36Sopenharmony_ci *action = 0; 136462306a36Sopenharmony_ci return 0; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci *action = 1; 136762306a36Sopenharmony_ci return 0; 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci/* 137162306a36Sopenharmony_ci * Pick up the last hashvalue from an intermediate node. 137262306a36Sopenharmony_ci */ 137362306a36Sopenharmony_ciSTATIC uint 137462306a36Sopenharmony_cixfs_da3_node_lasthash( 137562306a36Sopenharmony_ci struct xfs_inode *dp, 137662306a36Sopenharmony_ci struct xfs_buf *bp, 137762306a36Sopenharmony_ci int *count) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, bp->b_addr); 138262306a36Sopenharmony_ci if (count) 138362306a36Sopenharmony_ci *count = nodehdr.count; 138462306a36Sopenharmony_ci if (!nodehdr.count) 138562306a36Sopenharmony_ci return 0; 138662306a36Sopenharmony_ci return be32_to_cpu(nodehdr.btree[nodehdr.count - 1].hashval); 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci/* 139062306a36Sopenharmony_ci * Walk back up the tree adjusting hash values as necessary, 139162306a36Sopenharmony_ci * when we stop making changes, return. 139262306a36Sopenharmony_ci */ 139362306a36Sopenharmony_civoid 139462306a36Sopenharmony_cixfs_da3_fixhashpath( 139562306a36Sopenharmony_ci struct xfs_da_state *state, 139662306a36Sopenharmony_ci struct xfs_da_state_path *path) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct xfs_da_state_blk *blk; 139962306a36Sopenharmony_ci struct xfs_da_intnode *node; 140062306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 140162306a36Sopenharmony_ci xfs_dahash_t lasthash=0; 140262306a36Sopenharmony_ci int level; 140362306a36Sopenharmony_ci int count; 140462306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci trace_xfs_da_fixhashpath(state->args); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci level = path->active-1; 140962306a36Sopenharmony_ci blk = &path->blk[ level ]; 141062306a36Sopenharmony_ci switch (blk->magic) { 141162306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 141262306a36Sopenharmony_ci lasthash = xfs_attr_leaf_lasthash(blk->bp, &count); 141362306a36Sopenharmony_ci if (count == 0) 141462306a36Sopenharmony_ci return; 141562306a36Sopenharmony_ci break; 141662306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 141762306a36Sopenharmony_ci lasthash = xfs_dir2_leaf_lasthash(dp, blk->bp, &count); 141862306a36Sopenharmony_ci if (count == 0) 141962306a36Sopenharmony_ci return; 142062306a36Sopenharmony_ci break; 142162306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 142262306a36Sopenharmony_ci lasthash = xfs_da3_node_lasthash(dp, blk->bp, &count); 142362306a36Sopenharmony_ci if (count == 0) 142462306a36Sopenharmony_ci return; 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci for (blk--, level--; level >= 0; blk--, level--) { 142862306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci node = blk->bp->b_addr; 143162306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 143262306a36Sopenharmony_ci btree = nodehdr.btree; 143362306a36Sopenharmony_ci if (be32_to_cpu(btree[blk->index].hashval) == lasthash) 143462306a36Sopenharmony_ci break; 143562306a36Sopenharmony_ci blk->hashval = lasthash; 143662306a36Sopenharmony_ci btree[blk->index].hashval = cpu_to_be32(lasthash); 143762306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, blk->bp, 143862306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &btree[blk->index], 143962306a36Sopenharmony_ci sizeof(*btree))); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci lasthash = be32_to_cpu(btree[nodehdr.count - 1].hashval); 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci} 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci/* 144662306a36Sopenharmony_ci * Remove an entry from an intermediate node. 144762306a36Sopenharmony_ci */ 144862306a36Sopenharmony_ciSTATIC void 144962306a36Sopenharmony_cixfs_da3_node_remove( 145062306a36Sopenharmony_ci struct xfs_da_state *state, 145162306a36Sopenharmony_ci struct xfs_da_state_blk *drop_blk) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci struct xfs_da_intnode *node; 145462306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 145562306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 145662306a36Sopenharmony_ci int index; 145762306a36Sopenharmony_ci int tmp; 145862306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci trace_xfs_da_node_remove(state->args); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci node = drop_blk->bp->b_addr; 146362306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 146462306a36Sopenharmony_ci ASSERT(drop_blk->index < nodehdr.count); 146562306a36Sopenharmony_ci ASSERT(drop_blk->index >= 0); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* 146862306a36Sopenharmony_ci * Copy over the offending entry, or just zero it out. 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_ci index = drop_blk->index; 147162306a36Sopenharmony_ci btree = nodehdr.btree; 147262306a36Sopenharmony_ci if (index < nodehdr.count - 1) { 147362306a36Sopenharmony_ci tmp = nodehdr.count - index - 1; 147462306a36Sopenharmony_ci tmp *= (uint)sizeof(xfs_da_node_entry_t); 147562306a36Sopenharmony_ci memmove(&btree[index], &btree[index + 1], tmp); 147662306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, drop_blk->bp, 147762306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &btree[index], tmp)); 147862306a36Sopenharmony_ci index = nodehdr.count - 1; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci memset(&btree[index], 0, sizeof(xfs_da_node_entry_t)); 148162306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, drop_blk->bp, 148262306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &btree[index], sizeof(btree[index]))); 148362306a36Sopenharmony_ci nodehdr.count -= 1; 148462306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, node, &nodehdr); 148562306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, drop_blk->bp, 148662306a36Sopenharmony_ci XFS_DA_LOGRANGE(node, &node->hdr, state->args->geo->node_hdr_size)); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci /* 148962306a36Sopenharmony_ci * Copy the last hash value from the block to propagate upwards. 149062306a36Sopenharmony_ci */ 149162306a36Sopenharmony_ci drop_blk->hashval = be32_to_cpu(btree[index - 1].hashval); 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci/* 149562306a36Sopenharmony_ci * Unbalance the elements between two intermediate nodes, 149662306a36Sopenharmony_ci * move all Btree elements from one node into another. 149762306a36Sopenharmony_ci */ 149862306a36Sopenharmony_ciSTATIC void 149962306a36Sopenharmony_cixfs_da3_node_unbalance( 150062306a36Sopenharmony_ci struct xfs_da_state *state, 150162306a36Sopenharmony_ci struct xfs_da_state_blk *drop_blk, 150262306a36Sopenharmony_ci struct xfs_da_state_blk *save_blk) 150362306a36Sopenharmony_ci{ 150462306a36Sopenharmony_ci struct xfs_da_intnode *drop_node; 150562306a36Sopenharmony_ci struct xfs_da_intnode *save_node; 150662306a36Sopenharmony_ci struct xfs_da_node_entry *drop_btree; 150762306a36Sopenharmony_ci struct xfs_da_node_entry *save_btree; 150862306a36Sopenharmony_ci struct xfs_da3_icnode_hdr drop_hdr; 150962306a36Sopenharmony_ci struct xfs_da3_icnode_hdr save_hdr; 151062306a36Sopenharmony_ci struct xfs_trans *tp; 151162306a36Sopenharmony_ci int sindex; 151262306a36Sopenharmony_ci int tmp; 151362306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci trace_xfs_da_node_unbalance(state->args); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci drop_node = drop_blk->bp->b_addr; 151862306a36Sopenharmony_ci save_node = save_blk->bp->b_addr; 151962306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &drop_hdr, drop_node); 152062306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &save_hdr, save_node); 152162306a36Sopenharmony_ci drop_btree = drop_hdr.btree; 152262306a36Sopenharmony_ci save_btree = save_hdr.btree; 152362306a36Sopenharmony_ci tp = state->args->trans; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci /* 152662306a36Sopenharmony_ci * If the dying block has lower hashvals, then move all the 152762306a36Sopenharmony_ci * elements in the remaining block up to make a hole. 152862306a36Sopenharmony_ci */ 152962306a36Sopenharmony_ci if ((be32_to_cpu(drop_btree[0].hashval) < 153062306a36Sopenharmony_ci be32_to_cpu(save_btree[0].hashval)) || 153162306a36Sopenharmony_ci (be32_to_cpu(drop_btree[drop_hdr.count - 1].hashval) < 153262306a36Sopenharmony_ci be32_to_cpu(save_btree[save_hdr.count - 1].hashval))) { 153362306a36Sopenharmony_ci /* XXX: check this - is memmove dst correct? */ 153462306a36Sopenharmony_ci tmp = save_hdr.count * sizeof(xfs_da_node_entry_t); 153562306a36Sopenharmony_ci memmove(&save_btree[drop_hdr.count], &save_btree[0], tmp); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci sindex = 0; 153862306a36Sopenharmony_ci xfs_trans_log_buf(tp, save_blk->bp, 153962306a36Sopenharmony_ci XFS_DA_LOGRANGE(save_node, &save_btree[0], 154062306a36Sopenharmony_ci (save_hdr.count + drop_hdr.count) * 154162306a36Sopenharmony_ci sizeof(xfs_da_node_entry_t))); 154262306a36Sopenharmony_ci } else { 154362306a36Sopenharmony_ci sindex = save_hdr.count; 154462306a36Sopenharmony_ci xfs_trans_log_buf(tp, save_blk->bp, 154562306a36Sopenharmony_ci XFS_DA_LOGRANGE(save_node, &save_btree[sindex], 154662306a36Sopenharmony_ci drop_hdr.count * sizeof(xfs_da_node_entry_t))); 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* 155062306a36Sopenharmony_ci * Move all the B-tree elements from drop_blk to save_blk. 155162306a36Sopenharmony_ci */ 155262306a36Sopenharmony_ci tmp = drop_hdr.count * (uint)sizeof(xfs_da_node_entry_t); 155362306a36Sopenharmony_ci memcpy(&save_btree[sindex], &drop_btree[0], tmp); 155462306a36Sopenharmony_ci save_hdr.count += drop_hdr.count; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, save_node, &save_hdr); 155762306a36Sopenharmony_ci xfs_trans_log_buf(tp, save_blk->bp, 155862306a36Sopenharmony_ci XFS_DA_LOGRANGE(save_node, &save_node->hdr, 155962306a36Sopenharmony_ci state->args->geo->node_hdr_size)); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci /* 156262306a36Sopenharmony_ci * Save the last hashval in the remaining block for upward propagation. 156362306a36Sopenharmony_ci */ 156462306a36Sopenharmony_ci save_blk->hashval = be32_to_cpu(save_btree[save_hdr.count - 1].hashval); 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci/*======================================================================== 156862306a36Sopenharmony_ci * Routines used for finding things in the Btree. 156962306a36Sopenharmony_ci *========================================================================*/ 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci/* 157262306a36Sopenharmony_ci * Walk down the Btree looking for a particular filename, filling 157362306a36Sopenharmony_ci * in the state structure as we go. 157462306a36Sopenharmony_ci * 157562306a36Sopenharmony_ci * We will set the state structure to point to each of the elements 157662306a36Sopenharmony_ci * in each of the nodes where either the hashval is or should be. 157762306a36Sopenharmony_ci * 157862306a36Sopenharmony_ci * We support duplicate hashval's so for each entry in the current 157962306a36Sopenharmony_ci * node that could contain the desired hashval, descend. This is a 158062306a36Sopenharmony_ci * pruned depth-first tree search. 158162306a36Sopenharmony_ci */ 158262306a36Sopenharmony_ciint /* error */ 158362306a36Sopenharmony_cixfs_da3_node_lookup_int( 158462306a36Sopenharmony_ci struct xfs_da_state *state, 158562306a36Sopenharmony_ci int *result) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci struct xfs_da_state_blk *blk; 158862306a36Sopenharmony_ci struct xfs_da_blkinfo *curr; 158962306a36Sopenharmony_ci struct xfs_da_intnode *node; 159062306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 159162306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 159262306a36Sopenharmony_ci struct xfs_da_args *args; 159362306a36Sopenharmony_ci xfs_dablk_t blkno; 159462306a36Sopenharmony_ci xfs_dahash_t hashval; 159562306a36Sopenharmony_ci xfs_dahash_t btreehashval; 159662306a36Sopenharmony_ci int probe; 159762306a36Sopenharmony_ci int span; 159862306a36Sopenharmony_ci int max; 159962306a36Sopenharmony_ci int error; 160062306a36Sopenharmony_ci int retval; 160162306a36Sopenharmony_ci unsigned int expected_level = 0; 160262306a36Sopenharmony_ci uint16_t magic; 160362306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci args = state->args; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci /* 160862306a36Sopenharmony_ci * Descend thru the B-tree searching each level for the right 160962306a36Sopenharmony_ci * node to use, until the right hashval is found. 161062306a36Sopenharmony_ci */ 161162306a36Sopenharmony_ci blkno = args->geo->leafblk; 161262306a36Sopenharmony_ci for (blk = &state->path.blk[0], state->path.active = 1; 161362306a36Sopenharmony_ci state->path.active <= XFS_DA_NODE_MAXDEPTH; 161462306a36Sopenharmony_ci blk++, state->path.active++) { 161562306a36Sopenharmony_ci /* 161662306a36Sopenharmony_ci * Read the next node down in the tree. 161762306a36Sopenharmony_ci */ 161862306a36Sopenharmony_ci blk->blkno = blkno; 161962306a36Sopenharmony_ci error = xfs_da3_node_read(args->trans, args->dp, blkno, 162062306a36Sopenharmony_ci &blk->bp, args->whichfork); 162162306a36Sopenharmony_ci if (error) { 162262306a36Sopenharmony_ci blk->blkno = 0; 162362306a36Sopenharmony_ci state->path.active--; 162462306a36Sopenharmony_ci return error; 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci curr = blk->bp->b_addr; 162762306a36Sopenharmony_ci magic = be16_to_cpu(curr->magic); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (magic == XFS_ATTR_LEAF_MAGIC || 163062306a36Sopenharmony_ci magic == XFS_ATTR3_LEAF_MAGIC) { 163162306a36Sopenharmony_ci blk->magic = XFS_ATTR_LEAF_MAGIC; 163262306a36Sopenharmony_ci blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL); 163362306a36Sopenharmony_ci break; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci if (magic == XFS_DIR2_LEAFN_MAGIC || 163762306a36Sopenharmony_ci magic == XFS_DIR3_LEAFN_MAGIC) { 163862306a36Sopenharmony_ci blk->magic = XFS_DIR2_LEAFN_MAGIC; 163962306a36Sopenharmony_ci blk->hashval = xfs_dir2_leaf_lasthash(args->dp, 164062306a36Sopenharmony_ci blk->bp, NULL); 164162306a36Sopenharmony_ci break; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (magic != XFS_DA_NODE_MAGIC && magic != XFS_DA3_NODE_MAGIC) { 164562306a36Sopenharmony_ci xfs_buf_mark_corrupt(blk->bp); 164662306a36Sopenharmony_ci return -EFSCORRUPTED; 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci blk->magic = XFS_DA_NODE_MAGIC; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci /* 165262306a36Sopenharmony_ci * Search an intermediate node for a match. 165362306a36Sopenharmony_ci */ 165462306a36Sopenharmony_ci node = blk->bp->b_addr; 165562306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node); 165662306a36Sopenharmony_ci btree = nodehdr.btree; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* Tree taller than we can handle; bail out! */ 165962306a36Sopenharmony_ci if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) { 166062306a36Sopenharmony_ci xfs_buf_mark_corrupt(blk->bp); 166162306a36Sopenharmony_ci return -EFSCORRUPTED; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci /* Check the level from the root. */ 166562306a36Sopenharmony_ci if (blkno == args->geo->leafblk) 166662306a36Sopenharmony_ci expected_level = nodehdr.level - 1; 166762306a36Sopenharmony_ci else if (expected_level != nodehdr.level) { 166862306a36Sopenharmony_ci xfs_buf_mark_corrupt(blk->bp); 166962306a36Sopenharmony_ci return -EFSCORRUPTED; 167062306a36Sopenharmony_ci } else 167162306a36Sopenharmony_ci expected_level--; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci max = nodehdr.count; 167462306a36Sopenharmony_ci blk->hashval = be32_to_cpu(btree[max - 1].hashval); 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci /* 167762306a36Sopenharmony_ci * Binary search. (note: small blocks will skip loop) 167862306a36Sopenharmony_ci */ 167962306a36Sopenharmony_ci probe = span = max / 2; 168062306a36Sopenharmony_ci hashval = args->hashval; 168162306a36Sopenharmony_ci while (span > 4) { 168262306a36Sopenharmony_ci span /= 2; 168362306a36Sopenharmony_ci btreehashval = be32_to_cpu(btree[probe].hashval); 168462306a36Sopenharmony_ci if (btreehashval < hashval) 168562306a36Sopenharmony_ci probe += span; 168662306a36Sopenharmony_ci else if (btreehashval > hashval) 168762306a36Sopenharmony_ci probe -= span; 168862306a36Sopenharmony_ci else 168962306a36Sopenharmony_ci break; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci ASSERT((probe >= 0) && (probe < max)); 169262306a36Sopenharmony_ci ASSERT((span <= 4) || 169362306a36Sopenharmony_ci (be32_to_cpu(btree[probe].hashval) == hashval)); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci /* 169662306a36Sopenharmony_ci * Since we may have duplicate hashval's, find the first 169762306a36Sopenharmony_ci * matching hashval in the node. 169862306a36Sopenharmony_ci */ 169962306a36Sopenharmony_ci while (probe > 0 && 170062306a36Sopenharmony_ci be32_to_cpu(btree[probe].hashval) >= hashval) { 170162306a36Sopenharmony_ci probe--; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci while (probe < max && 170462306a36Sopenharmony_ci be32_to_cpu(btree[probe].hashval) < hashval) { 170562306a36Sopenharmony_ci probe++; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* 170962306a36Sopenharmony_ci * Pick the right block to descend on. 171062306a36Sopenharmony_ci */ 171162306a36Sopenharmony_ci if (probe == max) { 171262306a36Sopenharmony_ci blk->index = max - 1; 171362306a36Sopenharmony_ci blkno = be32_to_cpu(btree[max - 1].before); 171462306a36Sopenharmony_ci } else { 171562306a36Sopenharmony_ci blk->index = probe; 171662306a36Sopenharmony_ci blkno = be32_to_cpu(btree[probe].before); 171762306a36Sopenharmony_ci } 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci /* We can't point back to the root. */ 172062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(dp->i_mount, blkno == args->geo->leafblk)) 172162306a36Sopenharmony_ci return -EFSCORRUPTED; 172262306a36Sopenharmony_ci } 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(dp->i_mount, expected_level != 0)) 172562306a36Sopenharmony_ci return -EFSCORRUPTED; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci /* 172862306a36Sopenharmony_ci * A leaf block that ends in the hashval that we are interested in 172962306a36Sopenharmony_ci * (final hashval == search hashval) means that the next block may 173062306a36Sopenharmony_ci * contain more entries with the same hashval, shift upward to the 173162306a36Sopenharmony_ci * next leaf and keep searching. 173262306a36Sopenharmony_ci */ 173362306a36Sopenharmony_ci for (;;) { 173462306a36Sopenharmony_ci if (blk->magic == XFS_DIR2_LEAFN_MAGIC) { 173562306a36Sopenharmony_ci retval = xfs_dir2_leafn_lookup_int(blk->bp, args, 173662306a36Sopenharmony_ci &blk->index, state); 173762306a36Sopenharmony_ci } else if (blk->magic == XFS_ATTR_LEAF_MAGIC) { 173862306a36Sopenharmony_ci retval = xfs_attr3_leaf_lookup_int(blk->bp, args); 173962306a36Sopenharmony_ci blk->index = args->index; 174062306a36Sopenharmony_ci args->blkno = blk->blkno; 174162306a36Sopenharmony_ci } else { 174262306a36Sopenharmony_ci ASSERT(0); 174362306a36Sopenharmony_ci return -EFSCORRUPTED; 174462306a36Sopenharmony_ci } 174562306a36Sopenharmony_ci if (((retval == -ENOENT) || (retval == -ENOATTR)) && 174662306a36Sopenharmony_ci (blk->hashval == args->hashval)) { 174762306a36Sopenharmony_ci error = xfs_da3_path_shift(state, &state->path, 1, 1, 174862306a36Sopenharmony_ci &retval); 174962306a36Sopenharmony_ci if (error) 175062306a36Sopenharmony_ci return error; 175162306a36Sopenharmony_ci if (retval == 0) { 175262306a36Sopenharmony_ci continue; 175362306a36Sopenharmony_ci } else if (blk->magic == XFS_ATTR_LEAF_MAGIC) { 175462306a36Sopenharmony_ci /* path_shift() gives ENOENT */ 175562306a36Sopenharmony_ci retval = -ENOATTR; 175662306a36Sopenharmony_ci } 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci break; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci *result = retval; 176162306a36Sopenharmony_ci return 0; 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci/*======================================================================== 176562306a36Sopenharmony_ci * Utility routines. 176662306a36Sopenharmony_ci *========================================================================*/ 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci/* 176962306a36Sopenharmony_ci * Compare two intermediate nodes for "order". 177062306a36Sopenharmony_ci */ 177162306a36Sopenharmony_ciSTATIC int 177262306a36Sopenharmony_cixfs_da3_node_order( 177362306a36Sopenharmony_ci struct xfs_inode *dp, 177462306a36Sopenharmony_ci struct xfs_buf *node1_bp, 177562306a36Sopenharmony_ci struct xfs_buf *node2_bp) 177662306a36Sopenharmony_ci{ 177762306a36Sopenharmony_ci struct xfs_da_intnode *node1; 177862306a36Sopenharmony_ci struct xfs_da_intnode *node2; 177962306a36Sopenharmony_ci struct xfs_da_node_entry *btree1; 178062306a36Sopenharmony_ci struct xfs_da_node_entry *btree2; 178162306a36Sopenharmony_ci struct xfs_da3_icnode_hdr node1hdr; 178262306a36Sopenharmony_ci struct xfs_da3_icnode_hdr node2hdr; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci node1 = node1_bp->b_addr; 178562306a36Sopenharmony_ci node2 = node2_bp->b_addr; 178662306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &node1hdr, node1); 178762306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &node2hdr, node2); 178862306a36Sopenharmony_ci btree1 = node1hdr.btree; 178962306a36Sopenharmony_ci btree2 = node2hdr.btree; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci if (node1hdr.count > 0 && node2hdr.count > 0 && 179262306a36Sopenharmony_ci ((be32_to_cpu(btree2[0].hashval) < be32_to_cpu(btree1[0].hashval)) || 179362306a36Sopenharmony_ci (be32_to_cpu(btree2[node2hdr.count - 1].hashval) < 179462306a36Sopenharmony_ci be32_to_cpu(btree1[node1hdr.count - 1].hashval)))) { 179562306a36Sopenharmony_ci return 1; 179662306a36Sopenharmony_ci } 179762306a36Sopenharmony_ci return 0; 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci/* 180162306a36Sopenharmony_ci * Link a new block into a doubly linked list of blocks (of whatever type). 180262306a36Sopenharmony_ci */ 180362306a36Sopenharmony_ciint /* error */ 180462306a36Sopenharmony_cixfs_da3_blk_link( 180562306a36Sopenharmony_ci struct xfs_da_state *state, 180662306a36Sopenharmony_ci struct xfs_da_state_blk *old_blk, 180762306a36Sopenharmony_ci struct xfs_da_state_blk *new_blk) 180862306a36Sopenharmony_ci{ 180962306a36Sopenharmony_ci struct xfs_da_blkinfo *old_info; 181062306a36Sopenharmony_ci struct xfs_da_blkinfo *new_info; 181162306a36Sopenharmony_ci struct xfs_da_blkinfo *tmp_info; 181262306a36Sopenharmony_ci struct xfs_da_args *args; 181362306a36Sopenharmony_ci struct xfs_buf *bp; 181462306a36Sopenharmony_ci int before = 0; 181562306a36Sopenharmony_ci int error; 181662306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci /* 181962306a36Sopenharmony_ci * Set up environment. 182062306a36Sopenharmony_ci */ 182162306a36Sopenharmony_ci args = state->args; 182262306a36Sopenharmony_ci ASSERT(args != NULL); 182362306a36Sopenharmony_ci old_info = old_blk->bp->b_addr; 182462306a36Sopenharmony_ci new_info = new_blk->bp->b_addr; 182562306a36Sopenharmony_ci ASSERT(old_blk->magic == XFS_DA_NODE_MAGIC || 182662306a36Sopenharmony_ci old_blk->magic == XFS_DIR2_LEAFN_MAGIC || 182762306a36Sopenharmony_ci old_blk->magic == XFS_ATTR_LEAF_MAGIC); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci switch (old_blk->magic) { 183062306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 183162306a36Sopenharmony_ci before = xfs_attr_leaf_order(old_blk->bp, new_blk->bp); 183262306a36Sopenharmony_ci break; 183362306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 183462306a36Sopenharmony_ci before = xfs_dir2_leafn_order(dp, old_blk->bp, new_blk->bp); 183562306a36Sopenharmony_ci break; 183662306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 183762306a36Sopenharmony_ci before = xfs_da3_node_order(dp, old_blk->bp, new_blk->bp); 183862306a36Sopenharmony_ci break; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* 184262306a36Sopenharmony_ci * Link blocks in appropriate order. 184362306a36Sopenharmony_ci */ 184462306a36Sopenharmony_ci if (before) { 184562306a36Sopenharmony_ci /* 184662306a36Sopenharmony_ci * Link new block in before existing block. 184762306a36Sopenharmony_ci */ 184862306a36Sopenharmony_ci trace_xfs_da_link_before(args); 184962306a36Sopenharmony_ci new_info->forw = cpu_to_be32(old_blk->blkno); 185062306a36Sopenharmony_ci new_info->back = old_info->back; 185162306a36Sopenharmony_ci if (old_info->back) { 185262306a36Sopenharmony_ci error = xfs_da3_node_read(args->trans, dp, 185362306a36Sopenharmony_ci be32_to_cpu(old_info->back), 185462306a36Sopenharmony_ci &bp, args->whichfork); 185562306a36Sopenharmony_ci if (error) 185662306a36Sopenharmony_ci return error; 185762306a36Sopenharmony_ci ASSERT(bp != NULL); 185862306a36Sopenharmony_ci tmp_info = bp->b_addr; 185962306a36Sopenharmony_ci ASSERT(tmp_info->magic == old_info->magic); 186062306a36Sopenharmony_ci ASSERT(be32_to_cpu(tmp_info->forw) == old_blk->blkno); 186162306a36Sopenharmony_ci tmp_info->forw = cpu_to_be32(new_blk->blkno); 186262306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1); 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci old_info->back = cpu_to_be32(new_blk->blkno); 186562306a36Sopenharmony_ci } else { 186662306a36Sopenharmony_ci /* 186762306a36Sopenharmony_ci * Link new block in after existing block. 186862306a36Sopenharmony_ci */ 186962306a36Sopenharmony_ci trace_xfs_da_link_after(args); 187062306a36Sopenharmony_ci new_info->forw = old_info->forw; 187162306a36Sopenharmony_ci new_info->back = cpu_to_be32(old_blk->blkno); 187262306a36Sopenharmony_ci if (old_info->forw) { 187362306a36Sopenharmony_ci error = xfs_da3_node_read(args->trans, dp, 187462306a36Sopenharmony_ci be32_to_cpu(old_info->forw), 187562306a36Sopenharmony_ci &bp, args->whichfork); 187662306a36Sopenharmony_ci if (error) 187762306a36Sopenharmony_ci return error; 187862306a36Sopenharmony_ci ASSERT(bp != NULL); 187962306a36Sopenharmony_ci tmp_info = bp->b_addr; 188062306a36Sopenharmony_ci ASSERT(tmp_info->magic == old_info->magic); 188162306a36Sopenharmony_ci ASSERT(be32_to_cpu(tmp_info->back) == old_blk->blkno); 188262306a36Sopenharmony_ci tmp_info->back = cpu_to_be32(new_blk->blkno); 188362306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1); 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci old_info->forw = cpu_to_be32(new_blk->blkno); 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, old_blk->bp, 0, sizeof(*tmp_info) - 1); 188962306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, new_blk->bp, 0, sizeof(*tmp_info) - 1); 189062306a36Sopenharmony_ci return 0; 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci/* 189462306a36Sopenharmony_ci * Unlink a block from a doubly linked list of blocks. 189562306a36Sopenharmony_ci */ 189662306a36Sopenharmony_ciSTATIC int /* error */ 189762306a36Sopenharmony_cixfs_da3_blk_unlink( 189862306a36Sopenharmony_ci struct xfs_da_state *state, 189962306a36Sopenharmony_ci struct xfs_da_state_blk *drop_blk, 190062306a36Sopenharmony_ci struct xfs_da_state_blk *save_blk) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci struct xfs_da_blkinfo *drop_info; 190362306a36Sopenharmony_ci struct xfs_da_blkinfo *save_info; 190462306a36Sopenharmony_ci struct xfs_da_blkinfo *tmp_info; 190562306a36Sopenharmony_ci struct xfs_da_args *args; 190662306a36Sopenharmony_ci struct xfs_buf *bp; 190762306a36Sopenharmony_ci int error; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci /* 191062306a36Sopenharmony_ci * Set up environment. 191162306a36Sopenharmony_ci */ 191262306a36Sopenharmony_ci args = state->args; 191362306a36Sopenharmony_ci ASSERT(args != NULL); 191462306a36Sopenharmony_ci save_info = save_blk->bp->b_addr; 191562306a36Sopenharmony_ci drop_info = drop_blk->bp->b_addr; 191662306a36Sopenharmony_ci ASSERT(save_blk->magic == XFS_DA_NODE_MAGIC || 191762306a36Sopenharmony_ci save_blk->magic == XFS_DIR2_LEAFN_MAGIC || 191862306a36Sopenharmony_ci save_blk->magic == XFS_ATTR_LEAF_MAGIC); 191962306a36Sopenharmony_ci ASSERT(save_blk->magic == drop_blk->magic); 192062306a36Sopenharmony_ci ASSERT((be32_to_cpu(save_info->forw) == drop_blk->blkno) || 192162306a36Sopenharmony_ci (be32_to_cpu(save_info->back) == drop_blk->blkno)); 192262306a36Sopenharmony_ci ASSERT((be32_to_cpu(drop_info->forw) == save_blk->blkno) || 192362306a36Sopenharmony_ci (be32_to_cpu(drop_info->back) == save_blk->blkno)); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci /* 192662306a36Sopenharmony_ci * Unlink the leaf block from the doubly linked chain of leaves. 192762306a36Sopenharmony_ci */ 192862306a36Sopenharmony_ci if (be32_to_cpu(save_info->back) == drop_blk->blkno) { 192962306a36Sopenharmony_ci trace_xfs_da_unlink_back(args); 193062306a36Sopenharmony_ci save_info->back = drop_info->back; 193162306a36Sopenharmony_ci if (drop_info->back) { 193262306a36Sopenharmony_ci error = xfs_da3_node_read(args->trans, args->dp, 193362306a36Sopenharmony_ci be32_to_cpu(drop_info->back), 193462306a36Sopenharmony_ci &bp, args->whichfork); 193562306a36Sopenharmony_ci if (error) 193662306a36Sopenharmony_ci return error; 193762306a36Sopenharmony_ci ASSERT(bp != NULL); 193862306a36Sopenharmony_ci tmp_info = bp->b_addr; 193962306a36Sopenharmony_ci ASSERT(tmp_info->magic == save_info->magic); 194062306a36Sopenharmony_ci ASSERT(be32_to_cpu(tmp_info->forw) == drop_blk->blkno); 194162306a36Sopenharmony_ci tmp_info->forw = cpu_to_be32(save_blk->blkno); 194262306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 0, 194362306a36Sopenharmony_ci sizeof(*tmp_info) - 1); 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci } else { 194662306a36Sopenharmony_ci trace_xfs_da_unlink_forward(args); 194762306a36Sopenharmony_ci save_info->forw = drop_info->forw; 194862306a36Sopenharmony_ci if (drop_info->forw) { 194962306a36Sopenharmony_ci error = xfs_da3_node_read(args->trans, args->dp, 195062306a36Sopenharmony_ci be32_to_cpu(drop_info->forw), 195162306a36Sopenharmony_ci &bp, args->whichfork); 195262306a36Sopenharmony_ci if (error) 195362306a36Sopenharmony_ci return error; 195462306a36Sopenharmony_ci ASSERT(bp != NULL); 195562306a36Sopenharmony_ci tmp_info = bp->b_addr; 195662306a36Sopenharmony_ci ASSERT(tmp_info->magic == save_info->magic); 195762306a36Sopenharmony_ci ASSERT(be32_to_cpu(tmp_info->back) == drop_blk->blkno); 195862306a36Sopenharmony_ci tmp_info->back = cpu_to_be32(save_blk->blkno); 195962306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 0, 196062306a36Sopenharmony_ci sizeof(*tmp_info) - 1); 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, save_blk->bp, 0, sizeof(*save_info) - 1); 196562306a36Sopenharmony_ci return 0; 196662306a36Sopenharmony_ci} 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci/* 196962306a36Sopenharmony_ci * Move a path "forward" or "!forward" one block at the current level. 197062306a36Sopenharmony_ci * 197162306a36Sopenharmony_ci * This routine will adjust a "path" to point to the next block 197262306a36Sopenharmony_ci * "forward" (higher hashvalues) or "!forward" (lower hashvals) in the 197362306a36Sopenharmony_ci * Btree, including updating pointers to the intermediate nodes between 197462306a36Sopenharmony_ci * the new bottom and the root. 197562306a36Sopenharmony_ci */ 197662306a36Sopenharmony_ciint /* error */ 197762306a36Sopenharmony_cixfs_da3_path_shift( 197862306a36Sopenharmony_ci struct xfs_da_state *state, 197962306a36Sopenharmony_ci struct xfs_da_state_path *path, 198062306a36Sopenharmony_ci int forward, 198162306a36Sopenharmony_ci int release, 198262306a36Sopenharmony_ci int *result) 198362306a36Sopenharmony_ci{ 198462306a36Sopenharmony_ci struct xfs_da_state_blk *blk; 198562306a36Sopenharmony_ci struct xfs_da_blkinfo *info; 198662306a36Sopenharmony_ci struct xfs_da_args *args; 198762306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 198862306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 198962306a36Sopenharmony_ci struct xfs_buf *bp; 199062306a36Sopenharmony_ci xfs_dablk_t blkno = 0; 199162306a36Sopenharmony_ci int level; 199262306a36Sopenharmony_ci int error; 199362306a36Sopenharmony_ci struct xfs_inode *dp = state->args->dp; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci trace_xfs_da_path_shift(state->args); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci /* 199862306a36Sopenharmony_ci * Roll up the Btree looking for the first block where our 199962306a36Sopenharmony_ci * current index is not at the edge of the block. Note that 200062306a36Sopenharmony_ci * we skip the bottom layer because we want the sibling block. 200162306a36Sopenharmony_ci */ 200262306a36Sopenharmony_ci args = state->args; 200362306a36Sopenharmony_ci ASSERT(args != NULL); 200462306a36Sopenharmony_ci ASSERT(path != NULL); 200562306a36Sopenharmony_ci ASSERT((path->active > 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); 200662306a36Sopenharmony_ci level = (path->active-1) - 1; /* skip bottom layer in path */ 200762306a36Sopenharmony_ci for (; level >= 0; level--) { 200862306a36Sopenharmony_ci blk = &path->blk[level]; 200962306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, 201062306a36Sopenharmony_ci blk->bp->b_addr); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci if (forward && (blk->index < nodehdr.count - 1)) { 201362306a36Sopenharmony_ci blk->index++; 201462306a36Sopenharmony_ci blkno = be32_to_cpu(nodehdr.btree[blk->index].before); 201562306a36Sopenharmony_ci break; 201662306a36Sopenharmony_ci } else if (!forward && (blk->index > 0)) { 201762306a36Sopenharmony_ci blk->index--; 201862306a36Sopenharmony_ci blkno = be32_to_cpu(nodehdr.btree[blk->index].before); 201962306a36Sopenharmony_ci break; 202062306a36Sopenharmony_ci } 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ci if (level < 0) { 202362306a36Sopenharmony_ci *result = -ENOENT; /* we're out of our tree */ 202462306a36Sopenharmony_ci ASSERT(args->op_flags & XFS_DA_OP_OKNOENT); 202562306a36Sopenharmony_ci return 0; 202662306a36Sopenharmony_ci } 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci /* 202962306a36Sopenharmony_ci * Roll down the edge of the subtree until we reach the 203062306a36Sopenharmony_ci * same depth we were at originally. 203162306a36Sopenharmony_ci */ 203262306a36Sopenharmony_ci for (blk++, level++; level < path->active; blk++, level++) { 203362306a36Sopenharmony_ci /* 203462306a36Sopenharmony_ci * Read the next child block into a local buffer. 203562306a36Sopenharmony_ci */ 203662306a36Sopenharmony_ci error = xfs_da3_node_read(args->trans, dp, blkno, &bp, 203762306a36Sopenharmony_ci args->whichfork); 203862306a36Sopenharmony_ci if (error) 203962306a36Sopenharmony_ci return error; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci /* 204262306a36Sopenharmony_ci * Release the old block (if it's dirty, the trans doesn't 204362306a36Sopenharmony_ci * actually let go) and swap the local buffer into the path 204462306a36Sopenharmony_ci * structure. This ensures failure of the above read doesn't set 204562306a36Sopenharmony_ci * a NULL buffer in an active slot in the path. 204662306a36Sopenharmony_ci */ 204762306a36Sopenharmony_ci if (release) 204862306a36Sopenharmony_ci xfs_trans_brelse(args->trans, blk->bp); 204962306a36Sopenharmony_ci blk->blkno = blkno; 205062306a36Sopenharmony_ci blk->bp = bp; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci info = blk->bp->b_addr; 205362306a36Sopenharmony_ci ASSERT(info->magic == cpu_to_be16(XFS_DA_NODE_MAGIC) || 205462306a36Sopenharmony_ci info->magic == cpu_to_be16(XFS_DA3_NODE_MAGIC) || 205562306a36Sopenharmony_ci info->magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) || 205662306a36Sopenharmony_ci info->magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC) || 205762306a36Sopenharmony_ci info->magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC) || 205862306a36Sopenharmony_ci info->magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci /* 206262306a36Sopenharmony_ci * Note: we flatten the magic number to a single type so we 206362306a36Sopenharmony_ci * don't have to compare against crc/non-crc types elsewhere. 206462306a36Sopenharmony_ci */ 206562306a36Sopenharmony_ci switch (be16_to_cpu(info->magic)) { 206662306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 206762306a36Sopenharmony_ci case XFS_DA3_NODE_MAGIC: 206862306a36Sopenharmony_ci blk->magic = XFS_DA_NODE_MAGIC; 206962306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, 207062306a36Sopenharmony_ci bp->b_addr); 207162306a36Sopenharmony_ci btree = nodehdr.btree; 207262306a36Sopenharmony_ci blk->hashval = be32_to_cpu(btree[nodehdr.count - 1].hashval); 207362306a36Sopenharmony_ci if (forward) 207462306a36Sopenharmony_ci blk->index = 0; 207562306a36Sopenharmony_ci else 207662306a36Sopenharmony_ci blk->index = nodehdr.count - 1; 207762306a36Sopenharmony_ci blkno = be32_to_cpu(btree[blk->index].before); 207862306a36Sopenharmony_ci break; 207962306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 208062306a36Sopenharmony_ci case XFS_ATTR3_LEAF_MAGIC: 208162306a36Sopenharmony_ci blk->magic = XFS_ATTR_LEAF_MAGIC; 208262306a36Sopenharmony_ci ASSERT(level == path->active-1); 208362306a36Sopenharmony_ci blk->index = 0; 208462306a36Sopenharmony_ci blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL); 208562306a36Sopenharmony_ci break; 208662306a36Sopenharmony_ci case XFS_DIR2_LEAFN_MAGIC: 208762306a36Sopenharmony_ci case XFS_DIR3_LEAFN_MAGIC: 208862306a36Sopenharmony_ci blk->magic = XFS_DIR2_LEAFN_MAGIC; 208962306a36Sopenharmony_ci ASSERT(level == path->active-1); 209062306a36Sopenharmony_ci blk->index = 0; 209162306a36Sopenharmony_ci blk->hashval = xfs_dir2_leaf_lasthash(args->dp, 209262306a36Sopenharmony_ci blk->bp, NULL); 209362306a36Sopenharmony_ci break; 209462306a36Sopenharmony_ci default: 209562306a36Sopenharmony_ci ASSERT(0); 209662306a36Sopenharmony_ci break; 209762306a36Sopenharmony_ci } 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci *result = 0; 210062306a36Sopenharmony_ci return 0; 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci/*======================================================================== 210562306a36Sopenharmony_ci * Utility routines. 210662306a36Sopenharmony_ci *========================================================================*/ 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci/* 210962306a36Sopenharmony_ci * Implement a simple hash on a character string. 211062306a36Sopenharmony_ci * Rotate the hash value by 7 bits, then XOR each character in. 211162306a36Sopenharmony_ci * This is implemented with some source-level loop unrolling. 211262306a36Sopenharmony_ci */ 211362306a36Sopenharmony_cixfs_dahash_t 211462306a36Sopenharmony_cixfs_da_hashname(const uint8_t *name, int namelen) 211562306a36Sopenharmony_ci{ 211662306a36Sopenharmony_ci xfs_dahash_t hash; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci /* 211962306a36Sopenharmony_ci * Do four characters at a time as long as we can. 212062306a36Sopenharmony_ci */ 212162306a36Sopenharmony_ci for (hash = 0; namelen >= 4; namelen -= 4, name += 4) 212262306a36Sopenharmony_ci hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^ 212362306a36Sopenharmony_ci (name[3] << 0) ^ rol32(hash, 7 * 4); 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci /* 212662306a36Sopenharmony_ci * Now do the rest of the characters. 212762306a36Sopenharmony_ci */ 212862306a36Sopenharmony_ci switch (namelen) { 212962306a36Sopenharmony_ci case 3: 213062306a36Sopenharmony_ci return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^ 213162306a36Sopenharmony_ci rol32(hash, 7 * 3); 213262306a36Sopenharmony_ci case 2: 213362306a36Sopenharmony_ci return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2); 213462306a36Sopenharmony_ci case 1: 213562306a36Sopenharmony_ci return (name[0] << 0) ^ rol32(hash, 7 * 1); 213662306a36Sopenharmony_ci default: /* case 0: */ 213762306a36Sopenharmony_ci return hash; 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cienum xfs_dacmp 214262306a36Sopenharmony_cixfs_da_compname( 214362306a36Sopenharmony_ci struct xfs_da_args *args, 214462306a36Sopenharmony_ci const unsigned char *name, 214562306a36Sopenharmony_ci int len) 214662306a36Sopenharmony_ci{ 214762306a36Sopenharmony_ci return (args->namelen == len && memcmp(args->name, name, len) == 0) ? 214862306a36Sopenharmony_ci XFS_CMP_EXACT : XFS_CMP_DIFFERENT; 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ciint 215262306a36Sopenharmony_cixfs_da_grow_inode_int( 215362306a36Sopenharmony_ci struct xfs_da_args *args, 215462306a36Sopenharmony_ci xfs_fileoff_t *bno, 215562306a36Sopenharmony_ci int count) 215662306a36Sopenharmony_ci{ 215762306a36Sopenharmony_ci struct xfs_trans *tp = args->trans; 215862306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 215962306a36Sopenharmony_ci int w = args->whichfork; 216062306a36Sopenharmony_ci xfs_rfsblock_t nblks = dp->i_nblocks; 216162306a36Sopenharmony_ci struct xfs_bmbt_irec map, *mapp; 216262306a36Sopenharmony_ci int nmap, error, got, i, mapi; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* 216562306a36Sopenharmony_ci * Find a spot in the file space to put the new block. 216662306a36Sopenharmony_ci */ 216762306a36Sopenharmony_ci error = xfs_bmap_first_unused(tp, dp, count, bno, w); 216862306a36Sopenharmony_ci if (error) 216962306a36Sopenharmony_ci return error; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci /* 217262306a36Sopenharmony_ci * Try mapping it in one filesystem block. 217362306a36Sopenharmony_ci */ 217462306a36Sopenharmony_ci nmap = 1; 217562306a36Sopenharmony_ci error = xfs_bmapi_write(tp, dp, *bno, count, 217662306a36Sopenharmony_ci xfs_bmapi_aflag(w)|XFS_BMAPI_METADATA|XFS_BMAPI_CONTIG, 217762306a36Sopenharmony_ci args->total, &map, &nmap); 217862306a36Sopenharmony_ci if (error) 217962306a36Sopenharmony_ci return error; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci ASSERT(nmap <= 1); 218262306a36Sopenharmony_ci if (nmap == 1) { 218362306a36Sopenharmony_ci mapp = ↦ 218462306a36Sopenharmony_ci mapi = 1; 218562306a36Sopenharmony_ci } else if (nmap == 0 && count > 1) { 218662306a36Sopenharmony_ci xfs_fileoff_t b; 218762306a36Sopenharmony_ci int c; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci /* 219062306a36Sopenharmony_ci * If we didn't get it and the block might work if fragmented, 219162306a36Sopenharmony_ci * try without the CONTIG flag. Loop until we get it all. 219262306a36Sopenharmony_ci */ 219362306a36Sopenharmony_ci mapp = kmem_alloc(sizeof(*mapp) * count, 0); 219462306a36Sopenharmony_ci for (b = *bno, mapi = 0; b < *bno + count; ) { 219562306a36Sopenharmony_ci c = (int)(*bno + count - b); 219662306a36Sopenharmony_ci nmap = min(XFS_BMAP_MAX_NMAP, c); 219762306a36Sopenharmony_ci error = xfs_bmapi_write(tp, dp, b, c, 219862306a36Sopenharmony_ci xfs_bmapi_aflag(w)|XFS_BMAPI_METADATA, 219962306a36Sopenharmony_ci args->total, &mapp[mapi], &nmap); 220062306a36Sopenharmony_ci if (error) 220162306a36Sopenharmony_ci goto out_free_map; 220262306a36Sopenharmony_ci if (nmap < 1) 220362306a36Sopenharmony_ci break; 220462306a36Sopenharmony_ci mapi += nmap; 220562306a36Sopenharmony_ci b = mapp[mapi - 1].br_startoff + 220662306a36Sopenharmony_ci mapp[mapi - 1].br_blockcount; 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci } else { 220962306a36Sopenharmony_ci mapi = 0; 221062306a36Sopenharmony_ci mapp = NULL; 221162306a36Sopenharmony_ci } 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci /* 221462306a36Sopenharmony_ci * Count the blocks we got, make sure it matches the total. 221562306a36Sopenharmony_ci */ 221662306a36Sopenharmony_ci for (i = 0, got = 0; i < mapi; i++) 221762306a36Sopenharmony_ci got += mapp[i].br_blockcount; 221862306a36Sopenharmony_ci if (got != count || mapp[0].br_startoff != *bno || 221962306a36Sopenharmony_ci mapp[mapi - 1].br_startoff + mapp[mapi - 1].br_blockcount != 222062306a36Sopenharmony_ci *bno + count) { 222162306a36Sopenharmony_ci error = -ENOSPC; 222262306a36Sopenharmony_ci goto out_free_map; 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci /* account for newly allocated blocks in reserved blocks total */ 222662306a36Sopenharmony_ci args->total -= dp->i_nblocks - nblks; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ciout_free_map: 222962306a36Sopenharmony_ci if (mapp != &map) 223062306a36Sopenharmony_ci kmem_free(mapp); 223162306a36Sopenharmony_ci return error; 223262306a36Sopenharmony_ci} 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci/* 223562306a36Sopenharmony_ci * Add a block to the btree ahead of the file. 223662306a36Sopenharmony_ci * Return the new block number to the caller. 223762306a36Sopenharmony_ci */ 223862306a36Sopenharmony_ciint 223962306a36Sopenharmony_cixfs_da_grow_inode( 224062306a36Sopenharmony_ci struct xfs_da_args *args, 224162306a36Sopenharmony_ci xfs_dablk_t *new_blkno) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci xfs_fileoff_t bno; 224462306a36Sopenharmony_ci int error; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci trace_xfs_da_grow_inode(args); 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci bno = args->geo->leafblk; 224962306a36Sopenharmony_ci error = xfs_da_grow_inode_int(args, &bno, args->geo->fsbcount); 225062306a36Sopenharmony_ci if (!error) 225162306a36Sopenharmony_ci *new_blkno = (xfs_dablk_t)bno; 225262306a36Sopenharmony_ci return error; 225362306a36Sopenharmony_ci} 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci/* 225662306a36Sopenharmony_ci * Ick. We need to always be able to remove a btree block, even 225762306a36Sopenharmony_ci * if there's no space reservation because the filesystem is full. 225862306a36Sopenharmony_ci * This is called if xfs_bunmapi on a btree block fails due to ENOSPC. 225962306a36Sopenharmony_ci * It swaps the target block with the last block in the file. The 226062306a36Sopenharmony_ci * last block in the file can always be removed since it can't cause 226162306a36Sopenharmony_ci * a bmap btree split to do that. 226262306a36Sopenharmony_ci */ 226362306a36Sopenharmony_ciSTATIC int 226462306a36Sopenharmony_cixfs_da3_swap_lastblock( 226562306a36Sopenharmony_ci struct xfs_da_args *args, 226662306a36Sopenharmony_ci xfs_dablk_t *dead_blknop, 226762306a36Sopenharmony_ci struct xfs_buf **dead_bufp) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct xfs_da_blkinfo *dead_info; 227062306a36Sopenharmony_ci struct xfs_da_blkinfo *sib_info; 227162306a36Sopenharmony_ci struct xfs_da_intnode *par_node; 227262306a36Sopenharmony_ci struct xfs_da_intnode *dead_node; 227362306a36Sopenharmony_ci struct xfs_dir2_leaf *dead_leaf2; 227462306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 227562306a36Sopenharmony_ci struct xfs_da3_icnode_hdr par_hdr; 227662306a36Sopenharmony_ci struct xfs_inode *dp; 227762306a36Sopenharmony_ci struct xfs_trans *tp; 227862306a36Sopenharmony_ci struct xfs_mount *mp; 227962306a36Sopenharmony_ci struct xfs_buf *dead_buf; 228062306a36Sopenharmony_ci struct xfs_buf *last_buf; 228162306a36Sopenharmony_ci struct xfs_buf *sib_buf; 228262306a36Sopenharmony_ci struct xfs_buf *par_buf; 228362306a36Sopenharmony_ci xfs_dahash_t dead_hash; 228462306a36Sopenharmony_ci xfs_fileoff_t lastoff; 228562306a36Sopenharmony_ci xfs_dablk_t dead_blkno; 228662306a36Sopenharmony_ci xfs_dablk_t last_blkno; 228762306a36Sopenharmony_ci xfs_dablk_t sib_blkno; 228862306a36Sopenharmony_ci xfs_dablk_t par_blkno; 228962306a36Sopenharmony_ci int error; 229062306a36Sopenharmony_ci int w; 229162306a36Sopenharmony_ci int entno; 229262306a36Sopenharmony_ci int level; 229362306a36Sopenharmony_ci int dead_level; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci trace_xfs_da_swap_lastblock(args); 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci dead_buf = *dead_bufp; 229862306a36Sopenharmony_ci dead_blkno = *dead_blknop; 229962306a36Sopenharmony_ci tp = args->trans; 230062306a36Sopenharmony_ci dp = args->dp; 230162306a36Sopenharmony_ci w = args->whichfork; 230262306a36Sopenharmony_ci ASSERT(w == XFS_DATA_FORK); 230362306a36Sopenharmony_ci mp = dp->i_mount; 230462306a36Sopenharmony_ci lastoff = args->geo->freeblk; 230562306a36Sopenharmony_ci error = xfs_bmap_last_before(tp, dp, &lastoff, w); 230662306a36Sopenharmony_ci if (error) 230762306a36Sopenharmony_ci return error; 230862306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, lastoff == 0)) 230962306a36Sopenharmony_ci return -EFSCORRUPTED; 231062306a36Sopenharmony_ci /* 231162306a36Sopenharmony_ci * Read the last block in the btree space. 231262306a36Sopenharmony_ci */ 231362306a36Sopenharmony_ci last_blkno = (xfs_dablk_t)lastoff - args->geo->fsbcount; 231462306a36Sopenharmony_ci error = xfs_da3_node_read(tp, dp, last_blkno, &last_buf, w); 231562306a36Sopenharmony_ci if (error) 231662306a36Sopenharmony_ci return error; 231762306a36Sopenharmony_ci /* 231862306a36Sopenharmony_ci * Copy the last block into the dead buffer and log it. 231962306a36Sopenharmony_ci */ 232062306a36Sopenharmony_ci memcpy(dead_buf->b_addr, last_buf->b_addr, args->geo->blksize); 232162306a36Sopenharmony_ci xfs_trans_log_buf(tp, dead_buf, 0, args->geo->blksize - 1); 232262306a36Sopenharmony_ci dead_info = dead_buf->b_addr; 232362306a36Sopenharmony_ci /* 232462306a36Sopenharmony_ci * Get values from the moved block. 232562306a36Sopenharmony_ci */ 232662306a36Sopenharmony_ci if (dead_info->magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) || 232762306a36Sopenharmony_ci dead_info->magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC)) { 232862306a36Sopenharmony_ci struct xfs_dir3_icleaf_hdr leafhdr; 232962306a36Sopenharmony_ci struct xfs_dir2_leaf_entry *ents; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci dead_leaf2 = (xfs_dir2_leaf_t *)dead_info; 233262306a36Sopenharmony_ci xfs_dir2_leaf_hdr_from_disk(dp->i_mount, &leafhdr, 233362306a36Sopenharmony_ci dead_leaf2); 233462306a36Sopenharmony_ci ents = leafhdr.ents; 233562306a36Sopenharmony_ci dead_level = 0; 233662306a36Sopenharmony_ci dead_hash = be32_to_cpu(ents[leafhdr.count - 1].hashval); 233762306a36Sopenharmony_ci } else { 233862306a36Sopenharmony_ci struct xfs_da3_icnode_hdr deadhdr; 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci dead_node = (xfs_da_intnode_t *)dead_info; 234162306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &deadhdr, dead_node); 234262306a36Sopenharmony_ci btree = deadhdr.btree; 234362306a36Sopenharmony_ci dead_level = deadhdr.level; 234462306a36Sopenharmony_ci dead_hash = be32_to_cpu(btree[deadhdr.count - 1].hashval); 234562306a36Sopenharmony_ci } 234662306a36Sopenharmony_ci sib_buf = par_buf = NULL; 234762306a36Sopenharmony_ci /* 234862306a36Sopenharmony_ci * If the moved block has a left sibling, fix up the pointers. 234962306a36Sopenharmony_ci */ 235062306a36Sopenharmony_ci if ((sib_blkno = be32_to_cpu(dead_info->back))) { 235162306a36Sopenharmony_ci error = xfs_da3_node_read(tp, dp, sib_blkno, &sib_buf, w); 235262306a36Sopenharmony_ci if (error) 235362306a36Sopenharmony_ci goto done; 235462306a36Sopenharmony_ci sib_info = sib_buf->b_addr; 235562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, 235662306a36Sopenharmony_ci be32_to_cpu(sib_info->forw) != last_blkno || 235762306a36Sopenharmony_ci sib_info->magic != dead_info->magic)) { 235862306a36Sopenharmony_ci error = -EFSCORRUPTED; 235962306a36Sopenharmony_ci goto done; 236062306a36Sopenharmony_ci } 236162306a36Sopenharmony_ci sib_info->forw = cpu_to_be32(dead_blkno); 236262306a36Sopenharmony_ci xfs_trans_log_buf(tp, sib_buf, 236362306a36Sopenharmony_ci XFS_DA_LOGRANGE(sib_info, &sib_info->forw, 236462306a36Sopenharmony_ci sizeof(sib_info->forw))); 236562306a36Sopenharmony_ci sib_buf = NULL; 236662306a36Sopenharmony_ci } 236762306a36Sopenharmony_ci /* 236862306a36Sopenharmony_ci * If the moved block has a right sibling, fix up the pointers. 236962306a36Sopenharmony_ci */ 237062306a36Sopenharmony_ci if ((sib_blkno = be32_to_cpu(dead_info->forw))) { 237162306a36Sopenharmony_ci error = xfs_da3_node_read(tp, dp, sib_blkno, &sib_buf, w); 237262306a36Sopenharmony_ci if (error) 237362306a36Sopenharmony_ci goto done; 237462306a36Sopenharmony_ci sib_info = sib_buf->b_addr; 237562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, 237662306a36Sopenharmony_ci be32_to_cpu(sib_info->back) != last_blkno || 237762306a36Sopenharmony_ci sib_info->magic != dead_info->magic)) { 237862306a36Sopenharmony_ci error = -EFSCORRUPTED; 237962306a36Sopenharmony_ci goto done; 238062306a36Sopenharmony_ci } 238162306a36Sopenharmony_ci sib_info->back = cpu_to_be32(dead_blkno); 238262306a36Sopenharmony_ci xfs_trans_log_buf(tp, sib_buf, 238362306a36Sopenharmony_ci XFS_DA_LOGRANGE(sib_info, &sib_info->back, 238462306a36Sopenharmony_ci sizeof(sib_info->back))); 238562306a36Sopenharmony_ci sib_buf = NULL; 238662306a36Sopenharmony_ci } 238762306a36Sopenharmony_ci par_blkno = args->geo->leafblk; 238862306a36Sopenharmony_ci level = -1; 238962306a36Sopenharmony_ci /* 239062306a36Sopenharmony_ci * Walk down the tree looking for the parent of the moved block. 239162306a36Sopenharmony_ci */ 239262306a36Sopenharmony_ci for (;;) { 239362306a36Sopenharmony_ci error = xfs_da3_node_read(tp, dp, par_blkno, &par_buf, w); 239462306a36Sopenharmony_ci if (error) 239562306a36Sopenharmony_ci goto done; 239662306a36Sopenharmony_ci par_node = par_buf->b_addr; 239762306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node); 239862306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, 239962306a36Sopenharmony_ci level >= 0 && level != par_hdr.level + 1)) { 240062306a36Sopenharmony_ci error = -EFSCORRUPTED; 240162306a36Sopenharmony_ci goto done; 240262306a36Sopenharmony_ci } 240362306a36Sopenharmony_ci level = par_hdr.level; 240462306a36Sopenharmony_ci btree = par_hdr.btree; 240562306a36Sopenharmony_ci for (entno = 0; 240662306a36Sopenharmony_ci entno < par_hdr.count && 240762306a36Sopenharmony_ci be32_to_cpu(btree[entno].hashval) < dead_hash; 240862306a36Sopenharmony_ci entno++) 240962306a36Sopenharmony_ci continue; 241062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, entno == par_hdr.count)) { 241162306a36Sopenharmony_ci error = -EFSCORRUPTED; 241262306a36Sopenharmony_ci goto done; 241362306a36Sopenharmony_ci } 241462306a36Sopenharmony_ci par_blkno = be32_to_cpu(btree[entno].before); 241562306a36Sopenharmony_ci if (level == dead_level + 1) 241662306a36Sopenharmony_ci break; 241762306a36Sopenharmony_ci xfs_trans_brelse(tp, par_buf); 241862306a36Sopenharmony_ci par_buf = NULL; 241962306a36Sopenharmony_ci } 242062306a36Sopenharmony_ci /* 242162306a36Sopenharmony_ci * We're in the right parent block. 242262306a36Sopenharmony_ci * Look for the right entry. 242362306a36Sopenharmony_ci */ 242462306a36Sopenharmony_ci for (;;) { 242562306a36Sopenharmony_ci for (; 242662306a36Sopenharmony_ci entno < par_hdr.count && 242762306a36Sopenharmony_ci be32_to_cpu(btree[entno].before) != last_blkno; 242862306a36Sopenharmony_ci entno++) 242962306a36Sopenharmony_ci continue; 243062306a36Sopenharmony_ci if (entno < par_hdr.count) 243162306a36Sopenharmony_ci break; 243262306a36Sopenharmony_ci par_blkno = par_hdr.forw; 243362306a36Sopenharmony_ci xfs_trans_brelse(tp, par_buf); 243462306a36Sopenharmony_ci par_buf = NULL; 243562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, par_blkno == 0)) { 243662306a36Sopenharmony_ci error = -EFSCORRUPTED; 243762306a36Sopenharmony_ci goto done; 243862306a36Sopenharmony_ci } 243962306a36Sopenharmony_ci error = xfs_da3_node_read(tp, dp, par_blkno, &par_buf, w); 244062306a36Sopenharmony_ci if (error) 244162306a36Sopenharmony_ci goto done; 244262306a36Sopenharmony_ci par_node = par_buf->b_addr; 244362306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node); 244462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, par_hdr.level != level)) { 244562306a36Sopenharmony_ci error = -EFSCORRUPTED; 244662306a36Sopenharmony_ci goto done; 244762306a36Sopenharmony_ci } 244862306a36Sopenharmony_ci btree = par_hdr.btree; 244962306a36Sopenharmony_ci entno = 0; 245062306a36Sopenharmony_ci } 245162306a36Sopenharmony_ci /* 245262306a36Sopenharmony_ci * Update the parent entry pointing to the moved block. 245362306a36Sopenharmony_ci */ 245462306a36Sopenharmony_ci btree[entno].before = cpu_to_be32(dead_blkno); 245562306a36Sopenharmony_ci xfs_trans_log_buf(tp, par_buf, 245662306a36Sopenharmony_ci XFS_DA_LOGRANGE(par_node, &btree[entno].before, 245762306a36Sopenharmony_ci sizeof(btree[entno].before))); 245862306a36Sopenharmony_ci *dead_blknop = last_blkno; 245962306a36Sopenharmony_ci *dead_bufp = last_buf; 246062306a36Sopenharmony_ci return 0; 246162306a36Sopenharmony_cidone: 246262306a36Sopenharmony_ci if (par_buf) 246362306a36Sopenharmony_ci xfs_trans_brelse(tp, par_buf); 246462306a36Sopenharmony_ci if (sib_buf) 246562306a36Sopenharmony_ci xfs_trans_brelse(tp, sib_buf); 246662306a36Sopenharmony_ci xfs_trans_brelse(tp, last_buf); 246762306a36Sopenharmony_ci return error; 246862306a36Sopenharmony_ci} 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci/* 247162306a36Sopenharmony_ci * Remove a btree block from a directory or attribute. 247262306a36Sopenharmony_ci */ 247362306a36Sopenharmony_ciint 247462306a36Sopenharmony_cixfs_da_shrink_inode( 247562306a36Sopenharmony_ci struct xfs_da_args *args, 247662306a36Sopenharmony_ci xfs_dablk_t dead_blkno, 247762306a36Sopenharmony_ci struct xfs_buf *dead_buf) 247862306a36Sopenharmony_ci{ 247962306a36Sopenharmony_ci struct xfs_inode *dp; 248062306a36Sopenharmony_ci int done, error, w, count; 248162306a36Sopenharmony_ci struct xfs_trans *tp; 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci trace_xfs_da_shrink_inode(args); 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci dp = args->dp; 248662306a36Sopenharmony_ci w = args->whichfork; 248762306a36Sopenharmony_ci tp = args->trans; 248862306a36Sopenharmony_ci count = args->geo->fsbcount; 248962306a36Sopenharmony_ci for (;;) { 249062306a36Sopenharmony_ci /* 249162306a36Sopenharmony_ci * Remove extents. If we get ENOSPC for a dir we have to move 249262306a36Sopenharmony_ci * the last block to the place we want to kill. 249362306a36Sopenharmony_ci */ 249462306a36Sopenharmony_ci error = xfs_bunmapi(tp, dp, dead_blkno, count, 249562306a36Sopenharmony_ci xfs_bmapi_aflag(w), 0, &done); 249662306a36Sopenharmony_ci if (error == -ENOSPC) { 249762306a36Sopenharmony_ci if (w != XFS_DATA_FORK) 249862306a36Sopenharmony_ci break; 249962306a36Sopenharmony_ci error = xfs_da3_swap_lastblock(args, &dead_blkno, 250062306a36Sopenharmony_ci &dead_buf); 250162306a36Sopenharmony_ci if (error) 250262306a36Sopenharmony_ci break; 250362306a36Sopenharmony_ci } else { 250462306a36Sopenharmony_ci break; 250562306a36Sopenharmony_ci } 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci xfs_trans_binval(tp, dead_buf); 250862306a36Sopenharmony_ci return error; 250962306a36Sopenharmony_ci} 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_cistatic int 251262306a36Sopenharmony_cixfs_dabuf_map( 251362306a36Sopenharmony_ci struct xfs_inode *dp, 251462306a36Sopenharmony_ci xfs_dablk_t bno, 251562306a36Sopenharmony_ci unsigned int flags, 251662306a36Sopenharmony_ci int whichfork, 251762306a36Sopenharmony_ci struct xfs_buf_map **mapp, 251862306a36Sopenharmony_ci int *nmaps) 251962306a36Sopenharmony_ci{ 252062306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 252162306a36Sopenharmony_ci int nfsb = xfs_dabuf_nfsb(mp, whichfork); 252262306a36Sopenharmony_ci struct xfs_bmbt_irec irec, *irecs = &irec; 252362306a36Sopenharmony_ci struct xfs_buf_map *map = *mapp; 252462306a36Sopenharmony_ci xfs_fileoff_t off = bno; 252562306a36Sopenharmony_ci int error = 0, nirecs, i; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci if (nfsb > 1) 252862306a36Sopenharmony_ci irecs = kmem_zalloc(sizeof(irec) * nfsb, KM_NOFS); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci nirecs = nfsb; 253162306a36Sopenharmony_ci error = xfs_bmapi_read(dp, bno, nfsb, irecs, &nirecs, 253262306a36Sopenharmony_ci xfs_bmapi_aflag(whichfork)); 253362306a36Sopenharmony_ci if (error) 253462306a36Sopenharmony_ci goto out_free_irecs; 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci /* 253762306a36Sopenharmony_ci * Use the caller provided map for the single map case, else allocate a 253862306a36Sopenharmony_ci * larger one that needs to be free by the caller. 253962306a36Sopenharmony_ci */ 254062306a36Sopenharmony_ci if (nirecs > 1) { 254162306a36Sopenharmony_ci map = kmem_zalloc(nirecs * sizeof(struct xfs_buf_map), KM_NOFS); 254262306a36Sopenharmony_ci if (!map) { 254362306a36Sopenharmony_ci error = -ENOMEM; 254462306a36Sopenharmony_ci goto out_free_irecs; 254562306a36Sopenharmony_ci } 254662306a36Sopenharmony_ci *mapp = map; 254762306a36Sopenharmony_ci } 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci for (i = 0; i < nirecs; i++) { 255062306a36Sopenharmony_ci if (irecs[i].br_startblock == HOLESTARTBLOCK || 255162306a36Sopenharmony_ci irecs[i].br_startblock == DELAYSTARTBLOCK) 255262306a36Sopenharmony_ci goto invalid_mapping; 255362306a36Sopenharmony_ci if (off != irecs[i].br_startoff) 255462306a36Sopenharmony_ci goto invalid_mapping; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci map[i].bm_bn = XFS_FSB_TO_DADDR(mp, irecs[i].br_startblock); 255762306a36Sopenharmony_ci map[i].bm_len = XFS_FSB_TO_BB(mp, irecs[i].br_blockcount); 255862306a36Sopenharmony_ci off += irecs[i].br_blockcount; 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci if (off != bno + nfsb) 256262306a36Sopenharmony_ci goto invalid_mapping; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci *nmaps = nirecs; 256562306a36Sopenharmony_ciout_free_irecs: 256662306a36Sopenharmony_ci if (irecs != &irec) 256762306a36Sopenharmony_ci kmem_free(irecs); 256862306a36Sopenharmony_ci return error; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ciinvalid_mapping: 257162306a36Sopenharmony_ci /* Caller ok with no mapping. */ 257262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !(flags & XFS_DABUF_MAP_HOLE_OK))) { 257362306a36Sopenharmony_ci error = -EFSCORRUPTED; 257462306a36Sopenharmony_ci if (xfs_error_level >= XFS_ERRLEVEL_LOW) { 257562306a36Sopenharmony_ci xfs_alert(mp, "%s: bno %u inode %llu", 257662306a36Sopenharmony_ci __func__, bno, dp->i_ino); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci for (i = 0; i < nirecs; i++) { 257962306a36Sopenharmony_ci xfs_alert(mp, 258062306a36Sopenharmony_ci"[%02d] br_startoff %lld br_startblock %lld br_blockcount %lld br_state %d", 258162306a36Sopenharmony_ci i, irecs[i].br_startoff, 258262306a36Sopenharmony_ci irecs[i].br_startblock, 258362306a36Sopenharmony_ci irecs[i].br_blockcount, 258462306a36Sopenharmony_ci irecs[i].br_state); 258562306a36Sopenharmony_ci } 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci } else { 258862306a36Sopenharmony_ci *nmaps = 0; 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci goto out_free_irecs; 259162306a36Sopenharmony_ci} 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci/* 259462306a36Sopenharmony_ci * Get a buffer for the dir/attr block. 259562306a36Sopenharmony_ci */ 259662306a36Sopenharmony_ciint 259762306a36Sopenharmony_cixfs_da_get_buf( 259862306a36Sopenharmony_ci struct xfs_trans *tp, 259962306a36Sopenharmony_ci struct xfs_inode *dp, 260062306a36Sopenharmony_ci xfs_dablk_t bno, 260162306a36Sopenharmony_ci struct xfs_buf **bpp, 260262306a36Sopenharmony_ci int whichfork) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 260562306a36Sopenharmony_ci struct xfs_buf *bp; 260662306a36Sopenharmony_ci struct xfs_buf_map map, *mapp = ↦ 260762306a36Sopenharmony_ci int nmap = 1; 260862306a36Sopenharmony_ci int error; 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci *bpp = NULL; 261162306a36Sopenharmony_ci error = xfs_dabuf_map(dp, bno, 0, whichfork, &mapp, &nmap); 261262306a36Sopenharmony_ci if (error || nmap == 0) 261362306a36Sopenharmony_ci goto out_free; 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci error = xfs_trans_get_buf_map(tp, mp->m_ddev_targp, mapp, nmap, 0, &bp); 261662306a36Sopenharmony_ci if (error) 261762306a36Sopenharmony_ci goto out_free; 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci *bpp = bp; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ciout_free: 262262306a36Sopenharmony_ci if (mapp != &map) 262362306a36Sopenharmony_ci kmem_free(mapp); 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci return error; 262662306a36Sopenharmony_ci} 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci/* 262962306a36Sopenharmony_ci * Get a buffer for the dir/attr block, fill in the contents. 263062306a36Sopenharmony_ci */ 263162306a36Sopenharmony_ciint 263262306a36Sopenharmony_cixfs_da_read_buf( 263362306a36Sopenharmony_ci struct xfs_trans *tp, 263462306a36Sopenharmony_ci struct xfs_inode *dp, 263562306a36Sopenharmony_ci xfs_dablk_t bno, 263662306a36Sopenharmony_ci unsigned int flags, 263762306a36Sopenharmony_ci struct xfs_buf **bpp, 263862306a36Sopenharmony_ci int whichfork, 263962306a36Sopenharmony_ci const struct xfs_buf_ops *ops) 264062306a36Sopenharmony_ci{ 264162306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 264262306a36Sopenharmony_ci struct xfs_buf *bp; 264362306a36Sopenharmony_ci struct xfs_buf_map map, *mapp = ↦ 264462306a36Sopenharmony_ci int nmap = 1; 264562306a36Sopenharmony_ci int error; 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci *bpp = NULL; 264862306a36Sopenharmony_ci error = xfs_dabuf_map(dp, bno, flags, whichfork, &mapp, &nmap); 264962306a36Sopenharmony_ci if (error || !nmap) 265062306a36Sopenharmony_ci goto out_free; 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci error = xfs_trans_read_buf_map(mp, tp, mp->m_ddev_targp, mapp, nmap, 0, 265362306a36Sopenharmony_ci &bp, ops); 265462306a36Sopenharmony_ci if (error) 265562306a36Sopenharmony_ci goto out_free; 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci if (whichfork == XFS_ATTR_FORK) 265862306a36Sopenharmony_ci xfs_buf_set_ref(bp, XFS_ATTR_BTREE_REF); 265962306a36Sopenharmony_ci else 266062306a36Sopenharmony_ci xfs_buf_set_ref(bp, XFS_DIR_BTREE_REF); 266162306a36Sopenharmony_ci *bpp = bp; 266262306a36Sopenharmony_ciout_free: 266362306a36Sopenharmony_ci if (mapp != &map) 266462306a36Sopenharmony_ci kmem_free(mapp); 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci return error; 266762306a36Sopenharmony_ci} 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci/* 267062306a36Sopenharmony_ci * Readahead the dir/attr block. 267162306a36Sopenharmony_ci */ 267262306a36Sopenharmony_ciint 267362306a36Sopenharmony_cixfs_da_reada_buf( 267462306a36Sopenharmony_ci struct xfs_inode *dp, 267562306a36Sopenharmony_ci xfs_dablk_t bno, 267662306a36Sopenharmony_ci unsigned int flags, 267762306a36Sopenharmony_ci int whichfork, 267862306a36Sopenharmony_ci const struct xfs_buf_ops *ops) 267962306a36Sopenharmony_ci{ 268062306a36Sopenharmony_ci struct xfs_buf_map map; 268162306a36Sopenharmony_ci struct xfs_buf_map *mapp; 268262306a36Sopenharmony_ci int nmap; 268362306a36Sopenharmony_ci int error; 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci mapp = ↦ 268662306a36Sopenharmony_ci nmap = 1; 268762306a36Sopenharmony_ci error = xfs_dabuf_map(dp, bno, flags, whichfork, &mapp, &nmap); 268862306a36Sopenharmony_ci if (error || !nmap) 268962306a36Sopenharmony_ci goto out_free; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci xfs_buf_readahead_map(dp->i_mount->m_ddev_targp, mapp, nmap, ops); 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ciout_free: 269462306a36Sopenharmony_ci if (mapp != &map) 269562306a36Sopenharmony_ci kmem_free(mapp); 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci return error; 269862306a36Sopenharmony_ci} 2699