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 166 kB image not shown  

Quelle  xfs_bmap.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2006 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_sb.h"
#include "xfs_mount.h"
#include "xfs_defer.h"
#include "xfs_dir2.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
#include "xfs_trans.h"
#include "xfs_alloc.h"
#include "xfs_bmap.h"
#include "xfs_bmap_util.h"
#include "xfs_bmap_btree.h"
#include "xfs_rtbitmap.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_quota.h"
#include "xfs_trans_space.h"
#include "xfs_buf_item.h"
#include "xfs_trace.h"
#include "xfs_attr_leaf.h"
#include "xfs_filestream.h"
#include "xfs_rmap.h"
#include "xfs_ag.h"
#include "xfs_ag_resv.h"
#include "xfs_refcount.h"
#include "xfs_iomap.h"
#include "xfs_health.h"
#include "xfs_bmap_item.h"
#include "xfs_symlink_remote.h"
#include "xfs_inode_util.h"
#include "xfs_rtgroup.h"
#include "xfs_zone_alloc.h"

struct kmem_cache  *xfs_bmap_intent_cache;

/*
 * Miscellaneous helper functions
 */


/*
 * Compute and fill in the value of the maximum depth of a bmap btree
 * in this filesystem.  Done once, during mount.
 */

void
xfs_bmap_compute_maxlevels(
 xfs_mount_t *mp,  /* file system mount structure */
 int  whichfork) /* data or attr fork */
{
 uint64_t maxblocks; /* max blocks at this level */
 xfs_extnum_t maxleafents; /* max leaf entries possible */
 int  level;  /* btree level */
 int  maxrootrecs; /* max records in root block */
 int  minleafrecs; /* min records in leaf block */
 int  minnoderecs; /* min records in node block */
 int  sz;  /* root block size */

 /*
 * The maximum number of extents in a fork, hence the maximum number of
 * leaf entries, is controlled by the size of the on-disk extent count.
 *
 * Note that we can no longer assume that if we are in ATTR1 that the
 * fork offset of all the inodes will be
 * (xfs_default_attroffset(ip) >> 3) because we could have mounted with
 * ATTR2 and then mounted back with ATTR1, keeping the i_forkoff's fixed
 * but probably at various positions. Therefore, for both ATTR1 and
 * ATTR2 we have to assume the worst case scenario of a minimum size
 * available.
 */

 maxleafents = xfs_iext_max_nextents(xfs_has_large_extent_counts(mp),
    whichfork);
 if (whichfork == XFS_DATA_FORK)
  sz = xfs_bmdr_space_calc(MINDBTPTRS);
 else
  sz = xfs_bmdr_space_calc(MINABTPTRS);

 maxrootrecs = xfs_bmdr_maxrecs(sz, 0);
 minleafrecs = mp->m_bmap_dmnr[0];
 minnoderecs = mp->m_bmap_dmnr[1];
 maxblocks = howmany_64(maxleafents, minleafrecs);
 for (level = 1; maxblocks > 1; level++) {
  if (maxblocks <= maxrootrecs)
   maxblocks = 1;
  else
   maxblocks = howmany_64(maxblocks, minnoderecs);
 }
 mp->m_bm_maxlevels[whichfork] = level;
 ASSERT(mp->m_bm_maxlevels[whichfork] <= xfs_bmbt_maxlevels_ondisk());
}

unsigned int
xfs_bmap_compute_attr_offset(
 struct xfs_mount *mp)
{
 if (mp->m_sb.sb_inodesize == 256)
  return XFS_LITINO(mp) - xfs_bmdr_space_calc(MINABTPTRS);
 return xfs_bmdr_space_calc(6 * MINABTPTRS);
}

STATIC int    /* error */
xfs_bmbt_lookup_eq(
 struct xfs_btree_cur *cur,
 struct xfs_bmbt_irec *irec,
 int   *stat) /* success/failure */
{
 cur->bc_rec.b = *irec;
 return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
}

STATIC int    /* error */
xfs_bmbt_lookup_first(
 struct xfs_btree_cur *cur,
 int   *stat) /* success/failure */
{
 cur->bc_rec.b.br_startoff = 0;
 cur->bc_rec.b.br_startblock = 0;
 cur->bc_rec.b.br_blockcount = 0;
 return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
}

/*
 * Check if the inode needs to be converted to btree format.
 */

static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork)
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);

 return whichfork != XFS_COW_FORK &&
  ifp->if_format == XFS_DINODE_FMT_EXTENTS &&
  ifp->if_nextents > XFS_IFORK_MAXEXT(ip, whichfork);
}

/*
 * Check if the inode should be converted to extent format.
 */

static inline bool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork)
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);

 return whichfork != XFS_COW_FORK &&
  ifp->if_format == XFS_DINODE_FMT_BTREE &&
  ifp->if_nextents <= XFS_IFORK_MAXEXT(ip, whichfork);
}

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

STATIC int
xfs_bmbt_update(
 struct xfs_btree_cur *cur,
 struct xfs_bmbt_irec *irec)
{
 union xfs_btree_rec rec;

 xfs_bmbt_disk_set_all(&rec.bmbt, irec);
 return xfs_btree_update(cur, &rec);
}

/*
 * Compute the worst-case number of indirect blocks that will be used
 * for ip's delayed extent of length "len".
 */

xfs_filblks_t
xfs_bmap_worst_indlen(
 struct xfs_inode *ip,  /* incore inode pointer */
 xfs_filblks_t  len)  /* delayed extent length */
{
 struct xfs_mount *mp = ip->i_mount;
 int   maxrecs = mp->m_bmap_dmxr[0];
 int   level;
 xfs_filblks_t  rval;

 for (level = 0, rval = 0;
      level < XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK);
      level++) {
  len += maxrecs - 1;
  do_div(len, maxrecs);
  rval += len;
  if (len == 1)
   return rval + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) -
    level - 1;
  if (level == 0)
   maxrecs = mp->m_bmap_dmxr[1];
 }
 return rval;
}

/*
 * Calculate the default attribute fork offset for newly created inodes.
 */

uint
xfs_default_attroffset(
 struct xfs_inode *ip)
{
 if (ip->i_df.if_format == XFS_DINODE_FMT_DEV)
  return roundup(sizeof(xfs_dev_t), 8);
 return M_IGEO(ip->i_mount)->attr_fork_offset;
}

/*
 * Helper routine to reset inode i_forkoff field when switching attribute fork
 * from local to extent format - we reset it where possible to make space
 * available for inline data fork extents.
 */

STATIC void
xfs_bmap_forkoff_reset(
 xfs_inode_t *ip,
 int  whichfork)
{
 if (whichfork == XFS_ATTR_FORK &&
     ip->i_df.if_format != XFS_DINODE_FMT_DEV &&
     ip->i_df.if_format != XFS_DINODE_FMT_BTREE) {
  uint dfl_forkoff = xfs_default_attroffset(ip) >> 3;

  if (dfl_forkoff > ip->i_forkoff)
   ip->i_forkoff = dfl_forkoff;
 }
}

static int
xfs_bmap_read_buf(
 struct xfs_mount *mp,  /* file system mount point */
 struct xfs_trans *tp,  /* transaction pointer */
 xfs_fsblock_t  fsbno,  /* file system block number */
 struct xfs_buf  **bpp)  /* buffer for fsbno */
{
 struct xfs_buf  *bp;  /* return value */
 int   error;

 if (!xfs_verify_fsbno(mp, fsbno))
  return -EFSCORRUPTED;
 error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
   XFS_FSB_TO_DADDR(mp, fsbno), mp->m_bsize, 0, &bp,
   &xfs_bmbt_buf_ops);
 if (!error) {
  xfs_buf_set_ref(bp, XFS_BMAP_BTREE_REF);
  *bpp = bp;
 }
 return error;
}

#ifdef DEBUG
STATIC struct xfs_buf *
xfs_bmap_get_bp(
 struct xfs_btree_cur *cur,
 xfs_fsblock_t  bno)
{
 struct xfs_log_item *lip;
 int   i;

 if (!cur)
  return NULL;

 for (i = 0; i < cur->bc_maxlevels; i++) {
  if (!cur->bc_levels[i].bp)
   break;
  if (xfs_buf_daddr(cur->bc_levels[i].bp) == bno)
   return cur->bc_levels[i].bp;
 }

 /* Chase down all the log items to see if the bp is there */
 list_for_each_entry(lip, &cur->bc_tp->t_items, li_trans) {
  struct xfs_buf_log_item *bip = (struct xfs_buf_log_item *)lip;

  if (bip->bli_item.li_type == XFS_LI_BUF &&
      xfs_buf_daddr(bip->bli_buf) == bno)
   return bip->bli_buf;
 }

 return NULL;
}

