Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/fs/xfs/libxfs/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 146 kB image not shown  

Quelle  xfs_btree.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
 * All Rights Reserved.
 */

#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_bit.h"
#include "xfs_mount.h"
#include "xfs_inode.h"
#include "xfs_trans.h"
#include "xfs_buf_item.h"
#include "xfs_btree.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_trace.h"
#include "xfs_alloc.h"
#include "xfs_log.h"
#include "xfs_btree_staging.h"
#include "xfs_ag.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_rmap_btree.h"
#include "xfs_refcount_btree.h"
#include "xfs_health.h"
#include "xfs_buf_mem.h"
#include "xfs_btree_mem.h"
#include "xfs_rtrmap_btree.h"
#include "xfs_bmap.h"
#include "xfs_rmap.h"
#include "xfs_quota.h"
#include "xfs_metafile.h"
#include "xfs_rtrefcount_btree.h"

/*
 * Btree magic numbers.
 */

uint32_t
xfs_btree_magic(
 struct xfs_mount  *mp,
 const struct xfs_btree_ops *ops)
{
 int    idx = xfs_has_crc(mp) ? 1 : 0;
 __be32    magic = ops->buf_ops->magic[idx];

 /* Ensure we asked for crc for crc-only magics. */
 ASSERT(magic != 0);
 return be32_to_cpu(magic);
}

/*
 * These sibling pointer checks are optimised for null sibling pointers. This
 * happens a lot, and we don't need to byte swap at runtime if the sibling
 * pointer is NULL.
 *
 * These are explicitly marked at inline because the cost of calling them as
 * functions instead of inlining them is about 36 bytes extra code per call site
 * on x86-64. Yes, gcc-11 fails to inline them, and explicit inlining of these
 * two sibling check functions reduces the compiled code size by over 300
 * bytes.
 */

static inline xfs_failaddr_t
xfs_btree_check_fsblock_siblings(
 struct xfs_mount *mp,
 xfs_fsblock_t  fsb,
 __be64   dsibling)
{
 xfs_fsblock_t  sibling;

 if (dsibling == cpu_to_be64(NULLFSBLOCK))
  return NULL;

 sibling = be64_to_cpu(dsibling);
 if (sibling == fsb)
  return __this_address;
 if (!xfs_verify_fsbno(mp, sibling))
  return __this_address;
 return NULL;
}

static inline xfs_failaddr_t
xfs_btree_check_memblock_siblings(
 struct xfs_buftarg *btp,
 xfbno_t   bno,
 __be64   dsibling)
{
 xfbno_t   sibling;

 if (dsibling == cpu_to_be64(NULLFSBLOCK))
  return NULL;

 sibling = be64_to_cpu(dsibling);
 if (sibling == bno)
  return __this_address;
 if (!xmbuf_verify_daddr(btp, xfbno_to_daddr(sibling)))
  return __this_address;
 return NULL;
}

static inline xfs_failaddr_t
xfs_btree_check_agblock_siblings(
 struct xfs_perag *pag,
 xfs_agblock_t  agbno,
 __be32   dsibling)
{
 xfs_agblock_t  sibling;

 if (dsibling == cpu_to_be32(NULLAGBLOCK))
  return NULL;

 sibling = be32_to_cpu(dsibling);
 if (sibling == agbno)
  return __this_address;
 if (!xfs_verify_agbno(pag, sibling))
  return __this_address;
 return NULL;
}

static xfs_failaddr_t
__xfs_btree_check_lblock_hdr(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 int   level,
 struct xfs_buf  *bp)
{
 struct xfs_mount *mp = cur->bc_mp;

 if (xfs_has_crc(mp)) {
  if (!uuid_equal(&block->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid))
   return __this_address;
  if (block->bb_u.l.bb_blkno !=
      cpu_to_be64(bp ? xfs_buf_daddr(bp) : XFS_BUF_DADDR_NULL))
   return __this_address;
  if (block->bb_u.l.bb_pad != cpu_to_be32(0))
   return __this_address;
 }

 if (be32_to_cpu(block->bb_magic) != xfs_btree_magic(mp, cur->bc_ops))
  return __this_address;
 if (be16_to_cpu(block->bb_level) != level)
  return __this_address;
 if (be16_to_cpu(block->bb_numrecs) >
     cur->bc_ops->get_maxrecs(cur, level))
  return __this_address;

 return NULL;
}

/*
 * Check a long btree block header.  Return the address of the failing check,
 * or NULL if everything is ok.
 */

static xfs_failaddr_t
__xfs_btree_check_fsblock(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 int   level,
 struct xfs_buf  *bp)
{
 struct xfs_mount *mp = cur->bc_mp;
 xfs_failaddr_t  fa;
 xfs_fsblock_t  fsb;

 fa = __xfs_btree_check_lblock_hdr(cur, block, level, bp);
 if (fa)
  return fa;

 /*
 * For inode-rooted btrees, the root block sits in the inode fork.  In
 * that case bp is NULL, and the block must not have any siblings.
 */

 if (!bp) {
  if (block->bb_u.l.bb_leftsib != cpu_to_be64(NULLFSBLOCK))
   return __this_address;
  if (block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK))
   return __this_address;
  return NULL;
 }

 fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
 fa = xfs_btree_check_fsblock_siblings(mp, fsb,
   block->bb_u.l.bb_leftsib);
 if (!fa)
  fa = xfs_btree_check_fsblock_siblings(mp, fsb,
    block->bb_u.l.bb_rightsib);
 return fa;
}

/*
 * Check an in-memory btree block header.  Return the address of the failing
 * check, or NULL if everything is ok.
 */

static xfs_failaddr_t
__xfs_btree_check_memblock(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 int   level,
 struct xfs_buf  *bp)
{
 struct xfs_buftarg *btp = cur->bc_mem.xfbtree->target;
 xfs_failaddr_t  fa;
 xfbno_t   bno;

 fa = __xfs_btree_check_lblock_hdr(cur, block, level, bp);
 if (fa)
  return fa;

 bno = xfs_daddr_to_xfbno(xfs_buf_daddr(bp));
 fa = xfs_btree_check_memblock_siblings(btp, bno,
   block->bb_u.l.bb_leftsib);
 if (!fa)
  fa = xfs_btree_check_memblock_siblings(btp, bno,
    block->bb_u.l.bb_rightsib);
 return fa;
}

/*
 * Check a short btree block header.  Return the address of the failing check,
 * or NULL if everything is ok.
 */

static xfs_failaddr_t
__xfs_btree_check_agblock(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 int   level,
 struct xfs_buf  *bp)
{
 struct xfs_mount *mp = cur->bc_mp;
 struct xfs_perag *pag = to_perag(cur->bc_group);
 xfs_failaddr_t  fa;
 xfs_agblock_t  agbno;

 if (xfs_has_crc(mp)) {
  if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
   return __this_address;
  if (block->bb_u.s.bb_blkno != cpu_to_be64(xfs_buf_daddr(bp)))
   return __this_address;
 }

 if (be32_to_cpu(block->bb_magic) != xfs_btree_magic(mp, cur->bc_ops))
  return __this_address;
 if (be16_to_cpu(block->bb_level) != level)
  return __this_address;
 if (be16_to_cpu(block->bb_numrecs) >
     cur->bc_ops->get_maxrecs(cur, level))
  return __this_address;

 agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
 fa = xfs_btree_check_agblock_siblings(pag, agbno,
   block->bb_u.s.bb_leftsib);
 if (!fa)
  fa = xfs_btree_check_agblock_siblings(pag, agbno,
    block->bb_u.s.bb_rightsib);
 return fa;
}

/*
 * Internal btree block check.
 *
 * Return NULL if the block is ok or the address of the failed check otherwise.
 */

xfs_failaddr_t
__xfs_btree_check_block(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 int   level,
 struct xfs_buf  *bp)
{
 switch (cur->bc_ops->type) {
 case XFS_BTREE_TYPE_MEM:
  return __xfs_btree_check_memblock(cur, block, level, bp);
 case XFS_BTREE_TYPE_AG:
  return __xfs_btree_check_agblock(cur, block, level, bp);
 case XFS_BTREE_TYPE_INODE:
  return __xfs_btree_check_fsblock(cur, block, level, bp);
 default:
  ASSERT(0);
  return __this_address;
 }
}

static inline unsigned int xfs_btree_block_errtag(struct xfs_btree_cur *cur)
{
 if (cur->bc_ops->ptr_len == XFS_BTREE_SHORT_PTR_LEN)
  return XFS_ERRTAG_BTREE_CHECK_SBLOCK;
 return XFS_ERRTAG_BTREE_CHECK_LBLOCK;
}

/*
 * Debug routine: check that block header is ok.
 */

int
xfs_btree_check_block(
 struct xfs_btree_cur *cur, /* btree cursor */
 struct xfs_btree_block *block, /* generic btree block pointer */
 int   level, /* level of the btree block */
 struct xfs_buf  *bp) /* buffer containing block, if any */
{
 struct xfs_mount *mp = cur->bc_mp;
 xfs_failaddr_t  fa;

 fa = __xfs_btree_check_block(cur, block, level, bp);
 if (XFS_IS_CORRUPT(mp, fa != NULL) ||
     XFS_TEST_ERROR(false, mp, xfs_btree_block_errtag(cur))) {
  if (bp)
   trace_xfs_btree_corrupt(bp, _RET_IP_);
  xfs_btree_mark_sick(cur);
  return -EFSCORRUPTED;
 }
 return 0;
}

int
__xfs_btree_check_ptr(
 struct xfs_btree_cur  *cur,
 const union xfs_btree_ptr *ptr,
 int    index,
 int    level)
{
 if (level <= 0)
  return -EFSCORRUPTED;

 switch (cur->bc_ops->type) {
 case XFS_BTREE_TYPE_MEM:
  if (!xfbtree_verify_bno(cur->bc_mem.xfbtree,
    be64_to_cpu((&ptr->l)[index])))
   return -EFSCORRUPTED;
  break;
 case XFS_BTREE_TYPE_INODE:
  if (!xfs_verify_fsbno(cur->bc_mp,
    be64_to_cpu((&ptr->l)[index])))
   return -EFSCORRUPTED;
  break;
 case XFS_BTREE_TYPE_AG:
  if (!xfs_verify_agbno(to_perag(cur->bc_group),
    be32_to_cpu((&ptr->s)[index])))
   return -EFSCORRUPTED;
  break;
 }

 return 0;
}

