/* * Compute and fill in the value of the maximum depth of a bmap btree * in this filesystem. Done once, during mount.
*/ void
xfs_bmap_compute_maxlevels(
xfs_mount_t *mp, /* file system mount structure */ int whichfork) /* data or attr fork */
{
uint64_t maxblocks; /* max blocks at this level */
xfs_extnum_t maxleafents; /* max leaf entries possible */ int level; /* btree level */ int maxrootrecs; /* max records in root block */ int minleafrecs; /* min records in leaf block */ int minnoderecs; /* min records in node block */ int sz; /* root block size */
/* * The maximum number of extents in a fork, hence the maximum number of * leaf entries, is controlled by the size of the on-disk extent count. * * Note that we can no longer assume that if we are in ATTR1 that the * fork offset of all the inodes will be * (xfs_default_attroffset(ip) >> 3) because we could have mounted with * ATTR2 and then mounted back with ATTR1, keeping the i_forkoff's fixed * but probably at various positions. Therefore, for both ATTR1 and * ATTR2 we have to assume the worst case scenario of a minimum size * available.
*/
maxleafents = xfs_iext_max_nextents(xfs_has_large_extent_counts(mp),
whichfork); if (whichfork == XFS_DATA_FORK)
sz = xfs_bmdr_space_calc(MINDBTPTRS); else
sz = xfs_bmdr_space_calc(MINABTPTRS);
/* * Check if the inode needs to be converted to btree format.
*/ staticinlinebool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork)
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
/* * Check if the inode should be converted to extent format.
*/ staticinlinebool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork)
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
/* * Update the record referred to by cur to the value given by irec * This either works (return 0) or gets an EFSCORRUPTED error.
*/ STATICint
xfs_bmbt_update( struct xfs_btree_cur *cur, struct xfs_bmbt_irec *irec)
{ union xfs_btree_rec rec;
/* * Compute the worst-case number of indirect blocks that will be used * for ip's delayed extent of length "len".
*/
xfs_filblks_t
xfs_bmap_worst_indlen( struct xfs_inode *ip, /* incore inode pointer */
xfs_filblks_t len) /* delayed extent length */
{ struct xfs_mount *mp = ip->i_mount; int maxrecs = mp->m_bmap_dmxr[0]; int level;
xfs_filblks_t rval;
/* * Calculate the default attribute fork offset for newly created inodes.
*/
uint
xfs_default_attroffset( struct xfs_inode *ip)
{ if (ip->i_df.if_format == XFS_DINODE_FMT_DEV) return roundup(sizeof(xfs_dev_t), 8); return M_IGEO(ip->i_mount)->attr_fork_offset;
}
/* * Helper routine to reset inode i_forkoff field when switching attribute fork * from local to extent format - we reset it where possible to make space * available for inline data fork extents.
*/ STATICvoid
xfs_bmap_forkoff_reset(
xfs_inode_t *ip, int whichfork)
{ if (whichfork == XFS_ATTR_FORK &&
ip->i_df.if_format != XFS_DINODE_FMT_DEV &&
ip->i_df.if_format != XFS_DINODE_FMT_BTREE) {
uint dfl_forkoff = xfs_default_attroffset(ip) >> 3;
if (dfl_forkoff > ip->i_forkoff)
ip->i_forkoff = dfl_forkoff;
}
}
staticint
xfs_bmap_read_buf( struct xfs_mount *mp, /* file system mount point */ struct xfs_trans *tp, /* transaction pointer */
xfs_fsblock_t fsbno, /* file system block number */ struct xfs_buf **bpp) /* buffer for fsbno */
{ struct xfs_buf *bp; /* return value */ int error;
for (i = 0; i < cur->bc_maxlevels; i++) { if (!cur->bc_levels[i].bp) break; if (xfs_buf_daddr(cur->bc_levels[i].bp) == bno) return cur->bc_levels[i].bp;
}
/* Chase down all the log items to see if the bp is there */
list_for_each_entry(lip, &cur->bc_tp->t_items, li_trans) { struct xfs_buf_log_item *bip = (struct xfs_buf_log_item *)lip;
STATICvoid
xfs_check_block( struct xfs_btree_block *block,
xfs_mount_t *mp, int root, short sz)
{ int i, j, dmxr;
__be64 *pp, *thispa; /* pointer to block address */
xfs_bmbt_key_t *prevp, *keyp;
ASSERT(be16_to_cpu(block->bb_level) > 0);
prevp = NULL; for( i = 1; i <= xfs_btree_get_numrecs(block); i++) {
dmxr = mp->m_bmap_dmxr[0];
keyp = xfs_bmbt_key_addr(mp, block, i);
if (prevp) {
ASSERT(be64_to_cpu(prevp->br_startoff) <
be64_to_cpu(keyp->br_startoff));
}
prevp = keyp;
/* * Compare the block numbers to see if there are dups.
*/ if (root)
pp = xfs_bmap_broot_ptr_addr(mp, block, i, sz); else
pp = xfs_bmbt_ptr_addr(mp, block, i, dmxr);
for (j = i+1; j <= be16_to_cpu(block->bb_numrecs); j++) { if (root)
thispa = xfs_bmap_broot_ptr_addr(mp, block, j, sz); else
thispa = xfs_bmbt_ptr_addr(mp, block, j, dmxr); if (*thispa == *pp) {
xfs_warn(mp, "%s: thispa(%d) == pp(%d) %lld",
__func__, j, i,
(unsignedlonglong)be64_to_cpu(*thispa));
xfs_err(mp, "%s: ptrs are equal in node\n",
__func__);
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
}
}
}
}
/* * Check that the extents for the inode ip are in the right order in all * btree leaves. THis becomes prohibitively expensive for large extent count * files, so don't bother with inodes that have more than 10,000 extents in * them. The btree record ordering checks will still be done, so for such large * bmapbt constructs that is going to catch most corruptions.
*/ STATICvoid
xfs_bmap_check_leaf_extents( struct xfs_btree_cur *cur, /* btree cursor or null */
xfs_inode_t *ip, /* incore inode pointer */ int whichfork) /* data or attr fork */
{ struct xfs_mount *mp = ip->i_mount; struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_block *block; /* current btree block */
xfs_fsblock_t bno; /* block # of "block" */ struct xfs_buf *bp; /* buffer for "block" */ int error; /* error return value */
xfs_extnum_t i=0, j; /* index into the extents list */ int level; /* btree level, for checking */
__be64 *pp; /* pointer to block address */
xfs_bmbt_rec_t *ep; /* pointer to current extent */
xfs_bmbt_rec_t last = {0, 0}; /* last extent in prev block */
xfs_bmbt_rec_t *nextp; /* pointer to next extent */ int bp_release = 0;
if (ifp->if_format != XFS_DINODE_FMT_BTREE) return;
/* skip large extent count inodes */ if (ip->i_df.if_nextents > 10000) return;
bno = NULLFSBLOCK;
block = ifp->if_broot; /* * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
*/
level = be16_to_cpu(block->bb_level);
ASSERT(level > 0);
xfs_check_block(block, mp, 1, ifp->if_broot_bytes);
pp = xfs_bmap_broot_ptr_addr(mp, block, 1, ifp->if_broot_bytes);
bno = be64_to_cpu(*pp);
/* * Go down the tree until leaf level is reached, following the first * pointer (leftmost) at each level.
*/ while (level-- > 0) { /* See if buf is in cur first */
bp_release = 0;
bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno)); if (!bp) {
bp_release = 1;
error = xfs_bmap_read_buf(mp, NULL, bno, &bp); if (xfs_metadata_is_sick(error))
xfs_btree_mark_sick(cur); if (error) goto error_norelse;
}
block = XFS_BUF_TO_BLOCK(bp); if (level == 0) break;
/* * Check this block for basic sanity (increasing keys and * no duplicate blocks).
*/
/* * Here with bp and block set to the leftmost leaf node in the tree.
*/
i = 0;
/* * Loop over all leaf nodes checking that all extents are in the right order.
*/ for (;;) {
xfs_fsblock_t nextbno;
xfs_extnum_t num_recs;
num_recs = xfs_btree_get_numrecs(block);
/* * Read-ahead the next leaf block, if any.
*/
nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);
/* * Check all the extents to make sure they are OK. * If we had a previous block, the last entry should * conform with the first entry in this one.
*/
ep = xfs_bmbt_rec_addr(mp, block, 1); if (i) {
ASSERT(xfs_bmbt_disk_get_startoff(&last) +
xfs_bmbt_disk_get_blockcount(&last) <=
xfs_bmbt_disk_get_startoff(ep));
} for (j = 1; j < num_recs; j++) {
nextp = xfs_bmbt_rec_addr(mp, block, j + 1);
ASSERT(xfs_bmbt_disk_get_startoff(ep) +
xfs_bmbt_disk_get_blockcount(ep) <=
xfs_bmbt_disk_get_startoff(nextp));
ep = nextp;
}
last = *ep;
i += num_recs; if (bp_release) {
bp_release = 0;
xfs_trans_brelse(NULL, bp);
}
bno = nextbno; /* * If we've reached the end, stop.
*/ if (bno == NULLFSBLOCK) break;
bp_release = 0;
bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno)); if (!bp) {
bp_release = 1;
error = xfs_bmap_read_buf(mp, NULL, bno, &bp); if (xfs_metadata_is_sick(error))
xfs_btree_mark_sick(cur); if (error) goto error_norelse;
}
block = XFS_BUF_TO_BLOCK(bp);
}
return;
error0:
xfs_warn(mp, "%s: at error0", __func__); if (bp_release)
xfs_trans_brelse(NULL, bp);
error_norelse:
xfs_warn(mp, "%s: BAD after btree leaves for %llu extents",
__func__, i);
xfs_err(mp, "%s: CORRUPTED BTREE OR SOMETHING", __func__);
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); return;
}
/* * Validate that the bmbt_irecs being returned from bmapi are valid * given the caller's original parameters. Specifically check the * ranges of the returned irecs to ensure that they only extend beyond * the given parameters if the XFS_BMAPI_ENTIRE flag was set.
*/ STATICvoid
xfs_bmap_validate_ret(
xfs_fileoff_t bno,
xfs_filblks_t len,
uint32_t flags,
xfs_bmbt_irec_t *mval, int nmap, int ret_nmap)
{ int i; /* index to map values */
#else #define xfs_bmap_check_leaf_extents(cur, ip, whichfork) do { } while (0) #define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap) do { } while (0) #endif/* DEBUG */
/* * Inode fork format manipulation functions
*/
/* * Convert the inode format to extent format if it currently is in btree format, * but the extent list is small enough that it fits into the extent format. * * Since the extents are already in-core, all we have to do is give up the space * for the btree root and pitch the leaf block.
*/ STATICint/* error */
xfs_bmap_btree_to_extents( struct xfs_trans *tp, /* transaction pointer */ struct xfs_inode *ip, /* incore inode pointer */ struct xfs_btree_cur *cur, /* btree cursor */ int *logflagsp, /* inode logging flags */ int whichfork) /* data or attr fork */
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_mount *mp = ip->i_mount; struct xfs_btree_block *rblock = ifp->if_broot; struct xfs_btree_block *cblock;/* child btree block */
xfs_fsblock_t cbno; /* child block number */ struct xfs_buf *cbp; /* child block's buffer */ int error; /* error return value */
__be64 *pp; /* ptr to block address */ struct xfs_owner_info oinfo;
/* check if we actually need the extent format first: */ if (!xfs_bmap_wants_extents(ip, whichfork)) return 0;
/* * Make space in the inode incore. This needs to be undone if we fail * to expand the root.
*/
block = xfs_bmap_broot_realloc(ip, whichfork, 1);
/* * Fill in the root.
*/
xfs_bmbt_init_block(ip, block, NULL, 1, 1); /* * Need a cursor. Can't allocate until bb_level is filled in.
*/
cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); if (wasdel)
cur->bc_flags |= XFS_BTREE_BMBT_WASDEL; /* * Convert to a btree with two levels, one record in root.
*/
ifp->if_format = XFS_DINODE_FMT_BTREE;
memset(&args, 0, sizeof(args));
args.tp = tp;
args.mp = mp;
xfs_rmap_ino_bmbt_owner(&args.oinfo, ip->i_ino, whichfork);
/* * Fill in the root key and pointer.
*/
kp = xfs_bmbt_key_addr(mp, block, 1);
arp = xfs_bmbt_rec_addr(mp, ablock, 1);
kp->br_startoff = cpu_to_be64(xfs_bmbt_disk_get_startoff(arp));
pp = xfs_bmbt_ptr_addr(mp, block, 1, xfs_bmbt_get_maxrecs(cur,
be16_to_cpu(block->bb_level)));
*pp = cpu_to_be64(args.fsbno);
/* * Do all this logging at the end so that * the root is at the right level.
*/
xfs_btree_log_block(cur, abp, XFS_BB_ALL_BITS);
xfs_btree_log_recs(cur, abp, 1, be16_to_cpu(ablock->bb_numrecs));
ASSERT(*curp == NULL);
*curp = cur;
*logflagsp = XFS_ILOG_CORE | xfs_ilog_fbroot(whichfork); return 0;
/* * Convert a local file to an extents file. * This code is out of bounds for data forks of regular files, * since the file data needs to get logged so things will stay consistent. * (The bmap-level manipulations are ok, though).
*/ void
xfs_bmap_local_to_extents_empty( struct xfs_trans *tp, struct xfs_inode *ip, int whichfork)
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
/* * We don't want to deal with the case of keeping inode data inline yet. * So sending the data fork of a regular inode is invalid.
*/
ASSERT(!(S_ISREG(VFS_I(ip)->i_mode) && whichfork == XFS_DATA_FORK));
ifp = xfs_ifork_ptr(ip, whichfork);
ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
/* * Allocate a block. We know we need only one, since the * file currently fits in an inode.
*/
args.total = total;
args.minlen = args.maxlen = args.prod = 1;
error = xfs_alloc_vextent_start_ag(&args,
XFS_INO_TO_FSB(args.mp, ip->i_ino)); if (error) goto done;
/* Can't fail, the space was reserved. */
ASSERT(args.fsbno != NULLFSBLOCK);
ASSERT(args.len == 1);
error = xfs_trans_get_buf(tp, args.mp->m_ddev_targp,
XFS_FSB_TO_DADDR(args.mp, args.fsbno),
args.mp->m_bsize, 0, &bp); if (error) goto done;
/* * Initialize the block, copy the data and log the remote buffer. * * The callout is responsible for logging because the remote format * might differ from the local format and thus we don't know how much to * log here. Note that init_fn must also set the buffer log item type * correctly.
*/
init_fn(tp, bp, ip, ifp, priv);
/* account for the change in fork size */
xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
xfs_bmap_local_to_extents_empty(tp, ip, whichfork);
flags |= XFS_ILOG_CORE;
/* * Called from xfs_bmap_add_attrfork to handle local format files. Each * different data fork content type needs a different callout to do the * conversion. Some are basic and only require special block initialisation * callouts for the data formating, others (directories) are so specialised they * handle everything themselves. * * XXX (dgc): investigate whether directory conversion can use the generic * formatting callout. It should be possible - it's just a very complex * formatter.
*/ STATICint/* error */
xfs_bmap_add_attrfork_local( struct xfs_trans *tp, /* transaction pointer */ struct xfs_inode *ip, /* incore inode pointer */ int *flags) /* inode logging flags */
{ struct xfs_da_args dargs; /* args for dir/attr code */
if (ip->i_df.if_bytes <= xfs_inode_data_fork_size(ip)) return 0;
if (S_ISLNK(VFS_I(ip)->i_mode)) return xfs_bmap_local_to_extents(tp, ip, 1, flags,
XFS_DATA_FORK, xfs_symlink_local_to_remote,
NULL);
/* should only be called for types that support local format data */
ASSERT(0);
xfs_bmap_mark_sick(ip, XFS_ATTR_FORK); return -EFSCORRUPTED;
}
/* * Set an inode attr fork offset based on the format of the data fork.
*/ staticint
xfs_bmap_set_attrforkoff( struct xfs_inode *ip, int size, int *version)
{ int default_size = xfs_default_attroffset(ip) >> 3;
switch (ip->i_df.if_format) { case XFS_DINODE_FMT_DEV:
ip->i_forkoff = default_size; break; case XFS_DINODE_FMT_LOCAL: case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_BTREE:
ip->i_forkoff = xfs_attr_shortform_bytesfit(ip, size); if (!ip->i_forkoff)
ip->i_forkoff = default_size; elseif (xfs_has_attr2(ip->i_mount) && version)
*version = 2; break; default:
ASSERT(0); return -EINVAL;
}
return 0;
}
/* * Convert inode from non-attributed to attributed. Caller must hold the * ILOCK_EXCL and the file cannot have an attr fork.
*/ int/* error code */
xfs_bmap_add_attrfork( struct xfs_trans *tp, struct xfs_inode *ip, /* incore inode pointer */ int size, /* space new attribute needs */ int rsvd) /* xact may use reserved blks */
{ struct xfs_mount *mp = tp->t_mountp; int version = 1; /* superblock attr version */ int logflags; /* logging flags */ int error; /* error return value */
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL); if (!xfs_is_metadir_inode(ip))
ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
ASSERT(!xfs_inode_has_attr_fork(ip));
if (XFS_IS_CORRUPT(mp, ir.loaded != ifp->if_nextents)) {
xfs_bmap_mark_sick(ip, whichfork);
error = -EFSCORRUPTED; goto out;
}
ASSERT(ir.loaded == xfs_iext_count(ifp)); /* * Use release semantics so that we can use acquire semantics in * xfs_need_iread_extents and be guaranteed to see a valid mapping tree * after that load.
*/
smp_store_release(&ifp->if_needextents, 0); return 0;
out: if (xfs_metadata_is_sick(error))
xfs_bmap_mark_sick(ip, whichfork);
xfs_iext_destroy(ifp); return error;
}
/* * Returns the relative block number of the first unused block(s) in the given * fork with at least "len" logically contiguous blocks free. This is the * lowest-address hole if the fork has holes, else the first block past the end * of fork. Return 0 if the fork is currently local (in-inode).
*/ int/* error */
xfs_bmap_first_unused( struct xfs_trans *tp, /* transaction pointer */ struct xfs_inode *ip, /* incore inode */
xfs_extlen_t len, /* size of hole to find */
xfs_fileoff_t *first_unused, /* unused block */ int whichfork) /* data or attr fork */
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec got; struct xfs_iext_cursor icur;
xfs_fileoff_t lastaddr = 0;
xfs_fileoff_t lowest, max; int error;
error = xfs_iread_extents(tp, ip, whichfork); if (error) return error;
lowest = max = *first_unused;
for_each_xfs_iext(ifp, &icur, &got) { /* * See if the hole before this extent will work.
*/ if (got.br_startoff >= lowest + len &&
got.br_startoff - max >= len) break;
lastaddr = got.br_startoff + got.br_blockcount;
max = XFS_FILEOFF_MAX(lastaddr, lowest);
}
*first_unused = max; return 0;
}
/* * Returns the file-relative block number of the last block - 1 before * last_block (input value) in the file. * This is not based on i_size, it is based on the extent records. * Returns 0 for local files, as they do not have extent records.
*/ int/* error */
xfs_bmap_last_before( struct xfs_trans *tp, /* transaction pointer */ struct xfs_inode *ip, /* incore inode */
xfs_fileoff_t *last_block, /* last block */ int whichfork) /* data or attr fork */
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec got; struct xfs_iext_cursor icur; int error;
switch (ifp->if_format) { case XFS_DINODE_FMT_LOCAL:
*last_block = 0; return 0; case XFS_DINODE_FMT_BTREE: case XFS_DINODE_FMT_EXTENTS: break; default:
ASSERT(0);
xfs_bmap_mark_sick(ip, whichfork); return -EFSCORRUPTED;
}
error = xfs_iread_extents(tp, ip, whichfork); if (error) return error;
/* * Check the last inode extent to determine whether this allocation will result * in blocks being allocated at the end of the file. When we allocate new data * blocks at the end of the file which do not start at the previous data block, * we will try to align the new blocks at stripe unit boundaries. * * Returns 1 in bma->aeof if the file (fork) is empty as any new write will be * at, or past the EOF.
*/ STATICint
xfs_bmap_isaeof( struct xfs_bmalloca *bma, int whichfork)
{ struct xfs_bmbt_irec rec; int is_empty; int error;
/* * Check if we are allocation or past the last extent, or at least into * the last delayed allocated extent.
*/
bma->aeof = bma->offset >= rec.br_startoff + rec.br_blockcount ||
(bma->offset >= rec.br_startoff &&
isnullstartblock(rec.br_startblock)); return 0;
}
/* * Returns the file-relative block number of the first block past eof in * the file. This is not based on i_size, it is based on the extent records. * Returns 0 for local files, as they do not have extent records.
*/ int
xfs_bmap_last_offset( struct xfs_inode *ip,
xfs_fileoff_t *last_block, int whichfork)
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec rec; int is_empty; int error;
*last_block = 0;
if (ifp->if_format == XFS_DINODE_FMT_LOCAL) return 0;
if (XFS_IS_CORRUPT(ip->i_mount, !xfs_ifork_has_extents(ifp))) {
xfs_bmap_mark_sick(ip, whichfork); return -EFSCORRUPTED;
}
if (xfs_ifork_is_realtime(ip, whichfork) && xfs_has_rtgroups(mp)) { if (xfs_rtb_to_rgno(mp, left->br_startblock) !=
xfs_rtb_to_rgno(mp, right->br_startblock)) returnfalse;
}
returntrue;
}
/* * Convert a delayed allocation to a real allocation.
*/ STATICint/* error */
xfs_bmap_add_extent_delay_real( struct xfs_bmalloca *bma, int whichfork)
{ struct xfs_mount *mp = bma->ip->i_mount; struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); struct xfs_bmbt_irec *new = &bma->got; int error; /* error return value */ int i; /* temp state */
xfs_fileoff_t new_endoff; /* end offset of new entry */
xfs_bmbt_irec_t r[3]; /* neighbor extent entries */ /* left is 0, right is 1, prev is 2 */ int rval=0; /* return value (logging flags) */
uint32_t state = xfs_bmap_fork_to_state(whichfork);
xfs_filblks_t da_new; /* new count del alloc blocks used */
xfs_filblks_t da_old; /* old count del alloc blocks used */
xfs_filblks_t temp=0; /* value for da_new calculations */ int tmp_rval; /* partial logging flags */ struct xfs_bmbt_irec old;
#define LEFT r[0] #define RIGHT r[1] #define PREV r[2]
/* * Set up a bunch of variables to make the tests simpler.
*/
xfs_iext_get_extent(ifp, &bma->icur, &PREV);
new_endoff = new->br_startoff + new->br_blockcount;
ASSERT(isnullstartblock(PREV.br_startblock));
ASSERT(PREV.br_startoff <= new->br_startoff);
ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
/* * Set flags determining what part of the previous delayed allocation * extent is being replaced by a real allocation.
*/ if (PREV.br_startoff == new->br_startoff)
state |= BMAP_LEFT_FILLING; if (PREV.br_startoff + PREV.br_blockcount == new_endoff)
state |= BMAP_RIGHT_FILLING;
/* * Check and set flags if this segment has a left neighbor. * Don't set contiguous if the combined extent would be too large.
*/ if (xfs_iext_peek_prev_extent(ifp, &bma->icur, &LEFT)) {
state |= BMAP_LEFT_VALID; if (isnullstartblock(LEFT.br_startblock))
state |= BMAP_LEFT_DELAY;
}
/* * Check and set flags if this segment has a right neighbor. * Don't set contiguous if the combined extent would be too large. * Also check for all-three-contiguous being too large.
*/ if (xfs_iext_peek_next_extent(ifp, &bma->icur, &RIGHT)) {
state |= BMAP_RIGHT_VALID; if (isnullstartblock(RIGHT.br_startblock))
state |= BMAP_RIGHT_DELAY;
}
error = 0; /* * Switch out based on the FILLING and CONTIG state bits.
*/ switch (state & (BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG)) { case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: /* * Filling in all of a previously delayed allocation extent. * The left and right neighbors are both contiguous with new.
*/
LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
if (bma->cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_btree_delete(bma->cur, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_btree_decrement(bma->cur, 0, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(bma->cur, &LEFT); if (error) goto done;
}
ASSERT(da_new <= da_old); break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: /* * Filling in all of a previously delayed allocation extent. * The left neighbor is contiguous, the right is not.
*/
old = LEFT;
LEFT.br_blockcount += PREV.br_blockcount;
if (bma->cur == NULL)
rval = XFS_ILOG_DEXT; else {
rval = 0;
error = xfs_bmbt_lookup_eq(bma->cur, &old, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(bma->cur, &LEFT); if (error) goto done;
}
ASSERT(da_new <= da_old); break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: /* * Filling in all of a previously delayed allocation extent. * The right neighbor is contiguous, the left is not. Take care * with delay -> unwritten extent allocation here because the * delalloc record we are overwriting is always written.
*/
PREV.br_startblock = new->br_startblock;
PREV.br_blockcount += RIGHT.br_blockcount;
PREV.br_state = new->br_state;
if (bma->cur == NULL)
rval = XFS_ILOG_DEXT; else {
rval = 0;
error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(bma->cur, &PREV); if (error) goto done;
}
ASSERT(da_new <= da_old); break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: /* * Filling in all of a previously delayed allocation extent. * Neither the left nor right neighbors are contiguous with * the new one.
*/
PREV.br_startblock = new->br_startblock;
PREV.br_state = new->br_state;
xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV);
ifp->if_nextents++;
if (bma->cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(bma->cur, new, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 0)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_btree_insert(bma->cur, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
}
ASSERT(da_new <= da_old); break;
case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG: /* * Filling in the first part of a previous delayed allocation. * The left neighbor is contiguous.
*/
old = LEFT;
temp = PREV.br_blockcount - new->br_blockcount;
da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
startblockval(PREV.br_startblock));
if (bma->cur == NULL)
rval = XFS_ILOG_DEXT; else {
rval = 0;
error = xfs_bmbt_lookup_eq(bma->cur, &old, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(bma->cur, &LEFT); if (error) goto done;
}
ASSERT(da_new <= da_old); break;
case BMAP_LEFT_FILLING: /* * Filling in the first part of a previous delayed allocation. * The left neighbor is not contiguous.
*/
xfs_iext_update_extent(bma->ip, state, &bma->icur, new);
ifp->if_nextents++;
if (bma->cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(bma->cur, new, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 0)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_btree_insert(bma->cur, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
}
if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
&bma->cur, 1, &tmp_rval, whichfork);
rval |= tmp_rval; if (error) goto done;
}
case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: /* * Filling in the last part of a previous delayed allocation. * The right neighbor is contiguous with the new allocation.
*/
old = RIGHT;
RIGHT.br_startoff = new->br_startoff;
RIGHT.br_startblock = new->br_startblock;
RIGHT.br_blockcount += new->br_blockcount;
if (bma->cur == NULL)
rval = XFS_ILOG_DEXT; else {
rval = 0;
error = xfs_bmbt_lookup_eq(bma->cur, &old, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(bma->cur, &RIGHT); if (error) goto done;
}
case BMAP_RIGHT_FILLING: /* * Filling in the last part of a previous delayed allocation. * The right neighbor is not contiguous.
*/
xfs_iext_update_extent(bma->ip, state, &bma->icur, new);
ifp->if_nextents++;
if (bma->cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(bma->cur, new, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 0)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_btree_insert(bma->cur, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(bma->cur);
error = -EFSCORRUPTED; goto done;
}
}
if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
&bma->cur, 1, &tmp_rval, whichfork);
rval |= tmp_rval; if (error) goto done;
}
case 0: /* * Filling in the middle part of a previous delayed allocation. * Contiguity is impossible here. * This case is avoided almost all the time. * * We start with a delayed allocation: * * +ddddddddddddddddddddddddddddddddddddddddddddddddddddddd+ * PREV @ idx * * and we are allocating: * +rrrrrrrrrrrrrrrrr+ * new * * and we set it up for insertion as: * +ddddddddddddddddddd+rrrrrrrrrrrrrrrrr+ddddddddddddddddd+ * new * PREV @ idx LEFT RIGHT * inserted at idx + 1
*/
old = PREV;
/* LEFT is the new middle */
LEFT = *new;
/* RIGHT is the new right */
RIGHT.br_state = PREV.br_state;
RIGHT.br_startoff = new_endoff;
RIGHT.br_blockcount =
PREV.br_startoff + PREV.br_blockcount - new_endoff;
RIGHT.br_startblock =
nullstartblock(xfs_bmap_worst_indlen(bma->ip,
RIGHT.br_blockcount));
case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: case BMAP_LEFT_FILLING | BMAP_RIGHT_CONTIG: case BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: case BMAP_LEFT_CONTIG: case BMAP_RIGHT_CONTIG: /* * These cases are all impossible.
*/
ASSERT(0);
}
/* add reverse mapping unless caller opted out */ if (!(bma->flags & XFS_BMAPI_NORMAP))
xfs_rmap_map_extent(bma->tp, bma->ip, whichfork, new);
/* convert to a btree if necessary */ if (xfs_bmap_needs_btree(bma->ip, whichfork)) { int tmp_logflags; /* partial log flag return val */
if (da_new != da_old)
xfs_mod_delalloc(bma->ip, 0, (int64_t)da_new - da_old);
if (bma->cur) {
da_new += bma->cur->bc_bmap.allocated;
bma->cur->bc_bmap.allocated = 0;
}
/* adjust for changes in reserved delayed indirect blocks */ if (da_new < da_old)
xfs_add_fdblocks(mp, da_old - da_new); elseif (da_new > da_old)
error = xfs_dec_fdblocks(mp, da_new - da_old, true);
xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
done: if (whichfork != XFS_COW_FORK)
bma->logflags |= rval; return error; #undef LEFT #undef RIGHT #undef PREV
}
/* * Convert an unwritten allocation to a real allocation or vice versa.
*/ int/* error */
xfs_bmap_add_extent_unwritten_real( struct xfs_trans *tp,
xfs_inode_t *ip, /* incore inode pointer */ int whichfork, struct xfs_iext_cursor *icur, struct xfs_btree_cur **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp) /* inode logging flags */
{ struct xfs_btree_cur *cur; /* btree cursor */ int error; /* error return value */ int i; /* temp state */ struct xfs_ifork *ifp; /* inode fork pointer */
xfs_fileoff_t new_endoff; /* end offset of new entry */
xfs_bmbt_irec_t r[3]; /* neighbor extent entries */ /* left is 0, right is 1, prev is 2 */ int rval=0; /* return value (logging flags) */
uint32_t state = xfs_bmap_fork_to_state(whichfork); struct xfs_mount *mp = ip->i_mount; struct xfs_bmbt_irec old;
*logflagsp = 0;
cur = *curp;
ifp = xfs_ifork_ptr(ip, whichfork);
ASSERT(!isnullstartblock(new->br_startblock));
XFS_STATS_INC(mp, xs_add_exlist);
#define LEFT r[0] #define RIGHT r[1] #define PREV r[2]
/* * Set up a bunch of variables to make the tests simpler.
*/
error = 0;
xfs_iext_get_extent(ifp, icur, &PREV);
ASSERT(new->br_state != PREV.br_state);
new_endoff = new->br_startoff + new->br_blockcount;
ASSERT(PREV.br_startoff <= new->br_startoff);
ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
/* * Set flags determining what part of the previous oldext allocation * extent is being replaced by a newext allocation.
*/ if (PREV.br_startoff == new->br_startoff)
state |= BMAP_LEFT_FILLING; if (PREV.br_startoff + PREV.br_blockcount == new_endoff)
state |= BMAP_RIGHT_FILLING;
/* * Check and set flags if this segment has a left neighbor. * Don't set contiguous if the combined extent would be too large.
*/ if (xfs_iext_peek_prev_extent(ifp, icur, &LEFT)) {
state |= BMAP_LEFT_VALID; if (isnullstartblock(LEFT.br_startblock))
state |= BMAP_LEFT_DELAY;
}
/* * Check and set flags if this segment has a right neighbor. * Don't set contiguous if the combined extent would be too large. * Also check for all-three-contiguous being too large.
*/ if (xfs_iext_peek_next_extent(ifp, icur, &RIGHT)) {
state |= BMAP_RIGHT_VALID; if (isnullstartblock(RIGHT.br_startblock))
state |= BMAP_RIGHT_DELAY;
}
/* * Switch out based on the FILLING and CONTIG state bits.
*/ switch (state & (BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG)) { case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG |
BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: /* * Setting all of a previous oldext extent to newext. * The left and right neighbors are both contiguous with new.
*/
LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
xfs_iext_remove(ip, icur, state);
xfs_iext_remove(ip, icur, state);
xfs_iext_prev(ifp, icur);
xfs_iext_update_extent(ip, state, icur, &LEFT);
ifp->if_nextents -= 2; if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_delete(cur, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_decrement(cur, 0, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_delete(cur, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_decrement(cur, 0, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(cur, &LEFT); if (error) goto done;
} break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: /* * Setting all of a previous oldext extent to newext. * The left neighbor is contiguous, the right is not.
*/
LEFT.br_blockcount += PREV.br_blockcount;
xfs_iext_remove(ip, icur, state);
xfs_iext_prev(ifp, icur);
xfs_iext_update_extent(ip, state, icur, &LEFT);
ifp->if_nextents--; if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(cur, &PREV, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_delete(cur, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_decrement(cur, 0, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(cur, &LEFT); if (error) goto done;
} break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: /* * Setting all of a previous oldext extent to newext. * The right neighbor is contiguous, the left is not.
*/
PREV.br_blockcount += RIGHT.br_blockcount;
PREV.br_state = new->br_state;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_delete(cur, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
} if ((error = xfs_btree_decrement(cur, 0, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(cur, &PREV); if (error) goto done;
} break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: /* * Setting all of a previous oldext extent to newext. * Neither the left nor right neighbors are contiguous with * the new one.
*/
PREV.br_state = new->br_state;
xfs_iext_update_extent(ip, state, icur, &PREV);
if (cur == NULL)
rval = XFS_ILOG_DEXT; else {
rval = 0;
error = xfs_bmbt_lookup_eq(cur, new, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(cur, &PREV); if (error) goto done;
} break;
case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG: /* * Setting the first part of a previous oldext extent to newext. * The left neighbor is contiguous.
*/
LEFT.br_blockcount += new->br_blockcount;
if (cur == NULL)
rval = XFS_ILOG_DEXT; else {
rval = 0;
error = xfs_bmbt_lookup_eq(cur, &old, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(cur, &PREV); if (error) goto done;
error = xfs_btree_decrement(cur, 0, &i); if (error) goto done;
error = xfs_bmbt_update(cur, &LEFT); if (error) goto done;
} break;
case BMAP_LEFT_FILLING: /* * Setting the first part of a previous oldext extent to newext. * The left neighbor is not contiguous.
*/
old = PREV;
PREV.br_startoff += new->br_blockcount;
PREV.br_startblock += new->br_blockcount;
PREV.br_blockcount -= new->br_blockcount;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; else {
rval = XFS_ILOG_CORE;
error = xfs_bmbt_lookup_eq(cur, &old, &i); if (error) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
}
error = xfs_bmbt_update(cur, &PREV); if (error) goto done;
cur->bc_rec.b = *new; if ((error = xfs_btree_insert(cur, &i))) goto done; if (XFS_IS_CORRUPT(mp, i != 1)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED; goto done;
}
} break;
case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: /* * Setting the last part of a previous oldext extent to newext. * The right neighbor is contiguous with the new allocation.
*/
old = PREV;
PREV.br_blockcount -= new->br_blockcount;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.