STATIC void
xfs_check_block(
 struct xfs_btree_block *block,
 xfs_mount_t  *mp,
 int   root,
 short   sz)
{
 int   i, j, dmxr;
 __be64   *pp, *thispa; /* pointer to block address */
 xfs_bmbt_key_t  *prevp, *keyp;

 ASSERT(be16_to_cpu(block->bb_level) > 0);

 prevp = NULL;
 for( i = 1; i <= xfs_btree_get_numrecs(block); i++) {
  dmxr = mp->m_bmap_dmxr[0];
  keyp = xfs_bmbt_key_addr(mp, block, i);

  if (prevp) {
   ASSERT(be64_to_cpu(prevp->br_startoff) <
          be64_to_cpu(keyp->br_startoff));
  }
  prevp = keyp;

  /*
 * Compare the block numbers to see if there are dups.
 */

  if (root)
   pp = xfs_bmap_broot_ptr_addr(mp, block, i, sz);
  else
   pp = xfs_bmbt_ptr_addr(mp, block, i, dmxr);

  for (j = i+1; j <= be16_to_cpu(block->bb_numrecs); j++) {
   if (root)
    thispa = xfs_bmap_broot_ptr_addr(mp, block, j, sz);
   else
    thispa = xfs_bmbt_ptr_addr(mp, block, j, dmxr);
   if (*thispa == *pp) {
    xfs_warn(mp, "%s: thispa(%d) == pp(%d) %lld",
     __func__, j, i,
     (unsigned long long)be64_to_cpu(*thispa));
    xfs_err(mp, "%s: ptrs are equal in node\n",
     __func__);
    xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
   }
  }
 }
}

/*
 * Check that the extents for the inode ip are in the right order in all
 * btree leaves. THis becomes prohibitively expensive for large extent count
 * files, so don't bother with inodes that have more than 10,000 extents in
 * them. The btree record ordering checks will still be done, so for such large
 * bmapbt constructs that is going to catch most corruptions.
 */

STATIC void
xfs_bmap_check_leaf_extents(
 struct xfs_btree_cur *cur, /* btree cursor or null */
 xfs_inode_t  *ip,  /* incore inode pointer */
 int   whichfork) /* data or attr fork */
{
 struct xfs_mount *mp = ip->i_mount;
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
 struct xfs_btree_block *block; /* current btree block */
 xfs_fsblock_t  bno; /* block # of "block" */
 struct xfs_buf  *bp; /* buffer for "block" */
 int   error; /* error return value */
 xfs_extnum_t  i=0, j; /* index into the extents list */
 int   level; /* btree level, for checking */
 __be64   *pp; /* pointer to block address */
 xfs_bmbt_rec_t  *ep; /* pointer to current extent */
 xfs_bmbt_rec_t  last = {0, 0}; /* last extent in prev block */
 xfs_bmbt_rec_t  *nextp; /* pointer to next extent */
 int   bp_release = 0;

 if (ifp->if_format != XFS_DINODE_FMT_BTREE)
  return;

 /* skip large extent count inodes */
 if (ip->i_df.if_nextents > 10000)
  return;

 bno = NULLFSBLOCK;
 block = ifp->if_broot;
 /*
 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
 */

 level = be16_to_cpu(block->bb_level);
 ASSERT(level > 0);
 xfs_check_block(block, mp, 1, ifp->if_broot_bytes);
 pp = xfs_bmap_broot_ptr_addr(mp, block, 1, ifp->if_broot_bytes);
 bno = be64_to_cpu(*pp);

 ASSERT(bno != NULLFSBLOCK);
 ASSERT(XFS_FSB_TO_AGNO(mp, bno) < mp->m_sb.sb_agcount);
 ASSERT(XFS_FSB_TO_AGBNO(mp, bno) < mp->m_sb.sb_agblocks);

 /*
 * Go down the tree until leaf level is reached, following the first
 * pointer (leftmost) at each level.
 */

 while (level-- > 0) {
  /* See if buf is in cur first */
  bp_release = 0;
  bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno));
  if (!bp) {
   bp_release = 1;
   error = xfs_bmap_read_buf(mp, NULL, bno, &bp);
   if (xfs_metadata_is_sick(error))
    xfs_btree_mark_sick(cur);
   if (error)
    goto error_norelse;
  }
  block = XFS_BUF_TO_BLOCK(bp);
  if (level == 0)
   break;

  /*
 * Check this block for basic sanity (increasing keys and
 * no duplicate blocks).
 */


  xfs_check_block(block, mp, 0, 0);
  pp = xfs_bmbt_ptr_addr(mp, block, 1, mp->m_bmap_dmxr[1]);
  bno = be64_to_cpu(*pp);
  if (XFS_IS_CORRUPT(mp, !xfs_verify_fsbno(mp, bno))) {
   xfs_btree_mark_sick(cur);
   error = -EFSCORRUPTED;
   goto error0;
  }
  if (bp_release) {
   bp_release = 0;
   xfs_trans_brelse(NULL, bp);
  }
 }

 /*
 * Here with bp and block set to the leftmost leaf node in the tree.
 */

 i = 0;

 /*
 * Loop over all leaf nodes checking that all extents are in the right order.
 */

 for (;;) {
  xfs_fsblock_t nextbno;
  xfs_extnum_t num_recs;


  num_recs = xfs_btree_get_numrecs(block);

  /*
 * Read-ahead the next leaf block, if any.
 */


  nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);

  /*
 * Check all the extents to make sure they are OK.
 * If we had a previous block, the last entry should
 * conform with the first entry in this one.
 */


  ep = xfs_bmbt_rec_addr(mp, block, 1);
  if (i) {
   ASSERT(xfs_bmbt_disk_get_startoff(&last) +
          xfs_bmbt_disk_get_blockcount(&last) <=
          xfs_bmbt_disk_get_startoff(ep));
  }
  for (j = 1; j < num_recs; j++) {
   nextp = xfs_bmbt_rec_addr(mp, block, j + 1);
   ASSERT(xfs_bmbt_disk_get_startoff(ep) +
          xfs_bmbt_disk_get_blockcount(ep) <=
          xfs_bmbt_disk_get_startoff(nextp));
   ep = nextp;
  }

  last = *ep;
  i += num_recs;
  if (bp_release) {
   bp_release = 0;
   xfs_trans_brelse(NULL, bp);
  }
  bno = nextbno;
  /*
 * If we've reached the end, stop.
 */

  if (bno == NULLFSBLOCK)
   break;

  bp_release = 0;
  bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno));
  if (!bp) {
   bp_release = 1;
   error = xfs_bmap_read_buf(mp, NULL, bno, &bp);
   if (xfs_metadata_is_sick(error))
    xfs_btree_mark_sick(cur);
   if (error)
    goto error_norelse;
  }
  block = XFS_BUF_TO_BLOCK(bp);
 }

 return;

error0:
 xfs_warn(mp, "%s: at error0", __func__);
 if (bp_release)
  xfs_trans_brelse(NULL, bp);
error_norelse:
 xfs_warn(mp, "%s: BAD after btree leaves for %llu extents",
  __func__, i);
 xfs_err(mp, "%s: CORRUPTED BTREE OR SOMETHING", __func__);
 xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
 return;
}

/*
 * Validate that the bmbt_irecs being returned from bmapi are valid
 * given the caller's original parameters.  Specifically check the
 * ranges of the returned irecs to ensure that they only extend beyond
 * the given parameters if the XFS_BMAPI_ENTIRE flag was set.
 */

STATIC void
xfs_bmap_validate_ret(
 xfs_fileoff_t  bno,
 xfs_filblks_t  len,
 uint32_t  flags,
 xfs_bmbt_irec_t  *mval,
 int   nmap,
 int   ret_nmap)
{
 int   i;  /* index to map values */

 ASSERT(ret_nmap <= nmap);

 for (i = 0; i < ret_nmap; i++) {
  ASSERT(mval[i].br_blockcount > 0);
  if (!(flags & XFS_BMAPI_ENTIRE)) {
   ASSERT(mval[i].br_startoff >= bno);
   ASSERT(mval[i].br_blockcount <= len);
   ASSERT(mval[i].br_startoff + mval[i].br_blockcount <=
          bno + len);
  } else {
   ASSERT(mval[i].br_startoff < bno + len);
   ASSERT(mval[i].br_startoff + mval[i].br_blockcount >
          bno);
  }
  ASSERT(i == 0 ||
         mval[i - 1].br_startoff + mval[i - 1].br_blockcount ==
         mval[i].br_startoff);
  ASSERT(mval[i].br_startblock != DELAYSTARTBLOCK &&
         mval[i].br_startblock != HOLESTARTBLOCK);
  ASSERT(mval[i].br_state == XFS_EXT_NORM ||
         mval[i].br_state == XFS_EXT_UNWRITTEN);
 }
}

#else
#define xfs_bmap_check_leaf_extents(cur, ip, whichfork)  do { } while (0)
#define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap) do { } while (0)
#endif /* DEBUG */

/*
 * Inode fork format manipulation functions
 */


/*
 * Convert the inode format to extent format if it currently is in btree format,
 * but the extent list is small enough that it fits into the extent format.
 *
 * Since the extents are already in-core, all we have to do is give up the space
 * for the btree root and pitch the leaf block.
 */