/*
 * Check that a given (indexed) btree pointer at a certain level of a
 * btree is valid and doesn't point past where it should.
 */

static int
xfs_btree_check_ptr(
 struct xfs_btree_cur  *cur,
 const union xfs_btree_ptr *ptr,
 int    index,
 int    level)
{
 int    error;

 error = __xfs_btree_check_ptr(cur, ptr, index, level);
 if (error) {
  switch (cur->bc_ops->type) {
  case XFS_BTREE_TYPE_MEM:
   xfs_err(cur->bc_mp,
"In-memory: Corrupt %sbt flags 0x%x pointer at level %d index %d fa %pS.",
    cur->bc_ops->name, cur->bc_flags, level, index,
    __this_address);
   break;
  case XFS_BTREE_TYPE_INODE:
   xfs_err(cur->bc_mp,
"Inode %llu fork %d: Corrupt %sbt pointer at level %d index %d.",
    cur->bc_ino.ip->i_ino,
    cur->bc_ino.whichfork, cur->bc_ops->name,
    level, index);
   break;
  case XFS_BTREE_TYPE_AG:
   xfs_err(cur->bc_mp,
"AG %u: Corrupt %sbt pointer at level %d index %d.",
    cur->bc_group->xg_gno, cur->bc_ops->name,
    level, index);
   break;
  }
  xfs_btree_mark_sick(cur);
 }

 return error;
}

#ifdef DEBUG
define xfs_btree_debug_check_ptr xfs_btree_check_ptr
#else
define xfs_btree_debug_check_ptr(...) (0)
#endif

/*
 * Calculate CRC on the whole btree block and stuff it into the
 * long-form btree header.
 *
 * Prior to calculting the CRC, pull the LSN out of the buffer log item and put
 * it into the buffer so recovery knows what the last modification was that made
 * it to disk.
 */

void
xfs_btree_fsblock_calc_crc(
 struct xfs_buf  *bp)
{
 struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
 struct xfs_buf_log_item *bip = bp->b_log_item;

 if (!xfs_has_crc(bp->b_mount))
  return;
 if (bip)
  block->bb_u.l.bb_lsn = cpu_to_be64(bip->bli_item.li_lsn);
 xfs_buf_update_cksum(bp, XFS_BTREE_LBLOCK_CRC_OFF);
}

bool
xfs_btree_fsblock_verify_crc(
 struct xfs_buf  *bp)
{
 struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
 struct xfs_mount *mp = bp->b_mount;

 if (xfs_has_crc(mp)) {
  if (!xfs_log_check_lsn(mp, be64_to_cpu(block->bb_u.l.bb_lsn)))
   return false;
  return xfs_buf_verify_cksum(bp, XFS_BTREE_LBLOCK_CRC_OFF);
 }

 return true;
}

/*
 * Calculate CRC on the whole btree block and stuff it into the
 * short-form btree header.
 *
 * Prior to calculting the CRC, pull the LSN out of the buffer log item and put
 * it into the buffer so recovery knows what the last modification was that made
 * it to disk.
 */

void
xfs_btree_agblock_calc_crc(
 struct xfs_buf  *bp)
{
 struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
 struct xfs_buf_log_item *bip = bp->b_log_item;

 if (!xfs_has_crc(bp->b_mount))
  return;
 if (bip)
  block->bb_u.s.bb_lsn = cpu_to_be64(bip->bli_item.li_lsn);
 xfs_buf_update_cksum(bp, XFS_BTREE_SBLOCK_CRC_OFF);
}

bool
xfs_btree_agblock_verify_crc(
 struct xfs_buf  *bp)
{
 struct xfs_btree_block  *block = XFS_BUF_TO_BLOCK(bp);
 struct xfs_mount *mp = bp->b_mount;

 if (xfs_has_crc(mp)) {
  if (!xfs_log_check_lsn(mp, be64_to_cpu(block->bb_u.s.bb_lsn)))
   return false;
  return xfs_buf_verify_cksum(bp, XFS_BTREE_SBLOCK_CRC_OFF);
 }

 return true;
}

static int
xfs_btree_free_block(
 struct xfs_btree_cur *cur,
 struct xfs_buf  *bp)
{
 int   error;

 trace_xfs_btree_free_block(cur, bp);

 /*
 * Don't allow block freeing for a staging cursor, because staging
 * cursors do not support regular btree modifications.
 */

 if (unlikely(cur->bc_flags & XFS_BTREE_STAGING)) {
  ASSERT(0);
  return -EFSCORRUPTED;
 }

 error = cur->bc_ops->free_block(cur, bp);
 if (!error) {
  xfs_trans_binval(cur->bc_tp, bp);
  XFS_BTREE_STATS_INC(cur, free);
 }
 return error;
}

/*
 * Delete the btree cursor.
 */

void
xfs_btree_del_cursor(
 struct xfs_btree_cur *cur,  /* btree cursor */
 int   error)  /* del because of error */
{
 int   i;  /* btree level */

 /*
 * Clear the buffer pointers and release the buffers. If we're doing
 * this because of an error, inspect all of the entries in the bc_bufs
 * array for buffers to be unlocked. This is because some of the btree
 * code works from level n down to 0, and if we get an error along the
 * way we won't have initialized all the entries down to 0.
 */

 for (i = 0; i < cur->bc_nlevels; i++) {
  if (cur->bc_levels[i].bp)
   xfs_trans_brelse(cur->bc_tp, cur->bc_levels[i].bp);
  else if (!error)
   break;
 }

 /*
 * If we are doing a BMBT update, the number of unaccounted blocks
 * allocated during this cursor life time should be zero. If it's not
 * zero, then we should be shut down or on our way to shutdown due to
 * cancelling a dirty transaction on error.
 */

 ASSERT(!xfs_btree_is_bmap(cur->bc_ops) || cur->bc_bmap.allocated == 0 ||
        xfs_is_shutdown(cur->bc_mp) || error != 0);

 if (cur->bc_group)
  xfs_group_put(cur->bc_group);
 kmem_cache_free(cur->bc_cache, cur);
}

/* Return the buffer target for this btree's buffer. */
static inline struct xfs_buftarg *
xfs_btree_buftarg(
 struct xfs_btree_cur *cur)
{
 if (cur->bc_ops->type == XFS_BTREE_TYPE_MEM)
  return cur->bc_mem.xfbtree->target;
 return cur->bc_mp->m_ddev_targp;
}

/* Return the block size (in units of 512b sectors) for this btree. */
static inline unsigned int
xfs_btree_bbsize(
 struct xfs_btree_cur *cur)
{
 if (cur->bc_ops->type == XFS_BTREE_TYPE_MEM)
  return XFBNO_BBSIZE;
 return cur->bc_mp->m_bsize;
}

/*
 * Duplicate the btree cursor.
 * Allocate a new one, copy the record, re-get the buffers.
 */

int      /* error */
xfs_btree_dup_cursor(
 struct xfs_btree_cur *cur,  /* input cursor */
 struct xfs_btree_cur **ncur)  /* output cursor */
{
 struct xfs_mount *mp = cur->bc_mp;
 struct xfs_trans *tp = cur->bc_tp;
 struct xfs_buf  *bp;
 struct xfs_btree_cur *new;
 int   error;
 int   i;

 /*
 * Don't allow staging cursors to be duplicated because they're supposed
 * to be kept private to a single thread.
 */

 if (unlikely(cur->bc_flags & XFS_BTREE_STAGING)) {
  ASSERT(0);
  return -EFSCORRUPTED;
 }

 /*
 * Allocate a new cursor like the old one.
 */

 new = cur->bc_ops->dup_cursor(cur);

 /*
 * Copy the record currently in the cursor.
 */

 new->bc_rec = cur->bc_rec;

 /*
 * For each level current, re-get the buffer and copy the ptr value.
 */

 for (i = 0; i < new->bc_nlevels; i++) {
  new->bc_levels[i].ptr = cur->bc_levels[i].ptr;
  new->bc_levels[i].ra = cur->bc_levels[i].ra;
  bp = cur->bc_levels[i].bp;
  if (bp) {
   error = xfs_trans_read_buf(mp, tp,
     xfs_btree_buftarg(cur),
     xfs_buf_daddr(bp),
     xfs_btree_bbsize(cur), 0, &bp,
     cur->bc_ops->buf_ops);
   if (xfs_metadata_is_sick(error))
    xfs_btree_mark_sick(new);
   if (error) {
    xfs_btree_del_cursor(new, error);
    *ncur = NULL;
    return error;
   }
  }
  new->bc_levels[i].bp = bp;
 }
 *ncur = new;
 return 0;
}

