162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_bit.h" 1362306a36Sopenharmony_ci#include "xfs_sb.h" 1462306a36Sopenharmony_ci#include "xfs_mount.h" 1562306a36Sopenharmony_ci#include "xfs_defer.h" 1662306a36Sopenharmony_ci#include "xfs_dir2.h" 1762306a36Sopenharmony_ci#include "xfs_inode.h" 1862306a36Sopenharmony_ci#include "xfs_btree.h" 1962306a36Sopenharmony_ci#include "xfs_trans.h" 2062306a36Sopenharmony_ci#include "xfs_alloc.h" 2162306a36Sopenharmony_ci#include "xfs_bmap.h" 2262306a36Sopenharmony_ci#include "xfs_bmap_util.h" 2362306a36Sopenharmony_ci#include "xfs_bmap_btree.h" 2462306a36Sopenharmony_ci#include "xfs_rtalloc.h" 2562306a36Sopenharmony_ci#include "xfs_errortag.h" 2662306a36Sopenharmony_ci#include "xfs_error.h" 2762306a36Sopenharmony_ci#include "xfs_quota.h" 2862306a36Sopenharmony_ci#include "xfs_trans_space.h" 2962306a36Sopenharmony_ci#include "xfs_buf_item.h" 3062306a36Sopenharmony_ci#include "xfs_trace.h" 3162306a36Sopenharmony_ci#include "xfs_attr_leaf.h" 3262306a36Sopenharmony_ci#include "xfs_filestream.h" 3362306a36Sopenharmony_ci#include "xfs_rmap.h" 3462306a36Sopenharmony_ci#include "xfs_ag.h" 3562306a36Sopenharmony_ci#include "xfs_ag_resv.h" 3662306a36Sopenharmony_ci#include "xfs_refcount.h" 3762306a36Sopenharmony_ci#include "xfs_icache.h" 3862306a36Sopenharmony_ci#include "xfs_iomap.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct kmem_cache *xfs_bmap_intent_cache; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Miscellaneous helper functions 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Compute and fill in the value of the maximum depth of a bmap btree 4862306a36Sopenharmony_ci * in this filesystem. Done once, during mount. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_civoid 5162306a36Sopenharmony_cixfs_bmap_compute_maxlevels( 5262306a36Sopenharmony_ci xfs_mount_t *mp, /* file system mount structure */ 5362306a36Sopenharmony_ci int whichfork) /* data or attr fork */ 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci uint64_t maxblocks; /* max blocks at this level */ 5662306a36Sopenharmony_ci xfs_extnum_t maxleafents; /* max leaf entries possible */ 5762306a36Sopenharmony_ci int level; /* btree level */ 5862306a36Sopenharmony_ci int maxrootrecs; /* max records in root block */ 5962306a36Sopenharmony_ci int minleafrecs; /* min records in leaf block */ 6062306a36Sopenharmony_ci int minnoderecs; /* min records in node block */ 6162306a36Sopenharmony_ci int sz; /* root block size */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* 6462306a36Sopenharmony_ci * The maximum number of extents in a fork, hence the maximum number of 6562306a36Sopenharmony_ci * leaf entries, is controlled by the size of the on-disk extent count. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * Note that we can no longer assume that if we are in ATTR1 that the 6862306a36Sopenharmony_ci * fork offset of all the inodes will be 6962306a36Sopenharmony_ci * (xfs_default_attroffset(ip) >> 3) because we could have mounted with 7062306a36Sopenharmony_ci * ATTR2 and then mounted back with ATTR1, keeping the i_forkoff's fixed 7162306a36Sopenharmony_ci * but probably at various positions. Therefore, for both ATTR1 and 7262306a36Sopenharmony_ci * ATTR2 we have to assume the worst case scenario of a minimum size 7362306a36Sopenharmony_ci * available. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci maxleafents = xfs_iext_max_nextents(xfs_has_large_extent_counts(mp), 7662306a36Sopenharmony_ci whichfork); 7762306a36Sopenharmony_ci if (whichfork == XFS_DATA_FORK) 7862306a36Sopenharmony_ci sz = XFS_BMDR_SPACE_CALC(MINDBTPTRS); 7962306a36Sopenharmony_ci else 8062306a36Sopenharmony_ci sz = XFS_BMDR_SPACE_CALC(MINABTPTRS); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci maxrootrecs = xfs_bmdr_maxrecs(sz, 0); 8362306a36Sopenharmony_ci minleafrecs = mp->m_bmap_dmnr[0]; 8462306a36Sopenharmony_ci minnoderecs = mp->m_bmap_dmnr[1]; 8562306a36Sopenharmony_ci maxblocks = howmany_64(maxleafents, minleafrecs); 8662306a36Sopenharmony_ci for (level = 1; maxblocks > 1; level++) { 8762306a36Sopenharmony_ci if (maxblocks <= maxrootrecs) 8862306a36Sopenharmony_ci maxblocks = 1; 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci maxblocks = howmany_64(maxblocks, minnoderecs); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci mp->m_bm_maxlevels[whichfork] = level; 9362306a36Sopenharmony_ci ASSERT(mp->m_bm_maxlevels[whichfork] <= xfs_bmbt_maxlevels_ondisk()); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciunsigned int 9762306a36Sopenharmony_cixfs_bmap_compute_attr_offset( 9862306a36Sopenharmony_ci struct xfs_mount *mp) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (mp->m_sb.sb_inodesize == 256) 10162306a36Sopenharmony_ci return XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(MINABTPTRS); 10262306a36Sopenharmony_ci return XFS_BMDR_SPACE_CALC(6 * MINABTPTRS); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciSTATIC int /* error */ 10662306a36Sopenharmony_cixfs_bmbt_lookup_eq( 10762306a36Sopenharmony_ci struct xfs_btree_cur *cur, 10862306a36Sopenharmony_ci struct xfs_bmbt_irec *irec, 10962306a36Sopenharmony_ci int *stat) /* success/failure */ 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci cur->bc_rec.b = *irec; 11262306a36Sopenharmony_ci return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciSTATIC int /* error */ 11662306a36Sopenharmony_cixfs_bmbt_lookup_first( 11762306a36Sopenharmony_ci struct xfs_btree_cur *cur, 11862306a36Sopenharmony_ci int *stat) /* success/failure */ 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci cur->bc_rec.b.br_startoff = 0; 12162306a36Sopenharmony_ci cur->bc_rec.b.br_startblock = 0; 12262306a36Sopenharmony_ci cur->bc_rec.b.br_blockcount = 0; 12362306a36Sopenharmony_ci return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * Check if the inode needs to be converted to btree format. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return whichfork != XFS_COW_FORK && 13462306a36Sopenharmony_ci ifp->if_format == XFS_DINODE_FMT_EXTENTS && 13562306a36Sopenharmony_ci ifp->if_nextents > XFS_IFORK_MAXEXT(ip, whichfork); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* 13962306a36Sopenharmony_ci * Check if the inode should be converted to extent format. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic inline bool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return whichfork != XFS_COW_FORK && 14662306a36Sopenharmony_ci ifp->if_format == XFS_DINODE_FMT_BTREE && 14762306a36Sopenharmony_ci ifp->if_nextents <= XFS_IFORK_MAXEXT(ip, whichfork); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * Update the record referred to by cur to the value given by irec 15262306a36Sopenharmony_ci * This either works (return 0) or gets an EFSCORRUPTED error. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ciSTATIC int 15562306a36Sopenharmony_cixfs_bmbt_update( 15662306a36Sopenharmony_ci struct xfs_btree_cur *cur, 15762306a36Sopenharmony_ci struct xfs_bmbt_irec *irec) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci union xfs_btree_rec rec; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci xfs_bmbt_disk_set_all(&rec.bmbt, irec); 16262306a36Sopenharmony_ci return xfs_btree_update(cur, &rec); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * Compute the worst-case number of indirect blocks that will be used 16762306a36Sopenharmony_ci * for ip's delayed extent of length "len". 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ciSTATIC xfs_filblks_t 17062306a36Sopenharmony_cixfs_bmap_worst_indlen( 17162306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 17262306a36Sopenharmony_ci xfs_filblks_t len) /* delayed extent length */ 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int level; /* btree level number */ 17562306a36Sopenharmony_ci int maxrecs; /* maximum record count at this level */ 17662306a36Sopenharmony_ci xfs_mount_t *mp; /* mount structure */ 17762306a36Sopenharmony_ci xfs_filblks_t rval; /* return value */ 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci mp = ip->i_mount; 18062306a36Sopenharmony_ci maxrecs = mp->m_bmap_dmxr[0]; 18162306a36Sopenharmony_ci for (level = 0, rval = 0; 18262306a36Sopenharmony_ci level < XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK); 18362306a36Sopenharmony_ci level++) { 18462306a36Sopenharmony_ci len += maxrecs - 1; 18562306a36Sopenharmony_ci do_div(len, maxrecs); 18662306a36Sopenharmony_ci rval += len; 18762306a36Sopenharmony_ci if (len == 1) 18862306a36Sopenharmony_ci return rval + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 18962306a36Sopenharmony_ci level - 1; 19062306a36Sopenharmony_ci if (level == 0) 19162306a36Sopenharmony_ci maxrecs = mp->m_bmap_dmxr[1]; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci return rval; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * Calculate the default attribute fork offset for newly created inodes. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ciuint 20062306a36Sopenharmony_cixfs_default_attroffset( 20162306a36Sopenharmony_ci struct xfs_inode *ip) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (ip->i_df.if_format == XFS_DINODE_FMT_DEV) 20462306a36Sopenharmony_ci return roundup(sizeof(xfs_dev_t), 8); 20562306a36Sopenharmony_ci return M_IGEO(ip->i_mount)->attr_fork_offset; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * Helper routine to reset inode i_forkoff field when switching attribute fork 21062306a36Sopenharmony_ci * from local to extent format - we reset it where possible to make space 21162306a36Sopenharmony_ci * available for inline data fork extents. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ciSTATIC void 21462306a36Sopenharmony_cixfs_bmap_forkoff_reset( 21562306a36Sopenharmony_ci xfs_inode_t *ip, 21662306a36Sopenharmony_ci int whichfork) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci if (whichfork == XFS_ATTR_FORK && 21962306a36Sopenharmony_ci ip->i_df.if_format != XFS_DINODE_FMT_DEV && 22062306a36Sopenharmony_ci ip->i_df.if_format != XFS_DINODE_FMT_BTREE) { 22162306a36Sopenharmony_ci uint dfl_forkoff = xfs_default_attroffset(ip) >> 3; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (dfl_forkoff > ip->i_forkoff) 22462306a36Sopenharmony_ci ip->i_forkoff = dfl_forkoff; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#ifdef DEBUG 22962306a36Sopenharmony_ciSTATIC struct xfs_buf * 23062306a36Sopenharmony_cixfs_bmap_get_bp( 23162306a36Sopenharmony_ci struct xfs_btree_cur *cur, 23262306a36Sopenharmony_ci xfs_fsblock_t bno) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct xfs_log_item *lip; 23562306a36Sopenharmony_ci int i; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!cur) 23862306a36Sopenharmony_ci return NULL; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci for (i = 0; i < cur->bc_maxlevels; i++) { 24162306a36Sopenharmony_ci if (!cur->bc_levels[i].bp) 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci if (xfs_buf_daddr(cur->bc_levels[i].bp) == bno) 24462306a36Sopenharmony_ci return cur->bc_levels[i].bp; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Chase down all the log items to see if the bp is there */ 24862306a36Sopenharmony_ci list_for_each_entry(lip, &cur->bc_tp->t_items, li_trans) { 24962306a36Sopenharmony_ci struct xfs_buf_log_item *bip = (struct xfs_buf_log_item *)lip; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (bip->bli_item.li_type == XFS_LI_BUF && 25262306a36Sopenharmony_ci xfs_buf_daddr(bip->bli_buf) == bno) 25362306a36Sopenharmony_ci return bip->bli_buf; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return NULL; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciSTATIC void 26062306a36Sopenharmony_cixfs_check_block( 26162306a36Sopenharmony_ci struct xfs_btree_block *block, 26262306a36Sopenharmony_ci xfs_mount_t *mp, 26362306a36Sopenharmony_ci int root, 26462306a36Sopenharmony_ci short sz) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci int i, j, dmxr; 26762306a36Sopenharmony_ci __be64 *pp, *thispa; /* pointer to block address */ 26862306a36Sopenharmony_ci xfs_bmbt_key_t *prevp, *keyp; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ASSERT(be16_to_cpu(block->bb_level) > 0); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci prevp = NULL; 27362306a36Sopenharmony_ci for( i = 1; i <= xfs_btree_get_numrecs(block); i++) { 27462306a36Sopenharmony_ci dmxr = mp->m_bmap_dmxr[0]; 27562306a36Sopenharmony_ci keyp = XFS_BMBT_KEY_ADDR(mp, block, i); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (prevp) { 27862306a36Sopenharmony_ci ASSERT(be64_to_cpu(prevp->br_startoff) < 27962306a36Sopenharmony_ci be64_to_cpu(keyp->br_startoff)); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci prevp = keyp; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* 28462306a36Sopenharmony_ci * Compare the block numbers to see if there are dups. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci if (root) 28762306a36Sopenharmony_ci pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, i, sz); 28862306a36Sopenharmony_ci else 28962306a36Sopenharmony_ci pp = XFS_BMBT_PTR_ADDR(mp, block, i, dmxr); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci for (j = i+1; j <= be16_to_cpu(block->bb_numrecs); j++) { 29262306a36Sopenharmony_ci if (root) 29362306a36Sopenharmony_ci thispa = XFS_BMAP_BROOT_PTR_ADDR(mp, block, j, sz); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci thispa = XFS_BMBT_PTR_ADDR(mp, block, j, dmxr); 29662306a36Sopenharmony_ci if (*thispa == *pp) { 29762306a36Sopenharmony_ci xfs_warn(mp, "%s: thispa(%d) == pp(%d) %lld", 29862306a36Sopenharmony_ci __func__, j, i, 29962306a36Sopenharmony_ci (unsigned long long)be64_to_cpu(*thispa)); 30062306a36Sopenharmony_ci xfs_err(mp, "%s: ptrs are equal in node\n", 30162306a36Sopenharmony_ci __func__); 30262306a36Sopenharmony_ci xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* 30962306a36Sopenharmony_ci * Check that the extents for the inode ip are in the right order in all 31062306a36Sopenharmony_ci * btree leaves. THis becomes prohibitively expensive for large extent count 31162306a36Sopenharmony_ci * files, so don't bother with inodes that have more than 10,000 extents in 31262306a36Sopenharmony_ci * them. The btree record ordering checks will still be done, so for such large 31362306a36Sopenharmony_ci * bmapbt constructs that is going to catch most corruptions. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ciSTATIC void 31662306a36Sopenharmony_cixfs_bmap_check_leaf_extents( 31762306a36Sopenharmony_ci struct xfs_btree_cur *cur, /* btree cursor or null */ 31862306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 31962306a36Sopenharmony_ci int whichfork) /* data or attr fork */ 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 32262306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 32362306a36Sopenharmony_ci struct xfs_btree_block *block; /* current btree block */ 32462306a36Sopenharmony_ci xfs_fsblock_t bno; /* block # of "block" */ 32562306a36Sopenharmony_ci struct xfs_buf *bp; /* buffer for "block" */ 32662306a36Sopenharmony_ci int error; /* error return value */ 32762306a36Sopenharmony_ci xfs_extnum_t i=0, j; /* index into the extents list */ 32862306a36Sopenharmony_ci int level; /* btree level, for checking */ 32962306a36Sopenharmony_ci __be64 *pp; /* pointer to block address */ 33062306a36Sopenharmony_ci xfs_bmbt_rec_t *ep; /* pointer to current extent */ 33162306a36Sopenharmony_ci xfs_bmbt_rec_t last = {0, 0}; /* last extent in prev block */ 33262306a36Sopenharmony_ci xfs_bmbt_rec_t *nextp; /* pointer to next extent */ 33362306a36Sopenharmony_ci int bp_release = 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (ifp->if_format != XFS_DINODE_FMT_BTREE) 33662306a36Sopenharmony_ci return; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* skip large extent count inodes */ 33962306a36Sopenharmony_ci if (ip->i_df.if_nextents > 10000) 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci bno = NULLFSBLOCK; 34362306a36Sopenharmony_ci block = ifp->if_broot; 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci level = be16_to_cpu(block->bb_level); 34862306a36Sopenharmony_ci ASSERT(level > 0); 34962306a36Sopenharmony_ci xfs_check_block(block, mp, 1, ifp->if_broot_bytes); 35062306a36Sopenharmony_ci pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes); 35162306a36Sopenharmony_ci bno = be64_to_cpu(*pp); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ASSERT(bno != NULLFSBLOCK); 35462306a36Sopenharmony_ci ASSERT(XFS_FSB_TO_AGNO(mp, bno) < mp->m_sb.sb_agcount); 35562306a36Sopenharmony_ci ASSERT(XFS_FSB_TO_AGBNO(mp, bno) < mp->m_sb.sb_agblocks); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * Go down the tree until leaf level is reached, following the first 35962306a36Sopenharmony_ci * pointer (leftmost) at each level. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci while (level-- > 0) { 36262306a36Sopenharmony_ci /* See if buf is in cur first */ 36362306a36Sopenharmony_ci bp_release = 0; 36462306a36Sopenharmony_ci bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno)); 36562306a36Sopenharmony_ci if (!bp) { 36662306a36Sopenharmony_ci bp_release = 1; 36762306a36Sopenharmony_ci error = xfs_btree_read_bufl(mp, NULL, bno, &bp, 36862306a36Sopenharmony_ci XFS_BMAP_BTREE_REF, 36962306a36Sopenharmony_ci &xfs_bmbt_buf_ops); 37062306a36Sopenharmony_ci if (error) 37162306a36Sopenharmony_ci goto error_norelse; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci block = XFS_BUF_TO_BLOCK(bp); 37462306a36Sopenharmony_ci if (level == 0) 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * Check this block for basic sanity (increasing keys and 37962306a36Sopenharmony_ci * no duplicate blocks). 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci xfs_check_block(block, mp, 0, 0); 38362306a36Sopenharmony_ci pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]); 38462306a36Sopenharmony_ci bno = be64_to_cpu(*pp); 38562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_verify_fsbno(mp, bno))) { 38662306a36Sopenharmony_ci error = -EFSCORRUPTED; 38762306a36Sopenharmony_ci goto error0; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci if (bp_release) { 39062306a36Sopenharmony_ci bp_release = 0; 39162306a36Sopenharmony_ci xfs_trans_brelse(NULL, bp); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* 39662306a36Sopenharmony_ci * Here with bp and block set to the leftmost leaf node in the tree. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci i = 0; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* 40162306a36Sopenharmony_ci * Loop over all leaf nodes checking that all extents are in the right order. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci for (;;) { 40462306a36Sopenharmony_ci xfs_fsblock_t nextbno; 40562306a36Sopenharmony_ci xfs_extnum_t num_recs; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci num_recs = xfs_btree_get_numrecs(block); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* 41162306a36Sopenharmony_ci * Read-ahead the next leaf block, if any. 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* 41762306a36Sopenharmony_ci * Check all the extents to make sure they are OK. 41862306a36Sopenharmony_ci * If we had a previous block, the last entry should 41962306a36Sopenharmony_ci * conform with the first entry in this one. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ep = XFS_BMBT_REC_ADDR(mp, block, 1); 42362306a36Sopenharmony_ci if (i) { 42462306a36Sopenharmony_ci ASSERT(xfs_bmbt_disk_get_startoff(&last) + 42562306a36Sopenharmony_ci xfs_bmbt_disk_get_blockcount(&last) <= 42662306a36Sopenharmony_ci xfs_bmbt_disk_get_startoff(ep)); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci for (j = 1; j < num_recs; j++) { 42962306a36Sopenharmony_ci nextp = XFS_BMBT_REC_ADDR(mp, block, j + 1); 43062306a36Sopenharmony_ci ASSERT(xfs_bmbt_disk_get_startoff(ep) + 43162306a36Sopenharmony_ci xfs_bmbt_disk_get_blockcount(ep) <= 43262306a36Sopenharmony_ci xfs_bmbt_disk_get_startoff(nextp)); 43362306a36Sopenharmony_ci ep = nextp; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci last = *ep; 43762306a36Sopenharmony_ci i += num_recs; 43862306a36Sopenharmony_ci if (bp_release) { 43962306a36Sopenharmony_ci bp_release = 0; 44062306a36Sopenharmony_ci xfs_trans_brelse(NULL, bp); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci bno = nextbno; 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * If we've reached the end, stop. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci if (bno == NULLFSBLOCK) 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci bp_release = 0; 45062306a36Sopenharmony_ci bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno)); 45162306a36Sopenharmony_ci if (!bp) { 45262306a36Sopenharmony_ci bp_release = 1; 45362306a36Sopenharmony_ci error = xfs_btree_read_bufl(mp, NULL, bno, &bp, 45462306a36Sopenharmony_ci XFS_BMAP_BTREE_REF, 45562306a36Sopenharmony_ci &xfs_bmbt_buf_ops); 45662306a36Sopenharmony_ci if (error) 45762306a36Sopenharmony_ci goto error_norelse; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci block = XFS_BUF_TO_BLOCK(bp); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cierror0: 46562306a36Sopenharmony_ci xfs_warn(mp, "%s: at error0", __func__); 46662306a36Sopenharmony_ci if (bp_release) 46762306a36Sopenharmony_ci xfs_trans_brelse(NULL, bp); 46862306a36Sopenharmony_cierror_norelse: 46962306a36Sopenharmony_ci xfs_warn(mp, "%s: BAD after btree leaves for %llu extents", 47062306a36Sopenharmony_ci __func__, i); 47162306a36Sopenharmony_ci xfs_err(mp, "%s: CORRUPTED BTREE OR SOMETHING", __func__); 47262306a36Sopenharmony_ci xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); 47362306a36Sopenharmony_ci return; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/* 47762306a36Sopenharmony_ci * Validate that the bmbt_irecs being returned from bmapi are valid 47862306a36Sopenharmony_ci * given the caller's original parameters. Specifically check the 47962306a36Sopenharmony_ci * ranges of the returned irecs to ensure that they only extend beyond 48062306a36Sopenharmony_ci * the given parameters if the XFS_BMAPI_ENTIRE flag was set. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ciSTATIC void 48362306a36Sopenharmony_cixfs_bmap_validate_ret( 48462306a36Sopenharmony_ci xfs_fileoff_t bno, 48562306a36Sopenharmony_ci xfs_filblks_t len, 48662306a36Sopenharmony_ci uint32_t flags, 48762306a36Sopenharmony_ci xfs_bmbt_irec_t *mval, 48862306a36Sopenharmony_ci int nmap, 48962306a36Sopenharmony_ci int ret_nmap) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci int i; /* index to map values */ 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci ASSERT(ret_nmap <= nmap); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci for (i = 0; i < ret_nmap; i++) { 49662306a36Sopenharmony_ci ASSERT(mval[i].br_blockcount > 0); 49762306a36Sopenharmony_ci if (!(flags & XFS_BMAPI_ENTIRE)) { 49862306a36Sopenharmony_ci ASSERT(mval[i].br_startoff >= bno); 49962306a36Sopenharmony_ci ASSERT(mval[i].br_blockcount <= len); 50062306a36Sopenharmony_ci ASSERT(mval[i].br_startoff + mval[i].br_blockcount <= 50162306a36Sopenharmony_ci bno + len); 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci ASSERT(mval[i].br_startoff < bno + len); 50462306a36Sopenharmony_ci ASSERT(mval[i].br_startoff + mval[i].br_blockcount > 50562306a36Sopenharmony_ci bno); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci ASSERT(i == 0 || 50862306a36Sopenharmony_ci mval[i - 1].br_startoff + mval[i - 1].br_blockcount == 50962306a36Sopenharmony_ci mval[i].br_startoff); 51062306a36Sopenharmony_ci ASSERT(mval[i].br_startblock != DELAYSTARTBLOCK && 51162306a36Sopenharmony_ci mval[i].br_startblock != HOLESTARTBLOCK); 51262306a36Sopenharmony_ci ASSERT(mval[i].br_state == XFS_EXT_NORM || 51362306a36Sopenharmony_ci mval[i].br_state == XFS_EXT_UNWRITTEN); 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci#else 51862306a36Sopenharmony_ci#define xfs_bmap_check_leaf_extents(cur, ip, whichfork) do { } while (0) 51962306a36Sopenharmony_ci#define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap) do { } while (0) 52062306a36Sopenharmony_ci#endif /* DEBUG */ 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/* 52362306a36Sopenharmony_ci * Inode fork format manipulation functions 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* 52762306a36Sopenharmony_ci * Convert the inode format to extent format if it currently is in btree format, 52862306a36Sopenharmony_ci * but the extent list is small enough that it fits into the extent format. 52962306a36Sopenharmony_ci * 53062306a36Sopenharmony_ci * Since the extents are already in-core, all we have to do is give up the space 53162306a36Sopenharmony_ci * for the btree root and pitch the leaf block. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ciSTATIC int /* error */ 53462306a36Sopenharmony_cixfs_bmap_btree_to_extents( 53562306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 53662306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode pointer */ 53762306a36Sopenharmony_ci struct xfs_btree_cur *cur, /* btree cursor */ 53862306a36Sopenharmony_ci int *logflagsp, /* inode logging flags */ 53962306a36Sopenharmony_ci int whichfork) /* data or attr fork */ 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 54262306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 54362306a36Sopenharmony_ci struct xfs_btree_block *rblock = ifp->if_broot; 54462306a36Sopenharmony_ci struct xfs_btree_block *cblock;/* child btree block */ 54562306a36Sopenharmony_ci xfs_fsblock_t cbno; /* child block number */ 54662306a36Sopenharmony_ci struct xfs_buf *cbp; /* child block's buffer */ 54762306a36Sopenharmony_ci int error; /* error return value */ 54862306a36Sopenharmony_ci __be64 *pp; /* ptr to block address */ 54962306a36Sopenharmony_ci struct xfs_owner_info oinfo; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* check if we actually need the extent format first: */ 55262306a36Sopenharmony_ci if (!xfs_bmap_wants_extents(ip, whichfork)) 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci ASSERT(cur); 55662306a36Sopenharmony_ci ASSERT(whichfork != XFS_COW_FORK); 55762306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_BTREE); 55862306a36Sopenharmony_ci ASSERT(be16_to_cpu(rblock->bb_level) == 1); 55962306a36Sopenharmony_ci ASSERT(be16_to_cpu(rblock->bb_numrecs) == 1); 56062306a36Sopenharmony_ci ASSERT(xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0) == 1); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci pp = XFS_BMAP_BROOT_PTR_ADDR(mp, rblock, 1, ifp->if_broot_bytes); 56362306a36Sopenharmony_ci cbno = be64_to_cpu(*pp); 56462306a36Sopenharmony_ci#ifdef DEBUG 56562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(cur->bc_mp, !xfs_btree_check_lptr(cur, cbno, 1))) 56662306a36Sopenharmony_ci return -EFSCORRUPTED; 56762306a36Sopenharmony_ci#endif 56862306a36Sopenharmony_ci error = xfs_btree_read_bufl(mp, tp, cbno, &cbp, XFS_BMAP_BTREE_REF, 56962306a36Sopenharmony_ci &xfs_bmbt_buf_ops); 57062306a36Sopenharmony_ci if (error) 57162306a36Sopenharmony_ci return error; 57262306a36Sopenharmony_ci cblock = XFS_BUF_TO_BLOCK(cbp); 57362306a36Sopenharmony_ci if ((error = xfs_btree_check_block(cur, cblock, 0, cbp))) 57462306a36Sopenharmony_ci return error; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, whichfork); 57762306a36Sopenharmony_ci error = xfs_free_extent_later(cur->bc_tp, cbno, 1, &oinfo, 57862306a36Sopenharmony_ci XFS_AG_RESV_NONE); 57962306a36Sopenharmony_ci if (error) 58062306a36Sopenharmony_ci return error; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ip->i_nblocks--; 58362306a36Sopenharmony_ci xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L); 58462306a36Sopenharmony_ci xfs_trans_binval(tp, cbp); 58562306a36Sopenharmony_ci if (cur->bc_levels[0].bp == cbp) 58662306a36Sopenharmony_ci cur->bc_levels[0].bp = NULL; 58762306a36Sopenharmony_ci xfs_iroot_realloc(ip, -1, whichfork); 58862306a36Sopenharmony_ci ASSERT(ifp->if_broot == NULL); 58962306a36Sopenharmony_ci ifp->if_format = XFS_DINODE_FMT_EXTENTS; 59062306a36Sopenharmony_ci *logflagsp |= XFS_ILOG_CORE | xfs_ilog_fext(whichfork); 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/* 59562306a36Sopenharmony_ci * Convert an extents-format file into a btree-format file. 59662306a36Sopenharmony_ci * The new file will have a root block (in the inode) and a single child block. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ciSTATIC int /* error */ 59962306a36Sopenharmony_cixfs_bmap_extents_to_btree( 60062306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 60162306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode pointer */ 60262306a36Sopenharmony_ci struct xfs_btree_cur **curp, /* cursor returned to caller */ 60362306a36Sopenharmony_ci int wasdel, /* converting a delayed alloc */ 60462306a36Sopenharmony_ci int *logflagsp, /* inode logging flags */ 60562306a36Sopenharmony_ci int whichfork) /* data or attr fork */ 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct xfs_btree_block *ablock; /* allocated (child) bt block */ 60862306a36Sopenharmony_ci struct xfs_buf *abp; /* buffer for ablock */ 60962306a36Sopenharmony_ci struct xfs_alloc_arg args; /* allocation arguments */ 61062306a36Sopenharmony_ci struct xfs_bmbt_rec *arp; /* child record pointer */ 61162306a36Sopenharmony_ci struct xfs_btree_block *block; /* btree root block */ 61262306a36Sopenharmony_ci struct xfs_btree_cur *cur; /* bmap btree cursor */ 61362306a36Sopenharmony_ci int error; /* error return value */ 61462306a36Sopenharmony_ci struct xfs_ifork *ifp; /* inode fork pointer */ 61562306a36Sopenharmony_ci struct xfs_bmbt_key *kp; /* root block key pointer */ 61662306a36Sopenharmony_ci struct xfs_mount *mp; /* mount structure */ 61762306a36Sopenharmony_ci xfs_bmbt_ptr_t *pp; /* root block address pointer */ 61862306a36Sopenharmony_ci struct xfs_iext_cursor icur; 61962306a36Sopenharmony_ci struct xfs_bmbt_irec rec; 62062306a36Sopenharmony_ci xfs_extnum_t cnt = 0; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci mp = ip->i_mount; 62362306a36Sopenharmony_ci ASSERT(whichfork != XFS_COW_FORK); 62462306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 62562306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_EXTENTS); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * Make space in the inode incore. This needs to be undone if we fail 62962306a36Sopenharmony_ci * to expand the root. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci xfs_iroot_realloc(ip, 1, whichfork); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* 63462306a36Sopenharmony_ci * Fill in the root. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci block = ifp->if_broot; 63762306a36Sopenharmony_ci xfs_btree_init_block_int(mp, block, XFS_BUF_DADDR_NULL, 63862306a36Sopenharmony_ci XFS_BTNUM_BMAP, 1, 1, ip->i_ino, 63962306a36Sopenharmony_ci XFS_BTREE_LONG_PTRS); 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * Need a cursor. Can't allocate until bb_level is filled in. 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); 64462306a36Sopenharmony_ci cur->bc_ino.flags = wasdel ? XFS_BTCUR_BMBT_WASDEL : 0; 64562306a36Sopenharmony_ci /* 64662306a36Sopenharmony_ci * Convert to a btree with two levels, one record in root. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci ifp->if_format = XFS_DINODE_FMT_BTREE; 64962306a36Sopenharmony_ci memset(&args, 0, sizeof(args)); 65062306a36Sopenharmony_ci args.tp = tp; 65162306a36Sopenharmony_ci args.mp = mp; 65262306a36Sopenharmony_ci xfs_rmap_ino_bmbt_owner(&args.oinfo, ip->i_ino, whichfork); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci args.minlen = args.maxlen = args.prod = 1; 65562306a36Sopenharmony_ci args.wasdel = wasdel; 65662306a36Sopenharmony_ci *logflagsp = 0; 65762306a36Sopenharmony_ci error = xfs_alloc_vextent_start_ag(&args, 65862306a36Sopenharmony_ci XFS_INO_TO_FSB(mp, ip->i_ino)); 65962306a36Sopenharmony_ci if (error) 66062306a36Sopenharmony_ci goto out_root_realloc; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* 66362306a36Sopenharmony_ci * Allocation can't fail, the space was reserved. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) { 66662306a36Sopenharmony_ci error = -ENOSPC; 66762306a36Sopenharmony_ci goto out_root_realloc; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci cur->bc_ino.allocated++; 67162306a36Sopenharmony_ci ip->i_nblocks++; 67262306a36Sopenharmony_ci xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, 1L); 67362306a36Sopenharmony_ci error = xfs_trans_get_buf(tp, mp->m_ddev_targp, 67462306a36Sopenharmony_ci XFS_FSB_TO_DADDR(mp, args.fsbno), 67562306a36Sopenharmony_ci mp->m_bsize, 0, &abp); 67662306a36Sopenharmony_ci if (error) 67762306a36Sopenharmony_ci goto out_unreserve_dquot; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* 68062306a36Sopenharmony_ci * Fill in the child block. 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci abp->b_ops = &xfs_bmbt_buf_ops; 68362306a36Sopenharmony_ci ablock = XFS_BUF_TO_BLOCK(abp); 68462306a36Sopenharmony_ci xfs_btree_init_block_int(mp, ablock, xfs_buf_daddr(abp), 68562306a36Sopenharmony_ci XFS_BTNUM_BMAP, 0, 0, ip->i_ino, 68662306a36Sopenharmony_ci XFS_BTREE_LONG_PTRS); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci for_each_xfs_iext(ifp, &icur, &rec) { 68962306a36Sopenharmony_ci if (isnullstartblock(rec.br_startblock)) 69062306a36Sopenharmony_ci continue; 69162306a36Sopenharmony_ci arp = XFS_BMBT_REC_ADDR(mp, ablock, 1 + cnt); 69262306a36Sopenharmony_ci xfs_bmbt_disk_set_all(arp, &rec); 69362306a36Sopenharmony_ci cnt++; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci ASSERT(cnt == ifp->if_nextents); 69662306a36Sopenharmony_ci xfs_btree_set_numrecs(ablock, cnt); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * Fill in the root key and pointer. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci kp = XFS_BMBT_KEY_ADDR(mp, block, 1); 70262306a36Sopenharmony_ci arp = XFS_BMBT_REC_ADDR(mp, ablock, 1); 70362306a36Sopenharmony_ci kp->br_startoff = cpu_to_be64(xfs_bmbt_disk_get_startoff(arp)); 70462306a36Sopenharmony_ci pp = XFS_BMBT_PTR_ADDR(mp, block, 1, xfs_bmbt_get_maxrecs(cur, 70562306a36Sopenharmony_ci be16_to_cpu(block->bb_level))); 70662306a36Sopenharmony_ci *pp = cpu_to_be64(args.fsbno); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* 70962306a36Sopenharmony_ci * Do all this logging at the end so that 71062306a36Sopenharmony_ci * the root is at the right level. 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ci xfs_btree_log_block(cur, abp, XFS_BB_ALL_BITS); 71362306a36Sopenharmony_ci xfs_btree_log_recs(cur, abp, 1, be16_to_cpu(ablock->bb_numrecs)); 71462306a36Sopenharmony_ci ASSERT(*curp == NULL); 71562306a36Sopenharmony_ci *curp = cur; 71662306a36Sopenharmony_ci *logflagsp = XFS_ILOG_CORE | xfs_ilog_fbroot(whichfork); 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciout_unreserve_dquot: 72062306a36Sopenharmony_ci xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L); 72162306a36Sopenharmony_ciout_root_realloc: 72262306a36Sopenharmony_ci xfs_iroot_realloc(ip, -1, whichfork); 72362306a36Sopenharmony_ci ifp->if_format = XFS_DINODE_FMT_EXTENTS; 72462306a36Sopenharmony_ci ASSERT(ifp->if_broot == NULL); 72562306a36Sopenharmony_ci xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return error; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* 73162306a36Sopenharmony_ci * Convert a local file to an extents file. 73262306a36Sopenharmony_ci * This code is out of bounds for data forks of regular files, 73362306a36Sopenharmony_ci * since the file data needs to get logged so things will stay consistent. 73462306a36Sopenharmony_ci * (The bmap-level manipulations are ok, though). 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_civoid 73762306a36Sopenharmony_cixfs_bmap_local_to_extents_empty( 73862306a36Sopenharmony_ci struct xfs_trans *tp, 73962306a36Sopenharmony_ci struct xfs_inode *ip, 74062306a36Sopenharmony_ci int whichfork) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci ASSERT(whichfork != XFS_COW_FORK); 74562306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); 74662306a36Sopenharmony_ci ASSERT(ifp->if_bytes == 0); 74762306a36Sopenharmony_ci ASSERT(ifp->if_nextents == 0); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci xfs_bmap_forkoff_reset(ip, whichfork); 75062306a36Sopenharmony_ci ifp->if_u1.if_root = NULL; 75162306a36Sopenharmony_ci ifp->if_height = 0; 75262306a36Sopenharmony_ci ifp->if_format = XFS_DINODE_FMT_EXTENTS; 75362306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ciSTATIC int /* error */ 75862306a36Sopenharmony_cixfs_bmap_local_to_extents( 75962306a36Sopenharmony_ci xfs_trans_t *tp, /* transaction pointer */ 76062306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 76162306a36Sopenharmony_ci xfs_extlen_t total, /* total blocks needed by transaction */ 76262306a36Sopenharmony_ci int *logflagsp, /* inode logging flags */ 76362306a36Sopenharmony_ci int whichfork, 76462306a36Sopenharmony_ci void (*init_fn)(struct xfs_trans *tp, 76562306a36Sopenharmony_ci struct xfs_buf *bp, 76662306a36Sopenharmony_ci struct xfs_inode *ip, 76762306a36Sopenharmony_ci struct xfs_ifork *ifp)) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci int error = 0; 77062306a36Sopenharmony_ci int flags; /* logging flags returned */ 77162306a36Sopenharmony_ci struct xfs_ifork *ifp; /* inode fork pointer */ 77262306a36Sopenharmony_ci xfs_alloc_arg_t args; /* allocation arguments */ 77362306a36Sopenharmony_ci struct xfs_buf *bp; /* buffer for extent block */ 77462306a36Sopenharmony_ci struct xfs_bmbt_irec rec; 77562306a36Sopenharmony_ci struct xfs_iext_cursor icur; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * We don't want to deal with the case of keeping inode data inline yet. 77962306a36Sopenharmony_ci * So sending the data fork of a regular inode is invalid. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci ASSERT(!(S_ISREG(VFS_I(ip)->i_mode) && whichfork == XFS_DATA_FORK)); 78262306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 78362306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (!ifp->if_bytes) { 78662306a36Sopenharmony_ci xfs_bmap_local_to_extents_empty(tp, ip, whichfork); 78762306a36Sopenharmony_ci flags = XFS_ILOG_CORE; 78862306a36Sopenharmony_ci goto done; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci flags = 0; 79262306a36Sopenharmony_ci error = 0; 79362306a36Sopenharmony_ci memset(&args, 0, sizeof(args)); 79462306a36Sopenharmony_ci args.tp = tp; 79562306a36Sopenharmony_ci args.mp = ip->i_mount; 79662306a36Sopenharmony_ci args.total = total; 79762306a36Sopenharmony_ci args.minlen = args.maxlen = args.prod = 1; 79862306a36Sopenharmony_ci xfs_rmap_ino_owner(&args.oinfo, ip->i_ino, whichfork, 0); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* 80162306a36Sopenharmony_ci * Allocate a block. We know we need only one, since the 80262306a36Sopenharmony_ci * file currently fits in an inode. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci args.total = total; 80562306a36Sopenharmony_ci args.minlen = args.maxlen = args.prod = 1; 80662306a36Sopenharmony_ci error = xfs_alloc_vextent_start_ag(&args, 80762306a36Sopenharmony_ci XFS_INO_TO_FSB(args.mp, ip->i_ino)); 80862306a36Sopenharmony_ci if (error) 80962306a36Sopenharmony_ci goto done; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Can't fail, the space was reserved. */ 81262306a36Sopenharmony_ci ASSERT(args.fsbno != NULLFSBLOCK); 81362306a36Sopenharmony_ci ASSERT(args.len == 1); 81462306a36Sopenharmony_ci error = xfs_trans_get_buf(tp, args.mp->m_ddev_targp, 81562306a36Sopenharmony_ci XFS_FSB_TO_DADDR(args.mp, args.fsbno), 81662306a36Sopenharmony_ci args.mp->m_bsize, 0, &bp); 81762306a36Sopenharmony_ci if (error) 81862306a36Sopenharmony_ci goto done; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* 82162306a36Sopenharmony_ci * Initialize the block, copy the data and log the remote buffer. 82262306a36Sopenharmony_ci * 82362306a36Sopenharmony_ci * The callout is responsible for logging because the remote format 82462306a36Sopenharmony_ci * might differ from the local format and thus we don't know how much to 82562306a36Sopenharmony_ci * log here. Note that init_fn must also set the buffer log item type 82662306a36Sopenharmony_ci * correctly. 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_ci init_fn(tp, bp, ip, ifp); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* account for the change in fork size */ 83162306a36Sopenharmony_ci xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); 83262306a36Sopenharmony_ci xfs_bmap_local_to_extents_empty(tp, ip, whichfork); 83362306a36Sopenharmony_ci flags |= XFS_ILOG_CORE; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci ifp->if_u1.if_root = NULL; 83662306a36Sopenharmony_ci ifp->if_height = 0; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci rec.br_startoff = 0; 83962306a36Sopenharmony_ci rec.br_startblock = args.fsbno; 84062306a36Sopenharmony_ci rec.br_blockcount = 1; 84162306a36Sopenharmony_ci rec.br_state = XFS_EXT_NORM; 84262306a36Sopenharmony_ci xfs_iext_first(ifp, &icur); 84362306a36Sopenharmony_ci xfs_iext_insert(ip, &icur, &rec, 0); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci ifp->if_nextents = 1; 84662306a36Sopenharmony_ci ip->i_nblocks = 1; 84762306a36Sopenharmony_ci xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, 1L); 84862306a36Sopenharmony_ci flags |= xfs_ilog_fext(whichfork); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cidone: 85162306a36Sopenharmony_ci *logflagsp = flags; 85262306a36Sopenharmony_ci return error; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci/* 85662306a36Sopenharmony_ci * Called from xfs_bmap_add_attrfork to handle btree format files. 85762306a36Sopenharmony_ci */ 85862306a36Sopenharmony_ciSTATIC int /* error */ 85962306a36Sopenharmony_cixfs_bmap_add_attrfork_btree( 86062306a36Sopenharmony_ci xfs_trans_t *tp, /* transaction pointer */ 86162306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 86262306a36Sopenharmony_ci int *flags) /* inode logging flags */ 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct xfs_btree_block *block = ip->i_df.if_broot; 86562306a36Sopenharmony_ci struct xfs_btree_cur *cur; /* btree cursor */ 86662306a36Sopenharmony_ci int error; /* error return value */ 86762306a36Sopenharmony_ci xfs_mount_t *mp; /* file system mount struct */ 86862306a36Sopenharmony_ci int stat; /* newroot status */ 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci mp = ip->i_mount; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (XFS_BMAP_BMDR_SPACE(block) <= xfs_inode_data_fork_size(ip)) 87362306a36Sopenharmony_ci *flags |= XFS_ILOG_DBROOT; 87462306a36Sopenharmony_ci else { 87562306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, XFS_DATA_FORK); 87662306a36Sopenharmony_ci error = xfs_bmbt_lookup_first(cur, &stat); 87762306a36Sopenharmony_ci if (error) 87862306a36Sopenharmony_ci goto error0; 87962306a36Sopenharmony_ci /* must be at least one entry */ 88062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, stat != 1)) { 88162306a36Sopenharmony_ci error = -EFSCORRUPTED; 88262306a36Sopenharmony_ci goto error0; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci if ((error = xfs_btree_new_iroot(cur, flags, &stat))) 88562306a36Sopenharmony_ci goto error0; 88662306a36Sopenharmony_ci if (stat == 0) { 88762306a36Sopenharmony_ci xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); 88862306a36Sopenharmony_ci return -ENOSPC; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci cur->bc_ino.allocated = 0; 89162306a36Sopenharmony_ci xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_cierror0: 89562306a36Sopenharmony_ci xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); 89662306a36Sopenharmony_ci return error; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/* 90062306a36Sopenharmony_ci * Called from xfs_bmap_add_attrfork to handle extents format files. 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_ciSTATIC int /* error */ 90362306a36Sopenharmony_cixfs_bmap_add_attrfork_extents( 90462306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 90562306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode pointer */ 90662306a36Sopenharmony_ci int *flags) /* inode logging flags */ 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct xfs_btree_cur *cur; /* bmap btree cursor */ 90962306a36Sopenharmony_ci int error; /* error return value */ 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (ip->i_df.if_nextents * sizeof(struct xfs_bmbt_rec) <= 91262306a36Sopenharmony_ci xfs_inode_data_fork_size(ip)) 91362306a36Sopenharmony_ci return 0; 91462306a36Sopenharmony_ci cur = NULL; 91562306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(tp, ip, &cur, 0, flags, 91662306a36Sopenharmony_ci XFS_DATA_FORK); 91762306a36Sopenharmony_ci if (cur) { 91862306a36Sopenharmony_ci cur->bc_ino.allocated = 0; 91962306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci return error; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci/* 92562306a36Sopenharmony_ci * Called from xfs_bmap_add_attrfork to handle local format files. Each 92662306a36Sopenharmony_ci * different data fork content type needs a different callout to do the 92762306a36Sopenharmony_ci * conversion. Some are basic and only require special block initialisation 92862306a36Sopenharmony_ci * callouts for the data formating, others (directories) are so specialised they 92962306a36Sopenharmony_ci * handle everything themselves. 93062306a36Sopenharmony_ci * 93162306a36Sopenharmony_ci * XXX (dgc): investigate whether directory conversion can use the generic 93262306a36Sopenharmony_ci * formatting callout. It should be possible - it's just a very complex 93362306a36Sopenharmony_ci * formatter. 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ciSTATIC int /* error */ 93662306a36Sopenharmony_cixfs_bmap_add_attrfork_local( 93762306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 93862306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode pointer */ 93962306a36Sopenharmony_ci int *flags) /* inode logging flags */ 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct xfs_da_args dargs; /* args for dir/attr code */ 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (ip->i_df.if_bytes <= xfs_inode_data_fork_size(ip)) 94462306a36Sopenharmony_ci return 0; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (S_ISDIR(VFS_I(ip)->i_mode)) { 94762306a36Sopenharmony_ci memset(&dargs, 0, sizeof(dargs)); 94862306a36Sopenharmony_ci dargs.geo = ip->i_mount->m_dir_geo; 94962306a36Sopenharmony_ci dargs.dp = ip; 95062306a36Sopenharmony_ci dargs.total = dargs.geo->fsbcount; 95162306a36Sopenharmony_ci dargs.whichfork = XFS_DATA_FORK; 95262306a36Sopenharmony_ci dargs.trans = tp; 95362306a36Sopenharmony_ci return xfs_dir2_sf_to_block(&dargs); 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (S_ISLNK(VFS_I(ip)->i_mode)) 95762306a36Sopenharmony_ci return xfs_bmap_local_to_extents(tp, ip, 1, flags, 95862306a36Sopenharmony_ci XFS_DATA_FORK, 95962306a36Sopenharmony_ci xfs_symlink_local_to_remote); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* should only be called for types that support local format data */ 96262306a36Sopenharmony_ci ASSERT(0); 96362306a36Sopenharmony_ci return -EFSCORRUPTED; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/* 96762306a36Sopenharmony_ci * Set an inode attr fork offset based on the format of the data fork. 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_cistatic int 97062306a36Sopenharmony_cixfs_bmap_set_attrforkoff( 97162306a36Sopenharmony_ci struct xfs_inode *ip, 97262306a36Sopenharmony_ci int size, 97362306a36Sopenharmony_ci int *version) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci int default_size = xfs_default_attroffset(ip) >> 3; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci switch (ip->i_df.if_format) { 97862306a36Sopenharmony_ci case XFS_DINODE_FMT_DEV: 97962306a36Sopenharmony_ci ip->i_forkoff = default_size; 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 98262306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 98362306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 98462306a36Sopenharmony_ci ip->i_forkoff = xfs_attr_shortform_bytesfit(ip, size); 98562306a36Sopenharmony_ci if (!ip->i_forkoff) 98662306a36Sopenharmony_ci ip->i_forkoff = default_size; 98762306a36Sopenharmony_ci else if (xfs_has_attr2(ip->i_mount) && version) 98862306a36Sopenharmony_ci *version = 2; 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci default: 99162306a36Sopenharmony_ci ASSERT(0); 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/* 99962306a36Sopenharmony_ci * Convert inode from non-attributed to attributed. 100062306a36Sopenharmony_ci * Must not be in a transaction, ip must not be locked. 100162306a36Sopenharmony_ci */ 100262306a36Sopenharmony_ciint /* error code */ 100362306a36Sopenharmony_cixfs_bmap_add_attrfork( 100462306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 100562306a36Sopenharmony_ci int size, /* space new attribute needs */ 100662306a36Sopenharmony_ci int rsvd) /* xact may use reserved blks */ 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci xfs_mount_t *mp; /* mount structure */ 100962306a36Sopenharmony_ci xfs_trans_t *tp; /* transaction pointer */ 101062306a36Sopenharmony_ci int blks; /* space reservation */ 101162306a36Sopenharmony_ci int version = 1; /* superblock attr version */ 101262306a36Sopenharmony_ci int logflags; /* logging flags */ 101362306a36Sopenharmony_ci int error; /* error return value */ 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci ASSERT(xfs_inode_has_attr_fork(ip) == 0); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci mp = ip->i_mount; 101862306a36Sopenharmony_ci ASSERT(!XFS_NOT_DQATTACHED(mp, ip)); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci blks = XFS_ADDAFORK_SPACE_RES(mp); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0, 102362306a36Sopenharmony_ci rsvd, &tp); 102462306a36Sopenharmony_ci if (error) 102562306a36Sopenharmony_ci return error; 102662306a36Sopenharmony_ci if (xfs_inode_has_attr_fork(ip)) 102762306a36Sopenharmony_ci goto trans_cancel; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 103062306a36Sopenharmony_ci error = xfs_bmap_set_attrforkoff(ip, size, &version); 103162306a36Sopenharmony_ci if (error) 103262306a36Sopenharmony_ci goto trans_cancel; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0); 103562306a36Sopenharmony_ci logflags = 0; 103662306a36Sopenharmony_ci switch (ip->i_df.if_format) { 103762306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 103862306a36Sopenharmony_ci error = xfs_bmap_add_attrfork_local(tp, ip, &logflags); 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 104162306a36Sopenharmony_ci error = xfs_bmap_add_attrfork_extents(tp, ip, &logflags); 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 104462306a36Sopenharmony_ci error = xfs_bmap_add_attrfork_btree(tp, ip, &logflags); 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci default: 104762306a36Sopenharmony_ci error = 0; 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci if (logflags) 105162306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, logflags); 105262306a36Sopenharmony_ci if (error) 105362306a36Sopenharmony_ci goto trans_cancel; 105462306a36Sopenharmony_ci if (!xfs_has_attr(mp) || 105562306a36Sopenharmony_ci (!xfs_has_attr2(mp) && version == 2)) { 105662306a36Sopenharmony_ci bool log_sb = false; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci spin_lock(&mp->m_sb_lock); 105962306a36Sopenharmony_ci if (!xfs_has_attr(mp)) { 106062306a36Sopenharmony_ci xfs_add_attr(mp); 106162306a36Sopenharmony_ci log_sb = true; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci if (!xfs_has_attr2(mp) && version == 2) { 106462306a36Sopenharmony_ci xfs_add_attr2(mp); 106562306a36Sopenharmony_ci log_sb = true; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci spin_unlock(&mp->m_sb_lock); 106862306a36Sopenharmony_ci if (log_sb) 106962306a36Sopenharmony_ci xfs_log_sb(tp); 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci error = xfs_trans_commit(tp); 107362306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_EXCL); 107462306a36Sopenharmony_ci return error; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_citrans_cancel: 107762306a36Sopenharmony_ci xfs_trans_cancel(tp); 107862306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_EXCL); 107962306a36Sopenharmony_ci return error; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci/* 108362306a36Sopenharmony_ci * Internal and external extent tree search functions. 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistruct xfs_iread_state { 108762306a36Sopenharmony_ci struct xfs_iext_cursor icur; 108862306a36Sopenharmony_ci xfs_extnum_t loaded; 108962306a36Sopenharmony_ci}; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ciint 109262306a36Sopenharmony_cixfs_bmap_complain_bad_rec( 109362306a36Sopenharmony_ci struct xfs_inode *ip, 109462306a36Sopenharmony_ci int whichfork, 109562306a36Sopenharmony_ci xfs_failaddr_t fa, 109662306a36Sopenharmony_ci const struct xfs_bmbt_irec *irec) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 109962306a36Sopenharmony_ci const char *forkname; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci switch (whichfork) { 110262306a36Sopenharmony_ci case XFS_DATA_FORK: forkname = "data"; break; 110362306a36Sopenharmony_ci case XFS_ATTR_FORK: forkname = "attr"; break; 110462306a36Sopenharmony_ci case XFS_COW_FORK: forkname = "CoW"; break; 110562306a36Sopenharmony_ci default: forkname = "???"; break; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci xfs_warn(mp, 110962306a36Sopenharmony_ci "Bmap BTree record corruption in inode 0x%llx %s fork detected at %pS!", 111062306a36Sopenharmony_ci ip->i_ino, forkname, fa); 111162306a36Sopenharmony_ci xfs_warn(mp, 111262306a36Sopenharmony_ci "Offset 0x%llx, start block 0x%llx, block count 0x%llx state 0x%x", 111362306a36Sopenharmony_ci irec->br_startoff, irec->br_startblock, irec->br_blockcount, 111462306a36Sopenharmony_ci irec->br_state); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return -EFSCORRUPTED; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/* Stuff every bmbt record from this block into the incore extent map. */ 112062306a36Sopenharmony_cistatic int 112162306a36Sopenharmony_cixfs_iread_bmbt_block( 112262306a36Sopenharmony_ci struct xfs_btree_cur *cur, 112362306a36Sopenharmony_ci int level, 112462306a36Sopenharmony_ci void *priv) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci struct xfs_iread_state *ir = priv; 112762306a36Sopenharmony_ci struct xfs_mount *mp = cur->bc_mp; 112862306a36Sopenharmony_ci struct xfs_inode *ip = cur->bc_ino.ip; 112962306a36Sopenharmony_ci struct xfs_btree_block *block; 113062306a36Sopenharmony_ci struct xfs_buf *bp; 113162306a36Sopenharmony_ci struct xfs_bmbt_rec *frp; 113262306a36Sopenharmony_ci xfs_extnum_t num_recs; 113362306a36Sopenharmony_ci xfs_extnum_t j; 113462306a36Sopenharmony_ci int whichfork = cur->bc_ino.whichfork; 113562306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci block = xfs_btree_get_block(cur, level, &bp); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* Abort if we find more records than nextents. */ 114062306a36Sopenharmony_ci num_recs = xfs_btree_get_numrecs(block); 114162306a36Sopenharmony_ci if (unlikely(ir->loaded + num_recs > ifp->if_nextents)) { 114262306a36Sopenharmony_ci xfs_warn(ip->i_mount, "corrupt dinode %llu, (btree extents).", 114362306a36Sopenharmony_ci (unsigned long long)ip->i_ino); 114462306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, block, 114562306a36Sopenharmony_ci sizeof(*block), __this_address); 114662306a36Sopenharmony_ci return -EFSCORRUPTED; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* Copy records into the incore cache. */ 115062306a36Sopenharmony_ci frp = XFS_BMBT_REC_ADDR(mp, block, 1); 115162306a36Sopenharmony_ci for (j = 0; j < num_recs; j++, frp++, ir->loaded++) { 115262306a36Sopenharmony_ci struct xfs_bmbt_irec new; 115362306a36Sopenharmony_ci xfs_failaddr_t fa; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci xfs_bmbt_disk_get_all(frp, &new); 115662306a36Sopenharmony_ci fa = xfs_bmap_validate_extent(ip, whichfork, &new); 115762306a36Sopenharmony_ci if (fa) { 115862306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, 115962306a36Sopenharmony_ci "xfs_iread_extents(2)", frp, 116062306a36Sopenharmony_ci sizeof(*frp), fa); 116162306a36Sopenharmony_ci return xfs_bmap_complain_bad_rec(ip, whichfork, fa, 116262306a36Sopenharmony_ci &new); 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci xfs_iext_insert(ip, &ir->icur, &new, 116562306a36Sopenharmony_ci xfs_bmap_fork_to_state(whichfork)); 116662306a36Sopenharmony_ci trace_xfs_read_extent(ip, &ir->icur, 116762306a36Sopenharmony_ci xfs_bmap_fork_to_state(whichfork), _THIS_IP_); 116862306a36Sopenharmony_ci xfs_iext_next(ifp, &ir->icur); 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci/* 117562306a36Sopenharmony_ci * Read in extents from a btree-format inode. 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_ciint 117862306a36Sopenharmony_cixfs_iread_extents( 117962306a36Sopenharmony_ci struct xfs_trans *tp, 118062306a36Sopenharmony_ci struct xfs_inode *ip, 118162306a36Sopenharmony_ci int whichfork) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci struct xfs_iread_state ir; 118462306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 118562306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 118662306a36Sopenharmony_ci struct xfs_btree_cur *cur; 118762306a36Sopenharmony_ci int error; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (!xfs_need_iread_extents(ifp)) 119062306a36Sopenharmony_ci return 0; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci ir.loaded = 0; 119562306a36Sopenharmony_ci xfs_iext_first(ifp, &ir.icur); 119662306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); 119762306a36Sopenharmony_ci error = xfs_btree_visit_blocks(cur, xfs_iread_bmbt_block, 119862306a36Sopenharmony_ci XFS_BTREE_VISIT_RECORDS, &ir); 119962306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 120062306a36Sopenharmony_ci if (error) 120162306a36Sopenharmony_ci goto out; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, ir.loaded != ifp->if_nextents)) { 120462306a36Sopenharmony_ci error = -EFSCORRUPTED; 120562306a36Sopenharmony_ci goto out; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci ASSERT(ir.loaded == xfs_iext_count(ifp)); 120862306a36Sopenharmony_ci /* 120962306a36Sopenharmony_ci * Use release semantics so that we can use acquire semantics in 121062306a36Sopenharmony_ci * xfs_need_iread_extents and be guaranteed to see a valid mapping tree 121162306a36Sopenharmony_ci * after that load. 121262306a36Sopenharmony_ci */ 121362306a36Sopenharmony_ci smp_store_release(&ifp->if_needextents, 0); 121462306a36Sopenharmony_ci return 0; 121562306a36Sopenharmony_ciout: 121662306a36Sopenharmony_ci xfs_iext_destroy(ifp); 121762306a36Sopenharmony_ci return error; 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci/* 122162306a36Sopenharmony_ci * Returns the relative block number of the first unused block(s) in the given 122262306a36Sopenharmony_ci * fork with at least "len" logically contiguous blocks free. This is the 122362306a36Sopenharmony_ci * lowest-address hole if the fork has holes, else the first block past the end 122462306a36Sopenharmony_ci * of fork. Return 0 if the fork is currently local (in-inode). 122562306a36Sopenharmony_ci */ 122662306a36Sopenharmony_ciint /* error */ 122762306a36Sopenharmony_cixfs_bmap_first_unused( 122862306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 122962306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode */ 123062306a36Sopenharmony_ci xfs_extlen_t len, /* size of hole to find */ 123162306a36Sopenharmony_ci xfs_fileoff_t *first_unused, /* unused block */ 123262306a36Sopenharmony_ci int whichfork) /* data or attr fork */ 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 123562306a36Sopenharmony_ci struct xfs_bmbt_irec got; 123662306a36Sopenharmony_ci struct xfs_iext_cursor icur; 123762306a36Sopenharmony_ci xfs_fileoff_t lastaddr = 0; 123862306a36Sopenharmony_ci xfs_fileoff_t lowest, max; 123962306a36Sopenharmony_ci int error; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_LOCAL) { 124262306a36Sopenharmony_ci *first_unused = 0; 124362306a36Sopenharmony_ci return 0; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci ASSERT(xfs_ifork_has_extents(ifp)); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 124962306a36Sopenharmony_ci if (error) 125062306a36Sopenharmony_ci return error; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci lowest = max = *first_unused; 125362306a36Sopenharmony_ci for_each_xfs_iext(ifp, &icur, &got) { 125462306a36Sopenharmony_ci /* 125562306a36Sopenharmony_ci * See if the hole before this extent will work. 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_ci if (got.br_startoff >= lowest + len && 125862306a36Sopenharmony_ci got.br_startoff - max >= len) 125962306a36Sopenharmony_ci break; 126062306a36Sopenharmony_ci lastaddr = got.br_startoff + got.br_blockcount; 126162306a36Sopenharmony_ci max = XFS_FILEOFF_MAX(lastaddr, lowest); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci *first_unused = max; 126562306a36Sopenharmony_ci return 0; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci/* 126962306a36Sopenharmony_ci * Returns the file-relative block number of the last block - 1 before 127062306a36Sopenharmony_ci * last_block (input value) in the file. 127162306a36Sopenharmony_ci * This is not based on i_size, it is based on the extent records. 127262306a36Sopenharmony_ci * Returns 0 for local files, as they do not have extent records. 127362306a36Sopenharmony_ci */ 127462306a36Sopenharmony_ciint /* error */ 127562306a36Sopenharmony_cixfs_bmap_last_before( 127662306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 127762306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode */ 127862306a36Sopenharmony_ci xfs_fileoff_t *last_block, /* last block */ 127962306a36Sopenharmony_ci int whichfork) /* data or attr fork */ 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 128262306a36Sopenharmony_ci struct xfs_bmbt_irec got; 128362306a36Sopenharmony_ci struct xfs_iext_cursor icur; 128462306a36Sopenharmony_ci int error; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci switch (ifp->if_format) { 128762306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 128862306a36Sopenharmony_ci *last_block = 0; 128962306a36Sopenharmony_ci return 0; 129062306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 129162306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 129262306a36Sopenharmony_ci break; 129362306a36Sopenharmony_ci default: 129462306a36Sopenharmony_ci ASSERT(0); 129562306a36Sopenharmony_ci return -EFSCORRUPTED; 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 129962306a36Sopenharmony_ci if (error) 130062306a36Sopenharmony_ci return error; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (!xfs_iext_lookup_extent_before(ip, ifp, last_block, &icur, &got)) 130362306a36Sopenharmony_ci *last_block = 0; 130462306a36Sopenharmony_ci return 0; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ciint 130862306a36Sopenharmony_cixfs_bmap_last_extent( 130962306a36Sopenharmony_ci struct xfs_trans *tp, 131062306a36Sopenharmony_ci struct xfs_inode *ip, 131162306a36Sopenharmony_ci int whichfork, 131262306a36Sopenharmony_ci struct xfs_bmbt_irec *rec, 131362306a36Sopenharmony_ci int *is_empty) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 131662306a36Sopenharmony_ci struct xfs_iext_cursor icur; 131762306a36Sopenharmony_ci int error; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 132062306a36Sopenharmony_ci if (error) 132162306a36Sopenharmony_ci return error; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci xfs_iext_last(ifp, &icur); 132462306a36Sopenharmony_ci if (!xfs_iext_get_extent(ifp, &icur, rec)) 132562306a36Sopenharmony_ci *is_empty = 1; 132662306a36Sopenharmony_ci else 132762306a36Sopenharmony_ci *is_empty = 0; 132862306a36Sopenharmony_ci return 0; 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci/* 133262306a36Sopenharmony_ci * Check the last inode extent to determine whether this allocation will result 133362306a36Sopenharmony_ci * in blocks being allocated at the end of the file. When we allocate new data 133462306a36Sopenharmony_ci * blocks at the end of the file which do not start at the previous data block, 133562306a36Sopenharmony_ci * we will try to align the new blocks at stripe unit boundaries. 133662306a36Sopenharmony_ci * 133762306a36Sopenharmony_ci * Returns 1 in bma->aeof if the file (fork) is empty as any new write will be 133862306a36Sopenharmony_ci * at, or past the EOF. 133962306a36Sopenharmony_ci */ 134062306a36Sopenharmony_ciSTATIC int 134162306a36Sopenharmony_cixfs_bmap_isaeof( 134262306a36Sopenharmony_ci struct xfs_bmalloca *bma, 134362306a36Sopenharmony_ci int whichfork) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci struct xfs_bmbt_irec rec; 134662306a36Sopenharmony_ci int is_empty; 134762306a36Sopenharmony_ci int error; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci bma->aeof = false; 135062306a36Sopenharmony_ci error = xfs_bmap_last_extent(NULL, bma->ip, whichfork, &rec, 135162306a36Sopenharmony_ci &is_empty); 135262306a36Sopenharmony_ci if (error) 135362306a36Sopenharmony_ci return error; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (is_empty) { 135662306a36Sopenharmony_ci bma->aeof = true; 135762306a36Sopenharmony_ci return 0; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci /* 136162306a36Sopenharmony_ci * Check if we are allocation or past the last extent, or at least into 136262306a36Sopenharmony_ci * the last delayed allocated extent. 136362306a36Sopenharmony_ci */ 136462306a36Sopenharmony_ci bma->aeof = bma->offset >= rec.br_startoff + rec.br_blockcount || 136562306a36Sopenharmony_ci (bma->offset >= rec.br_startoff && 136662306a36Sopenharmony_ci isnullstartblock(rec.br_startblock)); 136762306a36Sopenharmony_ci return 0; 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci/* 137162306a36Sopenharmony_ci * Returns the file-relative block number of the first block past eof in 137262306a36Sopenharmony_ci * the file. This is not based on i_size, it is based on the extent records. 137362306a36Sopenharmony_ci * Returns 0 for local files, as they do not have extent records. 137462306a36Sopenharmony_ci */ 137562306a36Sopenharmony_ciint 137662306a36Sopenharmony_cixfs_bmap_last_offset( 137762306a36Sopenharmony_ci struct xfs_inode *ip, 137862306a36Sopenharmony_ci xfs_fileoff_t *last_block, 137962306a36Sopenharmony_ci int whichfork) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 138262306a36Sopenharmony_ci struct xfs_bmbt_irec rec; 138362306a36Sopenharmony_ci int is_empty; 138462306a36Sopenharmony_ci int error; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci *last_block = 0; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_LOCAL) 138962306a36Sopenharmony_ci return 0; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(ip->i_mount, !xfs_ifork_has_extents(ifp))) 139262306a36Sopenharmony_ci return -EFSCORRUPTED; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci error = xfs_bmap_last_extent(NULL, ip, whichfork, &rec, &is_empty); 139562306a36Sopenharmony_ci if (error || is_empty) 139662306a36Sopenharmony_ci return error; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci *last_block = rec.br_startoff + rec.br_blockcount; 139962306a36Sopenharmony_ci return 0; 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci/* 140362306a36Sopenharmony_ci * Extent tree manipulation functions used during allocation. 140462306a36Sopenharmony_ci */ 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci/* 140762306a36Sopenharmony_ci * Convert a delayed allocation to a real allocation. 140862306a36Sopenharmony_ci */ 140962306a36Sopenharmony_ciSTATIC int /* error */ 141062306a36Sopenharmony_cixfs_bmap_add_extent_delay_real( 141162306a36Sopenharmony_ci struct xfs_bmalloca *bma, 141262306a36Sopenharmony_ci int whichfork) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci struct xfs_mount *mp = bma->ip->i_mount; 141562306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); 141662306a36Sopenharmony_ci struct xfs_bmbt_irec *new = &bma->got; 141762306a36Sopenharmony_ci int error; /* error return value */ 141862306a36Sopenharmony_ci int i; /* temp state */ 141962306a36Sopenharmony_ci xfs_fileoff_t new_endoff; /* end offset of new entry */ 142062306a36Sopenharmony_ci xfs_bmbt_irec_t r[3]; /* neighbor extent entries */ 142162306a36Sopenharmony_ci /* left is 0, right is 1, prev is 2 */ 142262306a36Sopenharmony_ci int rval=0; /* return value (logging flags) */ 142362306a36Sopenharmony_ci uint32_t state = xfs_bmap_fork_to_state(whichfork); 142462306a36Sopenharmony_ci xfs_filblks_t da_new; /* new count del alloc blocks used */ 142562306a36Sopenharmony_ci xfs_filblks_t da_old; /* old count del alloc blocks used */ 142662306a36Sopenharmony_ci xfs_filblks_t temp=0; /* value for da_new calculations */ 142762306a36Sopenharmony_ci int tmp_rval; /* partial logging flags */ 142862306a36Sopenharmony_ci struct xfs_bmbt_irec old; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci ASSERT(whichfork != XFS_ATTR_FORK); 143162306a36Sopenharmony_ci ASSERT(!isnullstartblock(new->br_startblock)); 143262306a36Sopenharmony_ci ASSERT(!bma->cur || 143362306a36Sopenharmony_ci (bma->cur->bc_ino.flags & XFS_BTCUR_BMBT_WASDEL)); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_add_exlist); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci#define LEFT r[0] 143862306a36Sopenharmony_ci#define RIGHT r[1] 143962306a36Sopenharmony_ci#define PREV r[2] 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* 144262306a36Sopenharmony_ci * Set up a bunch of variables to make the tests simpler. 144362306a36Sopenharmony_ci */ 144462306a36Sopenharmony_ci xfs_iext_get_extent(ifp, &bma->icur, &PREV); 144562306a36Sopenharmony_ci new_endoff = new->br_startoff + new->br_blockcount; 144662306a36Sopenharmony_ci ASSERT(isnullstartblock(PREV.br_startblock)); 144762306a36Sopenharmony_ci ASSERT(PREV.br_startoff <= new->br_startoff); 144862306a36Sopenharmony_ci ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci da_old = startblockval(PREV.br_startblock); 145162306a36Sopenharmony_ci da_new = 0; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* 145462306a36Sopenharmony_ci * Set flags determining what part of the previous delayed allocation 145562306a36Sopenharmony_ci * extent is being replaced by a real allocation. 145662306a36Sopenharmony_ci */ 145762306a36Sopenharmony_ci if (PREV.br_startoff == new->br_startoff) 145862306a36Sopenharmony_ci state |= BMAP_LEFT_FILLING; 145962306a36Sopenharmony_ci if (PREV.br_startoff + PREV.br_blockcount == new_endoff) 146062306a36Sopenharmony_ci state |= BMAP_RIGHT_FILLING; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci /* 146362306a36Sopenharmony_ci * Check and set flags if this segment has a left neighbor. 146462306a36Sopenharmony_ci * Don't set contiguous if the combined extent would be too large. 146562306a36Sopenharmony_ci */ 146662306a36Sopenharmony_ci if (xfs_iext_peek_prev_extent(ifp, &bma->icur, &LEFT)) { 146762306a36Sopenharmony_ci state |= BMAP_LEFT_VALID; 146862306a36Sopenharmony_ci if (isnullstartblock(LEFT.br_startblock)) 146962306a36Sopenharmony_ci state |= BMAP_LEFT_DELAY; 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if ((state & BMAP_LEFT_VALID) && !(state & BMAP_LEFT_DELAY) && 147362306a36Sopenharmony_ci LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff && 147462306a36Sopenharmony_ci LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock && 147562306a36Sopenharmony_ci LEFT.br_state == new->br_state && 147662306a36Sopenharmony_ci LEFT.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN) 147762306a36Sopenharmony_ci state |= BMAP_LEFT_CONTIG; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci /* 148062306a36Sopenharmony_ci * Check and set flags if this segment has a right neighbor. 148162306a36Sopenharmony_ci * Don't set contiguous if the combined extent would be too large. 148262306a36Sopenharmony_ci * Also check for all-three-contiguous being too large. 148362306a36Sopenharmony_ci */ 148462306a36Sopenharmony_ci if (xfs_iext_peek_next_extent(ifp, &bma->icur, &RIGHT)) { 148562306a36Sopenharmony_ci state |= BMAP_RIGHT_VALID; 148662306a36Sopenharmony_ci if (isnullstartblock(RIGHT.br_startblock)) 148762306a36Sopenharmony_ci state |= BMAP_RIGHT_DELAY; 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci if ((state & BMAP_RIGHT_VALID) && !(state & BMAP_RIGHT_DELAY) && 149162306a36Sopenharmony_ci new_endoff == RIGHT.br_startoff && 149262306a36Sopenharmony_ci new->br_startblock + new->br_blockcount == RIGHT.br_startblock && 149362306a36Sopenharmony_ci new->br_state == RIGHT.br_state && 149462306a36Sopenharmony_ci new->br_blockcount + RIGHT.br_blockcount <= XFS_MAX_BMBT_EXTLEN && 149562306a36Sopenharmony_ci ((state & (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING | 149662306a36Sopenharmony_ci BMAP_RIGHT_FILLING)) != 149762306a36Sopenharmony_ci (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING | 149862306a36Sopenharmony_ci BMAP_RIGHT_FILLING) || 149962306a36Sopenharmony_ci LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount 150062306a36Sopenharmony_ci <= XFS_MAX_BMBT_EXTLEN)) 150162306a36Sopenharmony_ci state |= BMAP_RIGHT_CONTIG; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci error = 0; 150462306a36Sopenharmony_ci /* 150562306a36Sopenharmony_ci * Switch out based on the FILLING and CONTIG state bits. 150662306a36Sopenharmony_ci */ 150762306a36Sopenharmony_ci switch (state & (BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | 150862306a36Sopenharmony_ci BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG)) { 150962306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | 151062306a36Sopenharmony_ci BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: 151162306a36Sopenharmony_ci /* 151262306a36Sopenharmony_ci * Filling in all of a previously delayed allocation extent. 151362306a36Sopenharmony_ci * The left and right neighbors are both contiguous with new. 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_ci LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci xfs_iext_remove(bma->ip, &bma->icur, state); 151862306a36Sopenharmony_ci xfs_iext_remove(bma->ip, &bma->icur, state); 151962306a36Sopenharmony_ci xfs_iext_prev(ifp, &bma->icur); 152062306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &LEFT); 152162306a36Sopenharmony_ci ifp->if_nextents--; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (bma->cur == NULL) 152462306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 152562306a36Sopenharmony_ci else { 152662306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 152762306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i); 152862306a36Sopenharmony_ci if (error) 152962306a36Sopenharmony_ci goto done; 153062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 153162306a36Sopenharmony_ci error = -EFSCORRUPTED; 153262306a36Sopenharmony_ci goto done; 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci error = xfs_btree_delete(bma->cur, &i); 153562306a36Sopenharmony_ci if (error) 153662306a36Sopenharmony_ci goto done; 153762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 153862306a36Sopenharmony_ci error = -EFSCORRUPTED; 153962306a36Sopenharmony_ci goto done; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci error = xfs_btree_decrement(bma->cur, 0, &i); 154262306a36Sopenharmony_ci if (error) 154362306a36Sopenharmony_ci goto done; 154462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 154562306a36Sopenharmony_ci error = -EFSCORRUPTED; 154662306a36Sopenharmony_ci goto done; 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci error = xfs_bmbt_update(bma->cur, &LEFT); 154962306a36Sopenharmony_ci if (error) 155062306a36Sopenharmony_ci goto done; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci break; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: 155562306a36Sopenharmony_ci /* 155662306a36Sopenharmony_ci * Filling in all of a previously delayed allocation extent. 155762306a36Sopenharmony_ci * The left neighbor is contiguous, the right is not. 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci old = LEFT; 156062306a36Sopenharmony_ci LEFT.br_blockcount += PREV.br_blockcount; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci xfs_iext_remove(bma->ip, &bma->icur, state); 156362306a36Sopenharmony_ci xfs_iext_prev(ifp, &bma->icur); 156462306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &LEFT); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if (bma->cur == NULL) 156762306a36Sopenharmony_ci rval = XFS_ILOG_DEXT; 156862306a36Sopenharmony_ci else { 156962306a36Sopenharmony_ci rval = 0; 157062306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, &old, &i); 157162306a36Sopenharmony_ci if (error) 157262306a36Sopenharmony_ci goto done; 157362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 157462306a36Sopenharmony_ci error = -EFSCORRUPTED; 157562306a36Sopenharmony_ci goto done; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci error = xfs_bmbt_update(bma->cur, &LEFT); 157862306a36Sopenharmony_ci if (error) 157962306a36Sopenharmony_ci goto done; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci break; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: 158462306a36Sopenharmony_ci /* 158562306a36Sopenharmony_ci * Filling in all of a previously delayed allocation extent. 158662306a36Sopenharmony_ci * The right neighbor is contiguous, the left is not. Take care 158762306a36Sopenharmony_ci * with delay -> unwritten extent allocation here because the 158862306a36Sopenharmony_ci * delalloc record we are overwriting is always written. 158962306a36Sopenharmony_ci */ 159062306a36Sopenharmony_ci PREV.br_startblock = new->br_startblock; 159162306a36Sopenharmony_ci PREV.br_blockcount += RIGHT.br_blockcount; 159262306a36Sopenharmony_ci PREV.br_state = new->br_state; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci xfs_iext_next(ifp, &bma->icur); 159562306a36Sopenharmony_ci xfs_iext_remove(bma->ip, &bma->icur, state); 159662306a36Sopenharmony_ci xfs_iext_prev(ifp, &bma->icur); 159762306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (bma->cur == NULL) 160062306a36Sopenharmony_ci rval = XFS_ILOG_DEXT; 160162306a36Sopenharmony_ci else { 160262306a36Sopenharmony_ci rval = 0; 160362306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i); 160462306a36Sopenharmony_ci if (error) 160562306a36Sopenharmony_ci goto done; 160662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 160762306a36Sopenharmony_ci error = -EFSCORRUPTED; 160862306a36Sopenharmony_ci goto done; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci error = xfs_bmbt_update(bma->cur, &PREV); 161162306a36Sopenharmony_ci if (error) 161262306a36Sopenharmony_ci goto done; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci break; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: 161762306a36Sopenharmony_ci /* 161862306a36Sopenharmony_ci * Filling in all of a previously delayed allocation extent. 161962306a36Sopenharmony_ci * Neither the left nor right neighbors are contiguous with 162062306a36Sopenharmony_ci * the new one. 162162306a36Sopenharmony_ci */ 162262306a36Sopenharmony_ci PREV.br_startblock = new->br_startblock; 162362306a36Sopenharmony_ci PREV.br_state = new->br_state; 162462306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV); 162562306a36Sopenharmony_ci ifp->if_nextents++; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci if (bma->cur == NULL) 162862306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 162962306a36Sopenharmony_ci else { 163062306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 163162306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, new, &i); 163262306a36Sopenharmony_ci if (error) 163362306a36Sopenharmony_ci goto done; 163462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 163562306a36Sopenharmony_ci error = -EFSCORRUPTED; 163662306a36Sopenharmony_ci goto done; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci error = xfs_btree_insert(bma->cur, &i); 163962306a36Sopenharmony_ci if (error) 164062306a36Sopenharmony_ci goto done; 164162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 164262306a36Sopenharmony_ci error = -EFSCORRUPTED; 164362306a36Sopenharmony_ci goto done; 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci break; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG: 164962306a36Sopenharmony_ci /* 165062306a36Sopenharmony_ci * Filling in the first part of a previous delayed allocation. 165162306a36Sopenharmony_ci * The left neighbor is contiguous. 165262306a36Sopenharmony_ci */ 165362306a36Sopenharmony_ci old = LEFT; 165462306a36Sopenharmony_ci temp = PREV.br_blockcount - new->br_blockcount; 165562306a36Sopenharmony_ci da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp), 165662306a36Sopenharmony_ci startblockval(PREV.br_startblock)); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci LEFT.br_blockcount += new->br_blockcount; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci PREV.br_blockcount = temp; 166162306a36Sopenharmony_ci PREV.br_startoff += new->br_blockcount; 166262306a36Sopenharmony_ci PREV.br_startblock = nullstartblock(da_new); 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV); 166562306a36Sopenharmony_ci xfs_iext_prev(ifp, &bma->icur); 166662306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &LEFT); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (bma->cur == NULL) 166962306a36Sopenharmony_ci rval = XFS_ILOG_DEXT; 167062306a36Sopenharmony_ci else { 167162306a36Sopenharmony_ci rval = 0; 167262306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, &old, &i); 167362306a36Sopenharmony_ci if (error) 167462306a36Sopenharmony_ci goto done; 167562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 167662306a36Sopenharmony_ci error = -EFSCORRUPTED; 167762306a36Sopenharmony_ci goto done; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci error = xfs_bmbt_update(bma->cur, &LEFT); 168062306a36Sopenharmony_ci if (error) 168162306a36Sopenharmony_ci goto done; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci break; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci case BMAP_LEFT_FILLING: 168662306a36Sopenharmony_ci /* 168762306a36Sopenharmony_ci * Filling in the first part of a previous delayed allocation. 168862306a36Sopenharmony_ci * The left neighbor is not contiguous. 168962306a36Sopenharmony_ci */ 169062306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, new); 169162306a36Sopenharmony_ci ifp->if_nextents++; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (bma->cur == NULL) 169462306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 169562306a36Sopenharmony_ci else { 169662306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 169762306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, new, &i); 169862306a36Sopenharmony_ci if (error) 169962306a36Sopenharmony_ci goto done; 170062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 170162306a36Sopenharmony_ci error = -EFSCORRUPTED; 170262306a36Sopenharmony_ci goto done; 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci error = xfs_btree_insert(bma->cur, &i); 170562306a36Sopenharmony_ci if (error) 170662306a36Sopenharmony_ci goto done; 170762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 170862306a36Sopenharmony_ci error = -EFSCORRUPTED; 170962306a36Sopenharmony_ci goto done; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (xfs_bmap_needs_btree(bma->ip, whichfork)) { 171462306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(bma->tp, bma->ip, 171562306a36Sopenharmony_ci &bma->cur, 1, &tmp_rval, whichfork); 171662306a36Sopenharmony_ci rval |= tmp_rval; 171762306a36Sopenharmony_ci if (error) 171862306a36Sopenharmony_ci goto done; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci temp = PREV.br_blockcount - new->br_blockcount; 172262306a36Sopenharmony_ci da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp), 172362306a36Sopenharmony_ci startblockval(PREV.br_startblock) - 172462306a36Sopenharmony_ci (bma->cur ? bma->cur->bc_ino.allocated : 0)); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci PREV.br_startoff = new_endoff; 172762306a36Sopenharmony_ci PREV.br_blockcount = temp; 172862306a36Sopenharmony_ci PREV.br_startblock = nullstartblock(da_new); 172962306a36Sopenharmony_ci xfs_iext_next(ifp, &bma->icur); 173062306a36Sopenharmony_ci xfs_iext_insert(bma->ip, &bma->icur, &PREV, state); 173162306a36Sopenharmony_ci xfs_iext_prev(ifp, &bma->icur); 173262306a36Sopenharmony_ci break; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: 173562306a36Sopenharmony_ci /* 173662306a36Sopenharmony_ci * Filling in the last part of a previous delayed allocation. 173762306a36Sopenharmony_ci * The right neighbor is contiguous with the new allocation. 173862306a36Sopenharmony_ci */ 173962306a36Sopenharmony_ci old = RIGHT; 174062306a36Sopenharmony_ci RIGHT.br_startoff = new->br_startoff; 174162306a36Sopenharmony_ci RIGHT.br_startblock = new->br_startblock; 174262306a36Sopenharmony_ci RIGHT.br_blockcount += new->br_blockcount; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci if (bma->cur == NULL) 174562306a36Sopenharmony_ci rval = XFS_ILOG_DEXT; 174662306a36Sopenharmony_ci else { 174762306a36Sopenharmony_ci rval = 0; 174862306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, &old, &i); 174962306a36Sopenharmony_ci if (error) 175062306a36Sopenharmony_ci goto done; 175162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 175262306a36Sopenharmony_ci error = -EFSCORRUPTED; 175362306a36Sopenharmony_ci goto done; 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci error = xfs_bmbt_update(bma->cur, &RIGHT); 175662306a36Sopenharmony_ci if (error) 175762306a36Sopenharmony_ci goto done; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci temp = PREV.br_blockcount - new->br_blockcount; 176162306a36Sopenharmony_ci da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp), 176262306a36Sopenharmony_ci startblockval(PREV.br_startblock)); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci PREV.br_blockcount = temp; 176562306a36Sopenharmony_ci PREV.br_startblock = nullstartblock(da_new); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV); 176862306a36Sopenharmony_ci xfs_iext_next(ifp, &bma->icur); 176962306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &RIGHT); 177062306a36Sopenharmony_ci break; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci case BMAP_RIGHT_FILLING: 177362306a36Sopenharmony_ci /* 177462306a36Sopenharmony_ci * Filling in the last part of a previous delayed allocation. 177562306a36Sopenharmony_ci * The right neighbor is not contiguous. 177662306a36Sopenharmony_ci */ 177762306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, new); 177862306a36Sopenharmony_ci ifp->if_nextents++; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci if (bma->cur == NULL) 178162306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 178262306a36Sopenharmony_ci else { 178362306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 178462306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, new, &i); 178562306a36Sopenharmony_ci if (error) 178662306a36Sopenharmony_ci goto done; 178762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 178862306a36Sopenharmony_ci error = -EFSCORRUPTED; 178962306a36Sopenharmony_ci goto done; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci error = xfs_btree_insert(bma->cur, &i); 179262306a36Sopenharmony_ci if (error) 179362306a36Sopenharmony_ci goto done; 179462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 179562306a36Sopenharmony_ci error = -EFSCORRUPTED; 179662306a36Sopenharmony_ci goto done; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci } 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci if (xfs_bmap_needs_btree(bma->ip, whichfork)) { 180162306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(bma->tp, bma->ip, 180262306a36Sopenharmony_ci &bma->cur, 1, &tmp_rval, whichfork); 180362306a36Sopenharmony_ci rval |= tmp_rval; 180462306a36Sopenharmony_ci if (error) 180562306a36Sopenharmony_ci goto done; 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci temp = PREV.br_blockcount - new->br_blockcount; 180962306a36Sopenharmony_ci da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp), 181062306a36Sopenharmony_ci startblockval(PREV.br_startblock) - 181162306a36Sopenharmony_ci (bma->cur ? bma->cur->bc_ino.allocated : 0)); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci PREV.br_startblock = nullstartblock(da_new); 181462306a36Sopenharmony_ci PREV.br_blockcount = temp; 181562306a36Sopenharmony_ci xfs_iext_insert(bma->ip, &bma->icur, &PREV, state); 181662306a36Sopenharmony_ci xfs_iext_next(ifp, &bma->icur); 181762306a36Sopenharmony_ci break; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci case 0: 182062306a36Sopenharmony_ci /* 182162306a36Sopenharmony_ci * Filling in the middle part of a previous delayed allocation. 182262306a36Sopenharmony_ci * Contiguity is impossible here. 182362306a36Sopenharmony_ci * This case is avoided almost all the time. 182462306a36Sopenharmony_ci * 182562306a36Sopenharmony_ci * We start with a delayed allocation: 182662306a36Sopenharmony_ci * 182762306a36Sopenharmony_ci * +ddddddddddddddddddddddddddddddddddddddddddddddddddddddd+ 182862306a36Sopenharmony_ci * PREV @ idx 182962306a36Sopenharmony_ci * 183062306a36Sopenharmony_ci * and we are allocating: 183162306a36Sopenharmony_ci * +rrrrrrrrrrrrrrrrr+ 183262306a36Sopenharmony_ci * new 183362306a36Sopenharmony_ci * 183462306a36Sopenharmony_ci * and we set it up for insertion as: 183562306a36Sopenharmony_ci * +ddddddddddddddddddd+rrrrrrrrrrrrrrrrr+ddddddddddddddddd+ 183662306a36Sopenharmony_ci * new 183762306a36Sopenharmony_ci * PREV @ idx LEFT RIGHT 183862306a36Sopenharmony_ci * inserted at idx + 1 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_ci old = PREV; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci /* LEFT is the new middle */ 184362306a36Sopenharmony_ci LEFT = *new; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* RIGHT is the new right */ 184662306a36Sopenharmony_ci RIGHT.br_state = PREV.br_state; 184762306a36Sopenharmony_ci RIGHT.br_startoff = new_endoff; 184862306a36Sopenharmony_ci RIGHT.br_blockcount = 184962306a36Sopenharmony_ci PREV.br_startoff + PREV.br_blockcount - new_endoff; 185062306a36Sopenharmony_ci RIGHT.br_startblock = 185162306a36Sopenharmony_ci nullstartblock(xfs_bmap_worst_indlen(bma->ip, 185262306a36Sopenharmony_ci RIGHT.br_blockcount)); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci /* truncate PREV */ 185562306a36Sopenharmony_ci PREV.br_blockcount = new->br_startoff - PREV.br_startoff; 185662306a36Sopenharmony_ci PREV.br_startblock = 185762306a36Sopenharmony_ci nullstartblock(xfs_bmap_worst_indlen(bma->ip, 185862306a36Sopenharmony_ci PREV.br_blockcount)); 185962306a36Sopenharmony_ci xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV); 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci xfs_iext_next(ifp, &bma->icur); 186262306a36Sopenharmony_ci xfs_iext_insert(bma->ip, &bma->icur, &RIGHT, state); 186362306a36Sopenharmony_ci xfs_iext_insert(bma->ip, &bma->icur, &LEFT, state); 186462306a36Sopenharmony_ci ifp->if_nextents++; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci if (bma->cur == NULL) 186762306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 186862306a36Sopenharmony_ci else { 186962306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 187062306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(bma->cur, new, &i); 187162306a36Sopenharmony_ci if (error) 187262306a36Sopenharmony_ci goto done; 187362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 187462306a36Sopenharmony_ci error = -EFSCORRUPTED; 187562306a36Sopenharmony_ci goto done; 187662306a36Sopenharmony_ci } 187762306a36Sopenharmony_ci error = xfs_btree_insert(bma->cur, &i); 187862306a36Sopenharmony_ci if (error) 187962306a36Sopenharmony_ci goto done; 188062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 188162306a36Sopenharmony_ci error = -EFSCORRUPTED; 188262306a36Sopenharmony_ci goto done; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci if (xfs_bmap_needs_btree(bma->ip, whichfork)) { 188762306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(bma->tp, bma->ip, 188862306a36Sopenharmony_ci &bma->cur, 1, &tmp_rval, whichfork); 188962306a36Sopenharmony_ci rval |= tmp_rval; 189062306a36Sopenharmony_ci if (error) 189162306a36Sopenharmony_ci goto done; 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci da_new = startblockval(PREV.br_startblock) + 189562306a36Sopenharmony_ci startblockval(RIGHT.br_startblock); 189662306a36Sopenharmony_ci break; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 189962306a36Sopenharmony_ci case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 190062306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_CONTIG: 190162306a36Sopenharmony_ci case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: 190262306a36Sopenharmony_ci case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 190362306a36Sopenharmony_ci case BMAP_LEFT_CONTIG: 190462306a36Sopenharmony_ci case BMAP_RIGHT_CONTIG: 190562306a36Sopenharmony_ci /* 190662306a36Sopenharmony_ci * These cases are all impossible. 190762306a36Sopenharmony_ci */ 190862306a36Sopenharmony_ci ASSERT(0); 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci /* add reverse mapping unless caller opted out */ 191262306a36Sopenharmony_ci if (!(bma->flags & XFS_BMAPI_NORMAP)) 191362306a36Sopenharmony_ci xfs_rmap_map_extent(bma->tp, bma->ip, whichfork, new); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci /* convert to a btree if necessary */ 191662306a36Sopenharmony_ci if (xfs_bmap_needs_btree(bma->ip, whichfork)) { 191762306a36Sopenharmony_ci int tmp_logflags; /* partial log flag return val */ 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci ASSERT(bma->cur == NULL); 192062306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(bma->tp, bma->ip, 192162306a36Sopenharmony_ci &bma->cur, da_old > 0, &tmp_logflags, 192262306a36Sopenharmony_ci whichfork); 192362306a36Sopenharmony_ci bma->logflags |= tmp_logflags; 192462306a36Sopenharmony_ci if (error) 192562306a36Sopenharmony_ci goto done; 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci if (da_new != da_old) 192962306a36Sopenharmony_ci xfs_mod_delalloc(mp, (int64_t)da_new - da_old); 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci if (bma->cur) { 193262306a36Sopenharmony_ci da_new += bma->cur->bc_ino.allocated; 193362306a36Sopenharmony_ci bma->cur->bc_ino.allocated = 0; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci /* adjust for changes in reserved delayed indirect blocks */ 193762306a36Sopenharmony_ci if (da_new != da_old) { 193862306a36Sopenharmony_ci ASSERT(state == 0 || da_new < da_old); 193962306a36Sopenharmony_ci error = xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), 194062306a36Sopenharmony_ci false); 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork); 194462306a36Sopenharmony_cidone: 194562306a36Sopenharmony_ci if (whichfork != XFS_COW_FORK) 194662306a36Sopenharmony_ci bma->logflags |= rval; 194762306a36Sopenharmony_ci return error; 194862306a36Sopenharmony_ci#undef LEFT 194962306a36Sopenharmony_ci#undef RIGHT 195062306a36Sopenharmony_ci#undef PREV 195162306a36Sopenharmony_ci} 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci/* 195462306a36Sopenharmony_ci * Convert an unwritten allocation to a real allocation or vice versa. 195562306a36Sopenharmony_ci */ 195662306a36Sopenharmony_ciint /* error */ 195762306a36Sopenharmony_cixfs_bmap_add_extent_unwritten_real( 195862306a36Sopenharmony_ci struct xfs_trans *tp, 195962306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 196062306a36Sopenharmony_ci int whichfork, 196162306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 196262306a36Sopenharmony_ci struct xfs_btree_cur **curp, /* if *curp is null, not a btree */ 196362306a36Sopenharmony_ci xfs_bmbt_irec_t *new, /* new data to add to file extents */ 196462306a36Sopenharmony_ci int *logflagsp) /* inode logging flags */ 196562306a36Sopenharmony_ci{ 196662306a36Sopenharmony_ci struct xfs_btree_cur *cur; /* btree cursor */ 196762306a36Sopenharmony_ci int error; /* error return value */ 196862306a36Sopenharmony_ci int i; /* temp state */ 196962306a36Sopenharmony_ci struct xfs_ifork *ifp; /* inode fork pointer */ 197062306a36Sopenharmony_ci xfs_fileoff_t new_endoff; /* end offset of new entry */ 197162306a36Sopenharmony_ci xfs_bmbt_irec_t r[3]; /* neighbor extent entries */ 197262306a36Sopenharmony_ci /* left is 0, right is 1, prev is 2 */ 197362306a36Sopenharmony_ci int rval=0; /* return value (logging flags) */ 197462306a36Sopenharmony_ci uint32_t state = xfs_bmap_fork_to_state(whichfork); 197562306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 197662306a36Sopenharmony_ci struct xfs_bmbt_irec old; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci *logflagsp = 0; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci cur = *curp; 198162306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci ASSERT(!isnullstartblock(new->br_startblock)); 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_add_exlist); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci#define LEFT r[0] 198862306a36Sopenharmony_ci#define RIGHT r[1] 198962306a36Sopenharmony_ci#define PREV r[2] 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* 199262306a36Sopenharmony_ci * Set up a bunch of variables to make the tests simpler. 199362306a36Sopenharmony_ci */ 199462306a36Sopenharmony_ci error = 0; 199562306a36Sopenharmony_ci xfs_iext_get_extent(ifp, icur, &PREV); 199662306a36Sopenharmony_ci ASSERT(new->br_state != PREV.br_state); 199762306a36Sopenharmony_ci new_endoff = new->br_startoff + new->br_blockcount; 199862306a36Sopenharmony_ci ASSERT(PREV.br_startoff <= new->br_startoff); 199962306a36Sopenharmony_ci ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci /* 200262306a36Sopenharmony_ci * Set flags determining what part of the previous oldext allocation 200362306a36Sopenharmony_ci * extent is being replaced by a newext allocation. 200462306a36Sopenharmony_ci */ 200562306a36Sopenharmony_ci if (PREV.br_startoff == new->br_startoff) 200662306a36Sopenharmony_ci state |= BMAP_LEFT_FILLING; 200762306a36Sopenharmony_ci if (PREV.br_startoff + PREV.br_blockcount == new_endoff) 200862306a36Sopenharmony_ci state |= BMAP_RIGHT_FILLING; 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci /* 201162306a36Sopenharmony_ci * Check and set flags if this segment has a left neighbor. 201262306a36Sopenharmony_ci * Don't set contiguous if the combined extent would be too large. 201362306a36Sopenharmony_ci */ 201462306a36Sopenharmony_ci if (xfs_iext_peek_prev_extent(ifp, icur, &LEFT)) { 201562306a36Sopenharmony_ci state |= BMAP_LEFT_VALID; 201662306a36Sopenharmony_ci if (isnullstartblock(LEFT.br_startblock)) 201762306a36Sopenharmony_ci state |= BMAP_LEFT_DELAY; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci if ((state & BMAP_LEFT_VALID) && !(state & BMAP_LEFT_DELAY) && 202162306a36Sopenharmony_ci LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff && 202262306a36Sopenharmony_ci LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock && 202362306a36Sopenharmony_ci LEFT.br_state == new->br_state && 202462306a36Sopenharmony_ci LEFT.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN) 202562306a36Sopenharmony_ci state |= BMAP_LEFT_CONTIG; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci /* 202862306a36Sopenharmony_ci * Check and set flags if this segment has a right neighbor. 202962306a36Sopenharmony_ci * Don't set contiguous if the combined extent would be too large. 203062306a36Sopenharmony_ci * Also check for all-three-contiguous being too large. 203162306a36Sopenharmony_ci */ 203262306a36Sopenharmony_ci if (xfs_iext_peek_next_extent(ifp, icur, &RIGHT)) { 203362306a36Sopenharmony_ci state |= BMAP_RIGHT_VALID; 203462306a36Sopenharmony_ci if (isnullstartblock(RIGHT.br_startblock)) 203562306a36Sopenharmony_ci state |= BMAP_RIGHT_DELAY; 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci if ((state & BMAP_RIGHT_VALID) && !(state & BMAP_RIGHT_DELAY) && 203962306a36Sopenharmony_ci new_endoff == RIGHT.br_startoff && 204062306a36Sopenharmony_ci new->br_startblock + new->br_blockcount == RIGHT.br_startblock && 204162306a36Sopenharmony_ci new->br_state == RIGHT.br_state && 204262306a36Sopenharmony_ci new->br_blockcount + RIGHT.br_blockcount <= XFS_MAX_BMBT_EXTLEN && 204362306a36Sopenharmony_ci ((state & (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING | 204462306a36Sopenharmony_ci BMAP_RIGHT_FILLING)) != 204562306a36Sopenharmony_ci (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING | 204662306a36Sopenharmony_ci BMAP_RIGHT_FILLING) || 204762306a36Sopenharmony_ci LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount 204862306a36Sopenharmony_ci <= XFS_MAX_BMBT_EXTLEN)) 204962306a36Sopenharmony_ci state |= BMAP_RIGHT_CONTIG; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* 205262306a36Sopenharmony_ci * Switch out based on the FILLING and CONTIG state bits. 205362306a36Sopenharmony_ci */ 205462306a36Sopenharmony_ci switch (state & (BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | 205562306a36Sopenharmony_ci BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG)) { 205662306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | 205762306a36Sopenharmony_ci BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: 205862306a36Sopenharmony_ci /* 205962306a36Sopenharmony_ci * Setting all of a previous oldext extent to newext. 206062306a36Sopenharmony_ci * The left and right neighbors are both contiguous with new. 206162306a36Sopenharmony_ci */ 206262306a36Sopenharmony_ci LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 206562306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 206662306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 206762306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &LEFT); 206862306a36Sopenharmony_ci ifp->if_nextents -= 2; 206962306a36Sopenharmony_ci if (cur == NULL) 207062306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 207162306a36Sopenharmony_ci else { 207262306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 207362306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i); 207462306a36Sopenharmony_ci if (error) 207562306a36Sopenharmony_ci goto done; 207662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 207762306a36Sopenharmony_ci error = -EFSCORRUPTED; 207862306a36Sopenharmony_ci goto done; 207962306a36Sopenharmony_ci } 208062306a36Sopenharmony_ci if ((error = xfs_btree_delete(cur, &i))) 208162306a36Sopenharmony_ci goto done; 208262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 208362306a36Sopenharmony_ci error = -EFSCORRUPTED; 208462306a36Sopenharmony_ci goto done; 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci if ((error = xfs_btree_decrement(cur, 0, &i))) 208762306a36Sopenharmony_ci goto done; 208862306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 208962306a36Sopenharmony_ci error = -EFSCORRUPTED; 209062306a36Sopenharmony_ci goto done; 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci if ((error = xfs_btree_delete(cur, &i))) 209362306a36Sopenharmony_ci goto done; 209462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 209562306a36Sopenharmony_ci error = -EFSCORRUPTED; 209662306a36Sopenharmony_ci goto done; 209762306a36Sopenharmony_ci } 209862306a36Sopenharmony_ci if ((error = xfs_btree_decrement(cur, 0, &i))) 209962306a36Sopenharmony_ci goto done; 210062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 210162306a36Sopenharmony_ci error = -EFSCORRUPTED; 210262306a36Sopenharmony_ci goto done; 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &LEFT); 210562306a36Sopenharmony_ci if (error) 210662306a36Sopenharmony_ci goto done; 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci break; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: 211162306a36Sopenharmony_ci /* 211262306a36Sopenharmony_ci * Setting all of a previous oldext extent to newext. 211362306a36Sopenharmony_ci * The left neighbor is contiguous, the right is not. 211462306a36Sopenharmony_ci */ 211562306a36Sopenharmony_ci LEFT.br_blockcount += PREV.br_blockcount; 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 211862306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 211962306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &LEFT); 212062306a36Sopenharmony_ci ifp->if_nextents--; 212162306a36Sopenharmony_ci if (cur == NULL) 212262306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 212362306a36Sopenharmony_ci else { 212462306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 212562306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &PREV, &i); 212662306a36Sopenharmony_ci if (error) 212762306a36Sopenharmony_ci goto done; 212862306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 212962306a36Sopenharmony_ci error = -EFSCORRUPTED; 213062306a36Sopenharmony_ci goto done; 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci if ((error = xfs_btree_delete(cur, &i))) 213362306a36Sopenharmony_ci goto done; 213462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 213562306a36Sopenharmony_ci error = -EFSCORRUPTED; 213662306a36Sopenharmony_ci goto done; 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci if ((error = xfs_btree_decrement(cur, 0, &i))) 213962306a36Sopenharmony_ci goto done; 214062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 214162306a36Sopenharmony_ci error = -EFSCORRUPTED; 214262306a36Sopenharmony_ci goto done; 214362306a36Sopenharmony_ci } 214462306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &LEFT); 214562306a36Sopenharmony_ci if (error) 214662306a36Sopenharmony_ci goto done; 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci break; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: 215162306a36Sopenharmony_ci /* 215262306a36Sopenharmony_ci * Setting all of a previous oldext extent to newext. 215362306a36Sopenharmony_ci * The right neighbor is contiguous, the left is not. 215462306a36Sopenharmony_ci */ 215562306a36Sopenharmony_ci PREV.br_blockcount += RIGHT.br_blockcount; 215662306a36Sopenharmony_ci PREV.br_state = new->br_state; 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci xfs_iext_next(ifp, icur); 215962306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 216062306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 216162306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &PREV); 216262306a36Sopenharmony_ci ifp->if_nextents--; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci if (cur == NULL) 216562306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 216662306a36Sopenharmony_ci else { 216762306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 216862306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i); 216962306a36Sopenharmony_ci if (error) 217062306a36Sopenharmony_ci goto done; 217162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 217262306a36Sopenharmony_ci error = -EFSCORRUPTED; 217362306a36Sopenharmony_ci goto done; 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci if ((error = xfs_btree_delete(cur, &i))) 217662306a36Sopenharmony_ci goto done; 217762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 217862306a36Sopenharmony_ci error = -EFSCORRUPTED; 217962306a36Sopenharmony_ci goto done; 218062306a36Sopenharmony_ci } 218162306a36Sopenharmony_ci if ((error = xfs_btree_decrement(cur, 0, &i))) 218262306a36Sopenharmony_ci goto done; 218362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 218462306a36Sopenharmony_ci error = -EFSCORRUPTED; 218562306a36Sopenharmony_ci goto done; 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &PREV); 218862306a36Sopenharmony_ci if (error) 218962306a36Sopenharmony_ci goto done; 219062306a36Sopenharmony_ci } 219162306a36Sopenharmony_ci break; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: 219462306a36Sopenharmony_ci /* 219562306a36Sopenharmony_ci * Setting all of a previous oldext extent to newext. 219662306a36Sopenharmony_ci * Neither the left nor right neighbors are contiguous with 219762306a36Sopenharmony_ci * the new one. 219862306a36Sopenharmony_ci */ 219962306a36Sopenharmony_ci PREV.br_state = new->br_state; 220062306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &PREV); 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci if (cur == NULL) 220362306a36Sopenharmony_ci rval = XFS_ILOG_DEXT; 220462306a36Sopenharmony_ci else { 220562306a36Sopenharmony_ci rval = 0; 220662306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, new, &i); 220762306a36Sopenharmony_ci if (error) 220862306a36Sopenharmony_ci goto done; 220962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 221062306a36Sopenharmony_ci error = -EFSCORRUPTED; 221162306a36Sopenharmony_ci goto done; 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &PREV); 221462306a36Sopenharmony_ci if (error) 221562306a36Sopenharmony_ci goto done; 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci break; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG: 222062306a36Sopenharmony_ci /* 222162306a36Sopenharmony_ci * Setting the first part of a previous oldext extent to newext. 222262306a36Sopenharmony_ci * The left neighbor is contiguous. 222362306a36Sopenharmony_ci */ 222462306a36Sopenharmony_ci LEFT.br_blockcount += new->br_blockcount; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci old = PREV; 222762306a36Sopenharmony_ci PREV.br_startoff += new->br_blockcount; 222862306a36Sopenharmony_ci PREV.br_startblock += new->br_blockcount; 222962306a36Sopenharmony_ci PREV.br_blockcount -= new->br_blockcount; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &PREV); 223262306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 223362306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &LEFT); 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci if (cur == NULL) 223662306a36Sopenharmony_ci rval = XFS_ILOG_DEXT; 223762306a36Sopenharmony_ci else { 223862306a36Sopenharmony_ci rval = 0; 223962306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &old, &i); 224062306a36Sopenharmony_ci if (error) 224162306a36Sopenharmony_ci goto done; 224262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 224362306a36Sopenharmony_ci error = -EFSCORRUPTED; 224462306a36Sopenharmony_ci goto done; 224562306a36Sopenharmony_ci } 224662306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &PREV); 224762306a36Sopenharmony_ci if (error) 224862306a36Sopenharmony_ci goto done; 224962306a36Sopenharmony_ci error = xfs_btree_decrement(cur, 0, &i); 225062306a36Sopenharmony_ci if (error) 225162306a36Sopenharmony_ci goto done; 225262306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &LEFT); 225362306a36Sopenharmony_ci if (error) 225462306a36Sopenharmony_ci goto done; 225562306a36Sopenharmony_ci } 225662306a36Sopenharmony_ci break; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci case BMAP_LEFT_FILLING: 225962306a36Sopenharmony_ci /* 226062306a36Sopenharmony_ci * Setting the first part of a previous oldext extent to newext. 226162306a36Sopenharmony_ci * The left neighbor is not contiguous. 226262306a36Sopenharmony_ci */ 226362306a36Sopenharmony_ci old = PREV; 226462306a36Sopenharmony_ci PREV.br_startoff += new->br_blockcount; 226562306a36Sopenharmony_ci PREV.br_startblock += new->br_blockcount; 226662306a36Sopenharmony_ci PREV.br_blockcount -= new->br_blockcount; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &PREV); 226962306a36Sopenharmony_ci xfs_iext_insert(ip, icur, new, state); 227062306a36Sopenharmony_ci ifp->if_nextents++; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci if (cur == NULL) 227362306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 227462306a36Sopenharmony_ci else { 227562306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 227662306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &old, &i); 227762306a36Sopenharmony_ci if (error) 227862306a36Sopenharmony_ci goto done; 227962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 228062306a36Sopenharmony_ci error = -EFSCORRUPTED; 228162306a36Sopenharmony_ci goto done; 228262306a36Sopenharmony_ci } 228362306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &PREV); 228462306a36Sopenharmony_ci if (error) 228562306a36Sopenharmony_ci goto done; 228662306a36Sopenharmony_ci cur->bc_rec.b = *new; 228762306a36Sopenharmony_ci if ((error = xfs_btree_insert(cur, &i))) 228862306a36Sopenharmony_ci goto done; 228962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 229062306a36Sopenharmony_ci error = -EFSCORRUPTED; 229162306a36Sopenharmony_ci goto done; 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci break; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: 229762306a36Sopenharmony_ci /* 229862306a36Sopenharmony_ci * Setting the last part of a previous oldext extent to newext. 229962306a36Sopenharmony_ci * The right neighbor is contiguous with the new allocation. 230062306a36Sopenharmony_ci */ 230162306a36Sopenharmony_ci old = PREV; 230262306a36Sopenharmony_ci PREV.br_blockcount -= new->br_blockcount; 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci RIGHT.br_startoff = new->br_startoff; 230562306a36Sopenharmony_ci RIGHT.br_startblock = new->br_startblock; 230662306a36Sopenharmony_ci RIGHT.br_blockcount += new->br_blockcount; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &PREV); 230962306a36Sopenharmony_ci xfs_iext_next(ifp, icur); 231062306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &RIGHT); 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci if (cur == NULL) 231362306a36Sopenharmony_ci rval = XFS_ILOG_DEXT; 231462306a36Sopenharmony_ci else { 231562306a36Sopenharmony_ci rval = 0; 231662306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &old, &i); 231762306a36Sopenharmony_ci if (error) 231862306a36Sopenharmony_ci goto done; 231962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 232062306a36Sopenharmony_ci error = -EFSCORRUPTED; 232162306a36Sopenharmony_ci goto done; 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &PREV); 232462306a36Sopenharmony_ci if (error) 232562306a36Sopenharmony_ci goto done; 232662306a36Sopenharmony_ci error = xfs_btree_increment(cur, 0, &i); 232762306a36Sopenharmony_ci if (error) 232862306a36Sopenharmony_ci goto done; 232962306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &RIGHT); 233062306a36Sopenharmony_ci if (error) 233162306a36Sopenharmony_ci goto done; 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci break; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci case BMAP_RIGHT_FILLING: 233662306a36Sopenharmony_ci /* 233762306a36Sopenharmony_ci * Setting the last part of a previous oldext extent to newext. 233862306a36Sopenharmony_ci * The right neighbor is not contiguous. 233962306a36Sopenharmony_ci */ 234062306a36Sopenharmony_ci old = PREV; 234162306a36Sopenharmony_ci PREV.br_blockcount -= new->br_blockcount; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &PREV); 234462306a36Sopenharmony_ci xfs_iext_next(ifp, icur); 234562306a36Sopenharmony_ci xfs_iext_insert(ip, icur, new, state); 234662306a36Sopenharmony_ci ifp->if_nextents++; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci if (cur == NULL) 234962306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 235062306a36Sopenharmony_ci else { 235162306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 235262306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &old, &i); 235362306a36Sopenharmony_ci if (error) 235462306a36Sopenharmony_ci goto done; 235562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 235662306a36Sopenharmony_ci error = -EFSCORRUPTED; 235762306a36Sopenharmony_ci goto done; 235862306a36Sopenharmony_ci } 235962306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &PREV); 236062306a36Sopenharmony_ci if (error) 236162306a36Sopenharmony_ci goto done; 236262306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, new, &i); 236362306a36Sopenharmony_ci if (error) 236462306a36Sopenharmony_ci goto done; 236562306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 236662306a36Sopenharmony_ci error = -EFSCORRUPTED; 236762306a36Sopenharmony_ci goto done; 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci if ((error = xfs_btree_insert(cur, &i))) 237062306a36Sopenharmony_ci goto done; 237162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 237262306a36Sopenharmony_ci error = -EFSCORRUPTED; 237362306a36Sopenharmony_ci goto done; 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci } 237662306a36Sopenharmony_ci break; 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci case 0: 237962306a36Sopenharmony_ci /* 238062306a36Sopenharmony_ci * Setting the middle part of a previous oldext extent to 238162306a36Sopenharmony_ci * newext. Contiguity is impossible here. 238262306a36Sopenharmony_ci * One extent becomes three extents. 238362306a36Sopenharmony_ci */ 238462306a36Sopenharmony_ci old = PREV; 238562306a36Sopenharmony_ci PREV.br_blockcount = new->br_startoff - PREV.br_startoff; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci r[0] = *new; 238862306a36Sopenharmony_ci r[1].br_startoff = new_endoff; 238962306a36Sopenharmony_ci r[1].br_blockcount = 239062306a36Sopenharmony_ci old.br_startoff + old.br_blockcount - new_endoff; 239162306a36Sopenharmony_ci r[1].br_startblock = new->br_startblock + new->br_blockcount; 239262306a36Sopenharmony_ci r[1].br_state = PREV.br_state; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &PREV); 239562306a36Sopenharmony_ci xfs_iext_next(ifp, icur); 239662306a36Sopenharmony_ci xfs_iext_insert(ip, icur, &r[1], state); 239762306a36Sopenharmony_ci xfs_iext_insert(ip, icur, &r[0], state); 239862306a36Sopenharmony_ci ifp->if_nextents += 2; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci if (cur == NULL) 240162306a36Sopenharmony_ci rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 240262306a36Sopenharmony_ci else { 240362306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 240462306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &old, &i); 240562306a36Sopenharmony_ci if (error) 240662306a36Sopenharmony_ci goto done; 240762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 240862306a36Sopenharmony_ci error = -EFSCORRUPTED; 240962306a36Sopenharmony_ci goto done; 241062306a36Sopenharmony_ci } 241162306a36Sopenharmony_ci /* new right extent - oldext */ 241262306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &r[1]); 241362306a36Sopenharmony_ci if (error) 241462306a36Sopenharmony_ci goto done; 241562306a36Sopenharmony_ci /* new left extent - oldext */ 241662306a36Sopenharmony_ci cur->bc_rec.b = PREV; 241762306a36Sopenharmony_ci if ((error = xfs_btree_insert(cur, &i))) 241862306a36Sopenharmony_ci goto done; 241962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 242062306a36Sopenharmony_ci error = -EFSCORRUPTED; 242162306a36Sopenharmony_ci goto done; 242262306a36Sopenharmony_ci } 242362306a36Sopenharmony_ci /* 242462306a36Sopenharmony_ci * Reset the cursor to the position of the new extent 242562306a36Sopenharmony_ci * we are about to insert as we can't trust it after 242662306a36Sopenharmony_ci * the previous insert. 242762306a36Sopenharmony_ci */ 242862306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, new, &i); 242962306a36Sopenharmony_ci if (error) 243062306a36Sopenharmony_ci goto done; 243162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 243262306a36Sopenharmony_ci error = -EFSCORRUPTED; 243362306a36Sopenharmony_ci goto done; 243462306a36Sopenharmony_ci } 243562306a36Sopenharmony_ci /* new middle extent - newext */ 243662306a36Sopenharmony_ci if ((error = xfs_btree_insert(cur, &i))) 243762306a36Sopenharmony_ci goto done; 243862306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 243962306a36Sopenharmony_ci error = -EFSCORRUPTED; 244062306a36Sopenharmony_ci goto done; 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci } 244362306a36Sopenharmony_ci break; 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 244662306a36Sopenharmony_ci case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 244762306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_CONTIG: 244862306a36Sopenharmony_ci case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: 244962306a36Sopenharmony_ci case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 245062306a36Sopenharmony_ci case BMAP_LEFT_CONTIG: 245162306a36Sopenharmony_ci case BMAP_RIGHT_CONTIG: 245262306a36Sopenharmony_ci /* 245362306a36Sopenharmony_ci * These cases are all impossible. 245462306a36Sopenharmony_ci */ 245562306a36Sopenharmony_ci ASSERT(0); 245662306a36Sopenharmony_ci } 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci /* update reverse mappings */ 245962306a36Sopenharmony_ci xfs_rmap_convert_extent(mp, tp, ip, whichfork, new); 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci /* convert to a btree if necessary */ 246262306a36Sopenharmony_ci if (xfs_bmap_needs_btree(ip, whichfork)) { 246362306a36Sopenharmony_ci int tmp_logflags; /* partial log flag return val */ 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci ASSERT(cur == NULL); 246662306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(tp, ip, &cur, 0, 246762306a36Sopenharmony_ci &tmp_logflags, whichfork); 246862306a36Sopenharmony_ci *logflagsp |= tmp_logflags; 246962306a36Sopenharmony_ci if (error) 247062306a36Sopenharmony_ci goto done; 247162306a36Sopenharmony_ci } 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci /* clear out the allocated field, done with it now in any case. */ 247462306a36Sopenharmony_ci if (cur) { 247562306a36Sopenharmony_ci cur->bc_ino.allocated = 0; 247662306a36Sopenharmony_ci *curp = cur; 247762306a36Sopenharmony_ci } 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci xfs_bmap_check_leaf_extents(*curp, ip, whichfork); 248062306a36Sopenharmony_cidone: 248162306a36Sopenharmony_ci *logflagsp |= rval; 248262306a36Sopenharmony_ci return error; 248362306a36Sopenharmony_ci#undef LEFT 248462306a36Sopenharmony_ci#undef RIGHT 248562306a36Sopenharmony_ci#undef PREV 248662306a36Sopenharmony_ci} 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci/* 248962306a36Sopenharmony_ci * Convert a hole to a delayed allocation. 249062306a36Sopenharmony_ci */ 249162306a36Sopenharmony_ciSTATIC void 249262306a36Sopenharmony_cixfs_bmap_add_extent_hole_delay( 249362306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 249462306a36Sopenharmony_ci int whichfork, 249562306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 249662306a36Sopenharmony_ci xfs_bmbt_irec_t *new) /* new data to add to file extents */ 249762306a36Sopenharmony_ci{ 249862306a36Sopenharmony_ci struct xfs_ifork *ifp; /* inode fork pointer */ 249962306a36Sopenharmony_ci xfs_bmbt_irec_t left; /* left neighbor extent entry */ 250062306a36Sopenharmony_ci xfs_filblks_t newlen=0; /* new indirect size */ 250162306a36Sopenharmony_ci xfs_filblks_t oldlen=0; /* old indirect size */ 250262306a36Sopenharmony_ci xfs_bmbt_irec_t right; /* right neighbor extent entry */ 250362306a36Sopenharmony_ci uint32_t state = xfs_bmap_fork_to_state(whichfork); 250462306a36Sopenharmony_ci xfs_filblks_t temp; /* temp for indirect calculations */ 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 250762306a36Sopenharmony_ci ASSERT(isnullstartblock(new->br_startblock)); 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci /* 251062306a36Sopenharmony_ci * Check and set flags if this segment has a left neighbor 251162306a36Sopenharmony_ci */ 251262306a36Sopenharmony_ci if (xfs_iext_peek_prev_extent(ifp, icur, &left)) { 251362306a36Sopenharmony_ci state |= BMAP_LEFT_VALID; 251462306a36Sopenharmony_ci if (isnullstartblock(left.br_startblock)) 251562306a36Sopenharmony_ci state |= BMAP_LEFT_DELAY; 251662306a36Sopenharmony_ci } 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci /* 251962306a36Sopenharmony_ci * Check and set flags if the current (right) segment exists. 252062306a36Sopenharmony_ci * If it doesn't exist, we're converting the hole at end-of-file. 252162306a36Sopenharmony_ci */ 252262306a36Sopenharmony_ci if (xfs_iext_get_extent(ifp, icur, &right)) { 252362306a36Sopenharmony_ci state |= BMAP_RIGHT_VALID; 252462306a36Sopenharmony_ci if (isnullstartblock(right.br_startblock)) 252562306a36Sopenharmony_ci state |= BMAP_RIGHT_DELAY; 252662306a36Sopenharmony_ci } 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci /* 252962306a36Sopenharmony_ci * Set contiguity flags on the left and right neighbors. 253062306a36Sopenharmony_ci * Don't let extents get too large, even if the pieces are contiguous. 253162306a36Sopenharmony_ci */ 253262306a36Sopenharmony_ci if ((state & BMAP_LEFT_VALID) && (state & BMAP_LEFT_DELAY) && 253362306a36Sopenharmony_ci left.br_startoff + left.br_blockcount == new->br_startoff && 253462306a36Sopenharmony_ci left.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN) 253562306a36Sopenharmony_ci state |= BMAP_LEFT_CONTIG; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci if ((state & BMAP_RIGHT_VALID) && (state & BMAP_RIGHT_DELAY) && 253862306a36Sopenharmony_ci new->br_startoff + new->br_blockcount == right.br_startoff && 253962306a36Sopenharmony_ci new->br_blockcount + right.br_blockcount <= XFS_MAX_BMBT_EXTLEN && 254062306a36Sopenharmony_ci (!(state & BMAP_LEFT_CONTIG) || 254162306a36Sopenharmony_ci (left.br_blockcount + new->br_blockcount + 254262306a36Sopenharmony_ci right.br_blockcount <= XFS_MAX_BMBT_EXTLEN))) 254362306a36Sopenharmony_ci state |= BMAP_RIGHT_CONTIG; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci /* 254662306a36Sopenharmony_ci * Switch out based on the contiguity flags. 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_ci switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) { 254962306a36Sopenharmony_ci case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 255062306a36Sopenharmony_ci /* 255162306a36Sopenharmony_ci * New allocation is contiguous with delayed allocations 255262306a36Sopenharmony_ci * on the left and on the right. 255362306a36Sopenharmony_ci * Merge all three into a single extent record. 255462306a36Sopenharmony_ci */ 255562306a36Sopenharmony_ci temp = left.br_blockcount + new->br_blockcount + 255662306a36Sopenharmony_ci right.br_blockcount; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci oldlen = startblockval(left.br_startblock) + 255962306a36Sopenharmony_ci startblockval(new->br_startblock) + 256062306a36Sopenharmony_ci startblockval(right.br_startblock); 256162306a36Sopenharmony_ci newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 256262306a36Sopenharmony_ci oldlen); 256362306a36Sopenharmony_ci left.br_startblock = nullstartblock(newlen); 256462306a36Sopenharmony_ci left.br_blockcount = temp; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 256762306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 256862306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &left); 256962306a36Sopenharmony_ci break; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci case BMAP_LEFT_CONTIG: 257262306a36Sopenharmony_ci /* 257362306a36Sopenharmony_ci * New allocation is contiguous with a delayed allocation 257462306a36Sopenharmony_ci * on the left. 257562306a36Sopenharmony_ci * Merge the new allocation with the left neighbor. 257662306a36Sopenharmony_ci */ 257762306a36Sopenharmony_ci temp = left.br_blockcount + new->br_blockcount; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci oldlen = startblockval(left.br_startblock) + 258062306a36Sopenharmony_ci startblockval(new->br_startblock); 258162306a36Sopenharmony_ci newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 258262306a36Sopenharmony_ci oldlen); 258362306a36Sopenharmony_ci left.br_blockcount = temp; 258462306a36Sopenharmony_ci left.br_startblock = nullstartblock(newlen); 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 258762306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &left); 258862306a36Sopenharmony_ci break; 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci case BMAP_RIGHT_CONTIG: 259162306a36Sopenharmony_ci /* 259262306a36Sopenharmony_ci * New allocation is contiguous with a delayed allocation 259362306a36Sopenharmony_ci * on the right. 259462306a36Sopenharmony_ci * Merge the new allocation with the right neighbor. 259562306a36Sopenharmony_ci */ 259662306a36Sopenharmony_ci temp = new->br_blockcount + right.br_blockcount; 259762306a36Sopenharmony_ci oldlen = startblockval(new->br_startblock) + 259862306a36Sopenharmony_ci startblockval(right.br_startblock); 259962306a36Sopenharmony_ci newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 260062306a36Sopenharmony_ci oldlen); 260162306a36Sopenharmony_ci right.br_startoff = new->br_startoff; 260262306a36Sopenharmony_ci right.br_startblock = nullstartblock(newlen); 260362306a36Sopenharmony_ci right.br_blockcount = temp; 260462306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &right); 260562306a36Sopenharmony_ci break; 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci case 0: 260862306a36Sopenharmony_ci /* 260962306a36Sopenharmony_ci * New allocation is not contiguous with another 261062306a36Sopenharmony_ci * delayed allocation. 261162306a36Sopenharmony_ci * Insert a new entry. 261262306a36Sopenharmony_ci */ 261362306a36Sopenharmony_ci oldlen = newlen = 0; 261462306a36Sopenharmony_ci xfs_iext_insert(ip, icur, new, state); 261562306a36Sopenharmony_ci break; 261662306a36Sopenharmony_ci } 261762306a36Sopenharmony_ci if (oldlen != newlen) { 261862306a36Sopenharmony_ci ASSERT(oldlen > newlen); 261962306a36Sopenharmony_ci xfs_mod_fdblocks(ip->i_mount, (int64_t)(oldlen - newlen), 262062306a36Sopenharmony_ci false); 262162306a36Sopenharmony_ci /* 262262306a36Sopenharmony_ci * Nothing to do for disk quota accounting here. 262362306a36Sopenharmony_ci */ 262462306a36Sopenharmony_ci xfs_mod_delalloc(ip->i_mount, (int64_t)newlen - oldlen); 262562306a36Sopenharmony_ci } 262662306a36Sopenharmony_ci} 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci/* 262962306a36Sopenharmony_ci * Convert a hole to a real allocation. 263062306a36Sopenharmony_ci */ 263162306a36Sopenharmony_ciSTATIC int /* error */ 263262306a36Sopenharmony_cixfs_bmap_add_extent_hole_real( 263362306a36Sopenharmony_ci struct xfs_trans *tp, 263462306a36Sopenharmony_ci struct xfs_inode *ip, 263562306a36Sopenharmony_ci int whichfork, 263662306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 263762306a36Sopenharmony_ci struct xfs_btree_cur **curp, 263862306a36Sopenharmony_ci struct xfs_bmbt_irec *new, 263962306a36Sopenharmony_ci int *logflagsp, 264062306a36Sopenharmony_ci uint32_t flags) 264162306a36Sopenharmony_ci{ 264262306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 264362306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 264462306a36Sopenharmony_ci struct xfs_btree_cur *cur = *curp; 264562306a36Sopenharmony_ci int error; /* error return value */ 264662306a36Sopenharmony_ci int i; /* temp state */ 264762306a36Sopenharmony_ci xfs_bmbt_irec_t left; /* left neighbor extent entry */ 264862306a36Sopenharmony_ci xfs_bmbt_irec_t right; /* right neighbor extent entry */ 264962306a36Sopenharmony_ci int rval=0; /* return value (logging flags) */ 265062306a36Sopenharmony_ci uint32_t state = xfs_bmap_fork_to_state(whichfork); 265162306a36Sopenharmony_ci struct xfs_bmbt_irec old; 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci ASSERT(!isnullstartblock(new->br_startblock)); 265462306a36Sopenharmony_ci ASSERT(!cur || !(cur->bc_ino.flags & XFS_BTCUR_BMBT_WASDEL)); 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_add_exlist); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci /* 265962306a36Sopenharmony_ci * Check and set flags if this segment has a left neighbor. 266062306a36Sopenharmony_ci */ 266162306a36Sopenharmony_ci if (xfs_iext_peek_prev_extent(ifp, icur, &left)) { 266262306a36Sopenharmony_ci state |= BMAP_LEFT_VALID; 266362306a36Sopenharmony_ci if (isnullstartblock(left.br_startblock)) 266462306a36Sopenharmony_ci state |= BMAP_LEFT_DELAY; 266562306a36Sopenharmony_ci } 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci /* 266862306a36Sopenharmony_ci * Check and set flags if this segment has a current value. 266962306a36Sopenharmony_ci * Not true if we're inserting into the "hole" at eof. 267062306a36Sopenharmony_ci */ 267162306a36Sopenharmony_ci if (xfs_iext_get_extent(ifp, icur, &right)) { 267262306a36Sopenharmony_ci state |= BMAP_RIGHT_VALID; 267362306a36Sopenharmony_ci if (isnullstartblock(right.br_startblock)) 267462306a36Sopenharmony_ci state |= BMAP_RIGHT_DELAY; 267562306a36Sopenharmony_ci } 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci /* 267862306a36Sopenharmony_ci * We're inserting a real allocation between "left" and "right". 267962306a36Sopenharmony_ci * Set the contiguity flags. Don't let extents get too large. 268062306a36Sopenharmony_ci */ 268162306a36Sopenharmony_ci if ((state & BMAP_LEFT_VALID) && !(state & BMAP_LEFT_DELAY) && 268262306a36Sopenharmony_ci left.br_startoff + left.br_blockcount == new->br_startoff && 268362306a36Sopenharmony_ci left.br_startblock + left.br_blockcount == new->br_startblock && 268462306a36Sopenharmony_ci left.br_state == new->br_state && 268562306a36Sopenharmony_ci left.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN) 268662306a36Sopenharmony_ci state |= BMAP_LEFT_CONTIG; 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci if ((state & BMAP_RIGHT_VALID) && !(state & BMAP_RIGHT_DELAY) && 268962306a36Sopenharmony_ci new->br_startoff + new->br_blockcount == right.br_startoff && 269062306a36Sopenharmony_ci new->br_startblock + new->br_blockcount == right.br_startblock && 269162306a36Sopenharmony_ci new->br_state == right.br_state && 269262306a36Sopenharmony_ci new->br_blockcount + right.br_blockcount <= XFS_MAX_BMBT_EXTLEN && 269362306a36Sopenharmony_ci (!(state & BMAP_LEFT_CONTIG) || 269462306a36Sopenharmony_ci left.br_blockcount + new->br_blockcount + 269562306a36Sopenharmony_ci right.br_blockcount <= XFS_MAX_BMBT_EXTLEN)) 269662306a36Sopenharmony_ci state |= BMAP_RIGHT_CONTIG; 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci error = 0; 269962306a36Sopenharmony_ci /* 270062306a36Sopenharmony_ci * Select which case we're in here, and implement it. 270162306a36Sopenharmony_ci */ 270262306a36Sopenharmony_ci switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) { 270362306a36Sopenharmony_ci case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: 270462306a36Sopenharmony_ci /* 270562306a36Sopenharmony_ci * New allocation is contiguous with real allocations on the 270662306a36Sopenharmony_ci * left and on the right. 270762306a36Sopenharmony_ci * Merge all three into a single extent record. 270862306a36Sopenharmony_ci */ 270962306a36Sopenharmony_ci left.br_blockcount += new->br_blockcount + right.br_blockcount; 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 271262306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 271362306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &left); 271462306a36Sopenharmony_ci ifp->if_nextents--; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci if (cur == NULL) { 271762306a36Sopenharmony_ci rval = XFS_ILOG_CORE | xfs_ilog_fext(whichfork); 271862306a36Sopenharmony_ci } else { 271962306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 272062306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &right, &i); 272162306a36Sopenharmony_ci if (error) 272262306a36Sopenharmony_ci goto done; 272362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 272462306a36Sopenharmony_ci error = -EFSCORRUPTED; 272562306a36Sopenharmony_ci goto done; 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci error = xfs_btree_delete(cur, &i); 272862306a36Sopenharmony_ci if (error) 272962306a36Sopenharmony_ci goto done; 273062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 273162306a36Sopenharmony_ci error = -EFSCORRUPTED; 273262306a36Sopenharmony_ci goto done; 273362306a36Sopenharmony_ci } 273462306a36Sopenharmony_ci error = xfs_btree_decrement(cur, 0, &i); 273562306a36Sopenharmony_ci if (error) 273662306a36Sopenharmony_ci goto done; 273762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 273862306a36Sopenharmony_ci error = -EFSCORRUPTED; 273962306a36Sopenharmony_ci goto done; 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &left); 274262306a36Sopenharmony_ci if (error) 274362306a36Sopenharmony_ci goto done; 274462306a36Sopenharmony_ci } 274562306a36Sopenharmony_ci break; 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci case BMAP_LEFT_CONTIG: 274862306a36Sopenharmony_ci /* 274962306a36Sopenharmony_ci * New allocation is contiguous with a real allocation 275062306a36Sopenharmony_ci * on the left. 275162306a36Sopenharmony_ci * Merge the new allocation with the left neighbor. 275262306a36Sopenharmony_ci */ 275362306a36Sopenharmony_ci old = left; 275462306a36Sopenharmony_ci left.br_blockcount += new->br_blockcount; 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 275762306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &left); 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci if (cur == NULL) { 276062306a36Sopenharmony_ci rval = xfs_ilog_fext(whichfork); 276162306a36Sopenharmony_ci } else { 276262306a36Sopenharmony_ci rval = 0; 276362306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &old, &i); 276462306a36Sopenharmony_ci if (error) 276562306a36Sopenharmony_ci goto done; 276662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 276762306a36Sopenharmony_ci error = -EFSCORRUPTED; 276862306a36Sopenharmony_ci goto done; 276962306a36Sopenharmony_ci } 277062306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &left); 277162306a36Sopenharmony_ci if (error) 277262306a36Sopenharmony_ci goto done; 277362306a36Sopenharmony_ci } 277462306a36Sopenharmony_ci break; 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci case BMAP_RIGHT_CONTIG: 277762306a36Sopenharmony_ci /* 277862306a36Sopenharmony_ci * New allocation is contiguous with a real allocation 277962306a36Sopenharmony_ci * on the right. 278062306a36Sopenharmony_ci * Merge the new allocation with the right neighbor. 278162306a36Sopenharmony_ci */ 278262306a36Sopenharmony_ci old = right; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci right.br_startoff = new->br_startoff; 278562306a36Sopenharmony_ci right.br_startblock = new->br_startblock; 278662306a36Sopenharmony_ci right.br_blockcount += new->br_blockcount; 278762306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &right); 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci if (cur == NULL) { 279062306a36Sopenharmony_ci rval = xfs_ilog_fext(whichfork); 279162306a36Sopenharmony_ci } else { 279262306a36Sopenharmony_ci rval = 0; 279362306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &old, &i); 279462306a36Sopenharmony_ci if (error) 279562306a36Sopenharmony_ci goto done; 279662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 279762306a36Sopenharmony_ci error = -EFSCORRUPTED; 279862306a36Sopenharmony_ci goto done; 279962306a36Sopenharmony_ci } 280062306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &right); 280162306a36Sopenharmony_ci if (error) 280262306a36Sopenharmony_ci goto done; 280362306a36Sopenharmony_ci } 280462306a36Sopenharmony_ci break; 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci case 0: 280762306a36Sopenharmony_ci /* 280862306a36Sopenharmony_ci * New allocation is not contiguous with another 280962306a36Sopenharmony_ci * real allocation. 281062306a36Sopenharmony_ci * Insert a new entry. 281162306a36Sopenharmony_ci */ 281262306a36Sopenharmony_ci xfs_iext_insert(ip, icur, new, state); 281362306a36Sopenharmony_ci ifp->if_nextents++; 281462306a36Sopenharmony_ci 281562306a36Sopenharmony_ci if (cur == NULL) { 281662306a36Sopenharmony_ci rval = XFS_ILOG_CORE | xfs_ilog_fext(whichfork); 281762306a36Sopenharmony_ci } else { 281862306a36Sopenharmony_ci rval = XFS_ILOG_CORE; 281962306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, new, &i); 282062306a36Sopenharmony_ci if (error) 282162306a36Sopenharmony_ci goto done; 282262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 282362306a36Sopenharmony_ci error = -EFSCORRUPTED; 282462306a36Sopenharmony_ci goto done; 282562306a36Sopenharmony_ci } 282662306a36Sopenharmony_ci error = xfs_btree_insert(cur, &i); 282762306a36Sopenharmony_ci if (error) 282862306a36Sopenharmony_ci goto done; 282962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 283062306a36Sopenharmony_ci error = -EFSCORRUPTED; 283162306a36Sopenharmony_ci goto done; 283262306a36Sopenharmony_ci } 283362306a36Sopenharmony_ci } 283462306a36Sopenharmony_ci break; 283562306a36Sopenharmony_ci } 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci /* add reverse mapping unless caller opted out */ 283862306a36Sopenharmony_ci if (!(flags & XFS_BMAPI_NORMAP)) 283962306a36Sopenharmony_ci xfs_rmap_map_extent(tp, ip, whichfork, new); 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci /* convert to a btree if necessary */ 284262306a36Sopenharmony_ci if (xfs_bmap_needs_btree(ip, whichfork)) { 284362306a36Sopenharmony_ci int tmp_logflags; /* partial log flag return val */ 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci ASSERT(cur == NULL); 284662306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(tp, ip, curp, 0, 284762306a36Sopenharmony_ci &tmp_logflags, whichfork); 284862306a36Sopenharmony_ci *logflagsp |= tmp_logflags; 284962306a36Sopenharmony_ci cur = *curp; 285062306a36Sopenharmony_ci if (error) 285162306a36Sopenharmony_ci goto done; 285262306a36Sopenharmony_ci } 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci /* clear out the allocated field, done with it now in any case. */ 285562306a36Sopenharmony_ci if (cur) 285662306a36Sopenharmony_ci cur->bc_ino.allocated = 0; 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci xfs_bmap_check_leaf_extents(cur, ip, whichfork); 285962306a36Sopenharmony_cidone: 286062306a36Sopenharmony_ci *logflagsp |= rval; 286162306a36Sopenharmony_ci return error; 286262306a36Sopenharmony_ci} 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci/* 286562306a36Sopenharmony_ci * Functions used in the extent read, allocate and remove paths 286662306a36Sopenharmony_ci */ 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci/* 286962306a36Sopenharmony_ci * Adjust the size of the new extent based on i_extsize and rt extsize. 287062306a36Sopenharmony_ci */ 287162306a36Sopenharmony_ciint 287262306a36Sopenharmony_cixfs_bmap_extsize_align( 287362306a36Sopenharmony_ci xfs_mount_t *mp, 287462306a36Sopenharmony_ci xfs_bmbt_irec_t *gotp, /* next extent pointer */ 287562306a36Sopenharmony_ci xfs_bmbt_irec_t *prevp, /* previous extent pointer */ 287662306a36Sopenharmony_ci xfs_extlen_t extsz, /* align to this extent size */ 287762306a36Sopenharmony_ci int rt, /* is this a realtime inode? */ 287862306a36Sopenharmony_ci int eof, /* is extent at end-of-file? */ 287962306a36Sopenharmony_ci int delay, /* creating delalloc extent? */ 288062306a36Sopenharmony_ci int convert, /* overwriting unwritten extent? */ 288162306a36Sopenharmony_ci xfs_fileoff_t *offp, /* in/out: aligned offset */ 288262306a36Sopenharmony_ci xfs_extlen_t *lenp) /* in/out: aligned length */ 288362306a36Sopenharmony_ci{ 288462306a36Sopenharmony_ci xfs_fileoff_t orig_off; /* original offset */ 288562306a36Sopenharmony_ci xfs_extlen_t orig_alen; /* original length */ 288662306a36Sopenharmony_ci xfs_fileoff_t orig_end; /* original off+len */ 288762306a36Sopenharmony_ci xfs_fileoff_t nexto; /* next file offset */ 288862306a36Sopenharmony_ci xfs_fileoff_t prevo; /* previous file offset */ 288962306a36Sopenharmony_ci xfs_fileoff_t align_off; /* temp for offset */ 289062306a36Sopenharmony_ci xfs_extlen_t align_alen; /* temp for length */ 289162306a36Sopenharmony_ci xfs_extlen_t temp; /* temp for calculations */ 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci if (convert) 289462306a36Sopenharmony_ci return 0; 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci orig_off = align_off = *offp; 289762306a36Sopenharmony_ci orig_alen = align_alen = *lenp; 289862306a36Sopenharmony_ci orig_end = orig_off + orig_alen; 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci /* 290162306a36Sopenharmony_ci * If this request overlaps an existing extent, then don't 290262306a36Sopenharmony_ci * attempt to perform any additional alignment. 290362306a36Sopenharmony_ci */ 290462306a36Sopenharmony_ci if (!delay && !eof && 290562306a36Sopenharmony_ci (orig_off >= gotp->br_startoff) && 290662306a36Sopenharmony_ci (orig_end <= gotp->br_startoff + gotp->br_blockcount)) { 290762306a36Sopenharmony_ci return 0; 290862306a36Sopenharmony_ci } 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci /* 291162306a36Sopenharmony_ci * If the file offset is unaligned vs. the extent size 291262306a36Sopenharmony_ci * we need to align it. This will be possible unless 291362306a36Sopenharmony_ci * the file was previously written with a kernel that didn't 291462306a36Sopenharmony_ci * perform this alignment, or if a truncate shot us in the 291562306a36Sopenharmony_ci * foot. 291662306a36Sopenharmony_ci */ 291762306a36Sopenharmony_ci div_u64_rem(orig_off, extsz, &temp); 291862306a36Sopenharmony_ci if (temp) { 291962306a36Sopenharmony_ci align_alen += temp; 292062306a36Sopenharmony_ci align_off -= temp; 292162306a36Sopenharmony_ci } 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci /* Same adjustment for the end of the requested area. */ 292462306a36Sopenharmony_ci temp = (align_alen % extsz); 292562306a36Sopenharmony_ci if (temp) 292662306a36Sopenharmony_ci align_alen += extsz - temp; 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci /* 292962306a36Sopenharmony_ci * For large extent hint sizes, the aligned extent might be larger than 293062306a36Sopenharmony_ci * XFS_BMBT_MAX_EXTLEN. In that case, reduce the size by an extsz so 293162306a36Sopenharmony_ci * that it pulls the length back under XFS_BMBT_MAX_EXTLEN. The outer 293262306a36Sopenharmony_ci * allocation loops handle short allocation just fine, so it is safe to 293362306a36Sopenharmony_ci * do this. We only want to do it when we are forced to, though, because 293462306a36Sopenharmony_ci * it means more allocation operations are required. 293562306a36Sopenharmony_ci */ 293662306a36Sopenharmony_ci while (align_alen > XFS_MAX_BMBT_EXTLEN) 293762306a36Sopenharmony_ci align_alen -= extsz; 293862306a36Sopenharmony_ci ASSERT(align_alen <= XFS_MAX_BMBT_EXTLEN); 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci /* 294162306a36Sopenharmony_ci * If the previous block overlaps with this proposed allocation 294262306a36Sopenharmony_ci * then move the start forward without adjusting the length. 294362306a36Sopenharmony_ci */ 294462306a36Sopenharmony_ci if (prevp->br_startoff != NULLFILEOFF) { 294562306a36Sopenharmony_ci if (prevp->br_startblock == HOLESTARTBLOCK) 294662306a36Sopenharmony_ci prevo = prevp->br_startoff; 294762306a36Sopenharmony_ci else 294862306a36Sopenharmony_ci prevo = prevp->br_startoff + prevp->br_blockcount; 294962306a36Sopenharmony_ci } else 295062306a36Sopenharmony_ci prevo = 0; 295162306a36Sopenharmony_ci if (align_off != orig_off && align_off < prevo) 295262306a36Sopenharmony_ci align_off = prevo; 295362306a36Sopenharmony_ci /* 295462306a36Sopenharmony_ci * If the next block overlaps with this proposed allocation 295562306a36Sopenharmony_ci * then move the start back without adjusting the length, 295662306a36Sopenharmony_ci * but not before offset 0. 295762306a36Sopenharmony_ci * This may of course make the start overlap previous block, 295862306a36Sopenharmony_ci * and if we hit the offset 0 limit then the next block 295962306a36Sopenharmony_ci * can still overlap too. 296062306a36Sopenharmony_ci */ 296162306a36Sopenharmony_ci if (!eof && gotp->br_startoff != NULLFILEOFF) { 296262306a36Sopenharmony_ci if ((delay && gotp->br_startblock == HOLESTARTBLOCK) || 296362306a36Sopenharmony_ci (!delay && gotp->br_startblock == DELAYSTARTBLOCK)) 296462306a36Sopenharmony_ci nexto = gotp->br_startoff + gotp->br_blockcount; 296562306a36Sopenharmony_ci else 296662306a36Sopenharmony_ci nexto = gotp->br_startoff; 296762306a36Sopenharmony_ci } else 296862306a36Sopenharmony_ci nexto = NULLFILEOFF; 296962306a36Sopenharmony_ci if (!eof && 297062306a36Sopenharmony_ci align_off + align_alen != orig_end && 297162306a36Sopenharmony_ci align_off + align_alen > nexto) 297262306a36Sopenharmony_ci align_off = nexto > align_alen ? nexto - align_alen : 0; 297362306a36Sopenharmony_ci /* 297462306a36Sopenharmony_ci * If we're now overlapping the next or previous extent that 297562306a36Sopenharmony_ci * means we can't fit an extsz piece in this hole. Just move 297662306a36Sopenharmony_ci * the start forward to the first valid spot and set 297762306a36Sopenharmony_ci * the length so we hit the end. 297862306a36Sopenharmony_ci */ 297962306a36Sopenharmony_ci if (align_off != orig_off && align_off < prevo) 298062306a36Sopenharmony_ci align_off = prevo; 298162306a36Sopenharmony_ci if (align_off + align_alen != orig_end && 298262306a36Sopenharmony_ci align_off + align_alen > nexto && 298362306a36Sopenharmony_ci nexto != NULLFILEOFF) { 298462306a36Sopenharmony_ci ASSERT(nexto > prevo); 298562306a36Sopenharmony_ci align_alen = nexto - align_off; 298662306a36Sopenharmony_ci } 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci /* 298962306a36Sopenharmony_ci * If realtime, and the result isn't a multiple of the realtime 299062306a36Sopenharmony_ci * extent size we need to remove blocks until it is. 299162306a36Sopenharmony_ci */ 299262306a36Sopenharmony_ci if (rt && (temp = (align_alen % mp->m_sb.sb_rextsize))) { 299362306a36Sopenharmony_ci /* 299462306a36Sopenharmony_ci * We're not covering the original request, or 299562306a36Sopenharmony_ci * we won't be able to once we fix the length. 299662306a36Sopenharmony_ci */ 299762306a36Sopenharmony_ci if (orig_off < align_off || 299862306a36Sopenharmony_ci orig_end > align_off + align_alen || 299962306a36Sopenharmony_ci align_alen - temp < orig_alen) 300062306a36Sopenharmony_ci return -EINVAL; 300162306a36Sopenharmony_ci /* 300262306a36Sopenharmony_ci * Try to fix it by moving the start up. 300362306a36Sopenharmony_ci */ 300462306a36Sopenharmony_ci if (align_off + temp <= orig_off) { 300562306a36Sopenharmony_ci align_alen -= temp; 300662306a36Sopenharmony_ci align_off += temp; 300762306a36Sopenharmony_ci } 300862306a36Sopenharmony_ci /* 300962306a36Sopenharmony_ci * Try to fix it by moving the end in. 301062306a36Sopenharmony_ci */ 301162306a36Sopenharmony_ci else if (align_off + align_alen - temp >= orig_end) 301262306a36Sopenharmony_ci align_alen -= temp; 301362306a36Sopenharmony_ci /* 301462306a36Sopenharmony_ci * Set the start to the minimum then trim the length. 301562306a36Sopenharmony_ci */ 301662306a36Sopenharmony_ci else { 301762306a36Sopenharmony_ci align_alen -= orig_off - align_off; 301862306a36Sopenharmony_ci align_off = orig_off; 301962306a36Sopenharmony_ci align_alen -= align_alen % mp->m_sb.sb_rextsize; 302062306a36Sopenharmony_ci } 302162306a36Sopenharmony_ci /* 302262306a36Sopenharmony_ci * Result doesn't cover the request, fail it. 302362306a36Sopenharmony_ci */ 302462306a36Sopenharmony_ci if (orig_off < align_off || orig_end > align_off + align_alen) 302562306a36Sopenharmony_ci return -EINVAL; 302662306a36Sopenharmony_ci } else { 302762306a36Sopenharmony_ci ASSERT(orig_off >= align_off); 302862306a36Sopenharmony_ci /* see XFS_BMBT_MAX_EXTLEN handling above */ 302962306a36Sopenharmony_ci ASSERT(orig_end <= align_off + align_alen || 303062306a36Sopenharmony_ci align_alen + extsz > XFS_MAX_BMBT_EXTLEN); 303162306a36Sopenharmony_ci } 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci#ifdef DEBUG 303462306a36Sopenharmony_ci if (!eof && gotp->br_startoff != NULLFILEOFF) 303562306a36Sopenharmony_ci ASSERT(align_off + align_alen <= gotp->br_startoff); 303662306a36Sopenharmony_ci if (prevp->br_startoff != NULLFILEOFF) 303762306a36Sopenharmony_ci ASSERT(align_off >= prevp->br_startoff + prevp->br_blockcount); 303862306a36Sopenharmony_ci#endif 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci *lenp = align_alen; 304162306a36Sopenharmony_ci *offp = align_off; 304262306a36Sopenharmony_ci return 0; 304362306a36Sopenharmony_ci} 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci#define XFS_ALLOC_GAP_UNITS 4 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_civoid 304862306a36Sopenharmony_cixfs_bmap_adjacent( 304962306a36Sopenharmony_ci struct xfs_bmalloca *ap) /* bmap alloc argument struct */ 305062306a36Sopenharmony_ci{ 305162306a36Sopenharmony_ci xfs_fsblock_t adjust; /* adjustment to block numbers */ 305262306a36Sopenharmony_ci xfs_mount_t *mp; /* mount point structure */ 305362306a36Sopenharmony_ci int rt; /* true if inode is realtime */ 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci#define ISVALID(x,y) \ 305662306a36Sopenharmony_ci (rt ? \ 305762306a36Sopenharmony_ci (x) < mp->m_sb.sb_rblocks : \ 305862306a36Sopenharmony_ci XFS_FSB_TO_AGNO(mp, x) == XFS_FSB_TO_AGNO(mp, y) && \ 305962306a36Sopenharmony_ci XFS_FSB_TO_AGNO(mp, x) < mp->m_sb.sb_agcount && \ 306062306a36Sopenharmony_ci XFS_FSB_TO_AGBNO(mp, x) < mp->m_sb.sb_agblocks) 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci mp = ap->ip->i_mount; 306362306a36Sopenharmony_ci rt = XFS_IS_REALTIME_INODE(ap->ip) && 306462306a36Sopenharmony_ci (ap->datatype & XFS_ALLOC_USERDATA); 306562306a36Sopenharmony_ci /* 306662306a36Sopenharmony_ci * If allocating at eof, and there's a previous real block, 306762306a36Sopenharmony_ci * try to use its last block as our starting point. 306862306a36Sopenharmony_ci */ 306962306a36Sopenharmony_ci if (ap->eof && ap->prev.br_startoff != NULLFILEOFF && 307062306a36Sopenharmony_ci !isnullstartblock(ap->prev.br_startblock) && 307162306a36Sopenharmony_ci ISVALID(ap->prev.br_startblock + ap->prev.br_blockcount, 307262306a36Sopenharmony_ci ap->prev.br_startblock)) { 307362306a36Sopenharmony_ci ap->blkno = ap->prev.br_startblock + ap->prev.br_blockcount; 307462306a36Sopenharmony_ci /* 307562306a36Sopenharmony_ci * Adjust for the gap between prevp and us. 307662306a36Sopenharmony_ci */ 307762306a36Sopenharmony_ci adjust = ap->offset - 307862306a36Sopenharmony_ci (ap->prev.br_startoff + ap->prev.br_blockcount); 307962306a36Sopenharmony_ci if (adjust && 308062306a36Sopenharmony_ci ISVALID(ap->blkno + adjust, ap->prev.br_startblock)) 308162306a36Sopenharmony_ci ap->blkno += adjust; 308262306a36Sopenharmony_ci } 308362306a36Sopenharmony_ci /* 308462306a36Sopenharmony_ci * If not at eof, then compare the two neighbor blocks. 308562306a36Sopenharmony_ci * Figure out whether either one gives us a good starting point, 308662306a36Sopenharmony_ci * and pick the better one. 308762306a36Sopenharmony_ci */ 308862306a36Sopenharmony_ci else if (!ap->eof) { 308962306a36Sopenharmony_ci xfs_fsblock_t gotbno; /* right side block number */ 309062306a36Sopenharmony_ci xfs_fsblock_t gotdiff=0; /* right side difference */ 309162306a36Sopenharmony_ci xfs_fsblock_t prevbno; /* left side block number */ 309262306a36Sopenharmony_ci xfs_fsblock_t prevdiff=0; /* left side difference */ 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci /* 309562306a36Sopenharmony_ci * If there's a previous (left) block, select a requested 309662306a36Sopenharmony_ci * start block based on it. 309762306a36Sopenharmony_ci */ 309862306a36Sopenharmony_ci if (ap->prev.br_startoff != NULLFILEOFF && 309962306a36Sopenharmony_ci !isnullstartblock(ap->prev.br_startblock) && 310062306a36Sopenharmony_ci (prevbno = ap->prev.br_startblock + 310162306a36Sopenharmony_ci ap->prev.br_blockcount) && 310262306a36Sopenharmony_ci ISVALID(prevbno, ap->prev.br_startblock)) { 310362306a36Sopenharmony_ci /* 310462306a36Sopenharmony_ci * Calculate gap to end of previous block. 310562306a36Sopenharmony_ci */ 310662306a36Sopenharmony_ci adjust = prevdiff = ap->offset - 310762306a36Sopenharmony_ci (ap->prev.br_startoff + 310862306a36Sopenharmony_ci ap->prev.br_blockcount); 310962306a36Sopenharmony_ci /* 311062306a36Sopenharmony_ci * Figure the startblock based on the previous block's 311162306a36Sopenharmony_ci * end and the gap size. 311262306a36Sopenharmony_ci * Heuristic! 311362306a36Sopenharmony_ci * If the gap is large relative to the piece we're 311462306a36Sopenharmony_ci * allocating, or using it gives us an invalid block 311562306a36Sopenharmony_ci * number, then just use the end of the previous block. 311662306a36Sopenharmony_ci */ 311762306a36Sopenharmony_ci if (prevdiff <= XFS_ALLOC_GAP_UNITS * ap->length && 311862306a36Sopenharmony_ci ISVALID(prevbno + prevdiff, 311962306a36Sopenharmony_ci ap->prev.br_startblock)) 312062306a36Sopenharmony_ci prevbno += adjust; 312162306a36Sopenharmony_ci else 312262306a36Sopenharmony_ci prevdiff += adjust; 312362306a36Sopenharmony_ci } 312462306a36Sopenharmony_ci /* 312562306a36Sopenharmony_ci * No previous block or can't follow it, just default. 312662306a36Sopenharmony_ci */ 312762306a36Sopenharmony_ci else 312862306a36Sopenharmony_ci prevbno = NULLFSBLOCK; 312962306a36Sopenharmony_ci /* 313062306a36Sopenharmony_ci * If there's a following (right) block, select a requested 313162306a36Sopenharmony_ci * start block based on it. 313262306a36Sopenharmony_ci */ 313362306a36Sopenharmony_ci if (!isnullstartblock(ap->got.br_startblock)) { 313462306a36Sopenharmony_ci /* 313562306a36Sopenharmony_ci * Calculate gap to start of next block. 313662306a36Sopenharmony_ci */ 313762306a36Sopenharmony_ci adjust = gotdiff = ap->got.br_startoff - ap->offset; 313862306a36Sopenharmony_ci /* 313962306a36Sopenharmony_ci * Figure the startblock based on the next block's 314062306a36Sopenharmony_ci * start and the gap size. 314162306a36Sopenharmony_ci */ 314262306a36Sopenharmony_ci gotbno = ap->got.br_startblock; 314362306a36Sopenharmony_ci /* 314462306a36Sopenharmony_ci * Heuristic! 314562306a36Sopenharmony_ci * If the gap is large relative to the piece we're 314662306a36Sopenharmony_ci * allocating, or using it gives us an invalid block 314762306a36Sopenharmony_ci * number, then just use the start of the next block 314862306a36Sopenharmony_ci * offset by our length. 314962306a36Sopenharmony_ci */ 315062306a36Sopenharmony_ci if (gotdiff <= XFS_ALLOC_GAP_UNITS * ap->length && 315162306a36Sopenharmony_ci ISVALID(gotbno - gotdiff, gotbno)) 315262306a36Sopenharmony_ci gotbno -= adjust; 315362306a36Sopenharmony_ci else if (ISVALID(gotbno - ap->length, gotbno)) { 315462306a36Sopenharmony_ci gotbno -= ap->length; 315562306a36Sopenharmony_ci gotdiff += adjust - ap->length; 315662306a36Sopenharmony_ci } else 315762306a36Sopenharmony_ci gotdiff += adjust; 315862306a36Sopenharmony_ci } 315962306a36Sopenharmony_ci /* 316062306a36Sopenharmony_ci * No next block, just default. 316162306a36Sopenharmony_ci */ 316262306a36Sopenharmony_ci else 316362306a36Sopenharmony_ci gotbno = NULLFSBLOCK; 316462306a36Sopenharmony_ci /* 316562306a36Sopenharmony_ci * If both valid, pick the better one, else the only good 316662306a36Sopenharmony_ci * one, else ap->blkno is already set (to 0 or the inode block). 316762306a36Sopenharmony_ci */ 316862306a36Sopenharmony_ci if (prevbno != NULLFSBLOCK && gotbno != NULLFSBLOCK) 316962306a36Sopenharmony_ci ap->blkno = prevdiff <= gotdiff ? prevbno : gotbno; 317062306a36Sopenharmony_ci else if (prevbno != NULLFSBLOCK) 317162306a36Sopenharmony_ci ap->blkno = prevbno; 317262306a36Sopenharmony_ci else if (gotbno != NULLFSBLOCK) 317362306a36Sopenharmony_ci ap->blkno = gotbno; 317462306a36Sopenharmony_ci } 317562306a36Sopenharmony_ci#undef ISVALID 317662306a36Sopenharmony_ci} 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ciint 317962306a36Sopenharmony_cixfs_bmap_longest_free_extent( 318062306a36Sopenharmony_ci struct xfs_perag *pag, 318162306a36Sopenharmony_ci struct xfs_trans *tp, 318262306a36Sopenharmony_ci xfs_extlen_t *blen) 318362306a36Sopenharmony_ci{ 318462306a36Sopenharmony_ci xfs_extlen_t longest; 318562306a36Sopenharmony_ci int error = 0; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci if (!xfs_perag_initialised_agf(pag)) { 318862306a36Sopenharmony_ci error = xfs_alloc_read_agf(pag, tp, XFS_ALLOC_FLAG_TRYLOCK, 318962306a36Sopenharmony_ci NULL); 319062306a36Sopenharmony_ci if (error) 319162306a36Sopenharmony_ci return error; 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci longest = xfs_alloc_longest_free_extent(pag, 319562306a36Sopenharmony_ci xfs_alloc_min_freelist(pag->pag_mount, pag), 319662306a36Sopenharmony_ci xfs_ag_resv_needed(pag, XFS_AG_RESV_NONE)); 319762306a36Sopenharmony_ci if (*blen < longest) 319862306a36Sopenharmony_ci *blen = longest; 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci return 0; 320162306a36Sopenharmony_ci} 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_cistatic xfs_extlen_t 320462306a36Sopenharmony_cixfs_bmap_select_minlen( 320562306a36Sopenharmony_ci struct xfs_bmalloca *ap, 320662306a36Sopenharmony_ci struct xfs_alloc_arg *args, 320762306a36Sopenharmony_ci xfs_extlen_t blen) 320862306a36Sopenharmony_ci{ 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci /* 321162306a36Sopenharmony_ci * Since we used XFS_ALLOC_FLAG_TRYLOCK in _longest_free_extent(), it is 321262306a36Sopenharmony_ci * possible that there is enough contiguous free space for this request. 321362306a36Sopenharmony_ci */ 321462306a36Sopenharmony_ci if (blen < ap->minlen) 321562306a36Sopenharmony_ci return ap->minlen; 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci /* 321862306a36Sopenharmony_ci * If the best seen length is less than the request length, 321962306a36Sopenharmony_ci * use the best as the minimum, otherwise we've got the maxlen we 322062306a36Sopenharmony_ci * were asked for. 322162306a36Sopenharmony_ci */ 322262306a36Sopenharmony_ci if (blen < args->maxlen) 322362306a36Sopenharmony_ci return blen; 322462306a36Sopenharmony_ci return args->maxlen; 322562306a36Sopenharmony_ci} 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_cistatic int 322862306a36Sopenharmony_cixfs_bmap_btalloc_select_lengths( 322962306a36Sopenharmony_ci struct xfs_bmalloca *ap, 323062306a36Sopenharmony_ci struct xfs_alloc_arg *args, 323162306a36Sopenharmony_ci xfs_extlen_t *blen) 323262306a36Sopenharmony_ci{ 323362306a36Sopenharmony_ci struct xfs_mount *mp = args->mp; 323462306a36Sopenharmony_ci struct xfs_perag *pag; 323562306a36Sopenharmony_ci xfs_agnumber_t agno, startag; 323662306a36Sopenharmony_ci int error = 0; 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_ci if (ap->tp->t_flags & XFS_TRANS_LOWMODE) { 323962306a36Sopenharmony_ci args->total = ap->minlen; 324062306a36Sopenharmony_ci args->minlen = ap->minlen; 324162306a36Sopenharmony_ci return 0; 324262306a36Sopenharmony_ci } 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci args->total = ap->total; 324562306a36Sopenharmony_ci startag = XFS_FSB_TO_AGNO(mp, ap->blkno); 324662306a36Sopenharmony_ci if (startag == NULLAGNUMBER) 324762306a36Sopenharmony_ci startag = 0; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci *blen = 0; 325062306a36Sopenharmony_ci for_each_perag_wrap(mp, startag, agno, pag) { 325162306a36Sopenharmony_ci error = xfs_bmap_longest_free_extent(pag, args->tp, blen); 325262306a36Sopenharmony_ci if (error && error != -EAGAIN) 325362306a36Sopenharmony_ci break; 325462306a36Sopenharmony_ci error = 0; 325562306a36Sopenharmony_ci if (*blen >= args->maxlen) 325662306a36Sopenharmony_ci break; 325762306a36Sopenharmony_ci } 325862306a36Sopenharmony_ci if (pag) 325962306a36Sopenharmony_ci xfs_perag_rele(pag); 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci args->minlen = xfs_bmap_select_minlen(ap, args, *blen); 326262306a36Sopenharmony_ci return error; 326362306a36Sopenharmony_ci} 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci/* Update all inode and quota accounting for the allocation we just did. */ 326662306a36Sopenharmony_cistatic void 326762306a36Sopenharmony_cixfs_bmap_btalloc_accounting( 326862306a36Sopenharmony_ci struct xfs_bmalloca *ap, 326962306a36Sopenharmony_ci struct xfs_alloc_arg *args) 327062306a36Sopenharmony_ci{ 327162306a36Sopenharmony_ci if (ap->flags & XFS_BMAPI_COWFORK) { 327262306a36Sopenharmony_ci /* 327362306a36Sopenharmony_ci * COW fork blocks are in-core only and thus are treated as 327462306a36Sopenharmony_ci * in-core quota reservation (like delalloc blocks) even when 327562306a36Sopenharmony_ci * converted to real blocks. The quota reservation is not 327662306a36Sopenharmony_ci * accounted to disk until blocks are remapped to the data 327762306a36Sopenharmony_ci * fork. So if these blocks were previously delalloc, we 327862306a36Sopenharmony_ci * already have quota reservation and there's nothing to do 327962306a36Sopenharmony_ci * yet. 328062306a36Sopenharmony_ci */ 328162306a36Sopenharmony_ci if (ap->wasdel) { 328262306a36Sopenharmony_ci xfs_mod_delalloc(ap->ip->i_mount, -(int64_t)args->len); 328362306a36Sopenharmony_ci return; 328462306a36Sopenharmony_ci } 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci /* 328762306a36Sopenharmony_ci * Otherwise, we've allocated blocks in a hole. The transaction 328862306a36Sopenharmony_ci * has acquired in-core quota reservation for this extent. 328962306a36Sopenharmony_ci * Rather than account these as real blocks, however, we reduce 329062306a36Sopenharmony_ci * the transaction quota reservation based on the allocation. 329162306a36Sopenharmony_ci * This essentially transfers the transaction quota reservation 329262306a36Sopenharmony_ci * to that of a delalloc extent. 329362306a36Sopenharmony_ci */ 329462306a36Sopenharmony_ci ap->ip->i_delayed_blks += args->len; 329562306a36Sopenharmony_ci xfs_trans_mod_dquot_byino(ap->tp, ap->ip, XFS_TRANS_DQ_RES_BLKS, 329662306a36Sopenharmony_ci -(long)args->len); 329762306a36Sopenharmony_ci return; 329862306a36Sopenharmony_ci } 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci /* data/attr fork only */ 330162306a36Sopenharmony_ci ap->ip->i_nblocks += args->len; 330262306a36Sopenharmony_ci xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE); 330362306a36Sopenharmony_ci if (ap->wasdel) { 330462306a36Sopenharmony_ci ap->ip->i_delayed_blks -= args->len; 330562306a36Sopenharmony_ci xfs_mod_delalloc(ap->ip->i_mount, -(int64_t)args->len); 330662306a36Sopenharmony_ci } 330762306a36Sopenharmony_ci xfs_trans_mod_dquot_byino(ap->tp, ap->ip, 330862306a36Sopenharmony_ci ap->wasdel ? XFS_TRANS_DQ_DELBCOUNT : XFS_TRANS_DQ_BCOUNT, 330962306a36Sopenharmony_ci args->len); 331062306a36Sopenharmony_ci} 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_cistatic int 331362306a36Sopenharmony_cixfs_bmap_compute_alignments( 331462306a36Sopenharmony_ci struct xfs_bmalloca *ap, 331562306a36Sopenharmony_ci struct xfs_alloc_arg *args) 331662306a36Sopenharmony_ci{ 331762306a36Sopenharmony_ci struct xfs_mount *mp = args->mp; 331862306a36Sopenharmony_ci xfs_extlen_t align = 0; /* minimum allocation alignment */ 331962306a36Sopenharmony_ci int stripe_align = 0; 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci /* stripe alignment for allocation is determined by mount parameters */ 332262306a36Sopenharmony_ci if (mp->m_swidth && xfs_has_swalloc(mp)) 332362306a36Sopenharmony_ci stripe_align = mp->m_swidth; 332462306a36Sopenharmony_ci else if (mp->m_dalign) 332562306a36Sopenharmony_ci stripe_align = mp->m_dalign; 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci if (ap->flags & XFS_BMAPI_COWFORK) 332862306a36Sopenharmony_ci align = xfs_get_cowextsz_hint(ap->ip); 332962306a36Sopenharmony_ci else if (ap->datatype & XFS_ALLOC_USERDATA) 333062306a36Sopenharmony_ci align = xfs_get_extsz_hint(ap->ip); 333162306a36Sopenharmony_ci if (align) { 333262306a36Sopenharmony_ci if (xfs_bmap_extsize_align(mp, &ap->got, &ap->prev, align, 0, 333362306a36Sopenharmony_ci ap->eof, 0, ap->conv, &ap->offset, 333462306a36Sopenharmony_ci &ap->length)) 333562306a36Sopenharmony_ci ASSERT(0); 333662306a36Sopenharmony_ci ASSERT(ap->length); 333762306a36Sopenharmony_ci } 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci /* apply extent size hints if obtained earlier */ 334062306a36Sopenharmony_ci if (align) { 334162306a36Sopenharmony_ci args->prod = align; 334262306a36Sopenharmony_ci div_u64_rem(ap->offset, args->prod, &args->mod); 334362306a36Sopenharmony_ci if (args->mod) 334462306a36Sopenharmony_ci args->mod = args->prod - args->mod; 334562306a36Sopenharmony_ci } else if (mp->m_sb.sb_blocksize >= PAGE_SIZE) { 334662306a36Sopenharmony_ci args->prod = 1; 334762306a36Sopenharmony_ci args->mod = 0; 334862306a36Sopenharmony_ci } else { 334962306a36Sopenharmony_ci args->prod = PAGE_SIZE >> mp->m_sb.sb_blocklog; 335062306a36Sopenharmony_ci div_u64_rem(ap->offset, args->prod, &args->mod); 335162306a36Sopenharmony_ci if (args->mod) 335262306a36Sopenharmony_ci args->mod = args->prod - args->mod; 335362306a36Sopenharmony_ci } 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci return stripe_align; 335662306a36Sopenharmony_ci} 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_cistatic void 335962306a36Sopenharmony_cixfs_bmap_process_allocated_extent( 336062306a36Sopenharmony_ci struct xfs_bmalloca *ap, 336162306a36Sopenharmony_ci struct xfs_alloc_arg *args, 336262306a36Sopenharmony_ci xfs_fileoff_t orig_offset, 336362306a36Sopenharmony_ci xfs_extlen_t orig_length) 336462306a36Sopenharmony_ci{ 336562306a36Sopenharmony_ci ap->blkno = args->fsbno; 336662306a36Sopenharmony_ci ap->length = args->len; 336762306a36Sopenharmony_ci /* 336862306a36Sopenharmony_ci * If the extent size hint is active, we tried to round the 336962306a36Sopenharmony_ci * caller's allocation request offset down to extsz and the 337062306a36Sopenharmony_ci * length up to another extsz boundary. If we found a free 337162306a36Sopenharmony_ci * extent we mapped it in starting at this new offset. If the 337262306a36Sopenharmony_ci * newly mapped space isn't long enough to cover any of the 337362306a36Sopenharmony_ci * range of offsets that was originally requested, move the 337462306a36Sopenharmony_ci * mapping up so that we can fill as much of the caller's 337562306a36Sopenharmony_ci * original request as possible. Free space is apparently 337662306a36Sopenharmony_ci * very fragmented so we're unlikely to be able to satisfy the 337762306a36Sopenharmony_ci * hints anyway. 337862306a36Sopenharmony_ci */ 337962306a36Sopenharmony_ci if (ap->length <= orig_length) 338062306a36Sopenharmony_ci ap->offset = orig_offset; 338162306a36Sopenharmony_ci else if (ap->offset + ap->length < orig_offset + orig_length) 338262306a36Sopenharmony_ci ap->offset = orig_offset + orig_length - ap->length; 338362306a36Sopenharmony_ci xfs_bmap_btalloc_accounting(ap, args); 338462306a36Sopenharmony_ci} 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci#ifdef DEBUG 338762306a36Sopenharmony_cistatic int 338862306a36Sopenharmony_cixfs_bmap_exact_minlen_extent_alloc( 338962306a36Sopenharmony_ci struct xfs_bmalloca *ap) 339062306a36Sopenharmony_ci{ 339162306a36Sopenharmony_ci struct xfs_mount *mp = ap->ip->i_mount; 339262306a36Sopenharmony_ci struct xfs_alloc_arg args = { .tp = ap->tp, .mp = mp }; 339362306a36Sopenharmony_ci xfs_fileoff_t orig_offset; 339462306a36Sopenharmony_ci xfs_extlen_t orig_length; 339562306a36Sopenharmony_ci int error; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci ASSERT(ap->length); 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci if (ap->minlen != 1) { 340062306a36Sopenharmony_ci ap->blkno = NULLFSBLOCK; 340162306a36Sopenharmony_ci ap->length = 0; 340262306a36Sopenharmony_ci return 0; 340362306a36Sopenharmony_ci } 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_ci orig_offset = ap->offset; 340662306a36Sopenharmony_ci orig_length = ap->length; 340762306a36Sopenharmony_ci 340862306a36Sopenharmony_ci args.alloc_minlen_only = 1; 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci xfs_bmap_compute_alignments(ap, &args); 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_ci /* 341362306a36Sopenharmony_ci * Unlike the longest extent available in an AG, we don't track 341462306a36Sopenharmony_ci * the length of an AG's shortest extent. 341562306a36Sopenharmony_ci * XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT is a debug only knob and 341662306a36Sopenharmony_ci * hence we can afford to start traversing from the 0th AG since 341762306a36Sopenharmony_ci * we need not be concerned about a drop in performance in 341862306a36Sopenharmony_ci * "debug only" code paths. 341962306a36Sopenharmony_ci */ 342062306a36Sopenharmony_ci ap->blkno = XFS_AGB_TO_FSB(mp, 0, 0); 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci args.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE; 342362306a36Sopenharmony_ci args.minlen = args.maxlen = ap->minlen; 342462306a36Sopenharmony_ci args.total = ap->total; 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_ci args.alignment = 1; 342762306a36Sopenharmony_ci args.minalignslop = 0; 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci args.minleft = ap->minleft; 343062306a36Sopenharmony_ci args.wasdel = ap->wasdel; 343162306a36Sopenharmony_ci args.resv = XFS_AG_RESV_NONE; 343262306a36Sopenharmony_ci args.datatype = ap->datatype; 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci error = xfs_alloc_vextent_first_ag(&args, ap->blkno); 343562306a36Sopenharmony_ci if (error) 343662306a36Sopenharmony_ci return error; 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci if (args.fsbno != NULLFSBLOCK) { 343962306a36Sopenharmony_ci xfs_bmap_process_allocated_extent(ap, &args, orig_offset, 344062306a36Sopenharmony_ci orig_length); 344162306a36Sopenharmony_ci } else { 344262306a36Sopenharmony_ci ap->blkno = NULLFSBLOCK; 344362306a36Sopenharmony_ci ap->length = 0; 344462306a36Sopenharmony_ci } 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci return 0; 344762306a36Sopenharmony_ci} 344862306a36Sopenharmony_ci#else 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci#define xfs_bmap_exact_minlen_extent_alloc(bma) (-EFSCORRUPTED) 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci#endif 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci/* 345562306a36Sopenharmony_ci * If we are not low on available data blocks and we are allocating at 345662306a36Sopenharmony_ci * EOF, optimise allocation for contiguous file extension and/or stripe 345762306a36Sopenharmony_ci * alignment of the new extent. 345862306a36Sopenharmony_ci * 345962306a36Sopenharmony_ci * NOTE: ap->aeof is only set if the allocation length is >= the 346062306a36Sopenharmony_ci * stripe unit and the allocation offset is at the end of file. 346162306a36Sopenharmony_ci */ 346262306a36Sopenharmony_cistatic int 346362306a36Sopenharmony_cixfs_bmap_btalloc_at_eof( 346462306a36Sopenharmony_ci struct xfs_bmalloca *ap, 346562306a36Sopenharmony_ci struct xfs_alloc_arg *args, 346662306a36Sopenharmony_ci xfs_extlen_t blen, 346762306a36Sopenharmony_ci int stripe_align, 346862306a36Sopenharmony_ci bool ag_only) 346962306a36Sopenharmony_ci{ 347062306a36Sopenharmony_ci struct xfs_mount *mp = args->mp; 347162306a36Sopenharmony_ci struct xfs_perag *caller_pag = args->pag; 347262306a36Sopenharmony_ci int error; 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci /* 347562306a36Sopenharmony_ci * If there are already extents in the file, try an exact EOF block 347662306a36Sopenharmony_ci * allocation to extend the file as a contiguous extent. If that fails, 347762306a36Sopenharmony_ci * or it's the first allocation in a file, just try for a stripe aligned 347862306a36Sopenharmony_ci * allocation. 347962306a36Sopenharmony_ci */ 348062306a36Sopenharmony_ci if (ap->offset) { 348162306a36Sopenharmony_ci xfs_extlen_t nextminlen = 0; 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_ci /* 348462306a36Sopenharmony_ci * Compute the minlen+alignment for the next case. Set slop so 348562306a36Sopenharmony_ci * that the value of minlen+alignment+slop doesn't go up between 348662306a36Sopenharmony_ci * the calls. 348762306a36Sopenharmony_ci */ 348862306a36Sopenharmony_ci args->alignment = 1; 348962306a36Sopenharmony_ci if (blen > stripe_align && blen <= args->maxlen) 349062306a36Sopenharmony_ci nextminlen = blen - stripe_align; 349162306a36Sopenharmony_ci else 349262306a36Sopenharmony_ci nextminlen = args->minlen; 349362306a36Sopenharmony_ci if (nextminlen + stripe_align > args->minlen + 1) 349462306a36Sopenharmony_ci args->minalignslop = nextminlen + stripe_align - 349562306a36Sopenharmony_ci args->minlen - 1; 349662306a36Sopenharmony_ci else 349762306a36Sopenharmony_ci args->minalignslop = 0; 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci if (!caller_pag) 350062306a36Sopenharmony_ci args->pag = xfs_perag_get(mp, XFS_FSB_TO_AGNO(mp, ap->blkno)); 350162306a36Sopenharmony_ci error = xfs_alloc_vextent_exact_bno(args, ap->blkno); 350262306a36Sopenharmony_ci if (!caller_pag) { 350362306a36Sopenharmony_ci xfs_perag_put(args->pag); 350462306a36Sopenharmony_ci args->pag = NULL; 350562306a36Sopenharmony_ci } 350662306a36Sopenharmony_ci if (error) 350762306a36Sopenharmony_ci return error; 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci if (args->fsbno != NULLFSBLOCK) 351062306a36Sopenharmony_ci return 0; 351162306a36Sopenharmony_ci /* 351262306a36Sopenharmony_ci * Exact allocation failed. Reset to try an aligned allocation 351362306a36Sopenharmony_ci * according to the original allocation specification. 351462306a36Sopenharmony_ci */ 351562306a36Sopenharmony_ci args->alignment = stripe_align; 351662306a36Sopenharmony_ci args->minlen = nextminlen; 351762306a36Sopenharmony_ci args->minalignslop = 0; 351862306a36Sopenharmony_ci } else { 351962306a36Sopenharmony_ci /* 352062306a36Sopenharmony_ci * Adjust minlen to try and preserve alignment if we 352162306a36Sopenharmony_ci * can't guarantee an aligned maxlen extent. 352262306a36Sopenharmony_ci */ 352362306a36Sopenharmony_ci args->alignment = stripe_align; 352462306a36Sopenharmony_ci if (blen > args->alignment && 352562306a36Sopenharmony_ci blen <= args->maxlen + args->alignment) 352662306a36Sopenharmony_ci args->minlen = blen - args->alignment; 352762306a36Sopenharmony_ci args->minalignslop = 0; 352862306a36Sopenharmony_ci } 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci if (ag_only) { 353162306a36Sopenharmony_ci error = xfs_alloc_vextent_near_bno(args, ap->blkno); 353262306a36Sopenharmony_ci } else { 353362306a36Sopenharmony_ci args->pag = NULL; 353462306a36Sopenharmony_ci error = xfs_alloc_vextent_start_ag(args, ap->blkno); 353562306a36Sopenharmony_ci ASSERT(args->pag == NULL); 353662306a36Sopenharmony_ci args->pag = caller_pag; 353762306a36Sopenharmony_ci } 353862306a36Sopenharmony_ci if (error) 353962306a36Sopenharmony_ci return error; 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_ci if (args->fsbno != NULLFSBLOCK) 354262306a36Sopenharmony_ci return 0; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci /* 354562306a36Sopenharmony_ci * Allocation failed, so turn return the allocation args to their 354662306a36Sopenharmony_ci * original non-aligned state so the caller can proceed on allocation 354762306a36Sopenharmony_ci * failure as if this function was never called. 354862306a36Sopenharmony_ci */ 354962306a36Sopenharmony_ci args->alignment = 1; 355062306a36Sopenharmony_ci return 0; 355162306a36Sopenharmony_ci} 355262306a36Sopenharmony_ci 355362306a36Sopenharmony_ci/* 355462306a36Sopenharmony_ci * We have failed multiple allocation attempts so now are in a low space 355562306a36Sopenharmony_ci * allocation situation. Try a locality first full filesystem minimum length 355662306a36Sopenharmony_ci * allocation whilst still maintaining necessary total block reservation 355762306a36Sopenharmony_ci * requirements. 355862306a36Sopenharmony_ci * 355962306a36Sopenharmony_ci * If that fails, we are now critically low on space, so perform a last resort 356062306a36Sopenharmony_ci * allocation attempt: no reserve, no locality, blocking, minimum length, full 356162306a36Sopenharmony_ci * filesystem free space scan. We also indicate to future allocations in this 356262306a36Sopenharmony_ci * transaction that we are critically low on space so they don't waste time on 356362306a36Sopenharmony_ci * allocation modes that are unlikely to succeed. 356462306a36Sopenharmony_ci */ 356562306a36Sopenharmony_ciint 356662306a36Sopenharmony_cixfs_bmap_btalloc_low_space( 356762306a36Sopenharmony_ci struct xfs_bmalloca *ap, 356862306a36Sopenharmony_ci struct xfs_alloc_arg *args) 356962306a36Sopenharmony_ci{ 357062306a36Sopenharmony_ci int error; 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_ci if (args->minlen > ap->minlen) { 357362306a36Sopenharmony_ci args->minlen = ap->minlen; 357462306a36Sopenharmony_ci error = xfs_alloc_vextent_start_ag(args, ap->blkno); 357562306a36Sopenharmony_ci if (error || args->fsbno != NULLFSBLOCK) 357662306a36Sopenharmony_ci return error; 357762306a36Sopenharmony_ci } 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci /* Last ditch attempt before failure is declared. */ 358062306a36Sopenharmony_ci args->total = ap->minlen; 358162306a36Sopenharmony_ci error = xfs_alloc_vextent_first_ag(args, 0); 358262306a36Sopenharmony_ci if (error) 358362306a36Sopenharmony_ci return error; 358462306a36Sopenharmony_ci ap->tp->t_flags |= XFS_TRANS_LOWMODE; 358562306a36Sopenharmony_ci return 0; 358662306a36Sopenharmony_ci} 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_cistatic int 358962306a36Sopenharmony_cixfs_bmap_btalloc_filestreams( 359062306a36Sopenharmony_ci struct xfs_bmalloca *ap, 359162306a36Sopenharmony_ci struct xfs_alloc_arg *args, 359262306a36Sopenharmony_ci int stripe_align) 359362306a36Sopenharmony_ci{ 359462306a36Sopenharmony_ci xfs_extlen_t blen = 0; 359562306a36Sopenharmony_ci int error = 0; 359662306a36Sopenharmony_ci 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci error = xfs_filestream_select_ag(ap, args, &blen); 359962306a36Sopenharmony_ci if (error) 360062306a36Sopenharmony_ci return error; 360162306a36Sopenharmony_ci ASSERT(args->pag); 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci /* 360462306a36Sopenharmony_ci * If we are in low space mode, then optimal allocation will fail so 360562306a36Sopenharmony_ci * prepare for minimal allocation and jump to the low space algorithm 360662306a36Sopenharmony_ci * immediately. 360762306a36Sopenharmony_ci */ 360862306a36Sopenharmony_ci if (ap->tp->t_flags & XFS_TRANS_LOWMODE) { 360962306a36Sopenharmony_ci args->minlen = ap->minlen; 361062306a36Sopenharmony_ci ASSERT(args->fsbno == NULLFSBLOCK); 361162306a36Sopenharmony_ci goto out_low_space; 361262306a36Sopenharmony_ci } 361362306a36Sopenharmony_ci 361462306a36Sopenharmony_ci args->minlen = xfs_bmap_select_minlen(ap, args, blen); 361562306a36Sopenharmony_ci if (ap->aeof) 361662306a36Sopenharmony_ci error = xfs_bmap_btalloc_at_eof(ap, args, blen, stripe_align, 361762306a36Sopenharmony_ci true); 361862306a36Sopenharmony_ci 361962306a36Sopenharmony_ci if (!error && args->fsbno == NULLFSBLOCK) 362062306a36Sopenharmony_ci error = xfs_alloc_vextent_near_bno(args, ap->blkno); 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_ciout_low_space: 362362306a36Sopenharmony_ci /* 362462306a36Sopenharmony_ci * We are now done with the perag reference for the filestreams 362562306a36Sopenharmony_ci * association provided by xfs_filestream_select_ag(). Release it now as 362662306a36Sopenharmony_ci * we've either succeeded, had a fatal error or we are out of space and 362762306a36Sopenharmony_ci * need to do a full filesystem scan for free space which will take it's 362862306a36Sopenharmony_ci * own references. 362962306a36Sopenharmony_ci */ 363062306a36Sopenharmony_ci xfs_perag_rele(args->pag); 363162306a36Sopenharmony_ci args->pag = NULL; 363262306a36Sopenharmony_ci if (error || args->fsbno != NULLFSBLOCK) 363362306a36Sopenharmony_ci return error; 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci return xfs_bmap_btalloc_low_space(ap, args); 363662306a36Sopenharmony_ci} 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_cistatic int 363962306a36Sopenharmony_cixfs_bmap_btalloc_best_length( 364062306a36Sopenharmony_ci struct xfs_bmalloca *ap, 364162306a36Sopenharmony_ci struct xfs_alloc_arg *args, 364262306a36Sopenharmony_ci int stripe_align) 364362306a36Sopenharmony_ci{ 364462306a36Sopenharmony_ci xfs_extlen_t blen = 0; 364562306a36Sopenharmony_ci int error; 364662306a36Sopenharmony_ci 364762306a36Sopenharmony_ci ap->blkno = XFS_INO_TO_FSB(args->mp, ap->ip->i_ino); 364862306a36Sopenharmony_ci xfs_bmap_adjacent(ap); 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci /* 365162306a36Sopenharmony_ci * Search for an allocation group with a single extent large enough for 365262306a36Sopenharmony_ci * the request. If one isn't found, then adjust the minimum allocation 365362306a36Sopenharmony_ci * size to the largest space found. 365462306a36Sopenharmony_ci */ 365562306a36Sopenharmony_ci error = xfs_bmap_btalloc_select_lengths(ap, args, &blen); 365662306a36Sopenharmony_ci if (error) 365762306a36Sopenharmony_ci return error; 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci /* 366062306a36Sopenharmony_ci * Don't attempt optimal EOF allocation if previous allocations barely 366162306a36Sopenharmony_ci * succeeded due to being near ENOSPC. It is highly unlikely we'll get 366262306a36Sopenharmony_ci * optimal or even aligned allocations in this case, so don't waste time 366362306a36Sopenharmony_ci * trying. 366462306a36Sopenharmony_ci */ 366562306a36Sopenharmony_ci if (ap->aeof && !(ap->tp->t_flags & XFS_TRANS_LOWMODE)) { 366662306a36Sopenharmony_ci error = xfs_bmap_btalloc_at_eof(ap, args, blen, stripe_align, 366762306a36Sopenharmony_ci false); 366862306a36Sopenharmony_ci if (error || args->fsbno != NULLFSBLOCK) 366962306a36Sopenharmony_ci return error; 367062306a36Sopenharmony_ci } 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci error = xfs_alloc_vextent_start_ag(args, ap->blkno); 367362306a36Sopenharmony_ci if (error || args->fsbno != NULLFSBLOCK) 367462306a36Sopenharmony_ci return error; 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ci return xfs_bmap_btalloc_low_space(ap, args); 367762306a36Sopenharmony_ci} 367862306a36Sopenharmony_ci 367962306a36Sopenharmony_cistatic int 368062306a36Sopenharmony_cixfs_bmap_btalloc( 368162306a36Sopenharmony_ci struct xfs_bmalloca *ap) 368262306a36Sopenharmony_ci{ 368362306a36Sopenharmony_ci struct xfs_mount *mp = ap->ip->i_mount; 368462306a36Sopenharmony_ci struct xfs_alloc_arg args = { 368562306a36Sopenharmony_ci .tp = ap->tp, 368662306a36Sopenharmony_ci .mp = mp, 368762306a36Sopenharmony_ci .fsbno = NULLFSBLOCK, 368862306a36Sopenharmony_ci .oinfo = XFS_RMAP_OINFO_SKIP_UPDATE, 368962306a36Sopenharmony_ci .minleft = ap->minleft, 369062306a36Sopenharmony_ci .wasdel = ap->wasdel, 369162306a36Sopenharmony_ci .resv = XFS_AG_RESV_NONE, 369262306a36Sopenharmony_ci .datatype = ap->datatype, 369362306a36Sopenharmony_ci .alignment = 1, 369462306a36Sopenharmony_ci .minalignslop = 0, 369562306a36Sopenharmony_ci }; 369662306a36Sopenharmony_ci xfs_fileoff_t orig_offset; 369762306a36Sopenharmony_ci xfs_extlen_t orig_length; 369862306a36Sopenharmony_ci int error; 369962306a36Sopenharmony_ci int stripe_align; 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci ASSERT(ap->length); 370262306a36Sopenharmony_ci orig_offset = ap->offset; 370362306a36Sopenharmony_ci orig_length = ap->length; 370462306a36Sopenharmony_ci 370562306a36Sopenharmony_ci stripe_align = xfs_bmap_compute_alignments(ap, &args); 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci /* Trim the allocation back to the maximum an AG can fit. */ 370862306a36Sopenharmony_ci args.maxlen = min(ap->length, mp->m_ag_max_usable); 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_ci if ((ap->datatype & XFS_ALLOC_USERDATA) && 371162306a36Sopenharmony_ci xfs_inode_is_filestream(ap->ip)) 371262306a36Sopenharmony_ci error = xfs_bmap_btalloc_filestreams(ap, &args, stripe_align); 371362306a36Sopenharmony_ci else 371462306a36Sopenharmony_ci error = xfs_bmap_btalloc_best_length(ap, &args, stripe_align); 371562306a36Sopenharmony_ci if (error) 371662306a36Sopenharmony_ci return error; 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci if (args.fsbno != NULLFSBLOCK) { 371962306a36Sopenharmony_ci xfs_bmap_process_allocated_extent(ap, &args, orig_offset, 372062306a36Sopenharmony_ci orig_length); 372162306a36Sopenharmony_ci } else { 372262306a36Sopenharmony_ci ap->blkno = NULLFSBLOCK; 372362306a36Sopenharmony_ci ap->length = 0; 372462306a36Sopenharmony_ci } 372562306a36Sopenharmony_ci return 0; 372662306a36Sopenharmony_ci} 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_ci/* Trim extent to fit a logical block range. */ 372962306a36Sopenharmony_civoid 373062306a36Sopenharmony_cixfs_trim_extent( 373162306a36Sopenharmony_ci struct xfs_bmbt_irec *irec, 373262306a36Sopenharmony_ci xfs_fileoff_t bno, 373362306a36Sopenharmony_ci xfs_filblks_t len) 373462306a36Sopenharmony_ci{ 373562306a36Sopenharmony_ci xfs_fileoff_t distance; 373662306a36Sopenharmony_ci xfs_fileoff_t end = bno + len; 373762306a36Sopenharmony_ci 373862306a36Sopenharmony_ci if (irec->br_startoff + irec->br_blockcount <= bno || 373962306a36Sopenharmony_ci irec->br_startoff >= end) { 374062306a36Sopenharmony_ci irec->br_blockcount = 0; 374162306a36Sopenharmony_ci return; 374262306a36Sopenharmony_ci } 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_ci if (irec->br_startoff < bno) { 374562306a36Sopenharmony_ci distance = bno - irec->br_startoff; 374662306a36Sopenharmony_ci if (isnullstartblock(irec->br_startblock)) 374762306a36Sopenharmony_ci irec->br_startblock = DELAYSTARTBLOCK; 374862306a36Sopenharmony_ci if (irec->br_startblock != DELAYSTARTBLOCK && 374962306a36Sopenharmony_ci irec->br_startblock != HOLESTARTBLOCK) 375062306a36Sopenharmony_ci irec->br_startblock += distance; 375162306a36Sopenharmony_ci irec->br_startoff += distance; 375262306a36Sopenharmony_ci irec->br_blockcount -= distance; 375362306a36Sopenharmony_ci } 375462306a36Sopenharmony_ci 375562306a36Sopenharmony_ci if (end < irec->br_startoff + irec->br_blockcount) { 375662306a36Sopenharmony_ci distance = irec->br_startoff + irec->br_blockcount - end; 375762306a36Sopenharmony_ci irec->br_blockcount -= distance; 375862306a36Sopenharmony_ci } 375962306a36Sopenharmony_ci} 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci/* 376262306a36Sopenharmony_ci * Trim the returned map to the required bounds 376362306a36Sopenharmony_ci */ 376462306a36Sopenharmony_ciSTATIC void 376562306a36Sopenharmony_cixfs_bmapi_trim_map( 376662306a36Sopenharmony_ci struct xfs_bmbt_irec *mval, 376762306a36Sopenharmony_ci struct xfs_bmbt_irec *got, 376862306a36Sopenharmony_ci xfs_fileoff_t *bno, 376962306a36Sopenharmony_ci xfs_filblks_t len, 377062306a36Sopenharmony_ci xfs_fileoff_t obno, 377162306a36Sopenharmony_ci xfs_fileoff_t end, 377262306a36Sopenharmony_ci int n, 377362306a36Sopenharmony_ci uint32_t flags) 377462306a36Sopenharmony_ci{ 377562306a36Sopenharmony_ci if ((flags & XFS_BMAPI_ENTIRE) || 377662306a36Sopenharmony_ci got->br_startoff + got->br_blockcount <= obno) { 377762306a36Sopenharmony_ci *mval = *got; 377862306a36Sopenharmony_ci if (isnullstartblock(got->br_startblock)) 377962306a36Sopenharmony_ci mval->br_startblock = DELAYSTARTBLOCK; 378062306a36Sopenharmony_ci return; 378162306a36Sopenharmony_ci } 378262306a36Sopenharmony_ci 378362306a36Sopenharmony_ci if (obno > *bno) 378462306a36Sopenharmony_ci *bno = obno; 378562306a36Sopenharmony_ci ASSERT((*bno >= obno) || (n == 0)); 378662306a36Sopenharmony_ci ASSERT(*bno < end); 378762306a36Sopenharmony_ci mval->br_startoff = *bno; 378862306a36Sopenharmony_ci if (isnullstartblock(got->br_startblock)) 378962306a36Sopenharmony_ci mval->br_startblock = DELAYSTARTBLOCK; 379062306a36Sopenharmony_ci else 379162306a36Sopenharmony_ci mval->br_startblock = got->br_startblock + 379262306a36Sopenharmony_ci (*bno - got->br_startoff); 379362306a36Sopenharmony_ci /* 379462306a36Sopenharmony_ci * Return the minimum of what we got and what we asked for for 379562306a36Sopenharmony_ci * the length. We can use the len variable here because it is 379662306a36Sopenharmony_ci * modified below and we could have been there before coming 379762306a36Sopenharmony_ci * here if the first part of the allocation didn't overlap what 379862306a36Sopenharmony_ci * was asked for. 379962306a36Sopenharmony_ci */ 380062306a36Sopenharmony_ci mval->br_blockcount = XFS_FILBLKS_MIN(end - *bno, 380162306a36Sopenharmony_ci got->br_blockcount - (*bno - got->br_startoff)); 380262306a36Sopenharmony_ci mval->br_state = got->br_state; 380362306a36Sopenharmony_ci ASSERT(mval->br_blockcount <= len); 380462306a36Sopenharmony_ci return; 380562306a36Sopenharmony_ci} 380662306a36Sopenharmony_ci 380762306a36Sopenharmony_ci/* 380862306a36Sopenharmony_ci * Update and validate the extent map to return 380962306a36Sopenharmony_ci */ 381062306a36Sopenharmony_ciSTATIC void 381162306a36Sopenharmony_cixfs_bmapi_update_map( 381262306a36Sopenharmony_ci struct xfs_bmbt_irec **map, 381362306a36Sopenharmony_ci xfs_fileoff_t *bno, 381462306a36Sopenharmony_ci xfs_filblks_t *len, 381562306a36Sopenharmony_ci xfs_fileoff_t obno, 381662306a36Sopenharmony_ci xfs_fileoff_t end, 381762306a36Sopenharmony_ci int *n, 381862306a36Sopenharmony_ci uint32_t flags) 381962306a36Sopenharmony_ci{ 382062306a36Sopenharmony_ci xfs_bmbt_irec_t *mval = *map; 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci ASSERT((flags & XFS_BMAPI_ENTIRE) || 382362306a36Sopenharmony_ci ((mval->br_startoff + mval->br_blockcount) <= end)); 382462306a36Sopenharmony_ci ASSERT((flags & XFS_BMAPI_ENTIRE) || (mval->br_blockcount <= *len) || 382562306a36Sopenharmony_ci (mval->br_startoff < obno)); 382662306a36Sopenharmony_ci 382762306a36Sopenharmony_ci *bno = mval->br_startoff + mval->br_blockcount; 382862306a36Sopenharmony_ci *len = end - *bno; 382962306a36Sopenharmony_ci if (*n > 0 && mval->br_startoff == mval[-1].br_startoff) { 383062306a36Sopenharmony_ci /* update previous map with new information */ 383162306a36Sopenharmony_ci ASSERT(mval->br_startblock == mval[-1].br_startblock); 383262306a36Sopenharmony_ci ASSERT(mval->br_blockcount > mval[-1].br_blockcount); 383362306a36Sopenharmony_ci ASSERT(mval->br_state == mval[-1].br_state); 383462306a36Sopenharmony_ci mval[-1].br_blockcount = mval->br_blockcount; 383562306a36Sopenharmony_ci mval[-1].br_state = mval->br_state; 383662306a36Sopenharmony_ci } else if (*n > 0 && mval->br_startblock != DELAYSTARTBLOCK && 383762306a36Sopenharmony_ci mval[-1].br_startblock != DELAYSTARTBLOCK && 383862306a36Sopenharmony_ci mval[-1].br_startblock != HOLESTARTBLOCK && 383962306a36Sopenharmony_ci mval->br_startblock == mval[-1].br_startblock + 384062306a36Sopenharmony_ci mval[-1].br_blockcount && 384162306a36Sopenharmony_ci mval[-1].br_state == mval->br_state) { 384262306a36Sopenharmony_ci ASSERT(mval->br_startoff == 384362306a36Sopenharmony_ci mval[-1].br_startoff + mval[-1].br_blockcount); 384462306a36Sopenharmony_ci mval[-1].br_blockcount += mval->br_blockcount; 384562306a36Sopenharmony_ci } else if (*n > 0 && 384662306a36Sopenharmony_ci mval->br_startblock == DELAYSTARTBLOCK && 384762306a36Sopenharmony_ci mval[-1].br_startblock == DELAYSTARTBLOCK && 384862306a36Sopenharmony_ci mval->br_startoff == 384962306a36Sopenharmony_ci mval[-1].br_startoff + mval[-1].br_blockcount) { 385062306a36Sopenharmony_ci mval[-1].br_blockcount += mval->br_blockcount; 385162306a36Sopenharmony_ci mval[-1].br_state = mval->br_state; 385262306a36Sopenharmony_ci } else if (!((*n == 0) && 385362306a36Sopenharmony_ci ((mval->br_startoff + mval->br_blockcount) <= 385462306a36Sopenharmony_ci obno))) { 385562306a36Sopenharmony_ci mval++; 385662306a36Sopenharmony_ci (*n)++; 385762306a36Sopenharmony_ci } 385862306a36Sopenharmony_ci *map = mval; 385962306a36Sopenharmony_ci} 386062306a36Sopenharmony_ci 386162306a36Sopenharmony_ci/* 386262306a36Sopenharmony_ci * Map file blocks to filesystem blocks without allocation. 386362306a36Sopenharmony_ci */ 386462306a36Sopenharmony_ciint 386562306a36Sopenharmony_cixfs_bmapi_read( 386662306a36Sopenharmony_ci struct xfs_inode *ip, 386762306a36Sopenharmony_ci xfs_fileoff_t bno, 386862306a36Sopenharmony_ci xfs_filblks_t len, 386962306a36Sopenharmony_ci struct xfs_bmbt_irec *mval, 387062306a36Sopenharmony_ci int *nmap, 387162306a36Sopenharmony_ci uint32_t flags) 387262306a36Sopenharmony_ci{ 387362306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 387462306a36Sopenharmony_ci int whichfork = xfs_bmapi_whichfork(flags); 387562306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 387662306a36Sopenharmony_ci struct xfs_bmbt_irec got; 387762306a36Sopenharmony_ci xfs_fileoff_t obno; 387862306a36Sopenharmony_ci xfs_fileoff_t end; 387962306a36Sopenharmony_ci struct xfs_iext_cursor icur; 388062306a36Sopenharmony_ci int error; 388162306a36Sopenharmony_ci bool eof = false; 388262306a36Sopenharmony_ci int n = 0; 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci ASSERT(*nmap >= 1); 388562306a36Sopenharmony_ci ASSERT(!(flags & ~(XFS_BMAPI_ATTRFORK | XFS_BMAPI_ENTIRE))); 388662306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)); 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci if (WARN_ON_ONCE(!ifp)) 388962306a36Sopenharmony_ci return -EFSCORRUPTED; 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp)) || 389262306a36Sopenharmony_ci XFS_TEST_ERROR(false, mp, XFS_ERRTAG_BMAPIFORMAT)) 389362306a36Sopenharmony_ci return -EFSCORRUPTED; 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 389662306a36Sopenharmony_ci return -EIO; 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_blk_mapr); 389962306a36Sopenharmony_ci 390062306a36Sopenharmony_ci error = xfs_iread_extents(NULL, ip, whichfork); 390162306a36Sopenharmony_ci if (error) 390262306a36Sopenharmony_ci return error; 390362306a36Sopenharmony_ci 390462306a36Sopenharmony_ci if (!xfs_iext_lookup_extent(ip, ifp, bno, &icur, &got)) 390562306a36Sopenharmony_ci eof = true; 390662306a36Sopenharmony_ci end = bno + len; 390762306a36Sopenharmony_ci obno = bno; 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci while (bno < end && n < *nmap) { 391062306a36Sopenharmony_ci /* Reading past eof, act as though there's a hole up to end. */ 391162306a36Sopenharmony_ci if (eof) 391262306a36Sopenharmony_ci got.br_startoff = end; 391362306a36Sopenharmony_ci if (got.br_startoff > bno) { 391462306a36Sopenharmony_ci /* Reading in a hole. */ 391562306a36Sopenharmony_ci mval->br_startoff = bno; 391662306a36Sopenharmony_ci mval->br_startblock = HOLESTARTBLOCK; 391762306a36Sopenharmony_ci mval->br_blockcount = 391862306a36Sopenharmony_ci XFS_FILBLKS_MIN(len, got.br_startoff - bno); 391962306a36Sopenharmony_ci mval->br_state = XFS_EXT_NORM; 392062306a36Sopenharmony_ci bno += mval->br_blockcount; 392162306a36Sopenharmony_ci len -= mval->br_blockcount; 392262306a36Sopenharmony_ci mval++; 392362306a36Sopenharmony_ci n++; 392462306a36Sopenharmony_ci continue; 392562306a36Sopenharmony_ci } 392662306a36Sopenharmony_ci 392762306a36Sopenharmony_ci /* set up the extent map to return. */ 392862306a36Sopenharmony_ci xfs_bmapi_trim_map(mval, &got, &bno, len, obno, end, n, flags); 392962306a36Sopenharmony_ci xfs_bmapi_update_map(&mval, &bno, &len, obno, end, &n, flags); 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_ci /* If we're done, stop now. */ 393262306a36Sopenharmony_ci if (bno >= end || n >= *nmap) 393362306a36Sopenharmony_ci break; 393462306a36Sopenharmony_ci 393562306a36Sopenharmony_ci /* Else go on to the next record. */ 393662306a36Sopenharmony_ci if (!xfs_iext_next_extent(ifp, &icur, &got)) 393762306a36Sopenharmony_ci eof = true; 393862306a36Sopenharmony_ci } 393962306a36Sopenharmony_ci *nmap = n; 394062306a36Sopenharmony_ci return 0; 394162306a36Sopenharmony_ci} 394262306a36Sopenharmony_ci 394362306a36Sopenharmony_ci/* 394462306a36Sopenharmony_ci * Add a delayed allocation extent to an inode. Blocks are reserved from the 394562306a36Sopenharmony_ci * global pool and the extent inserted into the inode in-core extent tree. 394662306a36Sopenharmony_ci * 394762306a36Sopenharmony_ci * On entry, got refers to the first extent beyond the offset of the extent to 394862306a36Sopenharmony_ci * allocate or eof is specified if no such extent exists. On return, got refers 394962306a36Sopenharmony_ci * to the extent record that was inserted to the inode fork. 395062306a36Sopenharmony_ci * 395162306a36Sopenharmony_ci * Note that the allocated extent may have been merged with contiguous extents 395262306a36Sopenharmony_ci * during insertion into the inode fork. Thus, got does not reflect the current 395362306a36Sopenharmony_ci * state of the inode fork on return. If necessary, the caller can use lastx to 395462306a36Sopenharmony_ci * look up the updated record in the inode fork. 395562306a36Sopenharmony_ci */ 395662306a36Sopenharmony_ciint 395762306a36Sopenharmony_cixfs_bmapi_reserve_delalloc( 395862306a36Sopenharmony_ci struct xfs_inode *ip, 395962306a36Sopenharmony_ci int whichfork, 396062306a36Sopenharmony_ci xfs_fileoff_t off, 396162306a36Sopenharmony_ci xfs_filblks_t len, 396262306a36Sopenharmony_ci xfs_filblks_t prealloc, 396362306a36Sopenharmony_ci struct xfs_bmbt_irec *got, 396462306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 396562306a36Sopenharmony_ci int eof) 396662306a36Sopenharmony_ci{ 396762306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 396862306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 396962306a36Sopenharmony_ci xfs_extlen_t alen; 397062306a36Sopenharmony_ci xfs_extlen_t indlen; 397162306a36Sopenharmony_ci int error; 397262306a36Sopenharmony_ci xfs_fileoff_t aoff = off; 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci /* 397562306a36Sopenharmony_ci * Cap the alloc length. Keep track of prealloc so we know whether to 397662306a36Sopenharmony_ci * tag the inode before we return. 397762306a36Sopenharmony_ci */ 397862306a36Sopenharmony_ci alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN); 397962306a36Sopenharmony_ci if (!eof) 398062306a36Sopenharmony_ci alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff); 398162306a36Sopenharmony_ci if (prealloc && alen >= len) 398262306a36Sopenharmony_ci prealloc = alen - len; 398362306a36Sopenharmony_ci 398462306a36Sopenharmony_ci /* Figure out the extent size, adjust alen */ 398562306a36Sopenharmony_ci if (whichfork == XFS_COW_FORK) { 398662306a36Sopenharmony_ci struct xfs_bmbt_irec prev; 398762306a36Sopenharmony_ci xfs_extlen_t extsz = xfs_get_cowextsz_hint(ip); 398862306a36Sopenharmony_ci 398962306a36Sopenharmony_ci if (!xfs_iext_peek_prev_extent(ifp, icur, &prev)) 399062306a36Sopenharmony_ci prev.br_startoff = NULLFILEOFF; 399162306a36Sopenharmony_ci 399262306a36Sopenharmony_ci error = xfs_bmap_extsize_align(mp, got, &prev, extsz, 0, eof, 399362306a36Sopenharmony_ci 1, 0, &aoff, &alen); 399462306a36Sopenharmony_ci ASSERT(!error); 399562306a36Sopenharmony_ci } 399662306a36Sopenharmony_ci 399762306a36Sopenharmony_ci /* 399862306a36Sopenharmony_ci * Make a transaction-less quota reservation for delayed allocation 399962306a36Sopenharmony_ci * blocks. This number gets adjusted later. We return if we haven't 400062306a36Sopenharmony_ci * allocated blocks already inside this loop. 400162306a36Sopenharmony_ci */ 400262306a36Sopenharmony_ci error = xfs_quota_reserve_blkres(ip, alen); 400362306a36Sopenharmony_ci if (error) 400462306a36Sopenharmony_ci return error; 400562306a36Sopenharmony_ci 400662306a36Sopenharmony_ci /* 400762306a36Sopenharmony_ci * Split changing sb for alen and indlen since they could be coming 400862306a36Sopenharmony_ci * from different places. 400962306a36Sopenharmony_ci */ 401062306a36Sopenharmony_ci indlen = (xfs_extlen_t)xfs_bmap_worst_indlen(ip, alen); 401162306a36Sopenharmony_ci ASSERT(indlen > 0); 401262306a36Sopenharmony_ci 401362306a36Sopenharmony_ci error = xfs_mod_fdblocks(mp, -((int64_t)alen), false); 401462306a36Sopenharmony_ci if (error) 401562306a36Sopenharmony_ci goto out_unreserve_quota; 401662306a36Sopenharmony_ci 401762306a36Sopenharmony_ci error = xfs_mod_fdblocks(mp, -((int64_t)indlen), false); 401862306a36Sopenharmony_ci if (error) 401962306a36Sopenharmony_ci goto out_unreserve_blocks; 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci 402262306a36Sopenharmony_ci ip->i_delayed_blks += alen; 402362306a36Sopenharmony_ci xfs_mod_delalloc(ip->i_mount, alen + indlen); 402462306a36Sopenharmony_ci 402562306a36Sopenharmony_ci got->br_startoff = aoff; 402662306a36Sopenharmony_ci got->br_startblock = nullstartblock(indlen); 402762306a36Sopenharmony_ci got->br_blockcount = alen; 402862306a36Sopenharmony_ci got->br_state = XFS_EXT_NORM; 402962306a36Sopenharmony_ci 403062306a36Sopenharmony_ci xfs_bmap_add_extent_hole_delay(ip, whichfork, icur, got); 403162306a36Sopenharmony_ci 403262306a36Sopenharmony_ci /* 403362306a36Sopenharmony_ci * Tag the inode if blocks were preallocated. Note that COW fork 403462306a36Sopenharmony_ci * preallocation can occur at the start or end of the extent, even when 403562306a36Sopenharmony_ci * prealloc == 0, so we must also check the aligned offset and length. 403662306a36Sopenharmony_ci */ 403762306a36Sopenharmony_ci if (whichfork == XFS_DATA_FORK && prealloc) 403862306a36Sopenharmony_ci xfs_inode_set_eofblocks_tag(ip); 403962306a36Sopenharmony_ci if (whichfork == XFS_COW_FORK && (prealloc || aoff < off || alen > len)) 404062306a36Sopenharmony_ci xfs_inode_set_cowblocks_tag(ip); 404162306a36Sopenharmony_ci 404262306a36Sopenharmony_ci return 0; 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ciout_unreserve_blocks: 404562306a36Sopenharmony_ci xfs_mod_fdblocks(mp, alen, false); 404662306a36Sopenharmony_ciout_unreserve_quota: 404762306a36Sopenharmony_ci if (XFS_IS_QUOTA_ON(mp)) 404862306a36Sopenharmony_ci xfs_quota_unreserve_blkres(ip, alen); 404962306a36Sopenharmony_ci return error; 405062306a36Sopenharmony_ci} 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_cistatic int 405362306a36Sopenharmony_cixfs_bmap_alloc_userdata( 405462306a36Sopenharmony_ci struct xfs_bmalloca *bma) 405562306a36Sopenharmony_ci{ 405662306a36Sopenharmony_ci struct xfs_mount *mp = bma->ip->i_mount; 405762306a36Sopenharmony_ci int whichfork = xfs_bmapi_whichfork(bma->flags); 405862306a36Sopenharmony_ci int error; 405962306a36Sopenharmony_ci 406062306a36Sopenharmony_ci /* 406162306a36Sopenharmony_ci * Set the data type being allocated. For the data fork, the first data 406262306a36Sopenharmony_ci * in the file is treated differently to all other allocations. For the 406362306a36Sopenharmony_ci * attribute fork, we only need to ensure the allocated range is not on 406462306a36Sopenharmony_ci * the busy list. 406562306a36Sopenharmony_ci */ 406662306a36Sopenharmony_ci bma->datatype = XFS_ALLOC_NOBUSY; 406762306a36Sopenharmony_ci if (whichfork == XFS_DATA_FORK || whichfork == XFS_COW_FORK) { 406862306a36Sopenharmony_ci bma->datatype |= XFS_ALLOC_USERDATA; 406962306a36Sopenharmony_ci if (bma->offset == 0) 407062306a36Sopenharmony_ci bma->datatype |= XFS_ALLOC_INITIAL_USER_DATA; 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci if (mp->m_dalign && bma->length >= mp->m_dalign) { 407362306a36Sopenharmony_ci error = xfs_bmap_isaeof(bma, whichfork); 407462306a36Sopenharmony_ci if (error) 407562306a36Sopenharmony_ci return error; 407662306a36Sopenharmony_ci } 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci if (XFS_IS_REALTIME_INODE(bma->ip)) 407962306a36Sopenharmony_ci return xfs_bmap_rtalloc(bma); 408062306a36Sopenharmony_ci } 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_ci if (unlikely(XFS_TEST_ERROR(false, mp, 408362306a36Sopenharmony_ci XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT))) 408462306a36Sopenharmony_ci return xfs_bmap_exact_minlen_extent_alloc(bma); 408562306a36Sopenharmony_ci 408662306a36Sopenharmony_ci return xfs_bmap_btalloc(bma); 408762306a36Sopenharmony_ci} 408862306a36Sopenharmony_ci 408962306a36Sopenharmony_cistatic int 409062306a36Sopenharmony_cixfs_bmapi_allocate( 409162306a36Sopenharmony_ci struct xfs_bmalloca *bma) 409262306a36Sopenharmony_ci{ 409362306a36Sopenharmony_ci struct xfs_mount *mp = bma->ip->i_mount; 409462306a36Sopenharmony_ci int whichfork = xfs_bmapi_whichfork(bma->flags); 409562306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); 409662306a36Sopenharmony_ci int tmp_logflags = 0; 409762306a36Sopenharmony_ci int error; 409862306a36Sopenharmony_ci 409962306a36Sopenharmony_ci ASSERT(bma->length > 0); 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_ci /* 410262306a36Sopenharmony_ci * For the wasdelay case, we could also just allocate the stuff asked 410362306a36Sopenharmony_ci * for in this bmap call but that wouldn't be as good. 410462306a36Sopenharmony_ci */ 410562306a36Sopenharmony_ci if (bma->wasdel) { 410662306a36Sopenharmony_ci bma->length = (xfs_extlen_t)bma->got.br_blockcount; 410762306a36Sopenharmony_ci bma->offset = bma->got.br_startoff; 410862306a36Sopenharmony_ci if (!xfs_iext_peek_prev_extent(ifp, &bma->icur, &bma->prev)) 410962306a36Sopenharmony_ci bma->prev.br_startoff = NULLFILEOFF; 411062306a36Sopenharmony_ci } else { 411162306a36Sopenharmony_ci bma->length = XFS_FILBLKS_MIN(bma->length, XFS_MAX_BMBT_EXTLEN); 411262306a36Sopenharmony_ci if (!bma->eof) 411362306a36Sopenharmony_ci bma->length = XFS_FILBLKS_MIN(bma->length, 411462306a36Sopenharmony_ci bma->got.br_startoff - bma->offset); 411562306a36Sopenharmony_ci } 411662306a36Sopenharmony_ci 411762306a36Sopenharmony_ci if (bma->flags & XFS_BMAPI_CONTIG) 411862306a36Sopenharmony_ci bma->minlen = bma->length; 411962306a36Sopenharmony_ci else 412062306a36Sopenharmony_ci bma->minlen = 1; 412162306a36Sopenharmony_ci 412262306a36Sopenharmony_ci if (bma->flags & XFS_BMAPI_METADATA) { 412362306a36Sopenharmony_ci if (unlikely(XFS_TEST_ERROR(false, mp, 412462306a36Sopenharmony_ci XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT))) 412562306a36Sopenharmony_ci error = xfs_bmap_exact_minlen_extent_alloc(bma); 412662306a36Sopenharmony_ci else 412762306a36Sopenharmony_ci error = xfs_bmap_btalloc(bma); 412862306a36Sopenharmony_ci } else { 412962306a36Sopenharmony_ci error = xfs_bmap_alloc_userdata(bma); 413062306a36Sopenharmony_ci } 413162306a36Sopenharmony_ci if (error || bma->blkno == NULLFSBLOCK) 413262306a36Sopenharmony_ci return error; 413362306a36Sopenharmony_ci 413462306a36Sopenharmony_ci if (bma->flags & XFS_BMAPI_ZERO) { 413562306a36Sopenharmony_ci error = xfs_zero_extent(bma->ip, bma->blkno, bma->length); 413662306a36Sopenharmony_ci if (error) 413762306a36Sopenharmony_ci return error; 413862306a36Sopenharmony_ci } 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_BTREE && !bma->cur) 414162306a36Sopenharmony_ci bma->cur = xfs_bmbt_init_cursor(mp, bma->tp, bma->ip, whichfork); 414262306a36Sopenharmony_ci /* 414362306a36Sopenharmony_ci * Bump the number of extents we've allocated 414462306a36Sopenharmony_ci * in this call. 414562306a36Sopenharmony_ci */ 414662306a36Sopenharmony_ci bma->nallocs++; 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_ci if (bma->cur) 414962306a36Sopenharmony_ci bma->cur->bc_ino.flags = 415062306a36Sopenharmony_ci bma->wasdel ? XFS_BTCUR_BMBT_WASDEL : 0; 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci bma->got.br_startoff = bma->offset; 415362306a36Sopenharmony_ci bma->got.br_startblock = bma->blkno; 415462306a36Sopenharmony_ci bma->got.br_blockcount = bma->length; 415562306a36Sopenharmony_ci bma->got.br_state = XFS_EXT_NORM; 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci if (bma->flags & XFS_BMAPI_PREALLOC) 415862306a36Sopenharmony_ci bma->got.br_state = XFS_EXT_UNWRITTEN; 415962306a36Sopenharmony_ci 416062306a36Sopenharmony_ci if (bma->wasdel) 416162306a36Sopenharmony_ci error = xfs_bmap_add_extent_delay_real(bma, whichfork); 416262306a36Sopenharmony_ci else 416362306a36Sopenharmony_ci error = xfs_bmap_add_extent_hole_real(bma->tp, bma->ip, 416462306a36Sopenharmony_ci whichfork, &bma->icur, &bma->cur, &bma->got, 416562306a36Sopenharmony_ci &bma->logflags, bma->flags); 416662306a36Sopenharmony_ci 416762306a36Sopenharmony_ci bma->logflags |= tmp_logflags; 416862306a36Sopenharmony_ci if (error) 416962306a36Sopenharmony_ci return error; 417062306a36Sopenharmony_ci 417162306a36Sopenharmony_ci /* 417262306a36Sopenharmony_ci * Update our extent pointer, given that xfs_bmap_add_extent_delay_real 417362306a36Sopenharmony_ci * or xfs_bmap_add_extent_hole_real might have merged it into one of 417462306a36Sopenharmony_ci * the neighbouring ones. 417562306a36Sopenharmony_ci */ 417662306a36Sopenharmony_ci xfs_iext_get_extent(ifp, &bma->icur, &bma->got); 417762306a36Sopenharmony_ci 417862306a36Sopenharmony_ci ASSERT(bma->got.br_startoff <= bma->offset); 417962306a36Sopenharmony_ci ASSERT(bma->got.br_startoff + bma->got.br_blockcount >= 418062306a36Sopenharmony_ci bma->offset + bma->length); 418162306a36Sopenharmony_ci ASSERT(bma->got.br_state == XFS_EXT_NORM || 418262306a36Sopenharmony_ci bma->got.br_state == XFS_EXT_UNWRITTEN); 418362306a36Sopenharmony_ci return 0; 418462306a36Sopenharmony_ci} 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ciSTATIC int 418762306a36Sopenharmony_cixfs_bmapi_convert_unwritten( 418862306a36Sopenharmony_ci struct xfs_bmalloca *bma, 418962306a36Sopenharmony_ci struct xfs_bmbt_irec *mval, 419062306a36Sopenharmony_ci xfs_filblks_t len, 419162306a36Sopenharmony_ci uint32_t flags) 419262306a36Sopenharmony_ci{ 419362306a36Sopenharmony_ci int whichfork = xfs_bmapi_whichfork(flags); 419462306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); 419562306a36Sopenharmony_ci int tmp_logflags = 0; 419662306a36Sopenharmony_ci int error; 419762306a36Sopenharmony_ci 419862306a36Sopenharmony_ci /* check if we need to do unwritten->real conversion */ 419962306a36Sopenharmony_ci if (mval->br_state == XFS_EXT_UNWRITTEN && 420062306a36Sopenharmony_ci (flags & XFS_BMAPI_PREALLOC)) 420162306a36Sopenharmony_ci return 0; 420262306a36Sopenharmony_ci 420362306a36Sopenharmony_ci /* check if we need to do real->unwritten conversion */ 420462306a36Sopenharmony_ci if (mval->br_state == XFS_EXT_NORM && 420562306a36Sopenharmony_ci (flags & (XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT)) != 420662306a36Sopenharmony_ci (XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT)) 420762306a36Sopenharmony_ci return 0; 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci /* 421062306a36Sopenharmony_ci * Modify (by adding) the state flag, if writing. 421162306a36Sopenharmony_ci */ 421262306a36Sopenharmony_ci ASSERT(mval->br_blockcount <= len); 421362306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_BTREE && !bma->cur) { 421462306a36Sopenharmony_ci bma->cur = xfs_bmbt_init_cursor(bma->ip->i_mount, bma->tp, 421562306a36Sopenharmony_ci bma->ip, whichfork); 421662306a36Sopenharmony_ci } 421762306a36Sopenharmony_ci mval->br_state = (mval->br_state == XFS_EXT_UNWRITTEN) 421862306a36Sopenharmony_ci ? XFS_EXT_NORM : XFS_EXT_UNWRITTEN; 421962306a36Sopenharmony_ci 422062306a36Sopenharmony_ci /* 422162306a36Sopenharmony_ci * Before insertion into the bmbt, zero the range being converted 422262306a36Sopenharmony_ci * if required. 422362306a36Sopenharmony_ci */ 422462306a36Sopenharmony_ci if (flags & XFS_BMAPI_ZERO) { 422562306a36Sopenharmony_ci error = xfs_zero_extent(bma->ip, mval->br_startblock, 422662306a36Sopenharmony_ci mval->br_blockcount); 422762306a36Sopenharmony_ci if (error) 422862306a36Sopenharmony_ci return error; 422962306a36Sopenharmony_ci } 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, whichfork, 423262306a36Sopenharmony_ci &bma->icur, &bma->cur, mval, &tmp_logflags); 423362306a36Sopenharmony_ci /* 423462306a36Sopenharmony_ci * Log the inode core unconditionally in the unwritten extent conversion 423562306a36Sopenharmony_ci * path because the conversion might not have done so (e.g., if the 423662306a36Sopenharmony_ci * extent count hasn't changed). We need to make sure the inode is dirty 423762306a36Sopenharmony_ci * in the transaction for the sake of fsync(), even if nothing has 423862306a36Sopenharmony_ci * changed, because fsync() will not force the log for this transaction 423962306a36Sopenharmony_ci * unless it sees the inode pinned. 424062306a36Sopenharmony_ci * 424162306a36Sopenharmony_ci * Note: If we're only converting cow fork extents, there aren't 424262306a36Sopenharmony_ci * any on-disk updates to make, so we don't need to log anything. 424362306a36Sopenharmony_ci */ 424462306a36Sopenharmony_ci if (whichfork != XFS_COW_FORK) 424562306a36Sopenharmony_ci bma->logflags |= tmp_logflags | XFS_ILOG_CORE; 424662306a36Sopenharmony_ci if (error) 424762306a36Sopenharmony_ci return error; 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_ci /* 425062306a36Sopenharmony_ci * Update our extent pointer, given that 425162306a36Sopenharmony_ci * xfs_bmap_add_extent_unwritten_real might have merged it into one 425262306a36Sopenharmony_ci * of the neighbouring ones. 425362306a36Sopenharmony_ci */ 425462306a36Sopenharmony_ci xfs_iext_get_extent(ifp, &bma->icur, &bma->got); 425562306a36Sopenharmony_ci 425662306a36Sopenharmony_ci /* 425762306a36Sopenharmony_ci * We may have combined previously unwritten space with written space, 425862306a36Sopenharmony_ci * so generate another request. 425962306a36Sopenharmony_ci */ 426062306a36Sopenharmony_ci if (mval->br_blockcount < len) 426162306a36Sopenharmony_ci return -EAGAIN; 426262306a36Sopenharmony_ci return 0; 426362306a36Sopenharmony_ci} 426462306a36Sopenharmony_ci 426562306a36Sopenharmony_cixfs_extlen_t 426662306a36Sopenharmony_cixfs_bmapi_minleft( 426762306a36Sopenharmony_ci struct xfs_trans *tp, 426862306a36Sopenharmony_ci struct xfs_inode *ip, 426962306a36Sopenharmony_ci int fork) 427062306a36Sopenharmony_ci{ 427162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, fork); 427262306a36Sopenharmony_ci 427362306a36Sopenharmony_ci if (tp && tp->t_highest_agno != NULLAGNUMBER) 427462306a36Sopenharmony_ci return 0; 427562306a36Sopenharmony_ci if (ifp->if_format != XFS_DINODE_FMT_BTREE) 427662306a36Sopenharmony_ci return 1; 427762306a36Sopenharmony_ci return be16_to_cpu(ifp->if_broot->bb_level) + 1; 427862306a36Sopenharmony_ci} 427962306a36Sopenharmony_ci 428062306a36Sopenharmony_ci/* 428162306a36Sopenharmony_ci * Log whatever the flags say, even if error. Otherwise we might miss detecting 428262306a36Sopenharmony_ci * a case where the data is changed, there's an error, and it's not logged so we 428362306a36Sopenharmony_ci * don't shutdown when we should. Don't bother logging extents/btree changes if 428462306a36Sopenharmony_ci * we converted to the other format. 428562306a36Sopenharmony_ci */ 428662306a36Sopenharmony_cistatic void 428762306a36Sopenharmony_cixfs_bmapi_finish( 428862306a36Sopenharmony_ci struct xfs_bmalloca *bma, 428962306a36Sopenharmony_ci int whichfork, 429062306a36Sopenharmony_ci int error) 429162306a36Sopenharmony_ci{ 429262306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); 429362306a36Sopenharmony_ci 429462306a36Sopenharmony_ci if ((bma->logflags & xfs_ilog_fext(whichfork)) && 429562306a36Sopenharmony_ci ifp->if_format != XFS_DINODE_FMT_EXTENTS) 429662306a36Sopenharmony_ci bma->logflags &= ~xfs_ilog_fext(whichfork); 429762306a36Sopenharmony_ci else if ((bma->logflags & xfs_ilog_fbroot(whichfork)) && 429862306a36Sopenharmony_ci ifp->if_format != XFS_DINODE_FMT_BTREE) 429962306a36Sopenharmony_ci bma->logflags &= ~xfs_ilog_fbroot(whichfork); 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci if (bma->logflags) 430262306a36Sopenharmony_ci xfs_trans_log_inode(bma->tp, bma->ip, bma->logflags); 430362306a36Sopenharmony_ci if (bma->cur) 430462306a36Sopenharmony_ci xfs_btree_del_cursor(bma->cur, error); 430562306a36Sopenharmony_ci} 430662306a36Sopenharmony_ci 430762306a36Sopenharmony_ci/* 430862306a36Sopenharmony_ci * Map file blocks to filesystem blocks, and allocate blocks or convert the 430962306a36Sopenharmony_ci * extent state if necessary. Details behaviour is controlled by the flags 431062306a36Sopenharmony_ci * parameter. Only allocates blocks from a single allocation group, to avoid 431162306a36Sopenharmony_ci * locking problems. 431262306a36Sopenharmony_ci */ 431362306a36Sopenharmony_ciint 431462306a36Sopenharmony_cixfs_bmapi_write( 431562306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 431662306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode */ 431762306a36Sopenharmony_ci xfs_fileoff_t bno, /* starting file offs. mapped */ 431862306a36Sopenharmony_ci xfs_filblks_t len, /* length to map in file */ 431962306a36Sopenharmony_ci uint32_t flags, /* XFS_BMAPI_... */ 432062306a36Sopenharmony_ci xfs_extlen_t total, /* total blocks needed */ 432162306a36Sopenharmony_ci struct xfs_bmbt_irec *mval, /* output: map values */ 432262306a36Sopenharmony_ci int *nmap) /* i/o: mval size/count */ 432362306a36Sopenharmony_ci{ 432462306a36Sopenharmony_ci struct xfs_bmalloca bma = { 432562306a36Sopenharmony_ci .tp = tp, 432662306a36Sopenharmony_ci .ip = ip, 432762306a36Sopenharmony_ci .total = total, 432862306a36Sopenharmony_ci }; 432962306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 433062306a36Sopenharmony_ci int whichfork = xfs_bmapi_whichfork(flags); 433162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 433262306a36Sopenharmony_ci xfs_fileoff_t end; /* end of mapped file region */ 433362306a36Sopenharmony_ci bool eof = false; /* after the end of extents */ 433462306a36Sopenharmony_ci int error; /* error return */ 433562306a36Sopenharmony_ci int n; /* current extent index */ 433662306a36Sopenharmony_ci xfs_fileoff_t obno; /* old block number (offset) */ 433762306a36Sopenharmony_ci 433862306a36Sopenharmony_ci#ifdef DEBUG 433962306a36Sopenharmony_ci xfs_fileoff_t orig_bno; /* original block number value */ 434062306a36Sopenharmony_ci int orig_flags; /* original flags arg value */ 434162306a36Sopenharmony_ci xfs_filblks_t orig_len; /* original value of len arg */ 434262306a36Sopenharmony_ci struct xfs_bmbt_irec *orig_mval; /* original value of mval */ 434362306a36Sopenharmony_ci int orig_nmap; /* original value of *nmap */ 434462306a36Sopenharmony_ci 434562306a36Sopenharmony_ci orig_bno = bno; 434662306a36Sopenharmony_ci orig_len = len; 434762306a36Sopenharmony_ci orig_flags = flags; 434862306a36Sopenharmony_ci orig_mval = mval; 434962306a36Sopenharmony_ci orig_nmap = *nmap; 435062306a36Sopenharmony_ci#endif 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci ASSERT(*nmap >= 1); 435362306a36Sopenharmony_ci ASSERT(*nmap <= XFS_BMAP_MAX_NMAP); 435462306a36Sopenharmony_ci ASSERT(tp != NULL); 435562306a36Sopenharmony_ci ASSERT(len > 0); 435662306a36Sopenharmony_ci ASSERT(ifp->if_format != XFS_DINODE_FMT_LOCAL); 435762306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); 435862306a36Sopenharmony_ci ASSERT(!(flags & XFS_BMAPI_REMAP)); 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci /* zeroing is for currently only for data extents, not metadata */ 436162306a36Sopenharmony_ci ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) != 436262306a36Sopenharmony_ci (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)); 436362306a36Sopenharmony_ci /* 436462306a36Sopenharmony_ci * we can allocate unwritten extents or pre-zero allocated blocks, 436562306a36Sopenharmony_ci * but it makes no sense to do both at once. This would result in 436662306a36Sopenharmony_ci * zeroing the unwritten extent twice, but it still being an 436762306a36Sopenharmony_ci * unwritten extent.... 436862306a36Sopenharmony_ci */ 436962306a36Sopenharmony_ci ASSERT((flags & (XFS_BMAPI_PREALLOC | XFS_BMAPI_ZERO)) != 437062306a36Sopenharmony_ci (XFS_BMAPI_PREALLOC | XFS_BMAPI_ZERO)); 437162306a36Sopenharmony_ci 437262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp)) || 437362306a36Sopenharmony_ci XFS_TEST_ERROR(false, mp, XFS_ERRTAG_BMAPIFORMAT)) { 437462306a36Sopenharmony_ci return -EFSCORRUPTED; 437562306a36Sopenharmony_ci } 437662306a36Sopenharmony_ci 437762306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 437862306a36Sopenharmony_ci return -EIO; 437962306a36Sopenharmony_ci 438062306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_blk_mapw); 438162306a36Sopenharmony_ci 438262306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 438362306a36Sopenharmony_ci if (error) 438462306a36Sopenharmony_ci goto error0; 438562306a36Sopenharmony_ci 438662306a36Sopenharmony_ci if (!xfs_iext_lookup_extent(ip, ifp, bno, &bma.icur, &bma.got)) 438762306a36Sopenharmony_ci eof = true; 438862306a36Sopenharmony_ci if (!xfs_iext_peek_prev_extent(ifp, &bma.icur, &bma.prev)) 438962306a36Sopenharmony_ci bma.prev.br_startoff = NULLFILEOFF; 439062306a36Sopenharmony_ci bma.minleft = xfs_bmapi_minleft(tp, ip, whichfork); 439162306a36Sopenharmony_ci 439262306a36Sopenharmony_ci n = 0; 439362306a36Sopenharmony_ci end = bno + len; 439462306a36Sopenharmony_ci obno = bno; 439562306a36Sopenharmony_ci while (bno < end && n < *nmap) { 439662306a36Sopenharmony_ci bool need_alloc = false, wasdelay = false; 439762306a36Sopenharmony_ci 439862306a36Sopenharmony_ci /* in hole or beyond EOF? */ 439962306a36Sopenharmony_ci if (eof || bma.got.br_startoff > bno) { 440062306a36Sopenharmony_ci /* 440162306a36Sopenharmony_ci * CoW fork conversions should /never/ hit EOF or 440262306a36Sopenharmony_ci * holes. There should always be something for us 440362306a36Sopenharmony_ci * to work on. 440462306a36Sopenharmony_ci */ 440562306a36Sopenharmony_ci ASSERT(!((flags & XFS_BMAPI_CONVERT) && 440662306a36Sopenharmony_ci (flags & XFS_BMAPI_COWFORK))); 440762306a36Sopenharmony_ci 440862306a36Sopenharmony_ci need_alloc = true; 440962306a36Sopenharmony_ci } else if (isnullstartblock(bma.got.br_startblock)) { 441062306a36Sopenharmony_ci wasdelay = true; 441162306a36Sopenharmony_ci } 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_ci /* 441462306a36Sopenharmony_ci * First, deal with the hole before the allocated space 441562306a36Sopenharmony_ci * that we found, if any. 441662306a36Sopenharmony_ci */ 441762306a36Sopenharmony_ci if (need_alloc || wasdelay) { 441862306a36Sopenharmony_ci bma.eof = eof; 441962306a36Sopenharmony_ci bma.conv = !!(flags & XFS_BMAPI_CONVERT); 442062306a36Sopenharmony_ci bma.wasdel = wasdelay; 442162306a36Sopenharmony_ci bma.offset = bno; 442262306a36Sopenharmony_ci bma.flags = flags; 442362306a36Sopenharmony_ci 442462306a36Sopenharmony_ci /* 442562306a36Sopenharmony_ci * There's a 32/64 bit type mismatch between the 442662306a36Sopenharmony_ci * allocation length request (which can be 64 bits in 442762306a36Sopenharmony_ci * length) and the bma length request, which is 442862306a36Sopenharmony_ci * xfs_extlen_t and therefore 32 bits. Hence we have to 442962306a36Sopenharmony_ci * check for 32-bit overflows and handle them here. 443062306a36Sopenharmony_ci */ 443162306a36Sopenharmony_ci if (len > (xfs_filblks_t)XFS_MAX_BMBT_EXTLEN) 443262306a36Sopenharmony_ci bma.length = XFS_MAX_BMBT_EXTLEN; 443362306a36Sopenharmony_ci else 443462306a36Sopenharmony_ci bma.length = len; 443562306a36Sopenharmony_ci 443662306a36Sopenharmony_ci ASSERT(len > 0); 443762306a36Sopenharmony_ci ASSERT(bma.length > 0); 443862306a36Sopenharmony_ci error = xfs_bmapi_allocate(&bma); 443962306a36Sopenharmony_ci if (error) 444062306a36Sopenharmony_ci goto error0; 444162306a36Sopenharmony_ci if (bma.blkno == NULLFSBLOCK) 444262306a36Sopenharmony_ci break; 444362306a36Sopenharmony_ci 444462306a36Sopenharmony_ci /* 444562306a36Sopenharmony_ci * If this is a CoW allocation, record the data in 444662306a36Sopenharmony_ci * the refcount btree for orphan recovery. 444762306a36Sopenharmony_ci */ 444862306a36Sopenharmony_ci if (whichfork == XFS_COW_FORK) 444962306a36Sopenharmony_ci xfs_refcount_alloc_cow_extent(tp, bma.blkno, 445062306a36Sopenharmony_ci bma.length); 445162306a36Sopenharmony_ci } 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_ci /* Deal with the allocated space we found. */ 445462306a36Sopenharmony_ci xfs_bmapi_trim_map(mval, &bma.got, &bno, len, obno, 445562306a36Sopenharmony_ci end, n, flags); 445662306a36Sopenharmony_ci 445762306a36Sopenharmony_ci /* Execute unwritten extent conversion if necessary */ 445862306a36Sopenharmony_ci error = xfs_bmapi_convert_unwritten(&bma, mval, len, flags); 445962306a36Sopenharmony_ci if (error == -EAGAIN) 446062306a36Sopenharmony_ci continue; 446162306a36Sopenharmony_ci if (error) 446262306a36Sopenharmony_ci goto error0; 446362306a36Sopenharmony_ci 446462306a36Sopenharmony_ci /* update the extent map to return */ 446562306a36Sopenharmony_ci xfs_bmapi_update_map(&mval, &bno, &len, obno, end, &n, flags); 446662306a36Sopenharmony_ci 446762306a36Sopenharmony_ci /* 446862306a36Sopenharmony_ci * If we're done, stop now. Stop when we've allocated 446962306a36Sopenharmony_ci * XFS_BMAP_MAX_NMAP extents no matter what. Otherwise 447062306a36Sopenharmony_ci * the transaction may get too big. 447162306a36Sopenharmony_ci */ 447262306a36Sopenharmony_ci if (bno >= end || n >= *nmap || bma.nallocs >= *nmap) 447362306a36Sopenharmony_ci break; 447462306a36Sopenharmony_ci 447562306a36Sopenharmony_ci /* Else go on to the next record. */ 447662306a36Sopenharmony_ci bma.prev = bma.got; 447762306a36Sopenharmony_ci if (!xfs_iext_next_extent(ifp, &bma.icur, &bma.got)) 447862306a36Sopenharmony_ci eof = true; 447962306a36Sopenharmony_ci } 448062306a36Sopenharmony_ci *nmap = n; 448162306a36Sopenharmony_ci 448262306a36Sopenharmony_ci error = xfs_bmap_btree_to_extents(tp, ip, bma.cur, &bma.logflags, 448362306a36Sopenharmony_ci whichfork); 448462306a36Sopenharmony_ci if (error) 448562306a36Sopenharmony_ci goto error0; 448662306a36Sopenharmony_ci 448762306a36Sopenharmony_ci ASSERT(ifp->if_format != XFS_DINODE_FMT_BTREE || 448862306a36Sopenharmony_ci ifp->if_nextents > XFS_IFORK_MAXEXT(ip, whichfork)); 448962306a36Sopenharmony_ci xfs_bmapi_finish(&bma, whichfork, 0); 449062306a36Sopenharmony_ci xfs_bmap_validate_ret(orig_bno, orig_len, orig_flags, orig_mval, 449162306a36Sopenharmony_ci orig_nmap, *nmap); 449262306a36Sopenharmony_ci return 0; 449362306a36Sopenharmony_cierror0: 449462306a36Sopenharmony_ci xfs_bmapi_finish(&bma, whichfork, error); 449562306a36Sopenharmony_ci return error; 449662306a36Sopenharmony_ci} 449762306a36Sopenharmony_ci 449862306a36Sopenharmony_ci/* 449962306a36Sopenharmony_ci * Convert an existing delalloc extent to real blocks based on file offset. This 450062306a36Sopenharmony_ci * attempts to allocate the entire delalloc extent and may require multiple 450162306a36Sopenharmony_ci * invocations to allocate the target offset if a large enough physical extent 450262306a36Sopenharmony_ci * is not available. 450362306a36Sopenharmony_ci */ 450462306a36Sopenharmony_ciint 450562306a36Sopenharmony_cixfs_bmapi_convert_delalloc( 450662306a36Sopenharmony_ci struct xfs_inode *ip, 450762306a36Sopenharmony_ci int whichfork, 450862306a36Sopenharmony_ci xfs_off_t offset, 450962306a36Sopenharmony_ci struct iomap *iomap, 451062306a36Sopenharmony_ci unsigned int *seq) 451162306a36Sopenharmony_ci{ 451262306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 451362306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 451462306a36Sopenharmony_ci xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset); 451562306a36Sopenharmony_ci struct xfs_bmalloca bma = { NULL }; 451662306a36Sopenharmony_ci uint16_t flags = 0; 451762306a36Sopenharmony_ci struct xfs_trans *tp; 451862306a36Sopenharmony_ci int error; 451962306a36Sopenharmony_ci 452062306a36Sopenharmony_ci if (whichfork == XFS_COW_FORK) 452162306a36Sopenharmony_ci flags |= IOMAP_F_SHARED; 452262306a36Sopenharmony_ci 452362306a36Sopenharmony_ci /* 452462306a36Sopenharmony_ci * Space for the extent and indirect blocks was reserved when the 452562306a36Sopenharmony_ci * delalloc extent was created so there's no need to do so here. 452662306a36Sopenharmony_ci */ 452762306a36Sopenharmony_ci error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 452862306a36Sopenharmony_ci XFS_TRANS_RESERVE, &tp); 452962306a36Sopenharmony_ci if (error) 453062306a36Sopenharmony_ci return error; 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_ci xfs_ilock(ip, XFS_ILOCK_EXCL); 453362306a36Sopenharmony_ci xfs_trans_ijoin(tp, ip, 0); 453462306a36Sopenharmony_ci 453562306a36Sopenharmony_ci error = xfs_iext_count_may_overflow(ip, whichfork, 453662306a36Sopenharmony_ci XFS_IEXT_ADD_NOSPLIT_CNT); 453762306a36Sopenharmony_ci if (error == -EFBIG) 453862306a36Sopenharmony_ci error = xfs_iext_count_upgrade(tp, ip, 453962306a36Sopenharmony_ci XFS_IEXT_ADD_NOSPLIT_CNT); 454062306a36Sopenharmony_ci if (error) 454162306a36Sopenharmony_ci goto out_trans_cancel; 454262306a36Sopenharmony_ci 454362306a36Sopenharmony_ci if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &bma.icur, &bma.got) || 454462306a36Sopenharmony_ci bma.got.br_startoff > offset_fsb) { 454562306a36Sopenharmony_ci /* 454662306a36Sopenharmony_ci * No extent found in the range we are trying to convert. This 454762306a36Sopenharmony_ci * should only happen for the COW fork, where another thread 454862306a36Sopenharmony_ci * might have moved the extent to the data fork in the meantime. 454962306a36Sopenharmony_ci */ 455062306a36Sopenharmony_ci WARN_ON_ONCE(whichfork != XFS_COW_FORK); 455162306a36Sopenharmony_ci error = -EAGAIN; 455262306a36Sopenharmony_ci goto out_trans_cancel; 455362306a36Sopenharmony_ci } 455462306a36Sopenharmony_ci 455562306a36Sopenharmony_ci /* 455662306a36Sopenharmony_ci * If we find a real extent here we raced with another thread converting 455762306a36Sopenharmony_ci * the extent. Just return the real extent at this offset. 455862306a36Sopenharmony_ci */ 455962306a36Sopenharmony_ci if (!isnullstartblock(bma.got.br_startblock)) { 456062306a36Sopenharmony_ci xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags, 456162306a36Sopenharmony_ci xfs_iomap_inode_sequence(ip, flags)); 456262306a36Sopenharmony_ci *seq = READ_ONCE(ifp->if_seq); 456362306a36Sopenharmony_ci goto out_trans_cancel; 456462306a36Sopenharmony_ci } 456562306a36Sopenharmony_ci 456662306a36Sopenharmony_ci bma.tp = tp; 456762306a36Sopenharmony_ci bma.ip = ip; 456862306a36Sopenharmony_ci bma.wasdel = true; 456962306a36Sopenharmony_ci bma.offset = bma.got.br_startoff; 457062306a36Sopenharmony_ci bma.length = max_t(xfs_filblks_t, bma.got.br_blockcount, 457162306a36Sopenharmony_ci XFS_MAX_BMBT_EXTLEN); 457262306a36Sopenharmony_ci bma.minleft = xfs_bmapi_minleft(tp, ip, whichfork); 457362306a36Sopenharmony_ci 457462306a36Sopenharmony_ci /* 457562306a36Sopenharmony_ci * When we're converting the delalloc reservations backing dirty pages 457662306a36Sopenharmony_ci * in the page cache, we must be careful about how we create the new 457762306a36Sopenharmony_ci * extents: 457862306a36Sopenharmony_ci * 457962306a36Sopenharmony_ci * New CoW fork extents are created unwritten, turned into real extents 458062306a36Sopenharmony_ci * when we're about to write the data to disk, and mapped into the data 458162306a36Sopenharmony_ci * fork after the write finishes. End of story. 458262306a36Sopenharmony_ci * 458362306a36Sopenharmony_ci * New data fork extents must be mapped in as unwritten and converted 458462306a36Sopenharmony_ci * to real extents after the write succeeds to avoid exposing stale 458562306a36Sopenharmony_ci * disk contents if we crash. 458662306a36Sopenharmony_ci */ 458762306a36Sopenharmony_ci bma.flags = XFS_BMAPI_PREALLOC; 458862306a36Sopenharmony_ci if (whichfork == XFS_COW_FORK) 458962306a36Sopenharmony_ci bma.flags |= XFS_BMAPI_COWFORK; 459062306a36Sopenharmony_ci 459162306a36Sopenharmony_ci if (!xfs_iext_peek_prev_extent(ifp, &bma.icur, &bma.prev)) 459262306a36Sopenharmony_ci bma.prev.br_startoff = NULLFILEOFF; 459362306a36Sopenharmony_ci 459462306a36Sopenharmony_ci error = xfs_bmapi_allocate(&bma); 459562306a36Sopenharmony_ci if (error) 459662306a36Sopenharmony_ci goto out_finish; 459762306a36Sopenharmony_ci 459862306a36Sopenharmony_ci error = -ENOSPC; 459962306a36Sopenharmony_ci if (WARN_ON_ONCE(bma.blkno == NULLFSBLOCK)) 460062306a36Sopenharmony_ci goto out_finish; 460162306a36Sopenharmony_ci error = -EFSCORRUPTED; 460262306a36Sopenharmony_ci if (WARN_ON_ONCE(!xfs_valid_startblock(ip, bma.got.br_startblock))) 460362306a36Sopenharmony_ci goto out_finish; 460462306a36Sopenharmony_ci 460562306a36Sopenharmony_ci XFS_STATS_ADD(mp, xs_xstrat_bytes, XFS_FSB_TO_B(mp, bma.length)); 460662306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_xstrat_quick); 460762306a36Sopenharmony_ci 460862306a36Sopenharmony_ci ASSERT(!isnullstartblock(bma.got.br_startblock)); 460962306a36Sopenharmony_ci xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags, 461062306a36Sopenharmony_ci xfs_iomap_inode_sequence(ip, flags)); 461162306a36Sopenharmony_ci *seq = READ_ONCE(ifp->if_seq); 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_ci if (whichfork == XFS_COW_FORK) 461462306a36Sopenharmony_ci xfs_refcount_alloc_cow_extent(tp, bma.blkno, bma.length); 461562306a36Sopenharmony_ci 461662306a36Sopenharmony_ci error = xfs_bmap_btree_to_extents(tp, ip, bma.cur, &bma.logflags, 461762306a36Sopenharmony_ci whichfork); 461862306a36Sopenharmony_ci if (error) 461962306a36Sopenharmony_ci goto out_finish; 462062306a36Sopenharmony_ci 462162306a36Sopenharmony_ci xfs_bmapi_finish(&bma, whichfork, 0); 462262306a36Sopenharmony_ci error = xfs_trans_commit(tp); 462362306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_EXCL); 462462306a36Sopenharmony_ci return error; 462562306a36Sopenharmony_ci 462662306a36Sopenharmony_ciout_finish: 462762306a36Sopenharmony_ci xfs_bmapi_finish(&bma, whichfork, error); 462862306a36Sopenharmony_ciout_trans_cancel: 462962306a36Sopenharmony_ci xfs_trans_cancel(tp); 463062306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_EXCL); 463162306a36Sopenharmony_ci return error; 463262306a36Sopenharmony_ci} 463362306a36Sopenharmony_ci 463462306a36Sopenharmony_ciint 463562306a36Sopenharmony_cixfs_bmapi_remap( 463662306a36Sopenharmony_ci struct xfs_trans *tp, 463762306a36Sopenharmony_ci struct xfs_inode *ip, 463862306a36Sopenharmony_ci xfs_fileoff_t bno, 463962306a36Sopenharmony_ci xfs_filblks_t len, 464062306a36Sopenharmony_ci xfs_fsblock_t startblock, 464162306a36Sopenharmony_ci uint32_t flags) 464262306a36Sopenharmony_ci{ 464362306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 464462306a36Sopenharmony_ci struct xfs_ifork *ifp; 464562306a36Sopenharmony_ci struct xfs_btree_cur *cur = NULL; 464662306a36Sopenharmony_ci struct xfs_bmbt_irec got; 464762306a36Sopenharmony_ci struct xfs_iext_cursor icur; 464862306a36Sopenharmony_ci int whichfork = xfs_bmapi_whichfork(flags); 464962306a36Sopenharmony_ci int logflags = 0, error; 465062306a36Sopenharmony_ci 465162306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 465262306a36Sopenharmony_ci ASSERT(len > 0); 465362306a36Sopenharmony_ci ASSERT(len <= (xfs_filblks_t)XFS_MAX_BMBT_EXTLEN); 465462306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); 465562306a36Sopenharmony_ci ASSERT(!(flags & ~(XFS_BMAPI_ATTRFORK | XFS_BMAPI_PREALLOC | 465662306a36Sopenharmony_ci XFS_BMAPI_NORMAP))); 465762306a36Sopenharmony_ci ASSERT((flags & (XFS_BMAPI_ATTRFORK | XFS_BMAPI_PREALLOC)) != 465862306a36Sopenharmony_ci (XFS_BMAPI_ATTRFORK | XFS_BMAPI_PREALLOC)); 465962306a36Sopenharmony_ci 466062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp)) || 466162306a36Sopenharmony_ci XFS_TEST_ERROR(false, mp, XFS_ERRTAG_BMAPIFORMAT)) { 466262306a36Sopenharmony_ci return -EFSCORRUPTED; 466362306a36Sopenharmony_ci } 466462306a36Sopenharmony_ci 466562306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 466662306a36Sopenharmony_ci return -EIO; 466762306a36Sopenharmony_ci 466862306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 466962306a36Sopenharmony_ci if (error) 467062306a36Sopenharmony_ci return error; 467162306a36Sopenharmony_ci 467262306a36Sopenharmony_ci if (xfs_iext_lookup_extent(ip, ifp, bno, &icur, &got)) { 467362306a36Sopenharmony_ci /* make sure we only reflink into a hole. */ 467462306a36Sopenharmony_ci ASSERT(got.br_startoff > bno); 467562306a36Sopenharmony_ci ASSERT(got.br_startoff - bno >= len); 467662306a36Sopenharmony_ci } 467762306a36Sopenharmony_ci 467862306a36Sopenharmony_ci ip->i_nblocks += len; 467962306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 468062306a36Sopenharmony_ci 468162306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_BTREE) { 468262306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); 468362306a36Sopenharmony_ci cur->bc_ino.flags = 0; 468462306a36Sopenharmony_ci } 468562306a36Sopenharmony_ci 468662306a36Sopenharmony_ci got.br_startoff = bno; 468762306a36Sopenharmony_ci got.br_startblock = startblock; 468862306a36Sopenharmony_ci got.br_blockcount = len; 468962306a36Sopenharmony_ci if (flags & XFS_BMAPI_PREALLOC) 469062306a36Sopenharmony_ci got.br_state = XFS_EXT_UNWRITTEN; 469162306a36Sopenharmony_ci else 469262306a36Sopenharmony_ci got.br_state = XFS_EXT_NORM; 469362306a36Sopenharmony_ci 469462306a36Sopenharmony_ci error = xfs_bmap_add_extent_hole_real(tp, ip, whichfork, &icur, 469562306a36Sopenharmony_ci &cur, &got, &logflags, flags); 469662306a36Sopenharmony_ci if (error) 469762306a36Sopenharmony_ci goto error0; 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci error = xfs_bmap_btree_to_extents(tp, ip, cur, &logflags, whichfork); 470062306a36Sopenharmony_ci 470162306a36Sopenharmony_cierror0: 470262306a36Sopenharmony_ci if (ip->i_df.if_format != XFS_DINODE_FMT_EXTENTS) 470362306a36Sopenharmony_ci logflags &= ~XFS_ILOG_DEXT; 470462306a36Sopenharmony_ci else if (ip->i_df.if_format != XFS_DINODE_FMT_BTREE) 470562306a36Sopenharmony_ci logflags &= ~XFS_ILOG_DBROOT; 470662306a36Sopenharmony_ci 470762306a36Sopenharmony_ci if (logflags) 470862306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, logflags); 470962306a36Sopenharmony_ci if (cur) 471062306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 471162306a36Sopenharmony_ci return error; 471262306a36Sopenharmony_ci} 471362306a36Sopenharmony_ci 471462306a36Sopenharmony_ci/* 471562306a36Sopenharmony_ci * When a delalloc extent is split (e.g., due to a hole punch), the original 471662306a36Sopenharmony_ci * indlen reservation must be shared across the two new extents that are left 471762306a36Sopenharmony_ci * behind. 471862306a36Sopenharmony_ci * 471962306a36Sopenharmony_ci * Given the original reservation and the worst case indlen for the two new 472062306a36Sopenharmony_ci * extents (as calculated by xfs_bmap_worst_indlen()), split the original 472162306a36Sopenharmony_ci * reservation fairly across the two new extents. If necessary, steal available 472262306a36Sopenharmony_ci * blocks from a deleted extent to make up a reservation deficiency (e.g., if 472362306a36Sopenharmony_ci * ores == 1). The number of stolen blocks is returned. The availability and 472462306a36Sopenharmony_ci * subsequent accounting of stolen blocks is the responsibility of the caller. 472562306a36Sopenharmony_ci */ 472662306a36Sopenharmony_cistatic xfs_filblks_t 472762306a36Sopenharmony_cixfs_bmap_split_indlen( 472862306a36Sopenharmony_ci xfs_filblks_t ores, /* original res. */ 472962306a36Sopenharmony_ci xfs_filblks_t *indlen1, /* ext1 worst indlen */ 473062306a36Sopenharmony_ci xfs_filblks_t *indlen2, /* ext2 worst indlen */ 473162306a36Sopenharmony_ci xfs_filblks_t avail) /* stealable blocks */ 473262306a36Sopenharmony_ci{ 473362306a36Sopenharmony_ci xfs_filblks_t len1 = *indlen1; 473462306a36Sopenharmony_ci xfs_filblks_t len2 = *indlen2; 473562306a36Sopenharmony_ci xfs_filblks_t nres = len1 + len2; /* new total res. */ 473662306a36Sopenharmony_ci xfs_filblks_t stolen = 0; 473762306a36Sopenharmony_ci xfs_filblks_t resfactor; 473862306a36Sopenharmony_ci 473962306a36Sopenharmony_ci /* 474062306a36Sopenharmony_ci * Steal as many blocks as we can to try and satisfy the worst case 474162306a36Sopenharmony_ci * indlen for both new extents. 474262306a36Sopenharmony_ci */ 474362306a36Sopenharmony_ci if (ores < nres && avail) 474462306a36Sopenharmony_ci stolen = XFS_FILBLKS_MIN(nres - ores, avail); 474562306a36Sopenharmony_ci ores += stolen; 474662306a36Sopenharmony_ci 474762306a36Sopenharmony_ci /* nothing else to do if we've satisfied the new reservation */ 474862306a36Sopenharmony_ci if (ores >= nres) 474962306a36Sopenharmony_ci return stolen; 475062306a36Sopenharmony_ci 475162306a36Sopenharmony_ci /* 475262306a36Sopenharmony_ci * We can't meet the total required reservation for the two extents. 475362306a36Sopenharmony_ci * Calculate the percent of the overall shortage between both extents 475462306a36Sopenharmony_ci * and apply this percentage to each of the requested indlen values. 475562306a36Sopenharmony_ci * This distributes the shortage fairly and reduces the chances that one 475662306a36Sopenharmony_ci * of the two extents is left with nothing when extents are repeatedly 475762306a36Sopenharmony_ci * split. 475862306a36Sopenharmony_ci */ 475962306a36Sopenharmony_ci resfactor = (ores * 100); 476062306a36Sopenharmony_ci do_div(resfactor, nres); 476162306a36Sopenharmony_ci len1 *= resfactor; 476262306a36Sopenharmony_ci do_div(len1, 100); 476362306a36Sopenharmony_ci len2 *= resfactor; 476462306a36Sopenharmony_ci do_div(len2, 100); 476562306a36Sopenharmony_ci ASSERT(len1 + len2 <= ores); 476662306a36Sopenharmony_ci ASSERT(len1 < *indlen1 && len2 < *indlen2); 476762306a36Sopenharmony_ci 476862306a36Sopenharmony_ci /* 476962306a36Sopenharmony_ci * Hand out the remainder to each extent. If one of the two reservations 477062306a36Sopenharmony_ci * is zero, we want to make sure that one gets a block first. The loop 477162306a36Sopenharmony_ci * below starts with len1, so hand len2 a block right off the bat if it 477262306a36Sopenharmony_ci * is zero. 477362306a36Sopenharmony_ci */ 477462306a36Sopenharmony_ci ores -= (len1 + len2); 477562306a36Sopenharmony_ci ASSERT((*indlen1 - len1) + (*indlen2 - len2) >= ores); 477662306a36Sopenharmony_ci if (ores && !len2 && *indlen2) { 477762306a36Sopenharmony_ci len2++; 477862306a36Sopenharmony_ci ores--; 477962306a36Sopenharmony_ci } 478062306a36Sopenharmony_ci while (ores) { 478162306a36Sopenharmony_ci if (len1 < *indlen1) { 478262306a36Sopenharmony_ci len1++; 478362306a36Sopenharmony_ci ores--; 478462306a36Sopenharmony_ci } 478562306a36Sopenharmony_ci if (!ores) 478662306a36Sopenharmony_ci break; 478762306a36Sopenharmony_ci if (len2 < *indlen2) { 478862306a36Sopenharmony_ci len2++; 478962306a36Sopenharmony_ci ores--; 479062306a36Sopenharmony_ci } 479162306a36Sopenharmony_ci } 479262306a36Sopenharmony_ci 479362306a36Sopenharmony_ci *indlen1 = len1; 479462306a36Sopenharmony_ci *indlen2 = len2; 479562306a36Sopenharmony_ci 479662306a36Sopenharmony_ci return stolen; 479762306a36Sopenharmony_ci} 479862306a36Sopenharmony_ci 479962306a36Sopenharmony_ciint 480062306a36Sopenharmony_cixfs_bmap_del_extent_delay( 480162306a36Sopenharmony_ci struct xfs_inode *ip, 480262306a36Sopenharmony_ci int whichfork, 480362306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 480462306a36Sopenharmony_ci struct xfs_bmbt_irec *got, 480562306a36Sopenharmony_ci struct xfs_bmbt_irec *del) 480662306a36Sopenharmony_ci{ 480762306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 480862306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 480962306a36Sopenharmony_ci struct xfs_bmbt_irec new; 481062306a36Sopenharmony_ci int64_t da_old, da_new, da_diff = 0; 481162306a36Sopenharmony_ci xfs_fileoff_t del_endoff, got_endoff; 481262306a36Sopenharmony_ci xfs_filblks_t got_indlen, new_indlen, stolen; 481362306a36Sopenharmony_ci uint32_t state = xfs_bmap_fork_to_state(whichfork); 481462306a36Sopenharmony_ci int error = 0; 481562306a36Sopenharmony_ci bool isrt; 481662306a36Sopenharmony_ci 481762306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_del_exlist); 481862306a36Sopenharmony_ci 481962306a36Sopenharmony_ci isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); 482062306a36Sopenharmony_ci del_endoff = del->br_startoff + del->br_blockcount; 482162306a36Sopenharmony_ci got_endoff = got->br_startoff + got->br_blockcount; 482262306a36Sopenharmony_ci da_old = startblockval(got->br_startblock); 482362306a36Sopenharmony_ci da_new = 0; 482462306a36Sopenharmony_ci 482562306a36Sopenharmony_ci ASSERT(del->br_blockcount > 0); 482662306a36Sopenharmony_ci ASSERT(got->br_startoff <= del->br_startoff); 482762306a36Sopenharmony_ci ASSERT(got_endoff >= del_endoff); 482862306a36Sopenharmony_ci 482962306a36Sopenharmony_ci if (isrt) { 483062306a36Sopenharmony_ci uint64_t rtexts = del->br_blockcount; 483162306a36Sopenharmony_ci 483262306a36Sopenharmony_ci do_div(rtexts, mp->m_sb.sb_rextsize); 483362306a36Sopenharmony_ci xfs_mod_frextents(mp, rtexts); 483462306a36Sopenharmony_ci } 483562306a36Sopenharmony_ci 483662306a36Sopenharmony_ci /* 483762306a36Sopenharmony_ci * Update the inode delalloc counter now and wait to update the 483862306a36Sopenharmony_ci * sb counters as we might have to borrow some blocks for the 483962306a36Sopenharmony_ci * indirect block accounting. 484062306a36Sopenharmony_ci */ 484162306a36Sopenharmony_ci ASSERT(!isrt); 484262306a36Sopenharmony_ci error = xfs_quota_unreserve_blkres(ip, del->br_blockcount); 484362306a36Sopenharmony_ci if (error) 484462306a36Sopenharmony_ci return error; 484562306a36Sopenharmony_ci ip->i_delayed_blks -= del->br_blockcount; 484662306a36Sopenharmony_ci 484762306a36Sopenharmony_ci if (got->br_startoff == del->br_startoff) 484862306a36Sopenharmony_ci state |= BMAP_LEFT_FILLING; 484962306a36Sopenharmony_ci if (got_endoff == del_endoff) 485062306a36Sopenharmony_ci state |= BMAP_RIGHT_FILLING; 485162306a36Sopenharmony_ci 485262306a36Sopenharmony_ci switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) { 485362306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: 485462306a36Sopenharmony_ci /* 485562306a36Sopenharmony_ci * Matches the whole extent. Delete the entry. 485662306a36Sopenharmony_ci */ 485762306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 485862306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 485962306a36Sopenharmony_ci break; 486062306a36Sopenharmony_ci case BMAP_LEFT_FILLING: 486162306a36Sopenharmony_ci /* 486262306a36Sopenharmony_ci * Deleting the first part of the extent. 486362306a36Sopenharmony_ci */ 486462306a36Sopenharmony_ci got->br_startoff = del_endoff; 486562306a36Sopenharmony_ci got->br_blockcount -= del->br_blockcount; 486662306a36Sopenharmony_ci da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, 486762306a36Sopenharmony_ci got->br_blockcount), da_old); 486862306a36Sopenharmony_ci got->br_startblock = nullstartblock((int)da_new); 486962306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, got); 487062306a36Sopenharmony_ci break; 487162306a36Sopenharmony_ci case BMAP_RIGHT_FILLING: 487262306a36Sopenharmony_ci /* 487362306a36Sopenharmony_ci * Deleting the last part of the extent. 487462306a36Sopenharmony_ci */ 487562306a36Sopenharmony_ci got->br_blockcount = got->br_blockcount - del->br_blockcount; 487662306a36Sopenharmony_ci da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, 487762306a36Sopenharmony_ci got->br_blockcount), da_old); 487862306a36Sopenharmony_ci got->br_startblock = nullstartblock((int)da_new); 487962306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, got); 488062306a36Sopenharmony_ci break; 488162306a36Sopenharmony_ci case 0: 488262306a36Sopenharmony_ci /* 488362306a36Sopenharmony_ci * Deleting the middle of the extent. 488462306a36Sopenharmony_ci * 488562306a36Sopenharmony_ci * Distribute the original indlen reservation across the two new 488662306a36Sopenharmony_ci * extents. Steal blocks from the deleted extent if necessary. 488762306a36Sopenharmony_ci * Stealing blocks simply fudges the fdblocks accounting below. 488862306a36Sopenharmony_ci * Warn if either of the new indlen reservations is zero as this 488962306a36Sopenharmony_ci * can lead to delalloc problems. 489062306a36Sopenharmony_ci */ 489162306a36Sopenharmony_ci got->br_blockcount = del->br_startoff - got->br_startoff; 489262306a36Sopenharmony_ci got_indlen = xfs_bmap_worst_indlen(ip, got->br_blockcount); 489362306a36Sopenharmony_ci 489462306a36Sopenharmony_ci new.br_blockcount = got_endoff - del_endoff; 489562306a36Sopenharmony_ci new_indlen = xfs_bmap_worst_indlen(ip, new.br_blockcount); 489662306a36Sopenharmony_ci 489762306a36Sopenharmony_ci WARN_ON_ONCE(!got_indlen || !new_indlen); 489862306a36Sopenharmony_ci stolen = xfs_bmap_split_indlen(da_old, &got_indlen, &new_indlen, 489962306a36Sopenharmony_ci del->br_blockcount); 490062306a36Sopenharmony_ci 490162306a36Sopenharmony_ci got->br_startblock = nullstartblock((int)got_indlen); 490262306a36Sopenharmony_ci 490362306a36Sopenharmony_ci new.br_startoff = del_endoff; 490462306a36Sopenharmony_ci new.br_state = got->br_state; 490562306a36Sopenharmony_ci new.br_startblock = nullstartblock((int)new_indlen); 490662306a36Sopenharmony_ci 490762306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, got); 490862306a36Sopenharmony_ci xfs_iext_next(ifp, icur); 490962306a36Sopenharmony_ci xfs_iext_insert(ip, icur, &new, state); 491062306a36Sopenharmony_ci 491162306a36Sopenharmony_ci da_new = got_indlen + new_indlen - stolen; 491262306a36Sopenharmony_ci del->br_blockcount -= stolen; 491362306a36Sopenharmony_ci break; 491462306a36Sopenharmony_ci } 491562306a36Sopenharmony_ci 491662306a36Sopenharmony_ci ASSERT(da_old >= da_new); 491762306a36Sopenharmony_ci da_diff = da_old - da_new; 491862306a36Sopenharmony_ci if (!isrt) 491962306a36Sopenharmony_ci da_diff += del->br_blockcount; 492062306a36Sopenharmony_ci if (da_diff) { 492162306a36Sopenharmony_ci xfs_mod_fdblocks(mp, da_diff, false); 492262306a36Sopenharmony_ci xfs_mod_delalloc(mp, -da_diff); 492362306a36Sopenharmony_ci } 492462306a36Sopenharmony_ci return error; 492562306a36Sopenharmony_ci} 492662306a36Sopenharmony_ci 492762306a36Sopenharmony_civoid 492862306a36Sopenharmony_cixfs_bmap_del_extent_cow( 492962306a36Sopenharmony_ci struct xfs_inode *ip, 493062306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 493162306a36Sopenharmony_ci struct xfs_bmbt_irec *got, 493262306a36Sopenharmony_ci struct xfs_bmbt_irec *del) 493362306a36Sopenharmony_ci{ 493462306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 493562306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); 493662306a36Sopenharmony_ci struct xfs_bmbt_irec new; 493762306a36Sopenharmony_ci xfs_fileoff_t del_endoff, got_endoff; 493862306a36Sopenharmony_ci uint32_t state = BMAP_COWFORK; 493962306a36Sopenharmony_ci 494062306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_del_exlist); 494162306a36Sopenharmony_ci 494262306a36Sopenharmony_ci del_endoff = del->br_startoff + del->br_blockcount; 494362306a36Sopenharmony_ci got_endoff = got->br_startoff + got->br_blockcount; 494462306a36Sopenharmony_ci 494562306a36Sopenharmony_ci ASSERT(del->br_blockcount > 0); 494662306a36Sopenharmony_ci ASSERT(got->br_startoff <= del->br_startoff); 494762306a36Sopenharmony_ci ASSERT(got_endoff >= del_endoff); 494862306a36Sopenharmony_ci ASSERT(!isnullstartblock(got->br_startblock)); 494962306a36Sopenharmony_ci 495062306a36Sopenharmony_ci if (got->br_startoff == del->br_startoff) 495162306a36Sopenharmony_ci state |= BMAP_LEFT_FILLING; 495262306a36Sopenharmony_ci if (got_endoff == del_endoff) 495362306a36Sopenharmony_ci state |= BMAP_RIGHT_FILLING; 495462306a36Sopenharmony_ci 495562306a36Sopenharmony_ci switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) { 495662306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: 495762306a36Sopenharmony_ci /* 495862306a36Sopenharmony_ci * Matches the whole extent. Delete the entry. 495962306a36Sopenharmony_ci */ 496062306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 496162306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 496262306a36Sopenharmony_ci break; 496362306a36Sopenharmony_ci case BMAP_LEFT_FILLING: 496462306a36Sopenharmony_ci /* 496562306a36Sopenharmony_ci * Deleting the first part of the extent. 496662306a36Sopenharmony_ci */ 496762306a36Sopenharmony_ci got->br_startoff = del_endoff; 496862306a36Sopenharmony_ci got->br_blockcount -= del->br_blockcount; 496962306a36Sopenharmony_ci got->br_startblock = del->br_startblock + del->br_blockcount; 497062306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, got); 497162306a36Sopenharmony_ci break; 497262306a36Sopenharmony_ci case BMAP_RIGHT_FILLING: 497362306a36Sopenharmony_ci /* 497462306a36Sopenharmony_ci * Deleting the last part of the extent. 497562306a36Sopenharmony_ci */ 497662306a36Sopenharmony_ci got->br_blockcount -= del->br_blockcount; 497762306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, got); 497862306a36Sopenharmony_ci break; 497962306a36Sopenharmony_ci case 0: 498062306a36Sopenharmony_ci /* 498162306a36Sopenharmony_ci * Deleting the middle of the extent. 498262306a36Sopenharmony_ci */ 498362306a36Sopenharmony_ci got->br_blockcount = del->br_startoff - got->br_startoff; 498462306a36Sopenharmony_ci 498562306a36Sopenharmony_ci new.br_startoff = del_endoff; 498662306a36Sopenharmony_ci new.br_blockcount = got_endoff - del_endoff; 498762306a36Sopenharmony_ci new.br_state = got->br_state; 498862306a36Sopenharmony_ci new.br_startblock = del->br_startblock + del->br_blockcount; 498962306a36Sopenharmony_ci 499062306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, got); 499162306a36Sopenharmony_ci xfs_iext_next(ifp, icur); 499262306a36Sopenharmony_ci xfs_iext_insert(ip, icur, &new, state); 499362306a36Sopenharmony_ci break; 499462306a36Sopenharmony_ci } 499562306a36Sopenharmony_ci ip->i_delayed_blks -= del->br_blockcount; 499662306a36Sopenharmony_ci} 499762306a36Sopenharmony_ci 499862306a36Sopenharmony_ci/* 499962306a36Sopenharmony_ci * Called by xfs_bmapi to update file extent records and the btree 500062306a36Sopenharmony_ci * after removing space. 500162306a36Sopenharmony_ci */ 500262306a36Sopenharmony_ciSTATIC int /* error */ 500362306a36Sopenharmony_cixfs_bmap_del_extent_real( 500462306a36Sopenharmony_ci xfs_inode_t *ip, /* incore inode pointer */ 500562306a36Sopenharmony_ci xfs_trans_t *tp, /* current transaction pointer */ 500662306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 500762306a36Sopenharmony_ci struct xfs_btree_cur *cur, /* if null, not a btree */ 500862306a36Sopenharmony_ci xfs_bmbt_irec_t *del, /* data to remove from extents */ 500962306a36Sopenharmony_ci int *logflagsp, /* inode logging flags */ 501062306a36Sopenharmony_ci int whichfork, /* data or attr fork */ 501162306a36Sopenharmony_ci uint32_t bflags) /* bmapi flags */ 501262306a36Sopenharmony_ci{ 501362306a36Sopenharmony_ci xfs_fsblock_t del_endblock=0; /* first block past del */ 501462306a36Sopenharmony_ci xfs_fileoff_t del_endoff; /* first offset past del */ 501562306a36Sopenharmony_ci int do_fx; /* free extent at end of routine */ 501662306a36Sopenharmony_ci int error; /* error return value */ 501762306a36Sopenharmony_ci int flags = 0;/* inode logging flags */ 501862306a36Sopenharmony_ci struct xfs_bmbt_irec got; /* current extent entry */ 501962306a36Sopenharmony_ci xfs_fileoff_t got_endoff; /* first offset past got */ 502062306a36Sopenharmony_ci int i; /* temp state */ 502162306a36Sopenharmony_ci struct xfs_ifork *ifp; /* inode fork pointer */ 502262306a36Sopenharmony_ci xfs_mount_t *mp; /* mount structure */ 502362306a36Sopenharmony_ci xfs_filblks_t nblks; /* quota/sb block count */ 502462306a36Sopenharmony_ci xfs_bmbt_irec_t new; /* new record to be inserted */ 502562306a36Sopenharmony_ci /* REFERENCED */ 502662306a36Sopenharmony_ci uint qfield; /* quota field to update */ 502762306a36Sopenharmony_ci uint32_t state = xfs_bmap_fork_to_state(whichfork); 502862306a36Sopenharmony_ci struct xfs_bmbt_irec old; 502962306a36Sopenharmony_ci 503062306a36Sopenharmony_ci mp = ip->i_mount; 503162306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_del_exlist); 503262306a36Sopenharmony_ci 503362306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 503462306a36Sopenharmony_ci ASSERT(del->br_blockcount > 0); 503562306a36Sopenharmony_ci xfs_iext_get_extent(ifp, icur, &got); 503662306a36Sopenharmony_ci ASSERT(got.br_startoff <= del->br_startoff); 503762306a36Sopenharmony_ci del_endoff = del->br_startoff + del->br_blockcount; 503862306a36Sopenharmony_ci got_endoff = got.br_startoff + got.br_blockcount; 503962306a36Sopenharmony_ci ASSERT(got_endoff >= del_endoff); 504062306a36Sopenharmony_ci ASSERT(!isnullstartblock(got.br_startblock)); 504162306a36Sopenharmony_ci qfield = 0; 504262306a36Sopenharmony_ci error = 0; 504362306a36Sopenharmony_ci 504462306a36Sopenharmony_ci /* 504562306a36Sopenharmony_ci * If it's the case where the directory code is running with no block 504662306a36Sopenharmony_ci * reservation, and the deleted block is in the middle of its extent, 504762306a36Sopenharmony_ci * and the resulting insert of an extent would cause transformation to 504862306a36Sopenharmony_ci * btree format, then reject it. The calling code will then swap blocks 504962306a36Sopenharmony_ci * around instead. We have to do this now, rather than waiting for the 505062306a36Sopenharmony_ci * conversion to btree format, since the transaction will be dirty then. 505162306a36Sopenharmony_ci */ 505262306a36Sopenharmony_ci if (tp->t_blk_res == 0 && 505362306a36Sopenharmony_ci ifp->if_format == XFS_DINODE_FMT_EXTENTS && 505462306a36Sopenharmony_ci ifp->if_nextents >= XFS_IFORK_MAXEXT(ip, whichfork) && 505562306a36Sopenharmony_ci del->br_startoff > got.br_startoff && del_endoff < got_endoff) 505662306a36Sopenharmony_ci return -ENOSPC; 505762306a36Sopenharmony_ci 505862306a36Sopenharmony_ci flags = XFS_ILOG_CORE; 505962306a36Sopenharmony_ci if (whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip)) { 506062306a36Sopenharmony_ci if (!(bflags & XFS_BMAPI_REMAP)) { 506162306a36Sopenharmony_ci error = xfs_rtfree_blocks(tp, del->br_startblock, 506262306a36Sopenharmony_ci del->br_blockcount); 506362306a36Sopenharmony_ci if (error) 506462306a36Sopenharmony_ci goto done; 506562306a36Sopenharmony_ci } 506662306a36Sopenharmony_ci 506762306a36Sopenharmony_ci do_fx = 0; 506862306a36Sopenharmony_ci qfield = XFS_TRANS_DQ_RTBCOUNT; 506962306a36Sopenharmony_ci } else { 507062306a36Sopenharmony_ci do_fx = 1; 507162306a36Sopenharmony_ci qfield = XFS_TRANS_DQ_BCOUNT; 507262306a36Sopenharmony_ci } 507362306a36Sopenharmony_ci nblks = del->br_blockcount; 507462306a36Sopenharmony_ci 507562306a36Sopenharmony_ci del_endblock = del->br_startblock + del->br_blockcount; 507662306a36Sopenharmony_ci if (cur) { 507762306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &got, &i); 507862306a36Sopenharmony_ci if (error) 507962306a36Sopenharmony_ci goto done; 508062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 508162306a36Sopenharmony_ci error = -EFSCORRUPTED; 508262306a36Sopenharmony_ci goto done; 508362306a36Sopenharmony_ci } 508462306a36Sopenharmony_ci } 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_ci if (got.br_startoff == del->br_startoff) 508762306a36Sopenharmony_ci state |= BMAP_LEFT_FILLING; 508862306a36Sopenharmony_ci if (got_endoff == del_endoff) 508962306a36Sopenharmony_ci state |= BMAP_RIGHT_FILLING; 509062306a36Sopenharmony_ci 509162306a36Sopenharmony_ci switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) { 509262306a36Sopenharmony_ci case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: 509362306a36Sopenharmony_ci /* 509462306a36Sopenharmony_ci * Matches the whole extent. Delete the entry. 509562306a36Sopenharmony_ci */ 509662306a36Sopenharmony_ci xfs_iext_remove(ip, icur, state); 509762306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 509862306a36Sopenharmony_ci ifp->if_nextents--; 509962306a36Sopenharmony_ci 510062306a36Sopenharmony_ci flags |= XFS_ILOG_CORE; 510162306a36Sopenharmony_ci if (!cur) { 510262306a36Sopenharmony_ci flags |= xfs_ilog_fext(whichfork); 510362306a36Sopenharmony_ci break; 510462306a36Sopenharmony_ci } 510562306a36Sopenharmony_ci if ((error = xfs_btree_delete(cur, &i))) 510662306a36Sopenharmony_ci goto done; 510762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 510862306a36Sopenharmony_ci error = -EFSCORRUPTED; 510962306a36Sopenharmony_ci goto done; 511062306a36Sopenharmony_ci } 511162306a36Sopenharmony_ci break; 511262306a36Sopenharmony_ci case BMAP_LEFT_FILLING: 511362306a36Sopenharmony_ci /* 511462306a36Sopenharmony_ci * Deleting the first part of the extent. 511562306a36Sopenharmony_ci */ 511662306a36Sopenharmony_ci got.br_startoff = del_endoff; 511762306a36Sopenharmony_ci got.br_startblock = del_endblock; 511862306a36Sopenharmony_ci got.br_blockcount -= del->br_blockcount; 511962306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &got); 512062306a36Sopenharmony_ci if (!cur) { 512162306a36Sopenharmony_ci flags |= xfs_ilog_fext(whichfork); 512262306a36Sopenharmony_ci break; 512362306a36Sopenharmony_ci } 512462306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &got); 512562306a36Sopenharmony_ci if (error) 512662306a36Sopenharmony_ci goto done; 512762306a36Sopenharmony_ci break; 512862306a36Sopenharmony_ci case BMAP_RIGHT_FILLING: 512962306a36Sopenharmony_ci /* 513062306a36Sopenharmony_ci * Deleting the last part of the extent. 513162306a36Sopenharmony_ci */ 513262306a36Sopenharmony_ci got.br_blockcount -= del->br_blockcount; 513362306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &got); 513462306a36Sopenharmony_ci if (!cur) { 513562306a36Sopenharmony_ci flags |= xfs_ilog_fext(whichfork); 513662306a36Sopenharmony_ci break; 513762306a36Sopenharmony_ci } 513862306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &got); 513962306a36Sopenharmony_ci if (error) 514062306a36Sopenharmony_ci goto done; 514162306a36Sopenharmony_ci break; 514262306a36Sopenharmony_ci case 0: 514362306a36Sopenharmony_ci /* 514462306a36Sopenharmony_ci * Deleting the middle of the extent. 514562306a36Sopenharmony_ci */ 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_ci old = got; 514862306a36Sopenharmony_ci 514962306a36Sopenharmony_ci got.br_blockcount = del->br_startoff - got.br_startoff; 515062306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &got); 515162306a36Sopenharmony_ci 515262306a36Sopenharmony_ci new.br_startoff = del_endoff; 515362306a36Sopenharmony_ci new.br_blockcount = got_endoff - del_endoff; 515462306a36Sopenharmony_ci new.br_state = got.br_state; 515562306a36Sopenharmony_ci new.br_startblock = del_endblock; 515662306a36Sopenharmony_ci 515762306a36Sopenharmony_ci flags |= XFS_ILOG_CORE; 515862306a36Sopenharmony_ci if (cur) { 515962306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &got); 516062306a36Sopenharmony_ci if (error) 516162306a36Sopenharmony_ci goto done; 516262306a36Sopenharmony_ci error = xfs_btree_increment(cur, 0, &i); 516362306a36Sopenharmony_ci if (error) 516462306a36Sopenharmony_ci goto done; 516562306a36Sopenharmony_ci cur->bc_rec.b = new; 516662306a36Sopenharmony_ci error = xfs_btree_insert(cur, &i); 516762306a36Sopenharmony_ci if (error && error != -ENOSPC) 516862306a36Sopenharmony_ci goto done; 516962306a36Sopenharmony_ci /* 517062306a36Sopenharmony_ci * If get no-space back from btree insert, it tried a 517162306a36Sopenharmony_ci * split, and we have a zero block reservation. Fix up 517262306a36Sopenharmony_ci * our state and return the error. 517362306a36Sopenharmony_ci */ 517462306a36Sopenharmony_ci if (error == -ENOSPC) { 517562306a36Sopenharmony_ci /* 517662306a36Sopenharmony_ci * Reset the cursor, don't trust it after any 517762306a36Sopenharmony_ci * insert operation. 517862306a36Sopenharmony_ci */ 517962306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &got, &i); 518062306a36Sopenharmony_ci if (error) 518162306a36Sopenharmony_ci goto done; 518262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 518362306a36Sopenharmony_ci error = -EFSCORRUPTED; 518462306a36Sopenharmony_ci goto done; 518562306a36Sopenharmony_ci } 518662306a36Sopenharmony_ci /* 518762306a36Sopenharmony_ci * Update the btree record back 518862306a36Sopenharmony_ci * to the original value. 518962306a36Sopenharmony_ci */ 519062306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &old); 519162306a36Sopenharmony_ci if (error) 519262306a36Sopenharmony_ci goto done; 519362306a36Sopenharmony_ci /* 519462306a36Sopenharmony_ci * Reset the extent record back 519562306a36Sopenharmony_ci * to the original value. 519662306a36Sopenharmony_ci */ 519762306a36Sopenharmony_ci xfs_iext_update_extent(ip, state, icur, &old); 519862306a36Sopenharmony_ci flags = 0; 519962306a36Sopenharmony_ci error = -ENOSPC; 520062306a36Sopenharmony_ci goto done; 520162306a36Sopenharmony_ci } 520262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 520362306a36Sopenharmony_ci error = -EFSCORRUPTED; 520462306a36Sopenharmony_ci goto done; 520562306a36Sopenharmony_ci } 520662306a36Sopenharmony_ci } else 520762306a36Sopenharmony_ci flags |= xfs_ilog_fext(whichfork); 520862306a36Sopenharmony_ci 520962306a36Sopenharmony_ci ifp->if_nextents++; 521062306a36Sopenharmony_ci xfs_iext_next(ifp, icur); 521162306a36Sopenharmony_ci xfs_iext_insert(ip, icur, &new, state); 521262306a36Sopenharmony_ci break; 521362306a36Sopenharmony_ci } 521462306a36Sopenharmony_ci 521562306a36Sopenharmony_ci /* remove reverse mapping */ 521662306a36Sopenharmony_ci xfs_rmap_unmap_extent(tp, ip, whichfork, del); 521762306a36Sopenharmony_ci 521862306a36Sopenharmony_ci /* 521962306a36Sopenharmony_ci * If we need to, add to list of extents to delete. 522062306a36Sopenharmony_ci */ 522162306a36Sopenharmony_ci if (do_fx && !(bflags & XFS_BMAPI_REMAP)) { 522262306a36Sopenharmony_ci if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK) { 522362306a36Sopenharmony_ci xfs_refcount_decrease_extent(tp, del); 522462306a36Sopenharmony_ci } else { 522562306a36Sopenharmony_ci error = __xfs_free_extent_later(tp, del->br_startblock, 522662306a36Sopenharmony_ci del->br_blockcount, NULL, 522762306a36Sopenharmony_ci XFS_AG_RESV_NONE, 522862306a36Sopenharmony_ci ((bflags & XFS_BMAPI_NODISCARD) || 522962306a36Sopenharmony_ci del->br_state == XFS_EXT_UNWRITTEN)); 523062306a36Sopenharmony_ci if (error) 523162306a36Sopenharmony_ci goto done; 523262306a36Sopenharmony_ci } 523362306a36Sopenharmony_ci } 523462306a36Sopenharmony_ci 523562306a36Sopenharmony_ci /* 523662306a36Sopenharmony_ci * Adjust inode # blocks in the file. 523762306a36Sopenharmony_ci */ 523862306a36Sopenharmony_ci if (nblks) 523962306a36Sopenharmony_ci ip->i_nblocks -= nblks; 524062306a36Sopenharmony_ci /* 524162306a36Sopenharmony_ci * Adjust quota data. 524262306a36Sopenharmony_ci */ 524362306a36Sopenharmony_ci if (qfield && !(bflags & XFS_BMAPI_REMAP)) 524462306a36Sopenharmony_ci xfs_trans_mod_dquot_byino(tp, ip, qfield, (long)-nblks); 524562306a36Sopenharmony_ci 524662306a36Sopenharmony_cidone: 524762306a36Sopenharmony_ci *logflagsp = flags; 524862306a36Sopenharmony_ci return error; 524962306a36Sopenharmony_ci} 525062306a36Sopenharmony_ci 525162306a36Sopenharmony_ci/* 525262306a36Sopenharmony_ci * Unmap (remove) blocks from a file. 525362306a36Sopenharmony_ci * If nexts is nonzero then the number of extents to remove is limited to 525462306a36Sopenharmony_ci * that value. If not all extents in the block range can be removed then 525562306a36Sopenharmony_ci * *done is set. 525662306a36Sopenharmony_ci */ 525762306a36Sopenharmony_ciint /* error */ 525862306a36Sopenharmony_ci__xfs_bunmapi( 525962306a36Sopenharmony_ci struct xfs_trans *tp, /* transaction pointer */ 526062306a36Sopenharmony_ci struct xfs_inode *ip, /* incore inode */ 526162306a36Sopenharmony_ci xfs_fileoff_t start, /* first file offset deleted */ 526262306a36Sopenharmony_ci xfs_filblks_t *rlen, /* i/o: amount remaining */ 526362306a36Sopenharmony_ci uint32_t flags, /* misc flags */ 526462306a36Sopenharmony_ci xfs_extnum_t nexts) /* number of extents max */ 526562306a36Sopenharmony_ci{ 526662306a36Sopenharmony_ci struct xfs_btree_cur *cur; /* bmap btree cursor */ 526762306a36Sopenharmony_ci struct xfs_bmbt_irec del; /* extent being deleted */ 526862306a36Sopenharmony_ci int error; /* error return value */ 526962306a36Sopenharmony_ci xfs_extnum_t extno; /* extent number in list */ 527062306a36Sopenharmony_ci struct xfs_bmbt_irec got; /* current extent record */ 527162306a36Sopenharmony_ci struct xfs_ifork *ifp; /* inode fork pointer */ 527262306a36Sopenharmony_ci int isrt; /* freeing in rt area */ 527362306a36Sopenharmony_ci int logflags; /* transaction logging flags */ 527462306a36Sopenharmony_ci xfs_extlen_t mod; /* rt extent offset */ 527562306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 527662306a36Sopenharmony_ci int tmp_logflags; /* partial logging flags */ 527762306a36Sopenharmony_ci int wasdel; /* was a delayed alloc extent */ 527862306a36Sopenharmony_ci int whichfork; /* data or attribute fork */ 527962306a36Sopenharmony_ci xfs_fsblock_t sum; 528062306a36Sopenharmony_ci xfs_filblks_t len = *rlen; /* length to unmap in file */ 528162306a36Sopenharmony_ci xfs_fileoff_t end; 528262306a36Sopenharmony_ci struct xfs_iext_cursor icur; 528362306a36Sopenharmony_ci bool done = false; 528462306a36Sopenharmony_ci 528562306a36Sopenharmony_ci trace_xfs_bunmap(ip, start, len, flags, _RET_IP_); 528662306a36Sopenharmony_ci 528762306a36Sopenharmony_ci whichfork = xfs_bmapi_whichfork(flags); 528862306a36Sopenharmony_ci ASSERT(whichfork != XFS_COW_FORK); 528962306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 529062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp))) 529162306a36Sopenharmony_ci return -EFSCORRUPTED; 529262306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 529362306a36Sopenharmony_ci return -EIO; 529462306a36Sopenharmony_ci 529562306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); 529662306a36Sopenharmony_ci ASSERT(len > 0); 529762306a36Sopenharmony_ci ASSERT(nexts >= 0); 529862306a36Sopenharmony_ci 529962306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 530062306a36Sopenharmony_ci if (error) 530162306a36Sopenharmony_ci return error; 530262306a36Sopenharmony_ci 530362306a36Sopenharmony_ci if (xfs_iext_count(ifp) == 0) { 530462306a36Sopenharmony_ci *rlen = 0; 530562306a36Sopenharmony_ci return 0; 530662306a36Sopenharmony_ci } 530762306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_blk_unmap); 530862306a36Sopenharmony_ci isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); 530962306a36Sopenharmony_ci end = start + len; 531062306a36Sopenharmony_ci 531162306a36Sopenharmony_ci if (!xfs_iext_lookup_extent_before(ip, ifp, &end, &icur, &got)) { 531262306a36Sopenharmony_ci *rlen = 0; 531362306a36Sopenharmony_ci return 0; 531462306a36Sopenharmony_ci } 531562306a36Sopenharmony_ci end--; 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_ci logflags = 0; 531862306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_BTREE) { 531962306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_BTREE); 532062306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); 532162306a36Sopenharmony_ci cur->bc_ino.flags = 0; 532262306a36Sopenharmony_ci } else 532362306a36Sopenharmony_ci cur = NULL; 532462306a36Sopenharmony_ci 532562306a36Sopenharmony_ci if (isrt) { 532662306a36Sopenharmony_ci /* 532762306a36Sopenharmony_ci * Synchronize by locking the bitmap inode. 532862306a36Sopenharmony_ci */ 532962306a36Sopenharmony_ci xfs_ilock(mp->m_rbmip, XFS_ILOCK_EXCL|XFS_ILOCK_RTBITMAP); 533062306a36Sopenharmony_ci xfs_trans_ijoin(tp, mp->m_rbmip, XFS_ILOCK_EXCL); 533162306a36Sopenharmony_ci xfs_ilock(mp->m_rsumip, XFS_ILOCK_EXCL|XFS_ILOCK_RTSUM); 533262306a36Sopenharmony_ci xfs_trans_ijoin(tp, mp->m_rsumip, XFS_ILOCK_EXCL); 533362306a36Sopenharmony_ci } 533462306a36Sopenharmony_ci 533562306a36Sopenharmony_ci extno = 0; 533662306a36Sopenharmony_ci while (end != (xfs_fileoff_t)-1 && end >= start && 533762306a36Sopenharmony_ci (nexts == 0 || extno < nexts)) { 533862306a36Sopenharmony_ci /* 533962306a36Sopenharmony_ci * Is the found extent after a hole in which end lives? 534062306a36Sopenharmony_ci * Just back up to the previous extent, if so. 534162306a36Sopenharmony_ci */ 534262306a36Sopenharmony_ci if (got.br_startoff > end && 534362306a36Sopenharmony_ci !xfs_iext_prev_extent(ifp, &icur, &got)) { 534462306a36Sopenharmony_ci done = true; 534562306a36Sopenharmony_ci break; 534662306a36Sopenharmony_ci } 534762306a36Sopenharmony_ci /* 534862306a36Sopenharmony_ci * Is the last block of this extent before the range 534962306a36Sopenharmony_ci * we're supposed to delete? If so, we're done. 535062306a36Sopenharmony_ci */ 535162306a36Sopenharmony_ci end = XFS_FILEOFF_MIN(end, 535262306a36Sopenharmony_ci got.br_startoff + got.br_blockcount - 1); 535362306a36Sopenharmony_ci if (end < start) 535462306a36Sopenharmony_ci break; 535562306a36Sopenharmony_ci /* 535662306a36Sopenharmony_ci * Then deal with the (possibly delayed) allocated space 535762306a36Sopenharmony_ci * we found. 535862306a36Sopenharmony_ci */ 535962306a36Sopenharmony_ci del = got; 536062306a36Sopenharmony_ci wasdel = isnullstartblock(del.br_startblock); 536162306a36Sopenharmony_ci 536262306a36Sopenharmony_ci if (got.br_startoff < start) { 536362306a36Sopenharmony_ci del.br_startoff = start; 536462306a36Sopenharmony_ci del.br_blockcount -= start - got.br_startoff; 536562306a36Sopenharmony_ci if (!wasdel) 536662306a36Sopenharmony_ci del.br_startblock += start - got.br_startoff; 536762306a36Sopenharmony_ci } 536862306a36Sopenharmony_ci if (del.br_startoff + del.br_blockcount > end + 1) 536962306a36Sopenharmony_ci del.br_blockcount = end + 1 - del.br_startoff; 537062306a36Sopenharmony_ci 537162306a36Sopenharmony_ci if (!isrt) 537262306a36Sopenharmony_ci goto delete; 537362306a36Sopenharmony_ci 537462306a36Sopenharmony_ci sum = del.br_startblock + del.br_blockcount; 537562306a36Sopenharmony_ci div_u64_rem(sum, mp->m_sb.sb_rextsize, &mod); 537662306a36Sopenharmony_ci if (mod) { 537762306a36Sopenharmony_ci /* 537862306a36Sopenharmony_ci * Realtime extent not lined up at the end. 537962306a36Sopenharmony_ci * The extent could have been split into written 538062306a36Sopenharmony_ci * and unwritten pieces, or we could just be 538162306a36Sopenharmony_ci * unmapping part of it. But we can't really 538262306a36Sopenharmony_ci * get rid of part of a realtime extent. 538362306a36Sopenharmony_ci */ 538462306a36Sopenharmony_ci if (del.br_state == XFS_EXT_UNWRITTEN) { 538562306a36Sopenharmony_ci /* 538662306a36Sopenharmony_ci * This piece is unwritten, or we're not 538762306a36Sopenharmony_ci * using unwritten extents. Skip over it. 538862306a36Sopenharmony_ci */ 538962306a36Sopenharmony_ci ASSERT(end >= mod); 539062306a36Sopenharmony_ci end -= mod > del.br_blockcount ? 539162306a36Sopenharmony_ci del.br_blockcount : mod; 539262306a36Sopenharmony_ci if (end < got.br_startoff && 539362306a36Sopenharmony_ci !xfs_iext_prev_extent(ifp, &icur, &got)) { 539462306a36Sopenharmony_ci done = true; 539562306a36Sopenharmony_ci break; 539662306a36Sopenharmony_ci } 539762306a36Sopenharmony_ci continue; 539862306a36Sopenharmony_ci } 539962306a36Sopenharmony_ci /* 540062306a36Sopenharmony_ci * It's written, turn it unwritten. 540162306a36Sopenharmony_ci * This is better than zeroing it. 540262306a36Sopenharmony_ci */ 540362306a36Sopenharmony_ci ASSERT(del.br_state == XFS_EXT_NORM); 540462306a36Sopenharmony_ci ASSERT(tp->t_blk_res > 0); 540562306a36Sopenharmony_ci /* 540662306a36Sopenharmony_ci * If this spans a realtime extent boundary, 540762306a36Sopenharmony_ci * chop it back to the start of the one we end at. 540862306a36Sopenharmony_ci */ 540962306a36Sopenharmony_ci if (del.br_blockcount > mod) { 541062306a36Sopenharmony_ci del.br_startoff += del.br_blockcount - mod; 541162306a36Sopenharmony_ci del.br_startblock += del.br_blockcount - mod; 541262306a36Sopenharmony_ci del.br_blockcount = mod; 541362306a36Sopenharmony_ci } 541462306a36Sopenharmony_ci del.br_state = XFS_EXT_UNWRITTEN; 541562306a36Sopenharmony_ci error = xfs_bmap_add_extent_unwritten_real(tp, ip, 541662306a36Sopenharmony_ci whichfork, &icur, &cur, &del, 541762306a36Sopenharmony_ci &logflags); 541862306a36Sopenharmony_ci if (error) 541962306a36Sopenharmony_ci goto error0; 542062306a36Sopenharmony_ci goto nodelete; 542162306a36Sopenharmony_ci } 542262306a36Sopenharmony_ci div_u64_rem(del.br_startblock, mp->m_sb.sb_rextsize, &mod); 542362306a36Sopenharmony_ci if (mod) { 542462306a36Sopenharmony_ci xfs_extlen_t off = mp->m_sb.sb_rextsize - mod; 542562306a36Sopenharmony_ci 542662306a36Sopenharmony_ci /* 542762306a36Sopenharmony_ci * Realtime extent is lined up at the end but not 542862306a36Sopenharmony_ci * at the front. We'll get rid of full extents if 542962306a36Sopenharmony_ci * we can. 543062306a36Sopenharmony_ci */ 543162306a36Sopenharmony_ci if (del.br_blockcount > off) { 543262306a36Sopenharmony_ci del.br_blockcount -= off; 543362306a36Sopenharmony_ci del.br_startoff += off; 543462306a36Sopenharmony_ci del.br_startblock += off; 543562306a36Sopenharmony_ci } else if (del.br_startoff == start && 543662306a36Sopenharmony_ci (del.br_state == XFS_EXT_UNWRITTEN || 543762306a36Sopenharmony_ci tp->t_blk_res == 0)) { 543862306a36Sopenharmony_ci /* 543962306a36Sopenharmony_ci * Can't make it unwritten. There isn't 544062306a36Sopenharmony_ci * a full extent here so just skip it. 544162306a36Sopenharmony_ci */ 544262306a36Sopenharmony_ci ASSERT(end >= del.br_blockcount); 544362306a36Sopenharmony_ci end -= del.br_blockcount; 544462306a36Sopenharmony_ci if (got.br_startoff > end && 544562306a36Sopenharmony_ci !xfs_iext_prev_extent(ifp, &icur, &got)) { 544662306a36Sopenharmony_ci done = true; 544762306a36Sopenharmony_ci break; 544862306a36Sopenharmony_ci } 544962306a36Sopenharmony_ci continue; 545062306a36Sopenharmony_ci } else if (del.br_state == XFS_EXT_UNWRITTEN) { 545162306a36Sopenharmony_ci struct xfs_bmbt_irec prev; 545262306a36Sopenharmony_ci xfs_fileoff_t unwrite_start; 545362306a36Sopenharmony_ci 545462306a36Sopenharmony_ci /* 545562306a36Sopenharmony_ci * This one is already unwritten. 545662306a36Sopenharmony_ci * It must have a written left neighbor. 545762306a36Sopenharmony_ci * Unwrite the killed part of that one and 545862306a36Sopenharmony_ci * try again. 545962306a36Sopenharmony_ci */ 546062306a36Sopenharmony_ci if (!xfs_iext_prev_extent(ifp, &icur, &prev)) 546162306a36Sopenharmony_ci ASSERT(0); 546262306a36Sopenharmony_ci ASSERT(prev.br_state == XFS_EXT_NORM); 546362306a36Sopenharmony_ci ASSERT(!isnullstartblock(prev.br_startblock)); 546462306a36Sopenharmony_ci ASSERT(del.br_startblock == 546562306a36Sopenharmony_ci prev.br_startblock + prev.br_blockcount); 546662306a36Sopenharmony_ci unwrite_start = max3(start, 546762306a36Sopenharmony_ci del.br_startoff - mod, 546862306a36Sopenharmony_ci prev.br_startoff); 546962306a36Sopenharmony_ci mod = unwrite_start - prev.br_startoff; 547062306a36Sopenharmony_ci prev.br_startoff = unwrite_start; 547162306a36Sopenharmony_ci prev.br_startblock += mod; 547262306a36Sopenharmony_ci prev.br_blockcount -= mod; 547362306a36Sopenharmony_ci prev.br_state = XFS_EXT_UNWRITTEN; 547462306a36Sopenharmony_ci error = xfs_bmap_add_extent_unwritten_real(tp, 547562306a36Sopenharmony_ci ip, whichfork, &icur, &cur, 547662306a36Sopenharmony_ci &prev, &logflags); 547762306a36Sopenharmony_ci if (error) 547862306a36Sopenharmony_ci goto error0; 547962306a36Sopenharmony_ci goto nodelete; 548062306a36Sopenharmony_ci } else { 548162306a36Sopenharmony_ci ASSERT(del.br_state == XFS_EXT_NORM); 548262306a36Sopenharmony_ci del.br_state = XFS_EXT_UNWRITTEN; 548362306a36Sopenharmony_ci error = xfs_bmap_add_extent_unwritten_real(tp, 548462306a36Sopenharmony_ci ip, whichfork, &icur, &cur, 548562306a36Sopenharmony_ci &del, &logflags); 548662306a36Sopenharmony_ci if (error) 548762306a36Sopenharmony_ci goto error0; 548862306a36Sopenharmony_ci goto nodelete; 548962306a36Sopenharmony_ci } 549062306a36Sopenharmony_ci } 549162306a36Sopenharmony_ci 549262306a36Sopenharmony_cidelete: 549362306a36Sopenharmony_ci if (wasdel) { 549462306a36Sopenharmony_ci error = xfs_bmap_del_extent_delay(ip, whichfork, &icur, 549562306a36Sopenharmony_ci &got, &del); 549662306a36Sopenharmony_ci } else { 549762306a36Sopenharmony_ci error = xfs_bmap_del_extent_real(ip, tp, &icur, cur, 549862306a36Sopenharmony_ci &del, &tmp_logflags, whichfork, 549962306a36Sopenharmony_ci flags); 550062306a36Sopenharmony_ci logflags |= tmp_logflags; 550162306a36Sopenharmony_ci } 550262306a36Sopenharmony_ci 550362306a36Sopenharmony_ci if (error) 550462306a36Sopenharmony_ci goto error0; 550562306a36Sopenharmony_ci 550662306a36Sopenharmony_ci end = del.br_startoff - 1; 550762306a36Sopenharmony_cinodelete: 550862306a36Sopenharmony_ci /* 550962306a36Sopenharmony_ci * If not done go on to the next (previous) record. 551062306a36Sopenharmony_ci */ 551162306a36Sopenharmony_ci if (end != (xfs_fileoff_t)-1 && end >= start) { 551262306a36Sopenharmony_ci if (!xfs_iext_get_extent(ifp, &icur, &got) || 551362306a36Sopenharmony_ci (got.br_startoff > end && 551462306a36Sopenharmony_ci !xfs_iext_prev_extent(ifp, &icur, &got))) { 551562306a36Sopenharmony_ci done = true; 551662306a36Sopenharmony_ci break; 551762306a36Sopenharmony_ci } 551862306a36Sopenharmony_ci extno++; 551962306a36Sopenharmony_ci } 552062306a36Sopenharmony_ci } 552162306a36Sopenharmony_ci if (done || end == (xfs_fileoff_t)-1 || end < start) 552262306a36Sopenharmony_ci *rlen = 0; 552362306a36Sopenharmony_ci else 552462306a36Sopenharmony_ci *rlen = end - start + 1; 552562306a36Sopenharmony_ci 552662306a36Sopenharmony_ci /* 552762306a36Sopenharmony_ci * Convert to a btree if necessary. 552862306a36Sopenharmony_ci */ 552962306a36Sopenharmony_ci if (xfs_bmap_needs_btree(ip, whichfork)) { 553062306a36Sopenharmony_ci ASSERT(cur == NULL); 553162306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(tp, ip, &cur, 0, 553262306a36Sopenharmony_ci &tmp_logflags, whichfork); 553362306a36Sopenharmony_ci logflags |= tmp_logflags; 553462306a36Sopenharmony_ci } else { 553562306a36Sopenharmony_ci error = xfs_bmap_btree_to_extents(tp, ip, cur, &logflags, 553662306a36Sopenharmony_ci whichfork); 553762306a36Sopenharmony_ci } 553862306a36Sopenharmony_ci 553962306a36Sopenharmony_cierror0: 554062306a36Sopenharmony_ci /* 554162306a36Sopenharmony_ci * Log everything. Do this after conversion, there's no point in 554262306a36Sopenharmony_ci * logging the extent records if we've converted to btree format. 554362306a36Sopenharmony_ci */ 554462306a36Sopenharmony_ci if ((logflags & xfs_ilog_fext(whichfork)) && 554562306a36Sopenharmony_ci ifp->if_format != XFS_DINODE_FMT_EXTENTS) 554662306a36Sopenharmony_ci logflags &= ~xfs_ilog_fext(whichfork); 554762306a36Sopenharmony_ci else if ((logflags & xfs_ilog_fbroot(whichfork)) && 554862306a36Sopenharmony_ci ifp->if_format != XFS_DINODE_FMT_BTREE) 554962306a36Sopenharmony_ci logflags &= ~xfs_ilog_fbroot(whichfork); 555062306a36Sopenharmony_ci /* 555162306a36Sopenharmony_ci * Log inode even in the error case, if the transaction 555262306a36Sopenharmony_ci * is dirty we'll need to shut down the filesystem. 555362306a36Sopenharmony_ci */ 555462306a36Sopenharmony_ci if (logflags) 555562306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, logflags); 555662306a36Sopenharmony_ci if (cur) { 555762306a36Sopenharmony_ci if (!error) 555862306a36Sopenharmony_ci cur->bc_ino.allocated = 0; 555962306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 556062306a36Sopenharmony_ci } 556162306a36Sopenharmony_ci return error; 556262306a36Sopenharmony_ci} 556362306a36Sopenharmony_ci 556462306a36Sopenharmony_ci/* Unmap a range of a file. */ 556562306a36Sopenharmony_ciint 556662306a36Sopenharmony_cixfs_bunmapi( 556762306a36Sopenharmony_ci xfs_trans_t *tp, 556862306a36Sopenharmony_ci struct xfs_inode *ip, 556962306a36Sopenharmony_ci xfs_fileoff_t bno, 557062306a36Sopenharmony_ci xfs_filblks_t len, 557162306a36Sopenharmony_ci uint32_t flags, 557262306a36Sopenharmony_ci xfs_extnum_t nexts, 557362306a36Sopenharmony_ci int *done) 557462306a36Sopenharmony_ci{ 557562306a36Sopenharmony_ci int error; 557662306a36Sopenharmony_ci 557762306a36Sopenharmony_ci error = __xfs_bunmapi(tp, ip, bno, &len, flags, nexts); 557862306a36Sopenharmony_ci *done = (len == 0); 557962306a36Sopenharmony_ci return error; 558062306a36Sopenharmony_ci} 558162306a36Sopenharmony_ci 558262306a36Sopenharmony_ci/* 558362306a36Sopenharmony_ci * Determine whether an extent shift can be accomplished by a merge with the 558462306a36Sopenharmony_ci * extent that precedes the target hole of the shift. 558562306a36Sopenharmony_ci */ 558662306a36Sopenharmony_ciSTATIC bool 558762306a36Sopenharmony_cixfs_bmse_can_merge( 558862306a36Sopenharmony_ci struct xfs_bmbt_irec *left, /* preceding extent */ 558962306a36Sopenharmony_ci struct xfs_bmbt_irec *got, /* current extent to shift */ 559062306a36Sopenharmony_ci xfs_fileoff_t shift) /* shift fsb */ 559162306a36Sopenharmony_ci{ 559262306a36Sopenharmony_ci xfs_fileoff_t startoff; 559362306a36Sopenharmony_ci 559462306a36Sopenharmony_ci startoff = got->br_startoff - shift; 559562306a36Sopenharmony_ci 559662306a36Sopenharmony_ci /* 559762306a36Sopenharmony_ci * The extent, once shifted, must be adjacent in-file and on-disk with 559862306a36Sopenharmony_ci * the preceding extent. 559962306a36Sopenharmony_ci */ 560062306a36Sopenharmony_ci if ((left->br_startoff + left->br_blockcount != startoff) || 560162306a36Sopenharmony_ci (left->br_startblock + left->br_blockcount != got->br_startblock) || 560262306a36Sopenharmony_ci (left->br_state != got->br_state) || 560362306a36Sopenharmony_ci (left->br_blockcount + got->br_blockcount > XFS_MAX_BMBT_EXTLEN)) 560462306a36Sopenharmony_ci return false; 560562306a36Sopenharmony_ci 560662306a36Sopenharmony_ci return true; 560762306a36Sopenharmony_ci} 560862306a36Sopenharmony_ci 560962306a36Sopenharmony_ci/* 561062306a36Sopenharmony_ci * A bmap extent shift adjusts the file offset of an extent to fill a preceding 561162306a36Sopenharmony_ci * hole in the file. If an extent shift would result in the extent being fully 561262306a36Sopenharmony_ci * adjacent to the extent that currently precedes the hole, we can merge with 561362306a36Sopenharmony_ci * the preceding extent rather than do the shift. 561462306a36Sopenharmony_ci * 561562306a36Sopenharmony_ci * This function assumes the caller has verified a shift-by-merge is possible 561662306a36Sopenharmony_ci * with the provided extents via xfs_bmse_can_merge(). 561762306a36Sopenharmony_ci */ 561862306a36Sopenharmony_ciSTATIC int 561962306a36Sopenharmony_cixfs_bmse_merge( 562062306a36Sopenharmony_ci struct xfs_trans *tp, 562162306a36Sopenharmony_ci struct xfs_inode *ip, 562262306a36Sopenharmony_ci int whichfork, 562362306a36Sopenharmony_ci xfs_fileoff_t shift, /* shift fsb */ 562462306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 562562306a36Sopenharmony_ci struct xfs_bmbt_irec *got, /* extent to shift */ 562662306a36Sopenharmony_ci struct xfs_bmbt_irec *left, /* preceding extent */ 562762306a36Sopenharmony_ci struct xfs_btree_cur *cur, 562862306a36Sopenharmony_ci int *logflags) /* output */ 562962306a36Sopenharmony_ci{ 563062306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 563162306a36Sopenharmony_ci struct xfs_bmbt_irec new; 563262306a36Sopenharmony_ci xfs_filblks_t blockcount; 563362306a36Sopenharmony_ci int error, i; 563462306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 563562306a36Sopenharmony_ci 563662306a36Sopenharmony_ci blockcount = left->br_blockcount + got->br_blockcount; 563762306a36Sopenharmony_ci 563862306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); 563962306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); 564062306a36Sopenharmony_ci ASSERT(xfs_bmse_can_merge(left, got, shift)); 564162306a36Sopenharmony_ci 564262306a36Sopenharmony_ci new = *left; 564362306a36Sopenharmony_ci new.br_blockcount = blockcount; 564462306a36Sopenharmony_ci 564562306a36Sopenharmony_ci /* 564662306a36Sopenharmony_ci * Update the on-disk extent count, the btree if necessary and log the 564762306a36Sopenharmony_ci * inode. 564862306a36Sopenharmony_ci */ 564962306a36Sopenharmony_ci ifp->if_nextents--; 565062306a36Sopenharmony_ci *logflags |= XFS_ILOG_CORE; 565162306a36Sopenharmony_ci if (!cur) { 565262306a36Sopenharmony_ci *logflags |= XFS_ILOG_DEXT; 565362306a36Sopenharmony_ci goto done; 565462306a36Sopenharmony_ci } 565562306a36Sopenharmony_ci 565662306a36Sopenharmony_ci /* lookup and remove the extent to merge */ 565762306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, got, &i); 565862306a36Sopenharmony_ci if (error) 565962306a36Sopenharmony_ci return error; 566062306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) 566162306a36Sopenharmony_ci return -EFSCORRUPTED; 566262306a36Sopenharmony_ci 566362306a36Sopenharmony_ci error = xfs_btree_delete(cur, &i); 566462306a36Sopenharmony_ci if (error) 566562306a36Sopenharmony_ci return error; 566662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) 566762306a36Sopenharmony_ci return -EFSCORRUPTED; 566862306a36Sopenharmony_ci 566962306a36Sopenharmony_ci /* lookup and update size of the previous extent */ 567062306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, left, &i); 567162306a36Sopenharmony_ci if (error) 567262306a36Sopenharmony_ci return error; 567362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) 567462306a36Sopenharmony_ci return -EFSCORRUPTED; 567562306a36Sopenharmony_ci 567662306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &new); 567762306a36Sopenharmony_ci if (error) 567862306a36Sopenharmony_ci return error; 567962306a36Sopenharmony_ci 568062306a36Sopenharmony_ci /* change to extent format if required after extent removal */ 568162306a36Sopenharmony_ci error = xfs_bmap_btree_to_extents(tp, ip, cur, logflags, whichfork); 568262306a36Sopenharmony_ci if (error) 568362306a36Sopenharmony_ci return error; 568462306a36Sopenharmony_ci 568562306a36Sopenharmony_cidone: 568662306a36Sopenharmony_ci xfs_iext_remove(ip, icur, 0); 568762306a36Sopenharmony_ci xfs_iext_prev(ifp, icur); 568862306a36Sopenharmony_ci xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), icur, 568962306a36Sopenharmony_ci &new); 569062306a36Sopenharmony_ci 569162306a36Sopenharmony_ci /* update reverse mapping. rmap functions merge the rmaps for us */ 569262306a36Sopenharmony_ci xfs_rmap_unmap_extent(tp, ip, whichfork, got); 569362306a36Sopenharmony_ci memcpy(&new, got, sizeof(new)); 569462306a36Sopenharmony_ci new.br_startoff = left->br_startoff + left->br_blockcount; 569562306a36Sopenharmony_ci xfs_rmap_map_extent(tp, ip, whichfork, &new); 569662306a36Sopenharmony_ci return 0; 569762306a36Sopenharmony_ci} 569862306a36Sopenharmony_ci 569962306a36Sopenharmony_cistatic int 570062306a36Sopenharmony_cixfs_bmap_shift_update_extent( 570162306a36Sopenharmony_ci struct xfs_trans *tp, 570262306a36Sopenharmony_ci struct xfs_inode *ip, 570362306a36Sopenharmony_ci int whichfork, 570462306a36Sopenharmony_ci struct xfs_iext_cursor *icur, 570562306a36Sopenharmony_ci struct xfs_bmbt_irec *got, 570662306a36Sopenharmony_ci struct xfs_btree_cur *cur, 570762306a36Sopenharmony_ci int *logflags, 570862306a36Sopenharmony_ci xfs_fileoff_t startoff) 570962306a36Sopenharmony_ci{ 571062306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 571162306a36Sopenharmony_ci struct xfs_bmbt_irec prev = *got; 571262306a36Sopenharmony_ci int error, i; 571362306a36Sopenharmony_ci 571462306a36Sopenharmony_ci *logflags |= XFS_ILOG_CORE; 571562306a36Sopenharmony_ci 571662306a36Sopenharmony_ci got->br_startoff = startoff; 571762306a36Sopenharmony_ci 571862306a36Sopenharmony_ci if (cur) { 571962306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &prev, &i); 572062306a36Sopenharmony_ci if (error) 572162306a36Sopenharmony_ci return error; 572262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) 572362306a36Sopenharmony_ci return -EFSCORRUPTED; 572462306a36Sopenharmony_ci 572562306a36Sopenharmony_ci error = xfs_bmbt_update(cur, got); 572662306a36Sopenharmony_ci if (error) 572762306a36Sopenharmony_ci return error; 572862306a36Sopenharmony_ci } else { 572962306a36Sopenharmony_ci *logflags |= XFS_ILOG_DEXT; 573062306a36Sopenharmony_ci } 573162306a36Sopenharmony_ci 573262306a36Sopenharmony_ci xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), icur, 573362306a36Sopenharmony_ci got); 573462306a36Sopenharmony_ci 573562306a36Sopenharmony_ci /* update reverse mapping */ 573662306a36Sopenharmony_ci xfs_rmap_unmap_extent(tp, ip, whichfork, &prev); 573762306a36Sopenharmony_ci xfs_rmap_map_extent(tp, ip, whichfork, got); 573862306a36Sopenharmony_ci return 0; 573962306a36Sopenharmony_ci} 574062306a36Sopenharmony_ci 574162306a36Sopenharmony_ciint 574262306a36Sopenharmony_cixfs_bmap_collapse_extents( 574362306a36Sopenharmony_ci struct xfs_trans *tp, 574462306a36Sopenharmony_ci struct xfs_inode *ip, 574562306a36Sopenharmony_ci xfs_fileoff_t *next_fsb, 574662306a36Sopenharmony_ci xfs_fileoff_t offset_shift_fsb, 574762306a36Sopenharmony_ci bool *done) 574862306a36Sopenharmony_ci{ 574962306a36Sopenharmony_ci int whichfork = XFS_DATA_FORK; 575062306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 575162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 575262306a36Sopenharmony_ci struct xfs_btree_cur *cur = NULL; 575362306a36Sopenharmony_ci struct xfs_bmbt_irec got, prev; 575462306a36Sopenharmony_ci struct xfs_iext_cursor icur; 575562306a36Sopenharmony_ci xfs_fileoff_t new_startoff; 575662306a36Sopenharmony_ci int error = 0; 575762306a36Sopenharmony_ci int logflags = 0; 575862306a36Sopenharmony_ci 575962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp)) || 576062306a36Sopenharmony_ci XFS_TEST_ERROR(false, mp, XFS_ERRTAG_BMAPIFORMAT)) { 576162306a36Sopenharmony_ci return -EFSCORRUPTED; 576262306a36Sopenharmony_ci } 576362306a36Sopenharmony_ci 576462306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 576562306a36Sopenharmony_ci return -EIO; 576662306a36Sopenharmony_ci 576762306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL)); 576862306a36Sopenharmony_ci 576962306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 577062306a36Sopenharmony_ci if (error) 577162306a36Sopenharmony_ci return error; 577262306a36Sopenharmony_ci 577362306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_BTREE) { 577462306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); 577562306a36Sopenharmony_ci cur->bc_ino.flags = 0; 577662306a36Sopenharmony_ci } 577762306a36Sopenharmony_ci 577862306a36Sopenharmony_ci if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &icur, &got)) { 577962306a36Sopenharmony_ci *done = true; 578062306a36Sopenharmony_ci goto del_cursor; 578162306a36Sopenharmony_ci } 578262306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, isnullstartblock(got.br_startblock))) { 578362306a36Sopenharmony_ci error = -EFSCORRUPTED; 578462306a36Sopenharmony_ci goto del_cursor; 578562306a36Sopenharmony_ci } 578662306a36Sopenharmony_ci 578762306a36Sopenharmony_ci new_startoff = got.br_startoff - offset_shift_fsb; 578862306a36Sopenharmony_ci if (xfs_iext_peek_prev_extent(ifp, &icur, &prev)) { 578962306a36Sopenharmony_ci if (new_startoff < prev.br_startoff + prev.br_blockcount) { 579062306a36Sopenharmony_ci error = -EINVAL; 579162306a36Sopenharmony_ci goto del_cursor; 579262306a36Sopenharmony_ci } 579362306a36Sopenharmony_ci 579462306a36Sopenharmony_ci if (xfs_bmse_can_merge(&prev, &got, offset_shift_fsb)) { 579562306a36Sopenharmony_ci error = xfs_bmse_merge(tp, ip, whichfork, 579662306a36Sopenharmony_ci offset_shift_fsb, &icur, &got, &prev, 579762306a36Sopenharmony_ci cur, &logflags); 579862306a36Sopenharmony_ci if (error) 579962306a36Sopenharmony_ci goto del_cursor; 580062306a36Sopenharmony_ci goto done; 580162306a36Sopenharmony_ci } 580262306a36Sopenharmony_ci } else { 580362306a36Sopenharmony_ci if (got.br_startoff < offset_shift_fsb) { 580462306a36Sopenharmony_ci error = -EINVAL; 580562306a36Sopenharmony_ci goto del_cursor; 580662306a36Sopenharmony_ci } 580762306a36Sopenharmony_ci } 580862306a36Sopenharmony_ci 580962306a36Sopenharmony_ci error = xfs_bmap_shift_update_extent(tp, ip, whichfork, &icur, &got, 581062306a36Sopenharmony_ci cur, &logflags, new_startoff); 581162306a36Sopenharmony_ci if (error) 581262306a36Sopenharmony_ci goto del_cursor; 581362306a36Sopenharmony_ci 581462306a36Sopenharmony_cidone: 581562306a36Sopenharmony_ci if (!xfs_iext_next_extent(ifp, &icur, &got)) { 581662306a36Sopenharmony_ci *done = true; 581762306a36Sopenharmony_ci goto del_cursor; 581862306a36Sopenharmony_ci } 581962306a36Sopenharmony_ci 582062306a36Sopenharmony_ci *next_fsb = got.br_startoff; 582162306a36Sopenharmony_cidel_cursor: 582262306a36Sopenharmony_ci if (cur) 582362306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 582462306a36Sopenharmony_ci if (logflags) 582562306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, logflags); 582662306a36Sopenharmony_ci return error; 582762306a36Sopenharmony_ci} 582862306a36Sopenharmony_ci 582962306a36Sopenharmony_ci/* Make sure we won't be right-shifting an extent past the maximum bound. */ 583062306a36Sopenharmony_ciint 583162306a36Sopenharmony_cixfs_bmap_can_insert_extents( 583262306a36Sopenharmony_ci struct xfs_inode *ip, 583362306a36Sopenharmony_ci xfs_fileoff_t off, 583462306a36Sopenharmony_ci xfs_fileoff_t shift) 583562306a36Sopenharmony_ci{ 583662306a36Sopenharmony_ci struct xfs_bmbt_irec got; 583762306a36Sopenharmony_ci int is_empty; 583862306a36Sopenharmony_ci int error = 0; 583962306a36Sopenharmony_ci 584062306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); 584162306a36Sopenharmony_ci 584262306a36Sopenharmony_ci if (xfs_is_shutdown(ip->i_mount)) 584362306a36Sopenharmony_ci return -EIO; 584462306a36Sopenharmony_ci 584562306a36Sopenharmony_ci xfs_ilock(ip, XFS_ILOCK_EXCL); 584662306a36Sopenharmony_ci error = xfs_bmap_last_extent(NULL, ip, XFS_DATA_FORK, &got, &is_empty); 584762306a36Sopenharmony_ci if (!error && !is_empty && got.br_startoff >= off && 584862306a36Sopenharmony_ci ((got.br_startoff + shift) & BMBT_STARTOFF_MASK) < got.br_startoff) 584962306a36Sopenharmony_ci error = -EINVAL; 585062306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_EXCL); 585162306a36Sopenharmony_ci 585262306a36Sopenharmony_ci return error; 585362306a36Sopenharmony_ci} 585462306a36Sopenharmony_ci 585562306a36Sopenharmony_ciint 585662306a36Sopenharmony_cixfs_bmap_insert_extents( 585762306a36Sopenharmony_ci struct xfs_trans *tp, 585862306a36Sopenharmony_ci struct xfs_inode *ip, 585962306a36Sopenharmony_ci xfs_fileoff_t *next_fsb, 586062306a36Sopenharmony_ci xfs_fileoff_t offset_shift_fsb, 586162306a36Sopenharmony_ci bool *done, 586262306a36Sopenharmony_ci xfs_fileoff_t stop_fsb) 586362306a36Sopenharmony_ci{ 586462306a36Sopenharmony_ci int whichfork = XFS_DATA_FORK; 586562306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 586662306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 586762306a36Sopenharmony_ci struct xfs_btree_cur *cur = NULL; 586862306a36Sopenharmony_ci struct xfs_bmbt_irec got, next; 586962306a36Sopenharmony_ci struct xfs_iext_cursor icur; 587062306a36Sopenharmony_ci xfs_fileoff_t new_startoff; 587162306a36Sopenharmony_ci int error = 0; 587262306a36Sopenharmony_ci int logflags = 0; 587362306a36Sopenharmony_ci 587462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp)) || 587562306a36Sopenharmony_ci XFS_TEST_ERROR(false, mp, XFS_ERRTAG_BMAPIFORMAT)) { 587662306a36Sopenharmony_ci return -EFSCORRUPTED; 587762306a36Sopenharmony_ci } 587862306a36Sopenharmony_ci 587962306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 588062306a36Sopenharmony_ci return -EIO; 588162306a36Sopenharmony_ci 588262306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL)); 588362306a36Sopenharmony_ci 588462306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 588562306a36Sopenharmony_ci if (error) 588662306a36Sopenharmony_ci return error; 588762306a36Sopenharmony_ci 588862306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_BTREE) { 588962306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); 589062306a36Sopenharmony_ci cur->bc_ino.flags = 0; 589162306a36Sopenharmony_ci } 589262306a36Sopenharmony_ci 589362306a36Sopenharmony_ci if (*next_fsb == NULLFSBLOCK) { 589462306a36Sopenharmony_ci xfs_iext_last(ifp, &icur); 589562306a36Sopenharmony_ci if (!xfs_iext_get_extent(ifp, &icur, &got) || 589662306a36Sopenharmony_ci stop_fsb > got.br_startoff) { 589762306a36Sopenharmony_ci *done = true; 589862306a36Sopenharmony_ci goto del_cursor; 589962306a36Sopenharmony_ci } 590062306a36Sopenharmony_ci } else { 590162306a36Sopenharmony_ci if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &icur, &got)) { 590262306a36Sopenharmony_ci *done = true; 590362306a36Sopenharmony_ci goto del_cursor; 590462306a36Sopenharmony_ci } 590562306a36Sopenharmony_ci } 590662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, isnullstartblock(got.br_startblock))) { 590762306a36Sopenharmony_ci error = -EFSCORRUPTED; 590862306a36Sopenharmony_ci goto del_cursor; 590962306a36Sopenharmony_ci } 591062306a36Sopenharmony_ci 591162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, stop_fsb > got.br_startoff)) { 591262306a36Sopenharmony_ci error = -EFSCORRUPTED; 591362306a36Sopenharmony_ci goto del_cursor; 591462306a36Sopenharmony_ci } 591562306a36Sopenharmony_ci 591662306a36Sopenharmony_ci new_startoff = got.br_startoff + offset_shift_fsb; 591762306a36Sopenharmony_ci if (xfs_iext_peek_next_extent(ifp, &icur, &next)) { 591862306a36Sopenharmony_ci if (new_startoff + got.br_blockcount > next.br_startoff) { 591962306a36Sopenharmony_ci error = -EINVAL; 592062306a36Sopenharmony_ci goto del_cursor; 592162306a36Sopenharmony_ci } 592262306a36Sopenharmony_ci 592362306a36Sopenharmony_ci /* 592462306a36Sopenharmony_ci * Unlike a left shift (which involves a hole punch), a right 592562306a36Sopenharmony_ci * shift does not modify extent neighbors in any way. We should 592662306a36Sopenharmony_ci * never find mergeable extents in this scenario. Check anyways 592762306a36Sopenharmony_ci * and warn if we encounter two extents that could be one. 592862306a36Sopenharmony_ci */ 592962306a36Sopenharmony_ci if (xfs_bmse_can_merge(&got, &next, offset_shift_fsb)) 593062306a36Sopenharmony_ci WARN_ON_ONCE(1); 593162306a36Sopenharmony_ci } 593262306a36Sopenharmony_ci 593362306a36Sopenharmony_ci error = xfs_bmap_shift_update_extent(tp, ip, whichfork, &icur, &got, 593462306a36Sopenharmony_ci cur, &logflags, new_startoff); 593562306a36Sopenharmony_ci if (error) 593662306a36Sopenharmony_ci goto del_cursor; 593762306a36Sopenharmony_ci 593862306a36Sopenharmony_ci if (!xfs_iext_prev_extent(ifp, &icur, &got) || 593962306a36Sopenharmony_ci stop_fsb >= got.br_startoff + got.br_blockcount) { 594062306a36Sopenharmony_ci *done = true; 594162306a36Sopenharmony_ci goto del_cursor; 594262306a36Sopenharmony_ci } 594362306a36Sopenharmony_ci 594462306a36Sopenharmony_ci *next_fsb = got.br_startoff; 594562306a36Sopenharmony_cidel_cursor: 594662306a36Sopenharmony_ci if (cur) 594762306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 594862306a36Sopenharmony_ci if (logflags) 594962306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, logflags); 595062306a36Sopenharmony_ci return error; 595162306a36Sopenharmony_ci} 595262306a36Sopenharmony_ci 595362306a36Sopenharmony_ci/* 595462306a36Sopenharmony_ci * Splits an extent into two extents at split_fsb block such that it is the 595562306a36Sopenharmony_ci * first block of the current_ext. @ext is a target extent to be split. 595662306a36Sopenharmony_ci * @split_fsb is a block where the extents is split. If split_fsb lies in a 595762306a36Sopenharmony_ci * hole or the first block of extents, just return 0. 595862306a36Sopenharmony_ci */ 595962306a36Sopenharmony_ciint 596062306a36Sopenharmony_cixfs_bmap_split_extent( 596162306a36Sopenharmony_ci struct xfs_trans *tp, 596262306a36Sopenharmony_ci struct xfs_inode *ip, 596362306a36Sopenharmony_ci xfs_fileoff_t split_fsb) 596462306a36Sopenharmony_ci{ 596562306a36Sopenharmony_ci int whichfork = XFS_DATA_FORK; 596662306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 596762306a36Sopenharmony_ci struct xfs_btree_cur *cur = NULL; 596862306a36Sopenharmony_ci struct xfs_bmbt_irec got; 596962306a36Sopenharmony_ci struct xfs_bmbt_irec new; /* split extent */ 597062306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 597162306a36Sopenharmony_ci xfs_fsblock_t gotblkcnt; /* new block count for got */ 597262306a36Sopenharmony_ci struct xfs_iext_cursor icur; 597362306a36Sopenharmony_ci int error = 0; 597462306a36Sopenharmony_ci int logflags = 0; 597562306a36Sopenharmony_ci int i = 0; 597662306a36Sopenharmony_ci 597762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp)) || 597862306a36Sopenharmony_ci XFS_TEST_ERROR(false, mp, XFS_ERRTAG_BMAPIFORMAT)) { 597962306a36Sopenharmony_ci return -EFSCORRUPTED; 598062306a36Sopenharmony_ci } 598162306a36Sopenharmony_ci 598262306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 598362306a36Sopenharmony_ci return -EIO; 598462306a36Sopenharmony_ci 598562306a36Sopenharmony_ci /* Read in all the extents */ 598662306a36Sopenharmony_ci error = xfs_iread_extents(tp, ip, whichfork); 598762306a36Sopenharmony_ci if (error) 598862306a36Sopenharmony_ci return error; 598962306a36Sopenharmony_ci 599062306a36Sopenharmony_ci /* 599162306a36Sopenharmony_ci * If there are not extents, or split_fsb lies in a hole we are done. 599262306a36Sopenharmony_ci */ 599362306a36Sopenharmony_ci if (!xfs_iext_lookup_extent(ip, ifp, split_fsb, &icur, &got) || 599462306a36Sopenharmony_ci got.br_startoff >= split_fsb) 599562306a36Sopenharmony_ci return 0; 599662306a36Sopenharmony_ci 599762306a36Sopenharmony_ci gotblkcnt = split_fsb - got.br_startoff; 599862306a36Sopenharmony_ci new.br_startoff = split_fsb; 599962306a36Sopenharmony_ci new.br_startblock = got.br_startblock + gotblkcnt; 600062306a36Sopenharmony_ci new.br_blockcount = got.br_blockcount - gotblkcnt; 600162306a36Sopenharmony_ci new.br_state = got.br_state; 600262306a36Sopenharmony_ci 600362306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_BTREE) { 600462306a36Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); 600562306a36Sopenharmony_ci cur->bc_ino.flags = 0; 600662306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &got, &i); 600762306a36Sopenharmony_ci if (error) 600862306a36Sopenharmony_ci goto del_cursor; 600962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 601062306a36Sopenharmony_ci error = -EFSCORRUPTED; 601162306a36Sopenharmony_ci goto del_cursor; 601262306a36Sopenharmony_ci } 601362306a36Sopenharmony_ci } 601462306a36Sopenharmony_ci 601562306a36Sopenharmony_ci got.br_blockcount = gotblkcnt; 601662306a36Sopenharmony_ci xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), &icur, 601762306a36Sopenharmony_ci &got); 601862306a36Sopenharmony_ci 601962306a36Sopenharmony_ci logflags = XFS_ILOG_CORE; 602062306a36Sopenharmony_ci if (cur) { 602162306a36Sopenharmony_ci error = xfs_bmbt_update(cur, &got); 602262306a36Sopenharmony_ci if (error) 602362306a36Sopenharmony_ci goto del_cursor; 602462306a36Sopenharmony_ci } else 602562306a36Sopenharmony_ci logflags |= XFS_ILOG_DEXT; 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_ci /* Add new extent */ 602862306a36Sopenharmony_ci xfs_iext_next(ifp, &icur); 602962306a36Sopenharmony_ci xfs_iext_insert(ip, &icur, &new, 0); 603062306a36Sopenharmony_ci ifp->if_nextents++; 603162306a36Sopenharmony_ci 603262306a36Sopenharmony_ci if (cur) { 603362306a36Sopenharmony_ci error = xfs_bmbt_lookup_eq(cur, &new, &i); 603462306a36Sopenharmony_ci if (error) 603562306a36Sopenharmony_ci goto del_cursor; 603662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 0)) { 603762306a36Sopenharmony_ci error = -EFSCORRUPTED; 603862306a36Sopenharmony_ci goto del_cursor; 603962306a36Sopenharmony_ci } 604062306a36Sopenharmony_ci error = xfs_btree_insert(cur, &i); 604162306a36Sopenharmony_ci if (error) 604262306a36Sopenharmony_ci goto del_cursor; 604362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, i != 1)) { 604462306a36Sopenharmony_ci error = -EFSCORRUPTED; 604562306a36Sopenharmony_ci goto del_cursor; 604662306a36Sopenharmony_ci } 604762306a36Sopenharmony_ci } 604862306a36Sopenharmony_ci 604962306a36Sopenharmony_ci /* 605062306a36Sopenharmony_ci * Convert to a btree if necessary. 605162306a36Sopenharmony_ci */ 605262306a36Sopenharmony_ci if (xfs_bmap_needs_btree(ip, whichfork)) { 605362306a36Sopenharmony_ci int tmp_logflags; /* partial log flag return val */ 605462306a36Sopenharmony_ci 605562306a36Sopenharmony_ci ASSERT(cur == NULL); 605662306a36Sopenharmony_ci error = xfs_bmap_extents_to_btree(tp, ip, &cur, 0, 605762306a36Sopenharmony_ci &tmp_logflags, whichfork); 605862306a36Sopenharmony_ci logflags |= tmp_logflags; 605962306a36Sopenharmony_ci } 606062306a36Sopenharmony_ci 606162306a36Sopenharmony_cidel_cursor: 606262306a36Sopenharmony_ci if (cur) { 606362306a36Sopenharmony_ci cur->bc_ino.allocated = 0; 606462306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 606562306a36Sopenharmony_ci } 606662306a36Sopenharmony_ci 606762306a36Sopenharmony_ci if (logflags) 606862306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, logflags); 606962306a36Sopenharmony_ci return error; 607062306a36Sopenharmony_ci} 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci/* Deferred mapping is only for real extents in the data fork. */ 607362306a36Sopenharmony_cistatic bool 607462306a36Sopenharmony_cixfs_bmap_is_update_needed( 607562306a36Sopenharmony_ci struct xfs_bmbt_irec *bmap) 607662306a36Sopenharmony_ci{ 607762306a36Sopenharmony_ci return bmap->br_startblock != HOLESTARTBLOCK && 607862306a36Sopenharmony_ci bmap->br_startblock != DELAYSTARTBLOCK; 607962306a36Sopenharmony_ci} 608062306a36Sopenharmony_ci 608162306a36Sopenharmony_ci/* Record a bmap intent. */ 608262306a36Sopenharmony_cistatic int 608362306a36Sopenharmony_ci__xfs_bmap_add( 608462306a36Sopenharmony_ci struct xfs_trans *tp, 608562306a36Sopenharmony_ci enum xfs_bmap_intent_type type, 608662306a36Sopenharmony_ci struct xfs_inode *ip, 608762306a36Sopenharmony_ci int whichfork, 608862306a36Sopenharmony_ci struct xfs_bmbt_irec *bmap) 608962306a36Sopenharmony_ci{ 609062306a36Sopenharmony_ci struct xfs_bmap_intent *bi; 609162306a36Sopenharmony_ci 609262306a36Sopenharmony_ci trace_xfs_bmap_defer(tp->t_mountp, 609362306a36Sopenharmony_ci XFS_FSB_TO_AGNO(tp->t_mountp, bmap->br_startblock), 609462306a36Sopenharmony_ci type, 609562306a36Sopenharmony_ci XFS_FSB_TO_AGBNO(tp->t_mountp, bmap->br_startblock), 609662306a36Sopenharmony_ci ip->i_ino, whichfork, 609762306a36Sopenharmony_ci bmap->br_startoff, 609862306a36Sopenharmony_ci bmap->br_blockcount, 609962306a36Sopenharmony_ci bmap->br_state); 610062306a36Sopenharmony_ci 610162306a36Sopenharmony_ci bi = kmem_cache_alloc(xfs_bmap_intent_cache, GFP_NOFS | __GFP_NOFAIL); 610262306a36Sopenharmony_ci INIT_LIST_HEAD(&bi->bi_list); 610362306a36Sopenharmony_ci bi->bi_type = type; 610462306a36Sopenharmony_ci bi->bi_owner = ip; 610562306a36Sopenharmony_ci bi->bi_whichfork = whichfork; 610662306a36Sopenharmony_ci bi->bi_bmap = *bmap; 610762306a36Sopenharmony_ci 610862306a36Sopenharmony_ci xfs_bmap_update_get_group(tp->t_mountp, bi); 610962306a36Sopenharmony_ci xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_BMAP, &bi->bi_list); 611062306a36Sopenharmony_ci return 0; 611162306a36Sopenharmony_ci} 611262306a36Sopenharmony_ci 611362306a36Sopenharmony_ci/* Map an extent into a file. */ 611462306a36Sopenharmony_civoid 611562306a36Sopenharmony_cixfs_bmap_map_extent( 611662306a36Sopenharmony_ci struct xfs_trans *tp, 611762306a36Sopenharmony_ci struct xfs_inode *ip, 611862306a36Sopenharmony_ci struct xfs_bmbt_irec *PREV) 611962306a36Sopenharmony_ci{ 612062306a36Sopenharmony_ci if (!xfs_bmap_is_update_needed(PREV)) 612162306a36Sopenharmony_ci return; 612262306a36Sopenharmony_ci 612362306a36Sopenharmony_ci __xfs_bmap_add(tp, XFS_BMAP_MAP, ip, XFS_DATA_FORK, PREV); 612462306a36Sopenharmony_ci} 612562306a36Sopenharmony_ci 612662306a36Sopenharmony_ci/* Unmap an extent out of a file. */ 612762306a36Sopenharmony_civoid 612862306a36Sopenharmony_cixfs_bmap_unmap_extent( 612962306a36Sopenharmony_ci struct xfs_trans *tp, 613062306a36Sopenharmony_ci struct xfs_inode *ip, 613162306a36Sopenharmony_ci struct xfs_bmbt_irec *PREV) 613262306a36Sopenharmony_ci{ 613362306a36Sopenharmony_ci if (!xfs_bmap_is_update_needed(PREV)) 613462306a36Sopenharmony_ci return; 613562306a36Sopenharmony_ci 613662306a36Sopenharmony_ci __xfs_bmap_add(tp, XFS_BMAP_UNMAP, ip, XFS_DATA_FORK, PREV); 613762306a36Sopenharmony_ci} 613862306a36Sopenharmony_ci 613962306a36Sopenharmony_ci/* 614062306a36Sopenharmony_ci * Process one of the deferred bmap operations. We pass back the 614162306a36Sopenharmony_ci * btree cursor to maintain our lock on the bmapbt between calls. 614262306a36Sopenharmony_ci */ 614362306a36Sopenharmony_ciint 614462306a36Sopenharmony_cixfs_bmap_finish_one( 614562306a36Sopenharmony_ci struct xfs_trans *tp, 614662306a36Sopenharmony_ci struct xfs_bmap_intent *bi) 614762306a36Sopenharmony_ci{ 614862306a36Sopenharmony_ci struct xfs_bmbt_irec *bmap = &bi->bi_bmap; 614962306a36Sopenharmony_ci int error = 0; 615062306a36Sopenharmony_ci 615162306a36Sopenharmony_ci ASSERT(tp->t_highest_agno == NULLAGNUMBER); 615262306a36Sopenharmony_ci 615362306a36Sopenharmony_ci trace_xfs_bmap_deferred(tp->t_mountp, 615462306a36Sopenharmony_ci XFS_FSB_TO_AGNO(tp->t_mountp, bmap->br_startblock), 615562306a36Sopenharmony_ci bi->bi_type, 615662306a36Sopenharmony_ci XFS_FSB_TO_AGBNO(tp->t_mountp, bmap->br_startblock), 615762306a36Sopenharmony_ci bi->bi_owner->i_ino, bi->bi_whichfork, 615862306a36Sopenharmony_ci bmap->br_startoff, bmap->br_blockcount, 615962306a36Sopenharmony_ci bmap->br_state); 616062306a36Sopenharmony_ci 616162306a36Sopenharmony_ci if (WARN_ON_ONCE(bi->bi_whichfork != XFS_DATA_FORK)) 616262306a36Sopenharmony_ci return -EFSCORRUPTED; 616362306a36Sopenharmony_ci 616462306a36Sopenharmony_ci if (XFS_TEST_ERROR(false, tp->t_mountp, 616562306a36Sopenharmony_ci XFS_ERRTAG_BMAP_FINISH_ONE)) 616662306a36Sopenharmony_ci return -EIO; 616762306a36Sopenharmony_ci 616862306a36Sopenharmony_ci switch (bi->bi_type) { 616962306a36Sopenharmony_ci case XFS_BMAP_MAP: 617062306a36Sopenharmony_ci error = xfs_bmapi_remap(tp, bi->bi_owner, bmap->br_startoff, 617162306a36Sopenharmony_ci bmap->br_blockcount, bmap->br_startblock, 0); 617262306a36Sopenharmony_ci bmap->br_blockcount = 0; 617362306a36Sopenharmony_ci break; 617462306a36Sopenharmony_ci case XFS_BMAP_UNMAP: 617562306a36Sopenharmony_ci error = __xfs_bunmapi(tp, bi->bi_owner, bmap->br_startoff, 617662306a36Sopenharmony_ci &bmap->br_blockcount, XFS_BMAPI_REMAP, 1); 617762306a36Sopenharmony_ci break; 617862306a36Sopenharmony_ci default: 617962306a36Sopenharmony_ci ASSERT(0); 618062306a36Sopenharmony_ci error = -EFSCORRUPTED; 618162306a36Sopenharmony_ci } 618262306a36Sopenharmony_ci 618362306a36Sopenharmony_ci return error; 618462306a36Sopenharmony_ci} 618562306a36Sopenharmony_ci 618662306a36Sopenharmony_ci/* Check that an inode's extent does not have invalid flags or bad ranges. */ 618762306a36Sopenharmony_cixfs_failaddr_t 618862306a36Sopenharmony_cixfs_bmap_validate_extent( 618962306a36Sopenharmony_ci struct xfs_inode *ip, 619062306a36Sopenharmony_ci int whichfork, 619162306a36Sopenharmony_ci struct xfs_bmbt_irec *irec) 619262306a36Sopenharmony_ci{ 619362306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 619462306a36Sopenharmony_ci 619562306a36Sopenharmony_ci if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount)) 619662306a36Sopenharmony_ci return __this_address; 619762306a36Sopenharmony_ci 619862306a36Sopenharmony_ci if (XFS_IS_REALTIME_INODE(ip) && whichfork == XFS_DATA_FORK) { 619962306a36Sopenharmony_ci if (!xfs_verify_rtext(mp, irec->br_startblock, 620062306a36Sopenharmony_ci irec->br_blockcount)) 620162306a36Sopenharmony_ci return __this_address; 620262306a36Sopenharmony_ci } else { 620362306a36Sopenharmony_ci if (!xfs_verify_fsbext(mp, irec->br_startblock, 620462306a36Sopenharmony_ci irec->br_blockcount)) 620562306a36Sopenharmony_ci return __this_address; 620662306a36Sopenharmony_ci } 620762306a36Sopenharmony_ci if (irec->br_state != XFS_EXT_NORM && whichfork != XFS_DATA_FORK) 620862306a36Sopenharmony_ci return __this_address; 620962306a36Sopenharmony_ci return NULL; 621062306a36Sopenharmony_ci} 621162306a36Sopenharmony_ci 621262306a36Sopenharmony_ciint __init 621362306a36Sopenharmony_cixfs_bmap_intent_init_cache(void) 621462306a36Sopenharmony_ci{ 621562306a36Sopenharmony_ci xfs_bmap_intent_cache = kmem_cache_create("xfs_bmap_intent", 621662306a36Sopenharmony_ci sizeof(struct xfs_bmap_intent), 621762306a36Sopenharmony_ci 0, 0, NULL); 621862306a36Sopenharmony_ci 621962306a36Sopenharmony_ci return xfs_bmap_intent_cache != NULL ? 0 : -ENOMEM; 622062306a36Sopenharmony_ci} 622162306a36Sopenharmony_ci 622262306a36Sopenharmony_civoid 622362306a36Sopenharmony_cixfs_bmap_intent_destroy_cache(void) 622462306a36Sopenharmony_ci{ 622562306a36Sopenharmony_ci kmem_cache_destroy(xfs_bmap_intent_cache); 622662306a36Sopenharmony_ci xfs_bmap_intent_cache = NULL; 622762306a36Sopenharmony_ci} 6228