STATIC int    /* error */
xfs_bmap_btree_to_extents(
 struct xfs_trans *tp, /* transaction pointer */
 struct xfs_inode *ip, /* incore inode pointer */
 struct xfs_btree_cur *cur, /* btree cursor */
 int   *logflagsp, /* inode logging flags */
 int   whichfork)  /* data or attr fork */
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
 struct xfs_mount *mp = ip->i_mount;
 struct xfs_btree_block *rblock = ifp->if_broot;
 struct xfs_btree_block *cblock;/* child btree block */
 xfs_fsblock_t  cbno; /* child block number */
 struct xfs_buf  *cbp; /* child block's buffer */
 int   error; /* error return value */
 __be64   *pp; /* ptr to block address */
 struct xfs_owner_info oinfo;

 /* check if we actually need the extent format first: */
 if (!xfs_bmap_wants_extents(ip, whichfork))
  return 0;

 ASSERT(cur);
 ASSERT(whichfork != XFS_COW_FORK);
 ASSERT(ifp->if_format == XFS_DINODE_FMT_BTREE);
 ASSERT(be16_to_cpu(rblock->bb_level) == 1);
 ASSERT(be16_to_cpu(rblock->bb_numrecs) == 1);
 ASSERT(xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, false) == 1);

 pp = xfs_bmap_broot_ptr_addr(mp, rblock, 1, ifp->if_broot_bytes);
 cbno = be64_to_cpu(*pp);
#ifdef DEBUG
 if (XFS_IS_CORRUPT(cur->bc_mp, !xfs_verify_fsbno(mp, cbno))) {
  xfs_btree_mark_sick(cur);
  return -EFSCORRUPTED;
 }
#endif
 error = xfs_bmap_read_buf(mp, tp, cbno, &cbp);
 if (xfs_metadata_is_sick(error))
  xfs_btree_mark_sick(cur);
 if (error)
  return error;
 cblock = XFS_BUF_TO_BLOCK(cbp);
 if ((error = xfs_btree_check_block(cur, cblock, 0, cbp)))
  return error;

 xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, whichfork);
 error = xfs_free_extent_later(cur->bc_tp, cbno, 1, &oinfo,
   XFS_AG_RESV_NONE, 0);
 if (error)
  return error;

 ip->i_nblocks--;
 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L);
 xfs_trans_binval(tp, cbp);
 if (cur->bc_levels[0].bp == cbp)
  cur->bc_levels[0].bp = NULL;
 xfs_bmap_broot_realloc(ip, whichfork, 0);
 ASSERT(ifp->if_broot == NULL);
 ifp->if_format = XFS_DINODE_FMT_EXTENTS;
 *logflagsp |= XFS_ILOG_CORE | xfs_ilog_fext(whichfork);
 return 0;
}

/*
 * Convert an extents-format file into a btree-format file.
 * The new file will have a root block (in the inode) and a single child block.
 */

STATIC int     /* error */
xfs_bmap_extents_to_btree(
 struct xfs_trans *tp,  /* transaction pointer */
 struct xfs_inode *ip,  /* incore inode pointer */
 struct xfs_btree_cur **curp,  /* cursor returned to caller */
 int   wasdel,  /* converting a delayed alloc */
 int   *logflagsp, /* inode logging flags */
 int   whichfork) /* data or attr fork */
{
 struct xfs_btree_block *ablock; /* allocated (child) bt block */
 struct xfs_buf  *abp;  /* buffer for ablock */
 struct xfs_alloc_arg args;  /* allocation arguments */
 struct xfs_bmbt_rec *arp;  /* child record pointer */
 struct xfs_btree_block *block;  /* btree root block */
 struct xfs_btree_cur *cur;  /* bmap btree cursor */
 int   error;  /* error return value */
 struct xfs_ifork *ifp;  /* inode fork pointer */
 struct xfs_bmbt_key *kp;  /* root block key pointer */
 struct xfs_mount *mp;  /* mount structure */
 xfs_bmbt_ptr_t  *pp;  /* root block address pointer */
 struct xfs_iext_cursor icur;
 struct xfs_bmbt_irec rec;
 xfs_extnum_t  cnt = 0;

 mp = ip->i_mount;
 ASSERT(whichfork != XFS_COW_FORK);
 ifp = xfs_ifork_ptr(ip, whichfork);
 ASSERT(ifp->if_format == XFS_DINODE_FMT_EXTENTS);

 /*
 * Make space in the inode incore. This needs to be undone if we fail
 * to expand the root.
 */

 block = xfs_bmap_broot_realloc(ip, whichfork, 1);

 /*
 * Fill in the root.
 */

 xfs_bmbt_init_block(ip, block, NULL, 1, 1);
 /*
 * Need a cursor.  Can't allocate until bb_level is filled in.
 */

 cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
 if (wasdel)
  cur->bc_flags |= XFS_BTREE_BMBT_WASDEL;
 /*
 * Convert to a btree with two levels, one record in root.
 */

 ifp->if_format = XFS_DINODE_FMT_BTREE;
 memset(&args, 0, sizeof(args));
 args.tp = tp;
 args.mp = mp;
 xfs_rmap_ino_bmbt_owner(&args.oinfo, ip->i_ino, whichfork);

 args.minlen = args.maxlen = args.prod = 1;
 args.wasdel = wasdel;
 *logflagsp = 0;
 error = xfs_alloc_vextent_start_ag(&args,
    XFS_INO_TO_FSB(mp, ip->i_ino));
 if (error)
  goto out_root_realloc;

 /*
 * Allocation can't fail, the space was reserved.
 */

 if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) {
  error = -ENOSPC;
  goto out_root_realloc;
 }

 cur->bc_bmap.allocated++;
 ip->i_nblocks++;
 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, 1L);
 error = xfs_trans_get_buf(tp, mp->m_ddev_targp,
   XFS_FSB_TO_DADDR(mp, args.fsbno),
   mp->m_bsize, 0, &abp);
 if (error)
  goto out_unreserve_dquot;

 /*
 * Fill in the child block.
 */

 ablock = XFS_BUF_TO_BLOCK(abp);
 xfs_bmbt_init_block(ip, ablock, abp, 0, 0);

 for_each_xfs_iext(ifp, &icur, &rec) {
  if (isnullstartblock(rec.br_startblock))
   continue;
  arp = xfs_bmbt_rec_addr(mp, ablock, 1 + cnt);
  xfs_bmbt_disk_set_all(arp, &rec);
  cnt++;
 }
 ASSERT(cnt == ifp->if_nextents);
 xfs_btree_set_numrecs(ablock, cnt);

 /*
 * Fill in the root key and pointer.
 */

 kp = xfs_bmbt_key_addr(mp, block, 1);
 arp = xfs_bmbt_rec_addr(mp, ablock, 1);
 kp->br_startoff = cpu_to_be64(xfs_bmbt_disk_get_startoff(arp));
 pp = xfs_bmbt_ptr_addr(mp, block, 1, xfs_bmbt_get_maxrecs(cur,
      be16_to_cpu(block->bb_level)));
 *pp = cpu_to_be64(args.fsbno);

 /*
 * Do all this logging at the end so that
 * the root is at the right level.
 */

 xfs_btree_log_block(cur, abp, XFS_BB_ALL_BITS);
 xfs_btree_log_recs(cur, abp, 1, be16_to_cpu(ablock->bb_numrecs));
 ASSERT(*curp == NULL);
 *curp = cur;
 *logflagsp = XFS_ILOG_CORE | xfs_ilog_fbroot(whichfork);
 return 0;

out_unreserve_dquot:
 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L);
out_root_realloc:
 xfs_bmap_broot_realloc(ip, whichfork, 0);
 ifp->if_format = XFS_DINODE_FMT_EXTENTS;
 ASSERT(ifp->if_broot == NULL);
 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);

 return error;
}

/*
 * Convert a local file to an extents file.
 * This code is out of bounds for data forks of regular files,
 * since the file data needs to get logged so things will stay consistent.
 * (The bmap-level manipulations are ok, though).
 */

void
xfs_bmap_local_to_extents_empty(
 struct xfs_trans *tp,
 struct xfs_inode *ip,
 int   whichfork)
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);

 ASSERT(whichfork != XFS_COW_FORK);
 ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
 ASSERT(ifp->if_bytes == 0);
 ASSERT(ifp->if_nextents == 0);

 xfs_bmap_forkoff_reset(ip, whichfork);
 ifp->if_data = NULL;
 ifp->if_height = 0;
 ifp->if_format = XFS_DINODE_FMT_EXTENTS;
 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}