/*
 * XFS btree block layout and addressing:
 *
 * There are two types of blocks in the btree: leaf and non-leaf blocks.
 *
 * The leaf record start with a header then followed by records containing
 * the values.  A non-leaf block also starts with the same header, and
 * then first contains lookup keys followed by an equal number of pointers
 * to the btree blocks at the previous level.
 *
 * +--------+-------+-------+-------+-------+-------+-------+
 * Leaf: | header | rec 1 | rec 2 | rec 3 | rec 4 | rec 5 | rec N |
 * +--------+-------+-------+-------+-------+-------+-------+
 *
 * +--------+-------+-------+-------+-------+-------+-------+
 * Non-Leaf: | header | key 1 | key 2 | key N | ptr 1 | ptr 2 | ptr N |
 * +--------+-------+-------+-------+-------+-------+-------+
 *
 * The header is called struct xfs_btree_block for reasons better left unknown
 * and comes in different versions for short (32bit) and long (64bit) block
 * pointers.  The record and key structures are defined by the btree instances
 * and opaque to the btree core.  The block pointers are simple disk endian
 * integers, available in a short (32bit) and long (64bit) variant.
 *
 * The helpers below calculate the offset of a given record, key or pointer
 * into a btree block (xfs_btree_*_offset) or return a pointer to the given
 * record, key or pointer (xfs_btree_*_addr).  Note that all addressing
 * inside the btree block is done using indices starting at one, not zero!
 *
 * If XFS_BTGEO_OVERLAPPING is set, then this btree supports keys containing
 * overlapping intervals.  In such a tree, records are still sorted lowest to
 * highest and indexed by the smallest key value that refers to the record.
 * However, nodes are different: each pointer has two associated keys -- one
 * indexing the lowest key available in the block(s) below (the same behavior
 * as the key in a regular btree) and another indexing the highest key
 * available in the block(s) below.  Because records are /not/ sorted by the
 * highest key, all leaf block updates require us to compute the highest key
 * that matches any record in the leaf and to recursively update the high keys
 * in the nodes going further up in the tree, if necessary.  Nodes look like
 * this:
 *
 * +--------+-----+-----+-----+-----+-----+-------+-------+-----+
 * Non-Leaf: | header | lo1 | hi1 | lo2 | hi2 | ... | ptr 1 | ptr 2 | ... |
 * +--------+-----+-----+-----+-----+-----+-------+-------+-----+
 *
 * To perform an interval query on an overlapped tree, perform the usual
 * depth-first search and use the low and high keys to decide if we can skip
 * that particular node.  If a leaf node is reached, return the records that
 * intersect the interval.  Note that an interval query may return numerous
 * entries.  For a non-overlapped tree, simply search for the record associated
 * with the lowest key and iterate forward until a non-matching record is
 * found.  Section 14.3 ("Interval Trees") of _Introduction to Algorithms_ by
 * Cormen, Leiserson, Rivest, and Stein (2nd or 3rd ed. only) discuss this in
 * more detail.
 *
 * Why do we care about overlapping intervals?  Let's say you have a bunch of
 * reverse mapping records on a reflink filesystem:
 *
 * 1: +- file A startblock B offset C length D -----------+
 * 2:      +- file E startblock F offset G length H --------------+
 * 3:      +- file I startblock F offset J length K --+
 * 4:                                                        +- file L... --+
 *
 * Now say we want to map block (B+D) into file A at offset (C+D).  Ideally,
 * we'd simply increment the length of record 1.  But how do we find the record
 * that ends at (B+D-1) (i.e. record 1)?  A LE lookup of (B+D-1) would return
 * record 3 because the keys are ordered first by startblock.  An interval
 * query would return records 1 and 2 because they both overlap (B+D-1), and
 * from that we can pick out record 1 as the appropriate left neighbor.
 *
 * In the non-overlapped case you can do a LE lookup and decrement the cursor
 * because a record's interval must end before the next record.
 */


/*
 * Return size of the btree block header for this btree instance.
 */

static inline size_t xfs_btree_block_len(struct xfs_btree_cur *cur)
{
 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN) {
  if (xfs_has_crc(cur->bc_mp))
   return XFS_BTREE_LBLOCK_CRC_LEN;
  return XFS_BTREE_LBLOCK_LEN;
 }
 if (xfs_has_crc(cur->bc_mp))
  return XFS_BTREE_SBLOCK_CRC_LEN;
 return XFS_BTREE_SBLOCK_LEN;
}

/*
 * Calculate offset of the n-th record in a btree block.
 */

STATIC size_t
xfs_btree_rec_offset(
 struct xfs_btree_cur *cur,
 int   n)
{
 return xfs_btree_block_len(cur) +
  (n - 1) * cur->bc_ops->rec_len;
}

/*
 * Calculate offset of the n-th key in a btree block.
 */

STATIC size_t
xfs_btree_key_offset(
 struct xfs_btree_cur *cur,
 int   n)
{
 return xfs_btree_block_len(cur) +
  (n - 1) * cur->bc_ops->key_len;
}

/*
 * Calculate offset of the n-th high key in a btree block.
 */

STATIC size_t
xfs_btree_high_key_offset(
 struct xfs_btree_cur *cur,
 int   n)
{
 return xfs_btree_block_len(cur) +
  (n - 1) * cur->bc_ops->key_len + (cur->bc_ops->key_len / 2);
}

/*
 * Calculate offset of the n-th block pointer in a btree block.
 */

STATIC size_t
xfs_btree_ptr_offset(
 struct xfs_btree_cur *cur,
 int   n,
 int   level)
{
 return xfs_btree_block_len(cur) +
  cur->bc_ops->get_maxrecs(cur, level) * cur->bc_ops->key_len +
  (n - 1) * cur->bc_ops->ptr_len;
}

/*
 * Return a pointer to the n-th record in the btree block.
 */

union xfs_btree_rec *
xfs_btree_rec_addr(
 struct xfs_btree_cur *cur,
 int   n,
 struct xfs_btree_block *block)
{
 return (union xfs_btree_rec *)
  ((char *)block + xfs_btree_rec_offset(cur, n));
}

/*
 * Return a pointer to the n-th key in the btree block.
 */

union xfs_btree_key *
xfs_btree_key_addr(
 struct xfs_btree_cur *cur,
 int   n,
 struct xfs_btree_block *block)
{
 return (union xfs_btree_key *)
  ((char *)block + xfs_btree_key_offset(cur, n));
}

/*
 * Return a pointer to the n-th high key in the btree block.
 */

union xfs_btree_key *
xfs_btree_high_key_addr(
 struct xfs_btree_cur *cur,
 int   n,
 struct xfs_btree_block *block)
{
 return (union xfs_btree_key *)
  ((char *)block + xfs_btree_high_key_offset(cur, n));
}

/*
 * Return a pointer to the n-th block pointer in the btree block.
 */

union xfs_btree_ptr *
xfs_btree_ptr_addr(
 struct xfs_btree_cur *cur,
 int   n,
 struct xfs_btree_block *block)
{
 int   level = xfs_btree_get_level(block);

 ASSERT(block->bb_level != 0);

 return (union xfs_btree_ptr *)
  ((char *)block + xfs_btree_ptr_offset(cur, n, level));
}

struct xfs_ifork *
xfs_btree_ifork_ptr(
 struct xfs_btree_cur *cur)
{
 ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_INODE);

 if (cur->bc_flags & XFS_BTREE_STAGING)
  return cur->bc_ino.ifake->if_fork;
 return xfs_ifork_ptr(cur->bc_ino.ip, cur->bc_ino.whichfork);
}

/*
 * Get the root block which is stored in the inode.
 *
 * For now this btree implementation assumes the btree root is always
 * stored in the if_broot field of an inode fork.
 */

STATIC struct xfs_btree_block *
xfs_btree_get_iroot(
 struct xfs_btree_cur *cur)
{
 struct xfs_ifork *ifp = xfs_btree_ifork_ptr(cur);

 return (struct xfs_btree_block *)ifp->if_broot;
}

/*
 * Retrieve the block pointer from the cursor at the given level.
 * This may be an inode btree root or from a buffer.
 */

struct xfs_btree_block *  /* generic btree block pointer */
xfs_btree_get_block(
 struct xfs_btree_cur *cur, /* btree cursor */
 int   level, /* level in btree */
 struct xfs_buf  **bpp) /* buffer containing the block */
{
 if (xfs_btree_at_iroot(cur, level)) {
  *bpp = NULL;
  return xfs_btree_get_iroot(cur);
 }

 *bpp = cur->bc_levels[level].bp;
 return XFS_BUF_TO_BLOCK(*bpp);
}

/*
 * Change the cursor to point to the first record at the given level.
 * Other levels are unaffected.
 */

STATIC int    /* success=1, failure=0 */
xfs_btree_firstrec(
 struct xfs_btree_cur *cur, /* btree cursor */
 int   level) /* level to change */
{
 struct xfs_btree_block *block; /* generic btree block pointer */
 struct xfs_buf  *bp; /* buffer containing block */

 /*
 * Get the block pointer for this level.
 */

 block = xfs_btree_get_block(cur, level, &bp);
 if (xfs_btree_check_block(cur, block, level, bp))
  return 0;
 /*
 * It's empty, there is no such record.
 */

 if (!block->bb_numrecs)
  return 0;
 /*
 * Set the ptr value to 1, that's the first record/key.
 */

 cur->bc_levels[level].ptr = 1;
 return 1;
}

/*
 * Change the cursor to point to the last record in the current block
 * at the given level.  Other levels are unaffected.
 */

STATIC int    /* success=1, failure=0 */
xfs_btree_lastrec(
 struct xfs_btree_cur *cur, /* btree cursor */
 int   level) /* level to change */
{
 struct xfs_btree_block *block; /* generic btree block pointer */
 struct xfs_buf  *bp; /* buffer containing block */

 /*
 * Get the block pointer for this level.
 */

 block = xfs_btree_get_block(cur, level, &bp);
 if (xfs_btree_check_block(cur, block, level, bp))
  return 0;
 /*
 * It's empty, there is no such record.
 */

 if (!block->bb_numrecs)
  return 0;
 /*
 * Set the ptr value to numrecs, that's the last record/key.
 */

 cur->bc_levels[level].ptr = be16_to_cpu(block->bb_numrecs);
 return 1;
}

/*
 * Compute first and last byte offsets for the fields given.
 * Interprets the offsets table, which contains struct field offsets.
 */

void
xfs_btree_offsets(
 uint32_t fields,  /* bitmask of fields */
 const short *offsets, /* table of field offsets */
 int  nbits,  /* number of bits to inspect */
 int  *first,  /* output: first byte offset */
 int  *last)  /* output: last byte offset */
{
 int  i;  /* current bit number */
 uint32_t imask;  /* mask for current bit number */

 ASSERT(fields != 0);
 /*
 * Find the lowest bit, so the first byte offset.
 */

 for (i = 0, imask = 1u; ; i++, imask <<= 1) {
  if (imask & fields) {
   *first = offsets[i];
   break;
  }
 }
 /*
 * Find the highest bit, so the last byte offset.
 */

 for (i = nbits - 1, imask = 1u << i; ; i--, imask >>= 1) {
  if (imask & fields) {
   *last = offsets[i + 1] - 1;
   break;
  }
 }
}

STATIC int
xfs_btree_readahead_fsblock(
 struct xfs_btree_cur *cur,
 int   lr,
 struct xfs_btree_block *block)
{
 struct xfs_mount *mp = cur->bc_mp;
 xfs_fsblock_t  left = be64_to_cpu(block->bb_u.l.bb_leftsib);
 xfs_fsblock_t  right = be64_to_cpu(block->bb_u.l.bb_rightsib);
 int   rval = 0;

 if ((lr & XFS_BTCUR_LEFTRA) && left != NULLFSBLOCK) {
  xfs_buf_readahead(mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, left),
    mp->m_bsize, cur->bc_ops->buf_ops);
  rval++;
 }

 if ((lr & XFS_BTCUR_RIGHTRA) && right != NULLFSBLOCK) {
  xfs_buf_readahead(mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, right),
    mp->m_bsize, cur->bc_ops->buf_ops);
  rval++;
 }

 return rval;
}