int     /* error */
xfs_bmap_local_to_extents(
 xfs_trans_t *tp,  /* transaction pointer */
 xfs_inode_t *ip,  /* incore inode pointer */
 xfs_extlen_t total,  /* total blocks needed by transaction */
 int  *logflagsp, /* inode logging flags */
 int  whichfork,
 void  (*init_fn)(struct xfs_trans *tp,
       struct xfs_buf *bp,
       struct xfs_inode *ip,
       struct xfs_ifork *ifp, void *priv),
 void  *priv)
{
 int  error = 0;
 int  flags;  /* logging flags returned */
 struct xfs_ifork *ifp;  /* inode fork pointer */
 xfs_alloc_arg_t args;  /* allocation arguments */
 struct xfs_buf *bp;  /* buffer for extent block */
 struct xfs_bmbt_irec rec;
 struct xfs_iext_cursor icur;

 /*
 * We don't want to deal with the case of keeping inode data inline yet.
 * So sending the data fork of a regular inode is invalid.
 */

 ASSERT(!(S_ISREG(VFS_I(ip)->i_mode) && whichfork == XFS_DATA_FORK));
 ifp = xfs_ifork_ptr(ip, whichfork);
 ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);

 if (!ifp->if_bytes) {
  xfs_bmap_local_to_extents_empty(tp, ip, whichfork);
  flags = XFS_ILOG_CORE;
  goto done;
 }

 flags = 0;
 error = 0;
 memset(&args, 0, sizeof(args));
 args.tp = tp;
 args.mp = ip->i_mount;
 args.total = total;
 args.minlen = args.maxlen = args.prod = 1;
 xfs_rmap_ino_owner(&args.oinfo, ip->i_ino, whichfork, 0);

 /*
 * Allocate a block.  We know we need only one, since the
 * file currently fits in an inode.
 */

 args.total = total;
 args.minlen = args.maxlen = args.prod = 1;
 error = xfs_alloc_vextent_start_ag(&args,
   XFS_INO_TO_FSB(args.mp, ip->i_ino));
 if (error)
  goto done;

 /* Can't fail, the space was reserved. */
 ASSERT(args.fsbno != NULLFSBLOCK);
 ASSERT(args.len == 1);
 error = xfs_trans_get_buf(tp, args.mp->m_ddev_targp,
   XFS_FSB_TO_DADDR(args.mp, args.fsbno),
   args.mp->m_bsize, 0, &bp);
 if (error)
  goto done;

 /*
 * Initialize the block, copy the data and log the remote buffer.
 *
 * The callout is responsible for logging because the remote format
 * might differ from the local format and thus we don't know how much to
 * log here. Note that init_fn must also set the buffer log item type
 * correctly.
 */

 init_fn(tp, bp, ip, ifp, priv);

 /* account for the change in fork size */
 xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
 xfs_bmap_local_to_extents_empty(tp, ip, whichfork);
 flags |= XFS_ILOG_CORE;

 ifp->if_data = NULL;
 ifp->if_height = 0;

 rec.br_startoff = 0;
 rec.br_startblock = args.fsbno;
 rec.br_blockcount = 1;
 rec.br_state = XFS_EXT_NORM;
 xfs_iext_first(ifp, &icur);
 xfs_iext_insert(ip, &icur, &rec, 0);

 ifp->if_nextents = 1;
 ip->i_nblocks = 1;
 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, 1L);
 flags |= xfs_ilog_fext(whichfork);

done:
 *logflagsp = flags;
 return error;
}

/*
 * Called from xfs_bmap_add_attrfork to handle btree format files.
 */

STATIC int     /* error */
xfs_bmap_add_attrfork_btree(
 xfs_trans_t  *tp,  /* transaction pointer */
 xfs_inode_t  *ip,  /* incore inode pointer */
 int   *flags)  /* inode logging flags */
{
 struct xfs_btree_block *block = ip->i_df.if_broot;
 struct xfs_btree_cur *cur;  /* btree cursor */
 int   error;  /* error return value */
 xfs_mount_t  *mp;  /* file system mount struct */
 int   stat;  /* newroot status */

 mp = ip->i_mount;

 if (xfs_bmap_bmdr_space(block) <= xfs_inode_data_fork_size(ip))
  *flags |= XFS_ILOG_DBROOT;
 else {
  cur = xfs_bmbt_init_cursor(mp, tp, ip, XFS_DATA_FORK);
  error = xfs_bmbt_lookup_first(cur, &stat);
  if (error)
   goto error0;
  /* must be at least one entry */
  if (XFS_IS_CORRUPT(mp, stat != 1)) {
   xfs_btree_mark_sick(cur);
   error = -EFSCORRUPTED;
   goto error0;
  }
  if ((error = xfs_btree_new_iroot(cur, flags, &stat)))
   goto error0;
  if (stat == 0) {
   xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
   return -ENOSPC;
  }
  cur->bc_bmap.allocated = 0;
  xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
 }
 return 0;
error0:
 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
 return error;
}

/*
 * Called from xfs_bmap_add_attrfork to handle extents format files.
 */

STATIC int     /* error */
xfs_bmap_add_attrfork_extents(
 struct xfs_trans *tp,  /* transaction pointer */
 struct xfs_inode *ip,  /* incore inode pointer */
 int   *flags)  /* inode logging flags */
{
 struct xfs_btree_cur *cur;  /* bmap btree cursor */
 int   error;  /* error return value */

 if (ip->i_df.if_nextents * sizeof(struct xfs_bmbt_rec) <=
     xfs_inode_data_fork_size(ip))
  return 0;
 cur = NULL;
 error = xfs_bmap_extents_to_btree(tp, ip, &cur, 0, flags,
       XFS_DATA_FORK);
 if (cur) {
  cur->bc_bmap.allocated = 0;
  xfs_btree_del_cursor(cur, error);
 }
 return error;
}

/*
 * Called from xfs_bmap_add_attrfork to handle local format files. Each
 * different data fork content type needs a different callout to do the
 * conversion. Some are basic and only require special block initialisation
 * callouts for the data formating, others (directories) are so specialised they
 * handle everything themselves.
 *
 * XXX (dgc): investigate whether directory conversion can use the generic
 * formatting callout. It should be possible - it's just a very complex
 * formatter.
 */

STATIC int     /* error */
xfs_bmap_add_attrfork_local(
 struct xfs_trans *tp,  /* transaction pointer */
 struct xfs_inode *ip,  /* incore inode pointer */
 int   *flags)  /* inode logging flags */
{
 struct xfs_da_args dargs;  /* args for dir/attr code */

 if (ip->i_df.if_bytes <= xfs_inode_data_fork_size(ip))
  return 0;

 if (S_ISDIR(VFS_I(ip)->i_mode)) {
  memset(&dargs, 0, sizeof(dargs));
  dargs.geo = ip->i_mount->m_dir_geo;
  dargs.dp = ip;
  dargs.total = dargs.geo->fsbcount;
  dargs.whichfork = XFS_DATA_FORK;
  dargs.trans = tp;
  dargs.owner = ip->i_ino;
  return xfs_dir2_sf_to_block(&dargs);
 }

 if (S_ISLNK(VFS_I(ip)->i_mode))
  return xfs_bmap_local_to_extents(tp, ip, 1, flags,
    XFS_DATA_FORK, xfs_symlink_local_to_remote,
    NULL);

 /* should only be called for types that support local format data */
 ASSERT(0);
 xfs_bmap_mark_sick(ip, XFS_ATTR_FORK);
 return -EFSCORRUPTED;
}

/*
 * Set an inode attr fork offset based on the format of the data fork.
 */

static int
xfs_bmap_set_attrforkoff(
 struct xfs_inode *ip,
 int   size,
 int   *version)
{
 int   default_size = xfs_default_attroffset(ip) >> 3;

 switch (ip->i_df.if_format) {
 case XFS_DINODE_FMT_DEV:
  ip->i_forkoff = default_size;
  break;
 case XFS_DINODE_FMT_LOCAL:
 case XFS_DINODE_FMT_EXTENTS:
 case XFS_DINODE_FMT_BTREE:
  ip->i_forkoff = xfs_attr_shortform_bytesfit(ip, size);
  if (!ip->i_forkoff)
   ip->i_forkoff = default_size;
  else if (xfs_has_attr2(ip->i_mount) && version)
   *version = 2;
  break;
 default:
  ASSERT(0);
  return -EINVAL;
 }

 return 0;
}

/*
 * Convert inode from non-attributed to attributed.  Caller must hold the
 * ILOCK_EXCL and the file cannot have an attr fork.
 */