STATIC int
xfs_btree_readahead_memblock(
 struct xfs_btree_cur *cur,
 int   lr,
 struct xfs_btree_block *block)
{
 struct xfs_buftarg *btp = cur->bc_mem.xfbtree->target;
 xfbno_t   left = be64_to_cpu(block->bb_u.l.bb_leftsib);
 xfbno_t   right = be64_to_cpu(block->bb_u.l.bb_rightsib);
 int   rval = 0;

 if ((lr & XFS_BTCUR_LEFTRA) && left != NULLFSBLOCK) {
  xfs_buf_readahead(btp, xfbno_to_daddr(left), XFBNO_BBSIZE,
    cur->bc_ops->buf_ops);
  rval++;
 }

 if ((lr & XFS_BTCUR_RIGHTRA) && right != NULLFSBLOCK) {
  xfs_buf_readahead(btp, xfbno_to_daddr(right), XFBNO_BBSIZE,
    cur->bc_ops->buf_ops);
  rval++;
 }

 return rval;
}

STATIC int
xfs_btree_readahead_agblock(
 struct xfs_btree_cur *cur,
 int   lr,
 struct xfs_btree_block *block)
{
 struct xfs_mount *mp = cur->bc_mp;
 struct xfs_perag *pag = to_perag(cur->bc_group);
 xfs_agblock_t  left = be32_to_cpu(block->bb_u.s.bb_leftsib);
 xfs_agblock_t  right = be32_to_cpu(block->bb_u.s.bb_rightsib);
 int   rval = 0;

 if ((lr & XFS_BTCUR_LEFTRA) && left != NULLAGBLOCK) {
  xfs_buf_readahead(mp->m_ddev_targp,
    xfs_agbno_to_daddr(pag, left), mp->m_bsize,
    cur->bc_ops->buf_ops);
  rval++;
 }

 if ((lr & XFS_BTCUR_RIGHTRA) && right != NULLAGBLOCK) {
  xfs_buf_readahead(mp->m_ddev_targp,
    xfs_agbno_to_daddr(pag, right), mp->m_bsize,
    cur->bc_ops->buf_ops);
  rval++;
 }

 return rval;
}

/*
 * Read-ahead btree blocks, at the given level.
 * Bits in lr are set from XFS_BTCUR_{LEFT,RIGHT}RA.
 */

STATIC int
xfs_btree_readahead(
 struct xfs_btree_cur *cur,  /* btree cursor */
 int   lev,  /* level in btree */
 int   lr)  /* left/right bits */
{
 struct xfs_btree_block *block;

 /*
 * No readahead needed if we are at the root level and the
 * btree root is stored in the inode.
 */

 if (xfs_btree_at_iroot(cur, lev))
  return 0;

 if ((cur->bc_levels[lev].ra | lr) == cur->bc_levels[lev].ra)
  return 0;

 cur->bc_levels[lev].ra |= lr;
 block = XFS_BUF_TO_BLOCK(cur->bc_levels[lev].bp);

 switch (cur->bc_ops->type) {
 case XFS_BTREE_TYPE_AG:
  return xfs_btree_readahead_agblock(cur, lr, block);
 case XFS_BTREE_TYPE_INODE:
  return xfs_btree_readahead_fsblock(cur, lr, block);
 case XFS_BTREE_TYPE_MEM:
  return xfs_btree_readahead_memblock(cur, lr, block);
 default:
  ASSERT(0);
  return 0;
 }
}

STATIC int
xfs_btree_ptr_to_daddr(
 struct xfs_btree_cur  *cur,
 const union xfs_btree_ptr *ptr,
 xfs_daddr_t   *daddr)
{
 int   error;

 error = xfs_btree_check_ptr(cur, ptr, 0, 1);
 if (error)
  return error;

 switch (cur->bc_ops->type) {
 case XFS_BTREE_TYPE_AG:
  *daddr = xfs_agbno_to_daddr(to_perag(cur->bc_group),
    be32_to_cpu(ptr->s));
  break;
 case XFS_BTREE_TYPE_INODE:
  *daddr = XFS_FSB_TO_DADDR(cur->bc_mp, be64_to_cpu(ptr->l));
  break;
 case XFS_BTREE_TYPE_MEM:
  *daddr = xfbno_to_daddr(be64_to_cpu(ptr->l));
  break;
 }
 return 0;
}

/*
 * Readahead @count btree blocks at the given @ptr location.
 *
 * We don't need to care about long or short form btrees here as we have a
 * method of converting the ptr directly to a daddr available to us.
 */

STATIC void
xfs_btree_readahead_ptr(
 struct xfs_btree_cur *cur,
 union xfs_btree_ptr *ptr,
 xfs_extlen_t  count)
{
 xfs_daddr_t  daddr;

 if (xfs_btree_ptr_to_daddr(cur, ptr, &daddr))
  return;
 xfs_buf_readahead(xfs_btree_buftarg(cur), daddr,
   xfs_btree_bbsize(cur) * count,
   cur->bc_ops->buf_ops);
}

/*
 * Set the buffer for level "lev" in the cursor to bp, releasing
 * any previous buffer.
 */

STATIC void
xfs_btree_setbuf(
 struct xfs_btree_cur *cur, /* btree cursor */
 int   lev, /* level in btree */
 struct xfs_buf  *bp) /* new buffer to set */
{
 struct xfs_btree_block *b; /* btree block */

 if (cur->bc_levels[lev].bp)
  xfs_trans_brelse(cur->bc_tp, cur->bc_levels[lev].bp);
 cur->bc_levels[lev].bp = bp;
 cur->bc_levels[lev].ra = 0;

 b = XFS_BUF_TO_BLOCK(bp);
 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN) {
  if (b->bb_u.l.bb_leftsib == cpu_to_be64(NULLFSBLOCK))
   cur->bc_levels[lev].ra |= XFS_BTCUR_LEFTRA;
  if (b->bb_u.l.bb_rightsib == cpu_to_be64(NULLFSBLOCK))
   cur->bc_levels[lev].ra |= XFS_BTCUR_RIGHTRA;
 } else {
  if (b->bb_u.s.bb_leftsib == cpu_to_be32(NULLAGBLOCK))
   cur->bc_levels[lev].ra |= XFS_BTCUR_LEFTRA;
  if (b->bb_u.s.bb_rightsib == cpu_to_be32(NULLAGBLOCK))
   cur->bc_levels[lev].ra |= XFS_BTCUR_RIGHTRA;
 }
}

bool
xfs_btree_ptr_is_null(
 struct xfs_btree_cur  *cur,
 const union xfs_btree_ptr *ptr)
{
 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN)
  return ptr->l == cpu_to_be64(NULLFSBLOCK);
 else
  return ptr->s == cpu_to_be32(NULLAGBLOCK);
}

void
xfs_btree_set_ptr_null(
 struct xfs_btree_cur *cur,
 union xfs_btree_ptr *ptr)
{
 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN)
  ptr->l = cpu_to_be64(NULLFSBLOCK);
 else
  ptr->s = cpu_to_be32(NULLAGBLOCK);
}

static inline bool
xfs_btree_ptrs_equal(
 struct xfs_btree_cur  *cur,
 union xfs_btree_ptr  *ptr1,
 union xfs_btree_ptr  *ptr2)
{
 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN)
  return ptr1->l == ptr2->l;
 return ptr1->s == ptr2->s;
}

/*
 * Get/set/init sibling pointers
 */

void
xfs_btree_get_sibling(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 union xfs_btree_ptr *ptr,
 int   lr)
{
 ASSERT(lr == XFS_BB_LEFTSIB || lr == XFS_BB_RIGHTSIB);

 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN) {
  if (lr == XFS_BB_RIGHTSIB)
   ptr->l = block->bb_u.l.bb_rightsib;
  else
   ptr->l = block->bb_u.l.bb_leftsib;
 } else {
  if (lr == XFS_BB_RIGHTSIB)
   ptr->s = block->bb_u.s.bb_rightsib;
  else
   ptr->s = block->bb_u.s.bb_leftsib;
 }
}

void
xfs_btree_set_sibling(
 struct xfs_btree_cur  *cur,
 struct xfs_btree_block  *block,
 const union xfs_btree_ptr *ptr,
 int    lr)
{
 ASSERT(lr == XFS_BB_LEFTSIB || lr == XFS_BB_RIGHTSIB);

 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN) {
  if (lr == XFS_BB_RIGHTSIB)
   block->bb_u.l.bb_rightsib = ptr->l;
  else
   block->bb_u.l.bb_leftsib = ptr->l;
 } else {
  if (lr == XFS_BB_RIGHTSIB)
   block->bb_u.s.bb_rightsib = ptr->s;
  else
   block->bb_u.s.bb_leftsib = ptr->s;
 }
}

static void
__xfs_btree_init_block(
 struct xfs_mount *mp,
 struct xfs_btree_block *buf,
 const struct xfs_btree_ops *ops,
 xfs_daddr_t  blkno,
 __u16   level,
 __u16   numrecs,
 __u64   owner)
{
 bool   crc = xfs_has_crc(mp);
 __u32   magic = xfs_btree_magic(mp, ops);

 buf->bb_magic = cpu_to_be32(magic);
 buf->bb_level = cpu_to_be16(level);
 buf->bb_numrecs = cpu_to_be16(numrecs);

 if (ops->ptr_len == XFS_BTREE_LONG_PTR_LEN) {
  buf->bb_u.l.bb_leftsib = cpu_to_be64(NULLFSBLOCK);
  buf->bb_u.l.bb_rightsib = cpu_to_be64(NULLFSBLOCK);
  if (crc) {
   buf->bb_u.l.bb_blkno = cpu_to_be64(blkno);
   buf->bb_u.l.bb_owner = cpu_to_be64(owner);
   uuid_copy(&buf->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid);
   buf->bb_u.l.bb_pad = 0;
   buf->bb_u.l.bb_lsn = 0;
  }
 } else {
  buf->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK);
  buf->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK);
  if (crc) {
   buf->bb_u.s.bb_blkno = cpu_to_be64(blkno);
   /* owner is a 32 bit value on short blocks */
   buf->bb_u.s.bb_owner = cpu_to_be32((__u32)owner);
   uuid_copy(&buf->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid);
   buf->bb_u.s.bb_lsn = 0;
  }
 }
}