int      /* error code */
xfs_bmap_add_attrfork(
 struct xfs_trans *tp,
 struct xfs_inode *ip,  /* incore inode pointer */
 int   size,  /* space new attribute needs */
 int   rsvd)  /* xact may use reserved blks */
{
 struct xfs_mount *mp = tp->t_mountp;
 int   version = 1; /* superblock attr version */
 int   logflags; /* logging flags */
 int   error;  /* error return value */

 xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
 if (!xfs_is_metadir_inode(ip))
  ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
 ASSERT(!xfs_inode_has_attr_fork(ip));

 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 error = xfs_bmap_set_attrforkoff(ip, size, &version);
 if (error)
  return error;

 xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0);
 logflags = 0;
 switch (ip->i_df.if_format) {
 case XFS_DINODE_FMT_LOCAL:
  error = xfs_bmap_add_attrfork_local(tp, ip, &logflags);
  break;
 case XFS_DINODE_FMT_EXTENTS:
  error = xfs_bmap_add_attrfork_extents(tp, ip, &logflags);
  break;
 case XFS_DINODE_FMT_BTREE:
  error = xfs_bmap_add_attrfork_btree(tp, ip, &logflags);
  break;
 default:
  error = 0;
  break;
 }
 if (logflags)
  xfs_trans_log_inode(tp, ip, logflags);
 if (error)
  return error;
 if (!xfs_has_attr(mp) ||
    (!xfs_has_attr2(mp) && version == 2)) {
  bool log_sb = false;

  spin_lock(&mp->m_sb_lock);
  if (!xfs_has_attr(mp)) {
   xfs_add_attr(mp);
   log_sb = true;
  }
  if (!xfs_has_attr2(mp) && version == 2) {
   xfs_add_attr2(mp);
   log_sb = true;
  }
  spin_unlock(&mp->m_sb_lock);
  if (log_sb)
   xfs_log_sb(tp);
 }

 return 0;
}

/*
 * Internal and external extent tree search functions.
 */


struct xfs_iread_state {
 struct xfs_iext_cursor icur;
 xfs_extnum_t  loaded;
};

int
xfs_bmap_complain_bad_rec(
 struct xfs_inode  *ip,
 int    whichfork,
 xfs_failaddr_t   fa,
 const struct xfs_bmbt_irec *irec)
{
 struct xfs_mount  *mp = ip->i_mount;
 const char   *forkname;

 switch (whichfork) {
 case XFS_DATA_FORK: forkname = "data"break;
 case XFS_ATTR_FORK: forkname = "attr"break;
 case XFS_COW_FORK: forkname = "CoW"break;
 default:  forkname = "???"break;
 }

 xfs_warn(mp,
 "Bmap BTree record corruption in inode 0x%llx %s fork detected at %pS!",
    ip->i_ino, forkname, fa);
 xfs_warn(mp,
  "Offset 0x%llx, start block 0x%llx, block count 0x%llx state 0x%x",
  irec->br_startoff, irec->br_startblock, irec->br_blockcount,
  irec->br_state);

 return -EFSCORRUPTED;
}

/* Stuff every bmbt record from this block into the incore extent map. */
static int
xfs_iread_bmbt_block(
 struct xfs_btree_cur *cur,
 int   level,
 void   *priv)
{
 struct xfs_iread_state *ir = priv;
 struct xfs_mount *mp = cur->bc_mp;
 struct xfs_inode *ip = cur->bc_ino.ip;
 struct xfs_btree_block *block;
 struct xfs_buf  *bp;
 struct xfs_bmbt_rec *frp;
 xfs_extnum_t  num_recs;
 xfs_extnum_t  j;
 int   whichfork = cur->bc_ino.whichfork;
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);

 block = xfs_btree_get_block(cur, level, &bp);

 /* Abort if we find more records than nextents. */
 num_recs = xfs_btree_get_numrecs(block);
 if (unlikely(ir->loaded + num_recs > ifp->if_nextents)) {
  xfs_warn(ip->i_mount, "corrupt dinode %llu, (btree extents).",
    (unsigned long long)ip->i_ino);
  xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, block,
    sizeof(*block), __this_address);
  xfs_bmap_mark_sick(ip, whichfork);
  return -EFSCORRUPTED;
 }

 /* Copy records into the incore cache. */
 frp = xfs_bmbt_rec_addr(mp, block, 1);
 for (j = 0; j < num_recs; j++, frp++, ir->loaded++) {
  struct xfs_bmbt_irec new;
  xfs_failaddr_t  fa;

  xfs_bmbt_disk_get_all(frp, &new);
  fa = xfs_bmap_validate_extent(ip, whichfork, &new);
  if (fa) {
   xfs_inode_verifier_error(ip, -EFSCORRUPTED,
     "xfs_iread_extents(2)", frp,
     sizeof(*frp), fa);
   xfs_bmap_mark_sick(ip, whichfork);
   return xfs_bmap_complain_bad_rec(ip, whichfork, fa,
     &new);
  }
  xfs_iext_insert(ip, &ir->icur, &new,
    xfs_bmap_fork_to_state(whichfork));
  trace_xfs_read_extent(ip, &ir->icur,
    xfs_bmap_fork_to_state(whichfork), _THIS_IP_);
  xfs_iext_next(ifp, &ir->icur);
 }

 return 0;
}

/*
 * Read in extents from a btree-format inode.
 */

int
xfs_iread_extents(
 struct xfs_trans *tp,
 struct xfs_inode *ip,
 int   whichfork)
{
 struct xfs_iread_state ir;
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
 struct xfs_mount *mp = ip->i_mount;
 struct xfs_btree_cur *cur;
 int   error;

 if (!xfs_need_iread_extents(ifp))
  return 0;

 xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);

 ir.loaded = 0;
 xfs_iext_first(ifp, &ir.icur);
 cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
 error = xfs_btree_visit_blocks(cur, xfs_iread_bmbt_block,
   XFS_BTREE_VISIT_RECORDS, &ir);
 xfs_btree_del_cursor(cur, error);
 if (error)
  goto out;

 if (XFS_IS_CORRUPT(mp, ir.loaded != ifp->if_nextents)) {
  xfs_bmap_mark_sick(ip, whichfork);
  error = -EFSCORRUPTED;
  goto out;
 }
 ASSERT(ir.loaded == xfs_iext_count(ifp));
 /*
 * Use release semantics so that we can use acquire semantics in
 * xfs_need_iread_extents and be guaranteed to see a valid mapping tree
 * after that load.
 */

 smp_store_release(&ifp->if_needextents, 0);
 return 0;
out:
 if (xfs_metadata_is_sick(error))
  xfs_bmap_mark_sick(ip, whichfork);
 xfs_iext_destroy(ifp);
 return error;
}

/*
 * Returns the relative block number of the first unused block(s) in the given
 * fork with at least "len" logically contiguous blocks free.  This is the
 * lowest-address hole if the fork has holes, else the first block past the end
 * of fork.  Return 0 if the fork is currently local (in-inode).
 */

int      /* error */
xfs_bmap_first_unused(
 struct xfs_trans *tp,  /* transaction pointer */
 struct xfs_inode *ip,  /* incore inode */
 xfs_extlen_t  len,  /* size of hole to find */
 xfs_fileoff_t  *first_unused, /* unused block */
 int   whichfork) /* data or attr fork */
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
 struct xfs_bmbt_irec got;
 struct xfs_iext_cursor icur;
 xfs_fileoff_t  lastaddr = 0;
 xfs_fileoff_t  lowest, max;
 int   error;

 if (ifp->if_format == XFS_DINODE_FMT_LOCAL) {
  *first_unused = 0;
  return 0;
 }

 ASSERT(xfs_ifork_has_extents(ifp));

 error = xfs_iread_extents(tp, ip, whichfork);
 if (error)
  return error;

 lowest = max = *first_unused;
 for_each_xfs_iext(ifp, &icur, &got) {
  /*
 * See if the hole before this extent will work.
 */

  if (got.br_startoff >= lowest + len &&
      got.br_startoff - max >= len)
   break;
  lastaddr = got.br_startoff + got.br_blockcount;
  max = XFS_FILEOFF_MAX(lastaddr, lowest);
 }

 *first_unused = max;
 return 0;
}

/*
 * Returns the file-relative block number of the last block - 1 before
 * last_block (input value) in the file.
 * This is not based on i_size, it is based on the extent records.
 * Returns 0 for local files, as they do not have extent records.
 */

int      /* error */
xfs_bmap_last_before(
 struct xfs_trans *tp,  /* transaction pointer */
 struct xfs_inode *ip,  /* incore inode */
 xfs_fileoff_t  *last_block, /* last block */
 int   whichfork) /* data or attr fork */
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
 struct xfs_bmbt_irec got;
 struct xfs_iext_cursor icur;
 int   error;

 switch (ifp->if_format) {
 case XFS_DINODE_FMT_LOCAL:
  *last_block = 0;
  return 0;
 case XFS_DINODE_FMT_BTREE:
 case XFS_DINODE_FMT_EXTENTS:
  break;
 default:
  ASSERT(0);
  xfs_bmap_mark_sick(ip, whichfork);
  return -EFSCORRUPTED;
 }

 error = xfs_iread_extents(tp, ip, whichfork);
 if (error)
  return error;

 if (!xfs_iext_lookup_extent_before(ip, ifp, last_block, &icur, &got))
  *last_block = 0;
 return 0;
}