void
xfs_btree_init_block(
 struct xfs_mount *mp,
 struct xfs_btree_block *block,
 const struct xfs_btree_ops *ops,
 __u16   level,
 __u16   numrecs,
 __u64   owner)
{
 __xfs_btree_init_block(mp, block, ops, XFS_BUF_DADDR_NULL, level,
   numrecs, owner);
}

void
xfs_btree_init_buf(
 struct xfs_mount  *mp,
 struct xfs_buf   *bp,
 const struct xfs_btree_ops *ops,
 __u16    level,
 __u16    numrecs,
 __u64    owner)
{
 __xfs_btree_init_block(mp, XFS_BUF_TO_BLOCK(bp), ops,
   xfs_buf_daddr(bp), level, numrecs, owner);
 bp->b_ops = ops->buf_ops;
}

static inline __u64
xfs_btree_owner(
 struct xfs_btree_cur    *cur)
{
 switch (cur->bc_ops->type) {
 case XFS_BTREE_TYPE_MEM:
  return cur->bc_mem.xfbtree->owner;
 case XFS_BTREE_TYPE_INODE:
  return cur->bc_ino.ip->i_ino;
 case XFS_BTREE_TYPE_AG:
  return cur->bc_group->xg_gno;
 default:
  ASSERT(0);
  return 0;
 }
}

void
xfs_btree_init_block_cur(
 struct xfs_btree_cur *cur,
 struct xfs_buf  *bp,
 int   level,
 int   numrecs)
{
 xfs_btree_init_buf(cur->bc_mp, bp, cur->bc_ops, level, numrecs,
   xfs_btree_owner(cur));
}

STATIC void
xfs_btree_buf_to_ptr(
 struct xfs_btree_cur *cur,
 struct xfs_buf  *bp,
 union xfs_btree_ptr *ptr)
{
 switch (cur->bc_ops->type) {
 case XFS_BTREE_TYPE_AG:
  ptr->s = cpu_to_be32(xfs_daddr_to_agbno(cur->bc_mp,
     xfs_buf_daddr(bp)));
  break;
 case XFS_BTREE_TYPE_INODE:
  ptr->l = cpu_to_be64(XFS_DADDR_TO_FSB(cur->bc_mp,
     xfs_buf_daddr(bp)));
  break;
 case XFS_BTREE_TYPE_MEM:
  ptr->l = cpu_to_be64(xfs_daddr_to_xfbno(xfs_buf_daddr(bp)));
  break;
 }
}

static inline void
xfs_btree_set_refs(
 struct xfs_btree_cur *cur,
 struct xfs_buf  *bp)
{
 xfs_buf_set_ref(bp, cur->bc_ops->lru_refs);
}

int
xfs_btree_get_buf_block(
 struct xfs_btree_cur  *cur,
 const union xfs_btree_ptr *ptr,
 struct xfs_btree_block  **block,
 struct xfs_buf   **bpp)
{
 xfs_daddr_t   d;
 int    error;

 error = xfs_btree_ptr_to_daddr(cur, ptr, &d);
 if (error)
  return error;
 error = xfs_trans_get_buf(cur->bc_tp, xfs_btree_buftarg(cur), d,
   xfs_btree_bbsize(cur), 0, bpp);
 if (error)
  return error;

 (*bpp)->b_ops = cur->bc_ops->buf_ops;
 *block = XFS_BUF_TO_BLOCK(*bpp);
 return 0;
}

/*
 * Read in the buffer at the given ptr and return the buffer and
 * the block pointer within the buffer.
 */

int
xfs_btree_read_buf_block(
 struct xfs_btree_cur  *cur,
 const union xfs_btree_ptr *ptr,
 int    flags,
 struct xfs_btree_block  **block,
 struct xfs_buf   **bpp)
{
 struct xfs_mount *mp = cur->bc_mp;
 xfs_daddr_t  d;
 int   error;

 /* need to sort out how callers deal with failures first */
 ASSERT(!(flags & XBF_TRYLOCK));

 error = xfs_btree_ptr_to_daddr(cur, ptr, &d);
 if (error)
  return error;
 error = xfs_trans_read_buf(mp, cur->bc_tp, xfs_btree_buftarg(cur), d,
   xfs_btree_bbsize(cur), flags, bpp,
   cur->bc_ops->buf_ops);
 if (xfs_metadata_is_sick(error))
  xfs_btree_mark_sick(cur);
 if (error)
  return error;

 xfs_btree_set_refs(cur, *bpp);
 *block = XFS_BUF_TO_BLOCK(*bpp);
 return 0;
}

/*
 * Copy keys from one btree block to another.
 */

void
xfs_btree_copy_keys(
 struct xfs_btree_cur  *cur,
 union xfs_btree_key  *dst_key,
 const union xfs_btree_key *src_key,
 int    numkeys)
{
 ASSERT(numkeys >= 0);
 memcpy(dst_key, src_key, numkeys * cur->bc_ops->key_len);
}

/*
 * Copy records from one btree block to another.
 */

STATIC void
xfs_btree_copy_recs(
 struct xfs_btree_cur *cur,
 union xfs_btree_rec *dst_rec,
 union xfs_btree_rec *src_rec,
 int   numrecs)
{
 ASSERT(numrecs >= 0);
 memcpy(dst_rec, src_rec, numrecs * cur->bc_ops->rec_len);
}

/*
 * Copy block pointers from one btree block to another.
 */

void
xfs_btree_copy_ptrs(
 struct xfs_btree_cur *cur,
 union xfs_btree_ptr *dst_ptr,
 const union xfs_btree_ptr *src_ptr,
 int   numptrs)
{
 ASSERT(numptrs >= 0);
 memcpy(dst_ptr, src_ptr, numptrs * cur->bc_ops->ptr_len);
}

/*
 * Shift keys one index left/right inside a single btree block.
 */

STATIC void
xfs_btree_shift_keys(
 struct xfs_btree_cur *cur,
 union xfs_btree_key *key,
 int   dir,
 int   numkeys)
{
 char   *dst_key;

 ASSERT(numkeys >= 0);
 ASSERT(dir == 1 || dir == -1);

 dst_key = (char *)key + (dir * cur->bc_ops->key_len);
 memmove(dst_key, key, numkeys * cur->bc_ops->key_len);
}

/*
 * Shift records one index left/right inside a single btree block.
 */

STATIC void
xfs_btree_shift_recs(
 struct xfs_btree_cur *cur,
 union xfs_btree_rec *rec,
 int   dir,
 int   numrecs)
{
 char   *dst_rec;

 ASSERT(numrecs >= 0);
 ASSERT(dir == 1 || dir == -1);

 dst_rec = (char *)rec + (dir * cur->bc_ops->rec_len);
 memmove(dst_rec, rec, numrecs * cur->bc_ops->rec_len);
}

/*
 * Shift block pointers one index left/right inside a single btree block.
 */

STATIC void
xfs_btree_shift_ptrs(
 struct xfs_btree_cur *cur,
 union xfs_btree_ptr *ptr,
 int   dir,
 int   numptrs)
{
 char   *dst_ptr;

 ASSERT(numptrs >= 0);
 ASSERT(dir == 1 || dir == -1);

 dst_ptr = (char *)ptr + (dir * cur->bc_ops->ptr_len);
 memmove(dst_ptr, ptr, numptrs * cur->bc_ops->ptr_len);
}

/*
 * Log key values from the btree block.
 */

STATIC void
xfs_btree_log_keys(
 struct xfs_btree_cur *cur,
 struct xfs_buf  *bp,
 int   first,
 int   last)
{

 if (bp) {
  xfs_trans_buf_set_type(cur->bc_tp, bp, XFS_BLFT_BTREE_BUF);
  xfs_trans_log_buf(cur->bc_tp, bp,
      xfs_btree_key_offset(cur, first),
      xfs_btree_key_offset(cur, last + 1) - 1);
 } else {
  xfs_trans_log_inode(cur->bc_tp, cur->bc_ino.ip,
    xfs_ilog_fbroot(cur->bc_ino.whichfork));
 }
}

/*
 * Log record values from the btree block.
 */

void
xfs_btree_log_recs(
 struct xfs_btree_cur *cur,
 struct xfs_buf  *bp,
 int   first,
 int   last)
{
 if (!bp) {
  xfs_trans_log_inode(cur->bc_tp, cur->bc_ino.ip,
    xfs_ilog_fbroot(cur->bc_ino.whichfork));
  return;
 }

 xfs_trans_buf_set_type(cur->bc_tp, bp, XFS_BLFT_BTREE_BUF);
 xfs_trans_log_buf(cur->bc_tp, bp,
     xfs_btree_rec_offset(cur, first),
     xfs_btree_rec_offset(cur, last + 1) - 1);
}

/*
 * Log block pointer fields from a btree block (nonleaf).
 */

STATIC void
xfs_btree_log_ptrs(
 struct xfs_btree_cur *cur, /* btree cursor */
 struct xfs_buf  *bp, /* buffer containing btree block */
 int   first, /* index of first pointer to log */
 int   last) /* index of last pointer to log */
{

 if (bp) {
  struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
  int   level = xfs_btree_get_level(block);

  xfs_trans_buf_set_type(cur->bc_tp, bp, XFS_BLFT_BTREE_BUF);
  xfs_trans_log_buf(cur->bc_tp, bp,
    xfs_btree_ptr_offset(cur, first, level),
    xfs_btree_ptr_offset(cur, last + 1, level) - 1);
 } else {
  xfs_trans_log_inode(cur->bc_tp, cur->bc_ino.ip,
   xfs_ilog_fbroot(cur->bc_ino.whichfork));
 }

}

/*
 * Log fields from a btree block header.
 */

void
xfs_btree_log_block(
 struct xfs_btree_cur *cur, /* btree cursor */
 struct xfs_buf  *bp, /* buffer containing btree block */
 uint32_t  fields) /* mask of fields: XFS_BB_... */
{
 int   first; /* first byte offset logged */
 int   last; /* last byte offset logged */
 static const short soffsets[] = { /* table of offsets (short) */
  offsetof(struct xfs_btree_block, bb_magic),
  offsetof(struct xfs_btree_block, bb_level),
  offsetof(struct xfs_btree_block, bb_numrecs),
  offsetof(struct xfs_btree_block, bb_u.s.bb_leftsib),
  offsetof(struct xfs_btree_block, bb_u.s.bb_rightsib),
  offsetof(struct xfs_btree_block, bb_u.s.bb_blkno),
  offsetof(struct xfs_btree_block, bb_u.s.bb_lsn),
  offsetof(struct xfs_btree_block, bb_u.s.bb_uuid),
  offsetof(struct xfs_btree_block, bb_u.s.bb_owner),
  offsetof(struct xfs_btree_block, bb_u.s.bb_crc),
  XFS_BTREE_SBLOCK_CRC_LEN
 };
 static const short loffsets[] = { /* table of offsets (long) */
  offsetof(struct xfs_btree_block, bb_magic),
  offsetof(struct xfs_btree_block, bb_level),
  offsetof(struct xfs_btree_block, bb_numrecs),
  offsetof(struct xfs_btree_block, bb_u.l.bb_leftsib),
  offsetof(struct xfs_btree_block, bb_u.l.bb_rightsib),
  offsetof(struct xfs_btree_block, bb_u.l.bb_blkno),
  offsetof(struct xfs_btree_block, bb_u.l.bb_lsn),
  offsetof(struct xfs_btree_block, bb_u.l.bb_uuid),
  offsetof(struct xfs_btree_block, bb_u.l.bb_owner),
  offsetof(struct xfs_btree_block, bb_u.l.bb_crc),
  offsetof(struct xfs_btree_block, bb_u.l.bb_pad),
  XFS_BTREE_LBLOCK_CRC_LEN
 };

 if (bp) {
  int nbits;

  if (xfs_has_crc(cur->bc_mp)) {
   /*
 * We don't log the CRC when updating a btree
 * block but instead recreate it during log
 * recovery.  As the log buffers have checksums
 * of their own this is safe and avoids logging a crc
 * update in a lot of places.
 */

   if (fields == XFS_BB_ALL_BITS)
    fields = XFS_BB_ALL_BITS_CRC;
   nbits = XFS_BB_NUM_BITS_CRC;
  } else {
   nbits = XFS_BB_NUM_BITS;
  }
  xfs_btree_offsets(fields,
      (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN) ?
     loffsets : soffsets,
      nbits, &first, &last);
  xfs_trans_buf_set_type(cur->bc_tp, bp, XFS_BLFT_BTREE_BUF);
  xfs_trans_log_buf(cur->bc_tp, bp, first, last);
 } else {
  xfs_trans_log_inode(cur->bc_tp, cur->bc_ino.ip,
   xfs_ilog_fbroot(cur->bc_ino.whichfork));
 }
}

/*
 * Increment cursor by one record at the level.
 * For nonzero levels the leaf-ward information is untouched.
 */

int      /* error */
xfs_btree_increment(
 struct xfs_btree_cur *cur,
 int   level,
 int   *stat)  /* success/failure */
{
 struct xfs_btree_block *block;
 union xfs_btree_ptr ptr;
 struct xfs_buf  *bp;
 int   error;  /* error return value */
 int   lev;

 ASSERT(level < cur->bc_nlevels);

 /* Read-ahead to the right at this level. */
 xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);

 /* Get a pointer to the btree block. */
 block = xfs_btree_get_block(cur, level, &bp);

#ifdef DEBUG
 error = xfs_btree_check_block(cur, block, level, bp);
 if (error)
  goto error0;
#endif

 /* We're done if we remain in the block after the increment. */
 if (++cur->bc_levels[level].ptr <= xfs_btree_get_numrecs(block))
  goto out1;

 /* Fail if we just went off the right edge of the tree. */
 xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
 if (xfs_btree_ptr_is_null(cur, &ptr))
  goto out0;

 XFS_BTREE_STATS_INC(cur, increment);

 /*
 * March up the tree incrementing pointers.
 * Stop when we don't go off the right edge of a block.
 */

 for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
  block = xfs_btree_get_block(cur, lev, &bp);

#ifdef DEBUG
  error = xfs_btree_check_block(cur, block, lev, bp);
  if (error)
   goto error0;
#endif

  if (++cur->bc_levels[lev].ptr <= xfs_btree_get_numrecs(block))
   break;

  /* Read-ahead the right block for the next loop. */
  xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
 }

 /*
 * If we went off the root then we are either seriously
 * confused or have the tree root in an inode.
 */

 if (lev == cur->bc_nlevels) {
  if (cur->bc_ops->type == XFS_BTREE_TYPE_INODE)
   goto out0;
  ASSERT(0);
  xfs_btree_mark_sick(cur);
  error = -EFSCORRUPTED;
  goto error0;
 }
 ASSERT(lev < cur->bc_nlevels);

 /*
 * Now walk back down the tree, fixing up the cursor's buffer
 * pointers and key numbers.
 */

 for (block = xfs_btree_get_block(cur, lev, &bp); lev > level; ) {
  union xfs_btree_ptr *ptrp;

  ptrp = xfs_btree_ptr_addr(cur, cur->bc_levels[lev].ptr, block);
  --lev;
  error = xfs_btree_read_buf_block(cur, ptrp, 0, &block, &bp);
  if (error)
   goto error0;

  xfs_btree_setbuf(cur, lev, bp);
  cur->bc_levels[lev].ptr = 1;
 }
out1:
 *stat = 1;
 return 0;

out0:
 *stat = 0;
 return 0;

error0:
 return error;
}

/*
 * Decrement cursor by one record at the level.
 * For nonzero levels the leaf-ward information is untouched.
 */

int      /* error */
xfs_btree_decrement(
 struct xfs_btree_cur *cur,
 int   level,
 int   *stat)  /* success/failure */
{
 struct xfs_btree_block *block;
 struct xfs_buf  *bp;
 int   error;  /* error return value */
 int   lev;
 union xfs_btree_ptr ptr;

 ASSERT(level < cur->bc_nlevels);

 /* Read-ahead to the left at this level. */
 xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);

 /* We're done if we remain in the block after the decrement. */
 if (--cur->bc_levels[level].ptr > 0)
  goto out1;

 /* Get a pointer to the btree block. */
 block = xfs_btree_get_block(cur, level, &bp);

#ifdef DEBUG
 error = xfs_btree_check_block(cur, block, level, bp);
 if (error)
  goto error0;
#endif

 /* Fail if we just went off the left edge of the tree. */
 xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_LEFTSIB);
 if (xfs_btree_ptr_is_null(cur, &ptr))
  goto out0;

 XFS_BTREE_STATS_INC(cur, decrement);

 /*
 * March up the tree decrementing pointers.
 * Stop when we don't go off the left edge of a block.
 */

 for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
  if (--cur->bc_levels[lev].ptr > 0)
   break;
  /* Read-ahead the left block for the next loop. */
  xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
 }

 /*
 * If we went off the root then we are seriously confused.
 * or the root of the tree is in an inode.
 */

 if (lev == cur->bc_nlevels) {
  if (cur->bc_ops->type == XFS_BTREE_TYPE_INODE)
   goto out0;
  ASSERT(0);
  xfs_btree_mark_sick(cur);
  error = -EFSCORRUPTED;
  goto error0;
 }
 ASSERT(lev < cur->bc_nlevels);

 /*
 * Now walk back down the tree, fixing up the cursor's buffer
 * pointers and key numbers.
 */

 for (block = xfs_btree_get_block(cur, lev, &bp); lev > level; ) {
  union xfs_btree_ptr *ptrp;

  ptrp = xfs_btree_ptr_addr(cur, cur->bc_levels[lev].ptr, block);
  --lev;
  error = xfs_btree_read_buf_block(cur, ptrp, 0, &block, &bp);
  if (error)
   goto error0;
  xfs_btree_setbuf(cur, lev, bp);
  cur->bc_levels[lev].ptr = xfs_btree_get_numrecs(block);
 }
out1:
 *stat = 1;
 return 0;

out0:
 *stat = 0;
 return 0;

error0:
 return error;
}

/*
 * Check the btree block owner now that we have the context to know who the
 * real owner is.
 */

static inline xfs_failaddr_t
xfs_btree_check_block_owner(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block)
{
 __u64   owner;

 if (!xfs_has_crc(cur->bc_mp) ||
     (cur->bc_flags & XFS_BTREE_BMBT_INVALID_OWNER))
  return NULL;

 owner = xfs_btree_owner(cur);
 if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN) {
  if (be64_to_cpu(block->bb_u.l.bb_owner) != owner)
   return __this_address;
 } else {
  if (be32_to_cpu(block->bb_u.s.bb_owner) != owner)
   return __this_address;
 }

 return NULL;
}

int
xfs_btree_lookup_get_block(
 struct xfs_btree_cur  *cur, /* btree cursor */
 int    level, /* level in the btree */
 const union xfs_btree_ptr *pp, /* ptr to btree block */
 struct xfs_btree_block  **blkp) /* return btree block */
{
 struct xfs_buf  *bp; /* buffer pointer for btree block */
 xfs_daddr_t  daddr;
 int   error = 0;

 /* special case the root block if in an inode */
 if (xfs_btree_at_iroot(cur, level)) {
  *blkp = xfs_btree_get_iroot(cur);
  return 0;
 }

 /*
 * If the old buffer at this level for the disk address we are
 * looking for re-use it.
 *
 * Otherwise throw it away and get a new one.
 */

 bp = cur->bc_levels[level].bp;
 error = xfs_btree_ptr_to_daddr(cur, pp, &daddr);
 if (error)
  return error;
 if (bp && xfs_buf_daddr(bp) == daddr) {
  *blkp = XFS_BUF_TO_BLOCK(bp);
  return 0;
 }

 error = xfs_btree_read_buf_block(cur, pp, 0, blkp, &bp);
 if (error)
  return error;

 /* Check the inode owner since the verifiers don't. */
 if (xfs_btree_check_block_owner(cur, *blkp) != NULL)
  goto out_bad;

 /* Did we get the level we were looking for? */
 if (be16_to_cpu((*blkp)->bb_level) != level)
  goto out_bad;

 /* Check that internal nodes have at least one record. */
 if (level != 0 && be16_to_cpu((*blkp)->bb_numrecs) == 0)
  goto out_bad;