int
xfs_bmap_last_extent(
 struct xfs_trans *tp,
 struct xfs_inode *ip,
 int   whichfork,
 struct xfs_bmbt_irec *rec,
 int   *is_empty)
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
 struct xfs_iext_cursor icur;
 int   error;

 error = xfs_iread_extents(tp, ip, whichfork);
 if (error)
  return error;

 xfs_iext_last(ifp, &icur);
 if (!xfs_iext_get_extent(ifp, &icur, rec))
  *is_empty = 1;
 else
  *is_empty = 0;
 return 0;
}

/*
 * Check the last inode extent to determine whether this allocation will result
 * in blocks being allocated at the end of the file. When we allocate new data
 * blocks at the end of the file which do not start at the previous data block,
 * we will try to align the new blocks at stripe unit boundaries.
 *
 * Returns 1 in bma->aeof if the file (fork) is empty as any new write will be
 * at, or past the EOF.
 */

STATIC int
xfs_bmap_isaeof(
 struct xfs_bmalloca *bma,
 int   whichfork)
{
 struct xfs_bmbt_irec rec;
 int   is_empty;
 int   error;

 bma->aeof = false;
 error = xfs_bmap_last_extent(NULL, bma->ip, whichfork, &rec,
         &is_empty);
 if (error)
  return error;

 if (is_empty) {
  bma->aeof = true;
  return 0;
 }

 /*
 * Check if we are allocation or past the last extent, or at least into
 * the last delayed allocated extent.
 */

 bma->aeof = bma->offset >= rec.br_startoff + rec.br_blockcount ||
  (bma->offset >= rec.br_startoff &&
   isnullstartblock(rec.br_startblock));
 return 0;
}

/*
 * Returns the file-relative block number of the first block past eof in
 * the file.  This is not based on i_size, it is based on the extent records.
 * Returns 0 for local files, as they do not have extent records.
 */

int
xfs_bmap_last_offset(
 struct xfs_inode *ip,
 xfs_fileoff_t  *last_block,
 int   whichfork)
{
 struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
 struct xfs_bmbt_irec rec;
 int   is_empty;
 int   error;

 *last_block = 0;

 if (ifp->if_format == XFS_DINODE_FMT_LOCAL)
  return 0;

 if (XFS_IS_CORRUPT(ip->i_mount, !xfs_ifork_has_extents(ifp))) {
  xfs_bmap_mark_sick(ip, whichfork);
  return -EFSCORRUPTED;
 }

 error = xfs_bmap_last_extent(NULL, ip, whichfork, &rec, &is_empty);
 if (error || is_empty)
  return error;

 *last_block = rec.br_startoff + rec.br_blockcount;
 return 0;
}

/*
 * Extent tree manipulation functions used during allocation.
 */


static inline bool
xfs_bmap_same_rtgroup(
 struct xfs_inode *ip,
 int   whichfork,
 struct xfs_bmbt_irec *left,
 struct xfs_bmbt_irec *right)
{
 struct xfs_mount *mp = ip->i_mount;

 if (xfs_ifork_is_realtime(ip, whichfork) && xfs_has_rtgroups(mp)) {
  if (xfs_rtb_to_rgno(mp, left->br_startblock) !=
      xfs_rtb_to_rgno(mp, right->br_startblock))
   return false;
 }

 return true;
}

/*
 * Convert a delayed allocation to a real allocation.
 */

STATIC int    /* error */
xfs_bmap_add_extent_delay_real(
 struct xfs_bmalloca *bma,
 int   whichfork)
{
 struct xfs_mount *mp = bma->ip->i_mount;
 struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork);
 struct xfs_bmbt_irec *new = &bma->got;
 int   error; /* error return value */
 int   i; /* temp state */
 xfs_fileoff_t  new_endoff; /* end offset of new entry */
 xfs_bmbt_irec_t  r[3]; /* neighbor extent entries */
     /* left is 0, right is 1, prev is 2 */
 int   rval=0; /* return value (logging flags) */
 uint32_t  state = xfs_bmap_fork_to_state(whichfork);
 xfs_filblks_t  da_new; /* new count del alloc blocks used */
 xfs_filblks_t  da_old; /* old count del alloc blocks used */
 xfs_filblks_t  temp=0; /* value for da_new calculations */
 int   tmp_rval; /* partial logging flags */
 struct xfs_bmbt_irec old;

 ASSERT(whichfork != XFS_ATTR_FORK);
 ASSERT(!isnullstartblock(new->br_startblock));
 ASSERT(!bma->cur || (bma->cur->bc_flags & XFS_BTREE_BMBT_WASDEL));

 XFS_STATS_INC(mp, xs_add_exlist);