 xfs_btree_setbuf(cur, level, bp);
 return 0;

out_bad:
 *blkp = NULL;
 xfs_buf_mark_corrupt(bp);
 xfs_trans_brelse(cur->bc_tp, bp);
 xfs_btree_mark_sick(cur);
 return -EFSCORRUPTED;
}

/*
 * Get current search key.  For level 0 we don't actually have a key
 * structure so we make one up from the record.  For all other levels
 * we just return the right key.
 */

STATIC union xfs_btree_key *
xfs_lookup_get_search_key(
 struct xfs_btree_cur *cur,
 int   level,
 int   keyno,
 struct xfs_btree_block *block,
 union xfs_btree_key *kp)
{
 if (level == 0) {
  cur->bc_ops->init_key_from_rec(kp,
    xfs_btree_rec_addr(cur, keyno, block));
  return kp;
 }

 return xfs_btree_key_addr(cur, keyno, block);
}

/*
 * Initialize a pointer to the root block.
 */

void
xfs_btree_init_ptr_from_cur(
 struct xfs_btree_cur *cur,
 union xfs_btree_ptr *ptr)
{
 if (cur->bc_ops->type == XFS_BTREE_TYPE_INODE) {
  /*
 * Inode-rooted btrees call xfs_btree_get_iroot to find the root
 * in xfs_btree_lookup_get_block and don't need a pointer here.
 */

  ptr->l = 0;
 } else if (cur->bc_flags & XFS_BTREE_STAGING) {
  ptr->s = cpu_to_be32(cur->bc_ag.afake->af_root);
 } else {
  cur->bc_ops->init_ptr_from_cur(cur, ptr);
 }
}

/*
 * Lookup the record.  The cursor is made to point to it, based on dir.
 * stat is set to 0 if can't find any such record, 1 for success.
 */

int     /* error */
xfs_btree_lookup(
 struct xfs_btree_cur *cur, /* btree cursor */
 xfs_lookup_t  dir, /* <=, ==, or >= */
 int   *stat) /* success/failure */
{
 struct xfs_btree_block *block; /* current btree block */
 int   cmp_r; /* current key comparison result */
 int   error; /* error return value */
 int   keyno; /* current key number */
 int   level; /* level in the btree */
 union xfs_btree_ptr *pp; /* ptr to btree block */
 union xfs_btree_ptr ptr; /* ptr to btree block */

 XFS_BTREE_STATS_INC(cur, lookup);

 /* No such thing as a zero-level tree. */
 if (XFS_IS_CORRUPT(cur->bc_mp, cur->bc_nlevels == 0)) {
  xfs_btree_mark_sick(cur);
  return -EFSCORRUPTED;
 }

 block = NULL;
 keyno = 0;

 /* initialise start pointer from cursor */
 xfs_btree_init_ptr_from_cur(cur, &ptr);
 pp = &ptr;

 /*
 * Iterate over each level in the btree, starting at the root.
 * For each level above the leaves, find the key we need, based
 * on the lookup record, then follow the corresponding block
 * pointer down to the next level.
 */

 for (level = cur->bc_nlevels - 1, cmp_r = 1; level >= 0; level--) {
  /* Get the block we need to do the lookup on. */
  error = xfs_btree_lookup_get_block(cur, level, pp, &block);
  if (error)
   goto error0;

  if (cmp_r == 0) {
   /*
 * If we already had a key match at a higher level, we
 * know we need to use the first entry in this block.
 */

   keyno = 1;
  } else {
   /* Otherwise search this block. Do a binary search. */

   int high; /* high entry number */
   int low; /* low entry number */

   /* Set low and high entry numbers, 1-based. */
   low = 1;
   high = xfs_btree_get_numrecs(block);
   if (!high) {
    /* Block is empty, must be an empty leaf. */
    if (level != 0 || cur->bc_nlevels != 1) {
     XFS_CORRUPTION_ERROR(__func__,
       XFS_ERRLEVEL_LOW,
       cur->bc_mp, block,
       sizeof(*block));
     xfs_btree_mark_sick(cur);
     return -EFSCORRUPTED;
    }

    cur->bc_levels[0].ptr = dir != XFS_LOOKUP_LE;
    *stat = 0;
    return 0;
   }

   /* Binary search the block. */
   while (low <= high) {
    union xfs_btree_key key;
    union xfs_btree_key *kp;

    XFS_BTREE_STATS_INC(cur, compare);

    /* keyno is average of low and high. */
    keyno = (low + high) >> 1;

    /* Get current search key */
    kp = xfs_lookup_get_search_key(cur, level,
      keyno, block, &key);

    /*
 * Compute comparison result to get next
 * direction:
 *  - less than, move right
 *  - greater than, move left
 *  - equal, we're done
 */

    cmp_r = cur->bc_ops->cmp_key_with_cur(cur, kp);
    if (cmp_r < 0)
     low = keyno + 1;
    else if (cmp_r > 0)
     high = keyno - 1;
    else
     break;
   }
  }

  /*
 * If there are more levels, set up for the next level
 * by getting the block number and filling in the cursor.
 */

  if (level > 0) {
   /*
 * If we moved left, need the previous key number,
 * unless there isn't one.
 */

   if (cmp_r > 0 && --keyno < 1)
    keyno = 1;
   pp = xfs_btree_ptr_addr(cur, keyno, block);

   error = xfs_btree_debug_check_ptr(cur, pp, 0, level);
   if (error)
    goto error0;

   cur->bc_levels[level].ptr = keyno;
  }
 }

 /* Done with the search. See if we need to adjust the results. */
 if (dir != XFS_LOOKUP_LE && cmp_r < 0) {
  keyno++;
  /*
 * If ge search and we went off the end of the block, but it's
 * not the last block, we're in the wrong block.
 */

  xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
  if (dir == XFS_LOOKUP_GE &&
      keyno > xfs_btree_get_numrecs(block) &&
      !xfs_btree_ptr_is_null(cur, &ptr)) {
   int i;

   cur->bc_levels[0].ptr = keyno;
   error = xfs_btree_increment(cur, 0, &i);
   if (error)
    goto error0;
   if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    return -EFSCORRUPTED;
   }
   *stat = 1;
   return 0;
  }
 } else if (dir == XFS_LOOKUP_LE && cmp_r > 0)
  keyno--;
 cur->bc_levels[0].ptr = keyno;

 /* Return if we succeeded or not. */
 if (keyno == 0 || keyno > xfs_btree_get_numrecs(block))
  *stat = 0;
 else if (dir != XFS_LOOKUP_EQ || cmp_r == 0)
  *stat = 1;
 else
  *stat = 0;
 return 0;

error0:
 return error;
}

/* Find the high key storage area from a regular key. */
union xfs_btree_key *
xfs_btree_high_key_from_key(
 struct xfs_btree_cur *cur,
 union xfs_btree_key *key)
{
 ASSERT(cur->bc_ops->geom_flags & XFS_BTGEO_OVERLAPPING);
 return (union xfs_btree_key *)((char *)key +
   (cur->bc_ops->key_len / 2));
}

/* Determine the low (and high if overlapped) keys of a leaf block */
STATIC void
xfs_btree_get_leaf_keys(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 union xfs_btree_key *key)
{
 union xfs_btree_key max_hkey;
 union xfs_btree_key hkey;
 union xfs_btree_rec *rec;
 union xfs_btree_key *high;
 int   n;

 rec = xfs_btree_rec_addr(cur, 1, block);
 cur->bc_ops->init_key_from_rec(key, rec);

 if (cur->bc_ops->geom_flags & XFS_BTGEO_OVERLAPPING) {

  cur->bc_ops->init_high_key_from_rec(&max_hkey, rec);
  for (n = 2; n <= xfs_btree_get_numrecs(block); n++) {
   rec = xfs_btree_rec_addr(cur, n, block);
   cur->bc_ops->init_high_key_from_rec(&hkey, rec);
   if (xfs_btree_keycmp_gt(cur, &hkey, &max_hkey))
    max_hkey = hkey;
  }

  high = xfs_btree_high_key_from_key(cur, key);
  memcpy(high, &max_hkey, cur->bc_ops->key_len / 2);
 }
}

/* Determine the low (and high if overlapped) keys of a node block */
STATIC void
xfs_btree_get_node_keys(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 union xfs_btree_key *key)
{
 union xfs_btree_key *hkey;
 union xfs_btree_key *max_hkey;
 union xfs_btree_key *high;
 int   n;

 if (cur->bc_ops->geom_flags & XFS_BTGEO_OVERLAPPING) {
  memcpy(key, xfs_btree_key_addr(cur, 1, block),
    cur->bc_ops->key_len / 2);

  max_hkey = xfs_btree_high_key_addr(cur, 1, block);
  for (n = 2; n <= xfs_btree_get_numrecs(block); n++) {
   hkey = xfs_btree_high_key_addr(cur, n, block);
   if (xfs_btree_keycmp_gt(cur, hkey, max_hkey))
    max_hkey = hkey;
  }

  high = xfs_btree_high_key_from_key(cur, key);
  memcpy(high, max_hkey, cur->bc_ops->key_len / 2);
 } else {
  memcpy(key, xfs_btree_key_addr(cur, 1, block),
    cur->bc_ops->key_len);
 }
}

/* Derive the keys for any btree block. */
void
xfs_btree_get_keys(
 struct xfs_btree_cur *cur,
 struct xfs_btree_block *block,
 union xfs_btree_key *key)
{
 if (be16_to_cpu(block->bb_level) == 0)
  xfs_btree_get_leaf_keys(cur, block, key);
 else
  xfs_btree_get_node_keys(cur, block, key);
}

/*
 * Decide if we need to update the parent keys of a btree block.  For
 * a standard btree this is only necessary if we're updating the first
 * record/key.  For an overlapping btree, we must always update the
 * keys because the highest key can be in any of the records or keys
 * in the block.
 */

static inline bool
xfs_btree_needs_key_update(
 struct xfs_btree_cur *cur,
 int   ptr)
{
 return (cur->bc_ops->geom_flags & XFS_BTGEO_OVERLAPPING) || ptr == 1;
}

/*
 * Update the low and high parent keys of the given level, progressing
 * towards the root.  If force_all is false, stop if the keys for a given
 * level do not need updating.
 */