#define LEFT  r[0]
#define RIGHT  r[1]
#define PREV  r[2]

 /*
 * Set up a bunch of variables to make the tests simpler.
 */

 xfs_iext_get_extent(ifp, &bma->icur, &PREV);
 new_endoff = new->br_startoff + new->br_blockcount;
 ASSERT(isnullstartblock(PREV.br_startblock));
 ASSERT(PREV.br_startoff <= new->br_startoff);
 ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);

 da_old = startblockval(PREV.br_startblock);
 da_new = 0;

 /*
 * Set flags determining what part of the previous delayed allocation
 * extent is being replaced by a real allocation.
 */

 if (PREV.br_startoff == new->br_startoff)
  state |= BMAP_LEFT_FILLING;
 if (PREV.br_startoff + PREV.br_blockcount == new_endoff)
  state |= BMAP_RIGHT_FILLING;

 /*
 * Check and set flags if this segment has a left neighbor.
 * Don't set contiguous if the combined extent would be too large.
 */

 if (xfs_iext_peek_prev_extent(ifp, &bma->icur, &LEFT)) {
  state |= BMAP_LEFT_VALID;
  if (isnullstartblock(LEFT.br_startblock))
   state |= BMAP_LEFT_DELAY;
 }

 if ((state & BMAP_LEFT_VALID) && !(state & BMAP_LEFT_DELAY) &&
     LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff &&
     LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock &&
     LEFT.br_state == new->br_state &&
     LEFT.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN &&
     xfs_bmap_same_rtgroup(bma->ip, whichfork, &LEFT, new))
  state |= BMAP_LEFT_CONTIG;

 /*
 * Check and set flags if this segment has a right neighbor.
 * Don't set contiguous if the combined extent would be too large.
 * Also check for all-three-contiguous being too large.
 */

 if (xfs_iext_peek_next_extent(ifp, &bma->icur, &RIGHT)) {
  state |= BMAP_RIGHT_VALID;
  if (isnullstartblock(RIGHT.br_startblock))
   state |= BMAP_RIGHT_DELAY;
 }

 if ((state & BMAP_RIGHT_VALID) && !(state & BMAP_RIGHT_DELAY) &&
     new_endoff == RIGHT.br_startoff &&
     new->br_startblock + new->br_blockcount == RIGHT.br_startblock &&
     new->br_state == RIGHT.br_state &&
     new->br_blockcount + RIGHT.br_blockcount <= XFS_MAX_BMBT_EXTLEN &&
     ((state & (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING |
         BMAP_RIGHT_FILLING)) !=
        (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING |
         BMAP_RIGHT_FILLING) ||
      LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount
   <= XFS_MAX_BMBT_EXTLEN) &&
     xfs_bmap_same_rtgroup(bma->ip, whichfork, new, &RIGHT))
  state |= BMAP_RIGHT_CONTIG;

 error = 0;
 /*
 * Switch out based on the FILLING and CONTIG state bits.
 */

 switch (state & (BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
    BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG)) {
 case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
      BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
  /*
 * Filling in all of a previously delayed allocation extent.
 * The left and right neighbors are both contiguous with new.
 */

  LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;

  xfs_iext_remove(bma->ip, &bma->icur, state);
  xfs_iext_remove(bma->ip, &bma->icur, state);
  xfs_iext_prev(ifp, &bma->icur);
  xfs_iext_update_extent(bma->ip, state, &bma->icur, &LEFT);
  ifp->if_nextents--;

  if (bma->cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_btree_delete(bma->cur, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_btree_decrement(bma->cur, 0, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(bma->cur, &LEFT);
   if (error)
    goto done;
  }
  ASSERT(da_new <= da_old);
  break;

 case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG:
  /*
 * Filling in all of a previously delayed allocation extent.
 * The left neighbor is contiguous, the right is not.
 */

  old = LEFT;
  LEFT.br_blockcount += PREV.br_blockcount;

  xfs_iext_remove(bma->ip, &bma->icur, state);
  xfs_iext_prev(ifp, &bma->icur);
  xfs_iext_update_extent(bma->ip, state, &bma->icur, &LEFT);

  if (bma->cur == NULL)
   rval = XFS_ILOG_DEXT;
  else {
   rval = 0;
   error = xfs_bmbt_lookup_eq(bma->cur, &old, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(bma->cur, &LEFT);
   if (error)
    goto done;
  }
  ASSERT(da_new <= da_old);
  break;

 case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
  /*
 * Filling in all of a previously delayed allocation extent.
 * The right neighbor is contiguous, the left is not. Take care
 * with delay -> unwritten extent allocation here because the
 * delalloc record we are overwriting is always written.
 */

  PREV.br_startblock = new->br_startblock;
  PREV.br_blockcount += RIGHT.br_blockcount;
  PREV.br_state = new->br_state;

  xfs_iext_next(ifp, &bma->icur);
  xfs_iext_remove(bma->ip, &bma->icur, state);
  xfs_iext_prev(ifp, &bma->icur);
  xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV);

  if (bma->cur == NULL)
   rval = XFS_ILOG_DEXT;
  else {
   rval = 0;
   error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(bma->cur, &PREV);
   if (error)
    goto done;
  }
  ASSERT(da_new <= da_old);
  break;

 case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
  /*
 * Filling in all of a previously delayed allocation extent.
 * Neither the left nor right neighbors are contiguous with
 * the new one.
 */

  PREV.br_startblock = new->br_startblock;
  PREV.br_state = new->br_state;
  xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV);
  ifp->if_nextents++;

  if (bma->cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 0)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_btree_insert(bma->cur, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
  }
  ASSERT(da_new <= da_old);
  break;

 case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG:
  /*
 * Filling in the first part of a previous delayed allocation.
 * The left neighbor is contiguous.
 */

  old = LEFT;
  temp = PREV.br_blockcount - new->br_blockcount;
  da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
    startblockval(PREV.br_startblock));

  LEFT.br_blockcount += new->br_blockcount;

  PREV.br_blockcount = temp;
  PREV.br_startoff += new->br_blockcount;
  PREV.br_startblock = nullstartblock(da_new);

  xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV);
  xfs_iext_prev(ifp, &bma->icur);
  xfs_iext_update_extent(bma->ip, state, &bma->icur, &LEFT);

  if (bma->cur == NULL)
   rval = XFS_ILOG_DEXT;
  else {
   rval = 0;
   error = xfs_bmbt_lookup_eq(bma->cur, &old, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(bma->cur, &LEFT);
   if (error)
    goto done;
  }
  ASSERT(da_new <= da_old);
  break;

 case BMAP_LEFT_FILLING:
  /*
 * Filling in the first part of a previous delayed allocation.
 * The left neighbor is not contiguous.
 */

  xfs_iext_update_extent(bma->ip, state, &bma->icur, new);
  ifp->if_nextents++;

  if (bma->cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 0)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_btree_insert(bma->cur, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
  }

  if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
   error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
     &bma->cur, 1, &tmp_rval, whichfork);
   rval |= tmp_rval;
   if (error)
    goto done;
  }

  temp = PREV.br_blockcount - new->br_blockcount;
  da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
   startblockval(PREV.br_startblock) -
   (bma->cur ? bma->cur->bc_bmap.allocated : 0));

  PREV.br_startoff = new_endoff;
  PREV.br_blockcount = temp;
  PREV.br_startblock = nullstartblock(da_new);
  xfs_iext_next(ifp, &bma->icur);
  xfs_iext_insert(bma->ip, &bma->icur, &PREV, state);
  xfs_iext_prev(ifp, &bma->icur);
  break;

 case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
  /*
 * Filling in the last part of a previous delayed allocation.
 * The right neighbor is contiguous with the new allocation.
 */

  old = RIGHT;
  RIGHT.br_startoff = new->br_startoff;
  RIGHT.br_startblock = new->br_startblock;
  RIGHT.br_blockcount += new->br_blockcount;

  if (bma->cur == NULL)
   rval = XFS_ILOG_DEXT;
  else {
   rval = 0;
   error = xfs_bmbt_lookup_eq(bma->cur, &old, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(bma->cur, &RIGHT);
   if (error)
    goto done;
  }

  temp = PREV.br_blockcount - new->br_blockcount;
  da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
   startblockval(PREV.br_startblock));

  PREV.br_blockcount = temp;
  PREV.br_startblock = nullstartblock(da_new);

  xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV);
  xfs_iext_next(ifp, &bma->icur);
  xfs_iext_update_extent(bma->ip, state, &bma->icur, &RIGHT);
  ASSERT(da_new <= da_old);
  break;

 case BMAP_RIGHT_FILLING:
  /*
 * Filling in the last part of a previous delayed allocation.
 * The right neighbor is not contiguous.
 */

  xfs_iext_update_extent(bma->ip, state, &bma->icur, new);
  ifp->if_nextents++;

  if (bma->cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 0)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_btree_insert(bma->cur, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
  }

  if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
   error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
    &bma->cur, 1, &tmp_rval, whichfork);
   rval |= tmp_rval;
   if (error)
    goto done;
  }

  temp = PREV.br_blockcount - new->br_blockcount;
  da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
   startblockval(PREV.br_startblock) -
   (bma->cur ? bma->cur->bc_bmap.allocated : 0));

  PREV.br_startblock = nullstartblock(da_new);
  PREV.br_blockcount = temp;
  xfs_iext_insert(bma->ip, &bma->icur, &PREV, state);
  xfs_iext_next(ifp, &bma->icur);
  ASSERT(da_new <= da_old);
  break;

 case 0:
  /*
 * Filling in the middle part of a previous delayed allocation.
 * Contiguity is impossible here.
 * This case is avoided almost all the time.
 *
 * We start with a delayed allocation:
 *
 * +ddddddddddddddddddddddddddddddddddddddddddddddddddddddd+
 *  PREV @ idx
 *
         * and we are allocating:
 *                     +rrrrrrrrrrrrrrrrr+
 *       new
 *
 * and we set it up for insertion as:
 * +ddddddddddddddddddd+rrrrrrrrrrrrrrrrr+ddddddddddddddddd+
 *                            new
 *  PREV @ idx          LEFT              RIGHT
 *                      inserted at idx + 1
 */

  old = PREV;

  /* LEFT is the new middle */
  LEFT = *new;

  /* RIGHT is the new right */
  RIGHT.br_state = PREV.br_state;
  RIGHT.br_startoff = new_endoff;
  RIGHT.br_blockcount =
   PREV.br_startoff + PREV.br_blockcount - new_endoff;
  RIGHT.br_startblock =
   nullstartblock(xfs_bmap_worst_indlen(bma->ip,
     RIGHT.br_blockcount));

  /* truncate PREV */
  PREV.br_blockcount = new->br_startoff - PREV.br_startoff;
  PREV.br_startblock =
   nullstartblock(xfs_bmap_worst_indlen(bma->ip,
     PREV.br_blockcount));
  xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV);

  xfs_iext_next(ifp, &bma->icur);
  xfs_iext_insert(bma->ip, &bma->icur, &RIGHT, state);
  xfs_iext_insert(bma->ip, &bma->icur, &LEFT, state);
  ifp->if_nextents++;

  if (bma->cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 0)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_btree_insert(bma->cur, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(bma->cur);
    error = -EFSCORRUPTED;
    goto done;
   }
  }

  if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
   error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
     &bma->cur, 1, &tmp_rval, whichfork);
   rval |= tmp_rval;
   if (error)
    goto done;
  }

  da_new = startblockval(PREV.br_startblock) +
    startblockval(RIGHT.br_startblock);
  break;

 case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
 case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
 case BMAP_LEFT_FILLING | BMAP_RIGHT_CONTIG:
 case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG:
 case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
 case BMAP_LEFT_CONTIG:
 case BMAP_RIGHT_CONTIG:
  /*
 * These cases are all impossible.
 */

  ASSERT(0);
 }

 /* add reverse mapping unless caller opted out */
 if (!(bma->flags & XFS_BMAPI_NORMAP))
  xfs_rmap_map_extent(bma->tp, bma->ip, whichfork, new);

 /* convert to a btree if necessary */
 if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
  int tmp_logflags; /* partial log flag return val */

  ASSERT(bma->cur == NULL);
  error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
    &bma->cur, da_old > 0, &tmp_logflags,
    whichfork);
  bma->logflags |= tmp_logflags;
  if (error)
   goto done;
 }

 if (da_new != da_old)
  xfs_mod_delalloc(bma->ip, 0, (int64_t)da_new - da_old);

 if (bma->cur) {
  da_new += bma->cur->bc_bmap.allocated;
  bma->cur->bc_bmap.allocated = 0;
 }

 /* adjust for changes in reserved delayed indirect blocks */
 if (da_new < da_old)
  xfs_add_fdblocks(mp, da_old - da_new);
 else if (da_new > da_old)
  error = xfs_dec_fdblocks(mp, da_new - da_old, true);

 xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
done:
 if (whichfork != XFS_COW_FORK)
  bma->logflags |= rval;
 return error;
#undef LEFT
#undef RIGHT
#undef PREV
}

/*
 * Convert an unwritten allocation to a real allocation or vice versa.
 */