STATIC int
__xfs_btree_updkeys(
 struct xfs_btree_cur *cur,
 int   level,
 struct xfs_btree_block *block,
 struct xfs_buf  *bp0,
 bool   force_all)
{
 union xfs_btree_key key; /* keys from current level */
 union xfs_btree_key *lkey; /* keys from the next level up */
 union xfs_btree_key *hkey;
 union xfs_btree_key *nlkey; /* keys from the next level up */
 union xfs_btree_key *nhkey;
 struct xfs_buf  *bp;
 int   ptr;

 ASSERT(cur->bc_ops->geom_flags & XFS_BTGEO_OVERLAPPING);

 /* Exit if there aren't any parent levels to update. */
 if (level + 1 >= cur->bc_nlevels)
  return 0;

 trace_xfs_btree_updkeys(cur, level, bp0);

 lkey = &key;
 hkey = xfs_btree_high_key_from_key(cur, lkey);
 xfs_btree_get_keys(cur, block, lkey);
 for (level++; level < cur->bc_nlevels; level++) {
#ifdef DEBUG
  int  error;
#endif
  block = xfs_btree_get_block(cur, level, &bp);
  trace_xfs_btree_updkeys(cur, level, bp);
#ifdef DEBUG
  error = xfs_btree_check_block(cur, block, level, bp);
  if (error)
   return error;
#endif
  ptr = cur->bc_levels[level].ptr;
  nlkey = xfs_btree_key_addr(cur, ptr, block);
  nhkey = xfs_btree_high_key_addr(cur, ptr, block);
  if (!force_all &&
      xfs_btree_keycmp_eq(cur, nlkey, lkey) &&
      xfs_btree_keycmp_eq(cur, nhkey, hkey))
   break;
  xfs_btree_copy_keys(cur, nlkey, lkey, 1);
  xfs_btree_log_keys(cur, bp, ptr, ptr);
  if (level + 1 >= cur->bc_nlevels)
   break;
  xfs_btree_get_node_keys(cur, block, lkey);
 }

 return 0;
}

/* Update all the keys from some level in cursor back to the root. */
STATIC int
xfs_btree_updkeys_force(
 struct xfs_btree_cur *cur,
 int   level)
{
 struct xfs_buf  *bp;
 struct xfs_btree_block *block;

 block = xfs_btree_get_block(cur, level, &bp);
 return __xfs_btree_updkeys(cur, level, block, bp, true);
}

/*
 * Update the parent keys of the given level, progressing towards the root.
 */

STATIC int
xfs_btree_update_keys(
 struct xfs_btree_cur *cur,
 int   level)
{
 struct xfs_btree_block *block;
 struct xfs_buf  *bp;
 union xfs_btree_key *kp;
 union xfs_btree_key key;
 int   ptr;

 ASSERT(level >= 0);

 block = xfs_btree_get_block(cur, level, &bp);
 if (cur->bc_ops->geom_flags & XFS_BTGEO_OVERLAPPING)
  return __xfs_btree_updkeys(cur, level, block, bp, false);

 /*
 * Go up the tree from this level toward the root.
 * At each level, update the key value to the value input.
 * Stop when we reach a level where the cursor isn't pointing
 * at the first entry in the block.
 */

 xfs_btree_get_keys(cur, block, &key);
 for (level++, ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
#ifdef DEBUG
  int  error;
#endif
  block = xfs_btree_get_block(cur, level, &bp);
#ifdef DEBUG
  error = xfs_btree_check_block(cur, block, level, bp);
  if (error)
   return error;
#endif
  ptr = cur->bc_levels[level].ptr;
  kp = xfs_btree_key_addr(cur, ptr, block);
  xfs_btree_copy_keys(cur, kp, &key, 1);
  xfs_btree_log_keys(cur, bp, ptr, ptr);
 }

 return 0;
}

/*
 * Update the record referred to by cur to the value in the
 * given record. This either works (return 0) or gets an
 * EFSCORRUPTED error.
 */

int
xfs_btree_update(
 struct xfs_btree_cur *cur,
 union xfs_btree_rec *rec)
{
 struct xfs_btree_block *block;
 struct xfs_buf  *bp;
 int   error;
 int   ptr;
 union xfs_btree_rec *rp;

 /* Pick up the current block. */
 block = xfs_btree_get_block(cur, 0, &bp);

#ifdef DEBUG
 error = xfs_btree_check_block(cur, block, 0, bp);
 if (error)
  goto error0;
#endif
 /* Get the address of the rec to be updated. */
 ptr = cur->bc_levels[0].ptr;
 rp = xfs_btree_rec_addr(cur, ptr, block);

 /* Fill in the new contents and log them. */
 xfs_btree_copy_recs(cur, rp, rec, 1);
 xfs_btree_log_recs(cur, bp, ptr, ptr);

 /* Pass new key value up to our parent. */
 if (xfs_btree_needs_key_update(cur, ptr)) {
  error = xfs_btree_update_keys(cur, 0);
  if (error)
   goto error0;
 }

 return 0;

error0:
 return error;
}

/*
 * Move 1 record left from cur/level if possible.
 * Update cur to reflect the new path.
 */

STATIC int     /* error */
xfs_btree_lshift(
 struct xfs_btree_cur *cur,
 int   level,
 int   *stat)  /* success/failure */
{
 struct xfs_buf  *lbp;  /* left buffer pointer */
 struct xfs_btree_block *left;  /* left btree block */
 int   lrecs;  /* left record count */
 struct xfs_buf  *rbp;  /* right buffer pointer */
 struct xfs_btree_block *right;  /* right btree block */
 struct xfs_btree_cur *tcur;  /* temporary btree cursor */
 int   rrecs;  /* right record count */
 union xfs_btree_ptr lptr;  /* left btree pointer */
 union xfs_btree_key *rkp = NULL; /* right btree key */
 union xfs_btree_ptr *rpp = NULL; /* right address pointer */
 union xfs_btree_rec *rrp = NULL; /* right record pointer */
 int   error;  /* error return value */
 int   i;

 if (xfs_btree_at_iroot(cur, level))
  goto out0;

 /* Set up variables for this block as "right". */
 right = xfs_btree_get_block(cur, level, &rbp);

#ifdef DEBUG
 error = xfs_btree_check_block(cur, right, level, rbp);
 if (error)
  goto error0;
#endif

 /* If we've got no left sibling then we can't shift an entry left. */
 xfs_btree_get_sibling(cur, right, &lptr, XFS_BB_LEFTSIB);
 if (xfs_btree_ptr_is_null(cur, &lptr))
  goto out0;

 /*
 * If the cursor entry is the one that would be moved, don't
 * do it... it's too complicated.
 */

 if (cur->bc_levels[level].ptr <= 1)
  goto out0;

 /* Set up the left neighbor as "left". */
 error = xfs_btree_read_buf_block(cur, &lptr, 0, &left, &lbp);
 if (error)
  goto error0;

 /* If it's full, it can't take another entry. */
 lrecs = xfs_btree_get_numrecs(left);
 if (lrecs == cur->bc_ops->get_maxrecs(cur, level))
  goto out0;

 rrecs = xfs_btree_get_numrecs(right);

 /*
 * We add one entry to the left side and remove one for the right side.
 * Account for it here, the changes will be updated on disk and logged
 * later.
 */

 lrecs++;
 rrecs--;

 XFS_BTREE_STATS_INC(cur, lshift);
 XFS_BTREE_STATS_ADD(cur, moves, 1);

 /*
 * If non-leaf, copy a key and a ptr to the left block.
 * Log the changes to the left block.
 */

 if (level > 0) {
  /* It's a non-leaf.  Move keys and pointers. */
  union xfs_btree_key *lkp; /* left btree key */
  union xfs_btree_ptr *lpp; /* left address pointer */

  lkp = xfs_btree_key_addr(cur, lrecs, left);
  rkp = xfs_btree_key_addr(cur, 1, right);

  lpp = xfs_btree_ptr_addr(cur, lrecs, left);
  rpp = xfs_btree_ptr_addr(cur, 1, right);

  error = xfs_btree_debug_check_ptr(cur, rpp, 0, level);
  if (error)
   goto error0;

  xfs_btree_copy_keys(cur, lkp, rkp, 1);
  xfs_btree_copy_ptrs(cur, lpp, rpp, 1);

  xfs_btree_log_keys(cur, lbp, lrecs, lrecs);
  xfs_btree_log_ptrs(cur, lbp, lrecs, lrecs);

  ASSERT(cur->bc_ops->keys_inorder(cur,
   xfs_btree_key_addr(cur, lrecs - 1, left), lkp));
 } else {
  /* It's a leaf.  Move records.  */
  union xfs_btree_rec *lrp; /* left record pointer */

  lrp = xfs_btree_rec_addr(cur, lrecs, left);
  rrp = xfs_btree_rec_addr(cur, 1, right);

  xfs_btree_copy_recs(cur, lrp, rrp, 1);
  xfs_btree_log_recs(cur, lbp, lrecs, lrecs);

  ASSERT(cur->bc_ops->recs_inorder(cur,
   xfs_btree_rec_addr(cur, lrecs - 1, left), lrp));
 }

 xfs_btree_set_numrecs(left, lrecs);
 xfs_btree_log_block(cur, lbp, XFS_BB_NUMRECS);

 xfs_btree_set_numrecs(right, rrecs);
 xfs_btree_log_block(cur, rbp, XFS_BB_NUMRECS);

 /*
 * Slide the contents of right down one entry.
 */

 XFS_BTREE_STATS_ADD(cur, moves, rrecs - 1);
 if (level > 0) {
  /* It's a nonleaf. operate on keys and ptrs */
  for (i = 0; i < rrecs; i++) {
   error = xfs_btree_debug_check_ptr(cur, rpp, i + 1, level);
   if (error)
    goto error0;
  }

  xfs_btree_shift_keys(cur,
    xfs_btree_key_addr(cur, 2, right),
    -1, rrecs);
  xfs_btree_shift_ptrs(cur,
    xfs_btree_ptr_addr(cur, 2, right),
    -1, rrecs);

  xfs_btree_log_keys(cur, rbp, 1, rrecs);
  xfs_btree_log_ptrs(cur, rbp, 1, rrecs);
 } else {
  /* It's a leaf. operate on records */
  xfs_btree_shift_recs(cur,
   xfs_btree_rec_addr(cur, 2, right),
   -1, rrecs);
  xfs_btree_log_recs(cur, rbp, 1, rrecs);
 }

 /*
 * Using a temporary cursor, update the parent key values of the
 * block on the left.
 */

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=92 G=91

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.