int     /* error */
xfs_bmap_add_extent_unwritten_real(
 struct xfs_trans *tp,
 xfs_inode_t  *ip, /* incore inode pointer */
 int   whichfork,
 struct xfs_iext_cursor *icur,
 struct xfs_btree_cur **curp, /* if *curp is null, not a btree */
 xfs_bmbt_irec_t  *new/* new data to add to file extents */
 int   *logflagsp) /* inode logging flags */
{
 struct xfs_btree_cur *cur; /* btree cursor */
 int   error; /* error return value */
 int   i; /* temp state */
 struct xfs_ifork *ifp; /* inode fork pointer */
 xfs_fileoff_t  new_endoff; /* end offset of new entry */
 xfs_bmbt_irec_t  r[3]; /* neighbor extent entries */
     /* left is 0, right is 1, prev is 2 */
 int   rval=0; /* return value (logging flags) */
 uint32_t  state = xfs_bmap_fork_to_state(whichfork);
 struct xfs_mount *mp = ip->i_mount;
 struct xfs_bmbt_irec old;

 *logflagsp = 0;

 cur = *curp;
 ifp = xfs_ifork_ptr(ip, whichfork);

 ASSERT(!isnullstartblock(new->br_startblock));

 XFS_STATS_INC(mp, xs_add_exlist);

#define LEFT  r[0]
#define RIGHT  r[1]
#define PREV  r[2]

 /*
 * Set up a bunch of variables to make the tests simpler.
 */

 error = 0;
 xfs_iext_get_extent(ifp, icur, &PREV);
 ASSERT(new->br_state != PREV.br_state);
 new_endoff = new->br_startoff + new->br_blockcount;
 ASSERT(PREV.br_startoff <= new->br_startoff);
 ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);

 /*
 * Set flags determining what part of the previous oldext allocation
 * extent is being replaced by a newext allocation.
 */

 if (PREV.br_startoff == new->br_startoff)
  state |= BMAP_LEFT_FILLING;
 if (PREV.br_startoff + PREV.br_blockcount == new_endoff)
  state |= BMAP_RIGHT_FILLING;

 /*
 * Check and set flags if this segment has a left neighbor.
 * Don't set contiguous if the combined extent would be too large.
 */

 if (xfs_iext_peek_prev_extent(ifp, icur, &LEFT)) {
  state |= BMAP_LEFT_VALID;
  if (isnullstartblock(LEFT.br_startblock))
   state |= BMAP_LEFT_DELAY;
 }

 if ((state & BMAP_LEFT_VALID) && !(state & BMAP_LEFT_DELAY) &&
     LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff &&
     LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock &&
     LEFT.br_state == new->br_state &&
     LEFT.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN &&
     xfs_bmap_same_rtgroup(ip, whichfork, &LEFT, new))
  state |= BMAP_LEFT_CONTIG;

 /*
 * Check and set flags if this segment has a right neighbor.
 * Don't set contiguous if the combined extent would be too large.
 * Also check for all-three-contiguous being too large.
 */

 if (xfs_iext_peek_next_extent(ifp, icur, &RIGHT)) {
  state |= BMAP_RIGHT_VALID;
  if (isnullstartblock(RIGHT.br_startblock))
   state |= BMAP_RIGHT_DELAY;
 }

 if ((state & BMAP_RIGHT_VALID) && !(state & BMAP_RIGHT_DELAY) &&
     new_endoff == RIGHT.br_startoff &&
     new->br_startblock + new->br_blockcount == RIGHT.br_startblock &&
     new->br_state == RIGHT.br_state &&
     new->br_blockcount + RIGHT.br_blockcount <= XFS_MAX_BMBT_EXTLEN &&
     ((state & (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING |
         BMAP_RIGHT_FILLING)) !=
        (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING |
         BMAP_RIGHT_FILLING) ||
      LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount
   <= XFS_MAX_BMBT_EXTLEN) &&
     xfs_bmap_same_rtgroup(ip, whichfork, new, &RIGHT))
  state |= BMAP_RIGHT_CONTIG;

 /*
 * Switch out based on the FILLING and CONTIG state bits.
 */

 switch (state & (BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
    BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG)) {
 case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
      BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
  /*
 * Setting all of a previous oldext extent to newext.
 * The left and right neighbors are both contiguous with new.
 */

  LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;

  xfs_iext_remove(ip, icur, state);
  xfs_iext_remove(ip, icur, state);
  xfs_iext_prev(ifp, icur);
  xfs_iext_update_extent(ip, state, icur, &LEFT);
  ifp->if_nextents -= 2;
  if (cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_delete(cur, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_decrement(cur, 0, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_delete(cur, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_decrement(cur, 0, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(cur, &LEFT);
   if (error)
    goto done;
  }
  break;

 case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG:
  /*
 * Setting all of a previous oldext extent to newext.
 * The left neighbor is contiguous, the right is not.
 */

  LEFT.br_blockcount += PREV.br_blockcount;

  xfs_iext_remove(ip, icur, state);
  xfs_iext_prev(ifp, icur);
  xfs_iext_update_extent(ip, state, icur, &LEFT);
  ifp->if_nextents--;
  if (cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(cur, &PREV, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_delete(cur, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_decrement(cur, 0, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(cur, &LEFT);
   if (error)
    goto done;
  }
  break;

 case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
  /*
 * Setting all of a previous oldext extent to newext.
 * The right neighbor is contiguous, the left is not.
 */

  PREV.br_blockcount += RIGHT.br_blockcount;
  PREV.br_state = new->br_state;

  xfs_iext_next(ifp, icur);
  xfs_iext_remove(ip, icur, state);
  xfs_iext_prev(ifp, icur);
  xfs_iext_update_extent(ip, state, icur, &PREV);
  ifp->if_nextents--;

  if (cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_delete(cur, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   if ((error = xfs_btree_decrement(cur, 0, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(cur, &PREV);
   if (error)
    goto done;
  }
  break;

 case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
  /*
 * Setting all of a previous oldext extent to newext.
 * Neither the left nor right neighbors are contiguous with
 * the new one.
 */

  PREV.br_state = new->br_state;
  xfs_iext_update_extent(ip, state, icur, &PREV);

  if (cur == NULL)
   rval = XFS_ILOG_DEXT;
  else {
   rval = 0;
   error = xfs_bmbt_lookup_eq(cur, new, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(cur, &PREV);
   if (error)
    goto done;
  }
  break;

 case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG:
  /*
 * Setting the first part of a previous oldext extent to newext.
 * The left neighbor is contiguous.
 */

  LEFT.br_blockcount += new->br_blockcount;

  old = PREV;
  PREV.br_startoff += new->br_blockcount;
  PREV.br_startblock += new->br_blockcount;
  PREV.br_blockcount -= new->br_blockcount;

  xfs_iext_update_extent(ip, state, icur, &PREV);
  xfs_iext_prev(ifp, icur);
  xfs_iext_update_extent(ip, state, icur, &LEFT);

  if (cur == NULL)
   rval = XFS_ILOG_DEXT;
  else {
   rval = 0;
   error = xfs_bmbt_lookup_eq(cur, &old, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(cur, &PREV);
   if (error)
    goto done;
   error = xfs_btree_decrement(cur, 0, &i);
   if (error)
    goto done;
   error = xfs_bmbt_update(cur, &LEFT);
   if (error)
    goto done;
  }
  break;

 case BMAP_LEFT_FILLING:
  /*
 * Setting the first part of a previous oldext extent to newext.
 * The left neighbor is not contiguous.
 */

  old = PREV;
  PREV.br_startoff += new->br_blockcount;
  PREV.br_startblock += new->br_blockcount;
  PREV.br_blockcount -= new->br_blockcount;

  xfs_iext_update_extent(ip, state, icur, &PREV);
  xfs_iext_insert(ip, icur, new, state);
  ifp->if_nextents++;

  if (cur == NULL)
   rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
  else {
   rval = XFS_ILOG_CORE;
   error = xfs_bmbt_lookup_eq(cur, &old, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
   error = xfs_bmbt_update(cur, &PREV);
   if (error)
    goto done;
   cur->bc_rec.b = *new;
   if ((error = xfs_btree_insert(cur, &i)))
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
    error = -EFSCORRUPTED;
    goto done;
   }
  }
  break;

 case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
  /*
 * Setting the last part of a previous oldext extent to newext.
 * The right neighbor is contiguous with the new allocation.
 */

  old = PREV;
  PREV.br_blockcount -= new->br_blockcount;

  RIGHT.br_startoff = new->br_startoff;
  RIGHT.br_startblock = new->br_startblock;
  RIGHT.br_blockcount += new->br_blockcount;

  xfs_iext_update_extent(ip, state, icur, &PREV);
  xfs_iext_next(ifp, icur);
  xfs_iext_update_extent(ip, state, icur, &RIGHT);

  if (cur == NULL)
   rval = XFS_ILOG_DEXT;
  else {
   rval = 0;
   error = xfs_bmbt_lookup_eq(cur, &old, &i);
   if (error)
    goto done;
   if (XFS_IS_CORRUPT(mp, i != 1)) {
    xfs_btree_mark_sick(cur);
--> --------------------

--> maximum size reached

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

Messung V0.5
C=91 H=74 G=82

¤ Dauer der Verarbeitung: 0.37 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.