Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20f7e9f3 authored by Dave Chinner's avatar Dave Chinner Committed by Ben Myers
Browse files

xfs: factor dir2 block read operations



In preparation for verifying dir2 block format buffers, factor
the read operations out of the block operations (lookup, addname,
getdents) and some of the additional logic to make it easier to
understand an dmodify the code.

Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarBen Myers <bpm@sgi.com>
Signed-off-by: default avatarBen Myers <bpm@sgi.com>
parent 4bb20a83
Loading
Loading
Loading
Loading
+209 −177
Original line number Original line Diff line number Diff line
@@ -56,172 +56,253 @@ xfs_dir_startup(void)
	xfs_dir_hash_dotdot = xfs_da_hashname((unsigned char *)"..", 2);
	xfs_dir_hash_dotdot = xfs_da_hashname((unsigned char *)"..", 2);
}
}


/*
static int
 * Add an entry to a block directory.
xfs_dir2_block_read(
 */
	struct xfs_trans	*tp,
int						/* error */
	struct xfs_inode	*dp,
xfs_dir2_block_addname(
	struct xfs_buf		**bpp)
	xfs_da_args_t		*args)		/* directory op arguments */
{
{
	xfs_dir2_data_free_t	*bf;		/* bestfree table in block */
	struct xfs_mount	*mp = dp->i_mount;
	xfs_dir2_data_hdr_t	*hdr;		/* block header */
	xfs_dir2_leaf_entry_t	*blp;		/* block leaf entries */
	struct xfs_buf		*bp;		/* buffer for block */
	xfs_dir2_block_tail_t	*btp;		/* block tail */
	int			compact;	/* need to compact leaf ents */
	xfs_dir2_data_entry_t	*dep;		/* block data entry */
	xfs_inode_t		*dp;		/* directory inode */
	xfs_dir2_data_unused_t	*dup;		/* block unused entry */
	int			error;		/* error return value */
	xfs_dir2_data_unused_t	*enddup=NULL;	/* unused at end of data */
	xfs_dahash_t		hash;		/* hash value of found entry */
	int			high;		/* high index for binary srch */
	int			highstale;	/* high stale index */
	int			lfloghigh=0;	/* last final leaf to log */
	int			lfloglow=0;	/* first final leaf to log */
	int			len;		/* length of the new entry */
	int			low;		/* low index for binary srch */
	int			lowstale;	/* low stale index */
	int			mid=0;		/* midpoint for binary srch */
	xfs_mount_t		*mp;		/* filesystem mount point */
	int			needlog;	/* need to log header */
	int			needscan;	/* need to rescan freespace */
	__be16			*tagp;		/* pointer to tag value */
	xfs_trans_t		*tp;		/* transaction structure */


	trace_xfs_dir2_block_addname(args);
	return xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, bpp,
					XFS_DATA_FORK, NULL);
}

static void
xfs_dir2_block_need_space(
	struct xfs_dir2_data_hdr	*hdr,
	struct xfs_dir2_block_tail	*btp,
	struct xfs_dir2_leaf_entry	*blp,
	__be16				**tagpp,
	struct xfs_dir2_data_unused	**dupp,
	struct xfs_dir2_data_unused	**enddupp,
	int				*compact,
	int				len)
{
	struct xfs_dir2_data_free	*bf;
	__be16				*tagp = NULL;
	struct xfs_dir2_data_unused	*dup = NULL;
	struct xfs_dir2_data_unused	*enddup = NULL;

	*compact = 0;
	bf = hdr->bestfree;


	dp = args->dp;
	tp = args->trans;
	mp = dp->i_mount;
	/*
	/*
	 * Read the (one and only) directory block into dabuf bp.
	 * If there are stale entries we'll use one for the leaf.
	 */
	 */
	error = xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp,
	if (btp->stale) {
				XFS_DATA_FORK, NULL);
		if (be16_to_cpu(bf[0].length) >= len) {
	if (error)
		return error;
	ASSERT(bp != NULL);
	hdr = bp->b_addr;
			/*
			/*
	 * Check the magic number, corrupted if wrong.
			 * The biggest entry enough to avoid compaction.
			 */
			 */
	if (unlikely(hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC))) {
			dup = (xfs_dir2_data_unused_t *)
		XFS_CORRUPTION_ERROR("xfs_dir2_block_addname",
			      ((char *)hdr + be16_to_cpu(bf[0].offset));
				     XFS_ERRLEVEL_LOW, mp, hdr);
			goto out;
		xfs_trans_brelse(tp, bp);
		return XFS_ERROR(EFSCORRUPTED);
		}
		}
	len = xfs_dir2_data_entsize(args->namelen);

		/*
		/*
	 * Set up pointers to parts of the block.
		 * Will need to compact to make this work.
		 * Tag just before the first leaf entry.
		 */
		 */
	bf = hdr->bestfree;
		*compact = 1;
	btp = xfs_dir2_block_tail_p(mp, hdr);
		tagp = (__be16 *)blp - 1;
	blp = xfs_dir2_block_leaf_p(btp);

		/* Data object just before the first leaf entry.  */
		dup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));

		/*
		/*
	 * No stale entries?  Need space for entry and new leaf.
		 * If it's not free then the data will go where the
		 * leaf data starts now, if it works at all.
		 */
		 */
	if (!btp->stale) {
		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
			if (be16_to_cpu(dup->length) + (be32_to_cpu(btp->stale) - 1) *
			    (uint)sizeof(*blp) < len)
				dup = NULL;
		} else if ((be32_to_cpu(btp->stale) - 1) * (uint)sizeof(*blp) < len)
			dup = NULL;
		else
			dup = (xfs_dir2_data_unused_t *)blp;
		goto out;
	}

	/*
	/*
	 * no stale entries, so just use free space.
	 * Tag just before the first leaf entry.
	 * Tag just before the first leaf entry.
	 */
	 */
	tagp = (__be16 *)blp - 1;
	tagp = (__be16 *)blp - 1;
		/*

		 * Data object just before the first leaf entry.
	/* Data object just before the first leaf entry.  */
		 */
	enddup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));
	enddup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));

	/*
	/*
	 * If it's not free then can't do this add without cleaning up:
	 * If it's not free then can't do this add without cleaning up:
	 * the space before the first leaf entry needs to be free so it
	 * the space before the first leaf entry needs to be free so it
	 * can be expanded to hold the pointer to the new entry.
	 * can be expanded to hold the pointer to the new entry.
	 */
	 */
		if (be16_to_cpu(enddup->freetag) != XFS_DIR2_DATA_FREE_TAG)
	if (be16_to_cpu(enddup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
			dup = enddup = NULL;
		/*
		/*
		 * Check out the biggest freespace and see if it's the same one.
		 * Check out the biggest freespace and see if it's the same one.
		 */
		 */
		else {
		dup = (xfs_dir2_data_unused_t *)
		dup = (xfs_dir2_data_unused_t *)
		      ((char *)hdr + be16_to_cpu(bf[0].offset));
		      ((char *)hdr + be16_to_cpu(bf[0].offset));
			if (dup == enddup) {
		if (dup != enddup) {
			/*
			/*
				 * It is the biggest freespace, is it too small
			 * Not the same free entry, just check its length.
				 * to hold the new leaf too?
			 */
			if (be16_to_cpu(dup->length) < len)
				dup = NULL;
			goto out;
		}

		/*
		 * It is the biggest freespace, can it hold the leaf too?
		 */
		 */
		if (be16_to_cpu(dup->length) < len + (uint)sizeof(*blp)) {
		if (be16_to_cpu(dup->length) < len + (uint)sizeof(*blp)) {
			/*
			/*
					 * Yes, we use the second-largest
			 * Yes, use the second-largest entry instead if it works.
					 * entry instead if it works.
			 */
			 */
			if (be16_to_cpu(bf[1].length) >= len)
			if (be16_to_cpu(bf[1].length) >= len)
				dup = (xfs_dir2_data_unused_t *)
				dup = (xfs_dir2_data_unused_t *)
						      ((char *)hdr +
				      ((char *)hdr + be16_to_cpu(bf[1].offset));
						       be16_to_cpu(bf[1].offset));
			else
			else
				dup = NULL;
				dup = NULL;
		}
		}
			} else {
	}
out:
	*tagpp = tagp;
	*dupp = dup;
	*enddupp = enddup;
}

/*
/*
				 * Not the same free entry,
 * compact the leaf entries.
				 * just check its length.
 * Leave the highest-numbered stale entry stale.
 * XXX should be the one closest to mid but mid is not yet computed.
 */
 */
				if (be16_to_cpu(dup->length) < len) {
static void
					dup = NULL;
xfs_dir2_block_compact(
				}
	struct xfs_trans		*tp,
	struct xfs_buf			*bp,
	struct xfs_dir2_data_hdr	*hdr,
	struct xfs_dir2_block_tail	*btp,
	struct xfs_dir2_leaf_entry	*blp,
	int				*needlog,
	int				*lfloghigh,
	int				*lfloglow)
{
	int			fromidx;	/* source leaf index */
	int			toidx;		/* target leaf index */
	int			needscan = 0;
	int			highstale;	/* high stale index */

	fromidx = toidx = be32_to_cpu(btp->count) - 1;
	highstale = *lfloghigh = -1;
	for (; fromidx >= 0; fromidx--) {
		if (blp[fromidx].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) {
			if (highstale == -1)
				highstale = toidx;
			else {
				if (*lfloghigh == -1)
					*lfloghigh = toidx;
				continue;
			}
			}
		}
		}
		compact = 0;
		if (fromidx < toidx)
			blp[toidx] = blp[fromidx];
		toidx--;
	}
	}
	*lfloglow = toidx + 1 - (be32_to_cpu(btp->stale) - 1);
	*lfloghigh -= be32_to_cpu(btp->stale) - 1;
	be32_add_cpu(&btp->count, -(be32_to_cpu(btp->stale) - 1));
	xfs_dir2_data_make_free(tp, bp,
		(xfs_dir2_data_aoff_t)((char *)blp - (char *)hdr),
		(xfs_dir2_data_aoff_t)((be32_to_cpu(btp->stale) - 1) * sizeof(*blp)),
		needlog, &needscan);
	blp += be32_to_cpu(btp->stale) - 1;
	btp->stale = cpu_to_be32(1);
	/*
	/*
	 * If there are stale entries we'll use one for the leaf.
	 * If we now need to rebuild the bestfree map, do so.
	 * Is the biggest entry enough to avoid compaction?
	 * This needs to happen before the next call to use_free.
	 */
	 */
	else if (be16_to_cpu(bf[0].length) >= len) {
	if (needscan)
		dup = (xfs_dir2_data_unused_t *)
		xfs_dir2_data_freescan(tp->t_mountp, hdr, needlog);
		      ((char *)hdr + be16_to_cpu(bf[0].offset));
		compact = 0;
}
}

/*
/*
	 * Will need to compact to make this work.
 * Add an entry to a block directory.
	 */
	else {
		/*
		 * Tag just before the first leaf entry.
 */
 */
		tagp = (__be16 *)blp - 1;
int						/* error */
xfs_dir2_block_addname(
	xfs_da_args_t		*args)		/* directory op arguments */
{
	xfs_dir2_data_hdr_t	*hdr;		/* block header */
	xfs_dir2_leaf_entry_t	*blp;		/* block leaf entries */
	struct xfs_buf		*bp;		/* buffer for block */
	xfs_dir2_block_tail_t	*btp;		/* block tail */
	int			compact;	/* need to compact leaf ents */
	xfs_dir2_data_entry_t	*dep;		/* block data entry */
	xfs_inode_t		*dp;		/* directory inode */
	xfs_dir2_data_unused_t	*dup;		/* block unused entry */
	int			error;		/* error return value */
	xfs_dir2_data_unused_t	*enddup=NULL;	/* unused at end of data */
	xfs_dahash_t		hash;		/* hash value of found entry */
	int			high;		/* high index for binary srch */
	int			highstale;	/* high stale index */
	int			lfloghigh=0;	/* last final leaf to log */
	int			lfloglow=0;	/* first final leaf to log */
	int			len;		/* length of the new entry */
	int			low;		/* low index for binary srch */
	int			lowstale;	/* low stale index */
	int			mid=0;		/* midpoint for binary srch */
	xfs_mount_t		*mp;		/* filesystem mount point */
	int			needlog;	/* need to log header */
	int			needscan;	/* need to rescan freespace */
	__be16			*tagp;		/* pointer to tag value */
	xfs_trans_t		*tp;		/* transaction structure */

	trace_xfs_dir2_block_addname(args);

	dp = args->dp;
	tp = args->trans;
	mp = dp->i_mount;

	/* Read the (one and only) directory block into bp. */
	error = xfs_dir2_block_read(tp, dp, &bp);
	if (error)
		return error;

	len = xfs_dir2_data_entsize(args->namelen);

	/*
	/*
		 * Data object just before the first leaf entry.
	 * Set up pointers to parts of the block.
	 */
	 */
		dup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));
	hdr = bp->b_addr;
	btp = xfs_dir2_block_tail_p(mp, hdr);
	blp = xfs_dir2_block_leaf_p(btp);

	/*
	/*
		 * If it's not free then the data will go where the
	 * Find out if we can reuse stale entries or whether we need extra
		 * leaf data starts now, if it works at all.
	 * space for entry and new leaf.
	 */
	 */
		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
	xfs_dir2_block_need_space(hdr, btp, blp, &tagp, &dup,
			if (be16_to_cpu(dup->length) + (be32_to_cpu(btp->stale) - 1) *
				  &enddup, &compact, len);
			    (uint)sizeof(*blp) < len)

				dup = NULL;
		} else if ((be32_to_cpu(btp->stale) - 1) * (uint)sizeof(*blp) < len)
			dup = NULL;
		else
			dup = (xfs_dir2_data_unused_t *)blp;
		compact = 1;
	}
	/*
	/*
	 * If this isn't a real add, we're done with the buffer.
	 * Done everything we need for a space check now.
	 */
	 */
	if (args->op_flags & XFS_DA_OP_JUSTCHECK)
	if (args->op_flags & XFS_DA_OP_JUSTCHECK) {
		xfs_trans_brelse(tp, bp);
		xfs_trans_brelse(tp, bp);
		if (!dup)
			return XFS_ERROR(ENOSPC);
		return 0;
	}

	/*
	/*
	 * If we don't have space for the new entry & leaf ...
	 * If we don't have space for the new entry & leaf ...
	 */
	 */
	if (!dup) {
	if (!dup) {
		/*
		/* Don't have a space reservation: return no-space.  */
		 * Not trying to actually do anything, or don't have
		if (args->total == 0)
		 * a space reservation: return no-space.
		 */
		if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
			return XFS_ERROR(ENOSPC);
			return XFS_ERROR(ENOSPC);
		/*
		/*
		 * Convert to the next larger format.
		 * Convert to the next larger format.
@@ -232,65 +313,24 @@ xfs_dir2_block_addname(
			return error;
			return error;
		return xfs_dir2_leaf_addname(args);
		return xfs_dir2_leaf_addname(args);
	}
	}
	/*

	 * Just checking, and it would work, so say so.
	 */
	if (args->op_flags & XFS_DA_OP_JUSTCHECK)
		return 0;
	needlog = needscan = 0;
	needlog = needscan = 0;
	/*
	 * If need to compact the leaf entries, do it now.
	 * Leave the highest-numbered stale entry stale.
	 * XXX should be the one closest to mid but mid is not yet computed.
	 */
	if (compact) {
		int	fromidx;		/* source leaf index */
		int	toidx;			/* target leaf index */


		for (fromidx = toidx = be32_to_cpu(btp->count) - 1,
			highstale = lfloghigh = -1;
		     fromidx >= 0;
		     fromidx--) {
			if (blp[fromidx].address ==
			    cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) {
				if (highstale == -1)
					highstale = toidx;
				else {
					if (lfloghigh == -1)
						lfloghigh = toidx;
					continue;
				}
			}
			if (fromidx < toidx)
				blp[toidx] = blp[fromidx];
			toidx--;
		}
		lfloglow = toidx + 1 - (be32_to_cpu(btp->stale) - 1);
		lfloghigh -= be32_to_cpu(btp->stale) - 1;
		be32_add_cpu(&btp->count, -(be32_to_cpu(btp->stale) - 1));
		xfs_dir2_data_make_free(tp, bp,
			(xfs_dir2_data_aoff_t)((char *)blp - (char *)hdr),
			(xfs_dir2_data_aoff_t)((be32_to_cpu(btp->stale) - 1) * sizeof(*blp)),
			&needlog, &needscan);
		blp += be32_to_cpu(btp->stale) - 1;
		btp->stale = cpu_to_be32(1);
	/*
	/*
		 * If we now need to rebuild the bestfree map, do so.
	 * If need to compact the leaf entries, do it now.
		 * This needs to happen before the next call to use_free.
	 */
	 */
		if (needscan) {
	if (compact)
			xfs_dir2_data_freescan(mp, hdr, &needlog);
		xfs_dir2_block_compact(tp, bp, hdr, btp, blp, &needlog,
			needscan = 0;
				      &lfloghigh, &lfloglow);
		}
	else if (btp->stale) {
	}
		/*
		/*
		 * Set leaf logging boundaries to impossible state.
		 * Set leaf logging boundaries to impossible state.
		 * For the no-stale case they're set explicitly.
		 * For the no-stale case they're set explicitly.
		 */
		 */
	else if (btp->stale) {
		lfloglow = be32_to_cpu(btp->count);
		lfloglow = be32_to_cpu(btp->count);
		lfloghigh = -1;
		lfloghigh = -1;
	}
	}

	/*
	/*
	 * Find the slot that's first lower than our hash value, -1 if none.
	 * Find the slot that's first lower than our hash value, -1 if none.
	 */
	 */
@@ -450,18 +490,13 @@ xfs_dir2_block_getdents(
	/*
	/*
	 * If the block number in the offset is out of range, we're done.
	 * If the block number in the offset is out of range, we're done.
	 */
	 */
	if (xfs_dir2_dataptr_to_db(mp, *offset) > mp->m_dirdatablk) {
	if (xfs_dir2_dataptr_to_db(mp, *offset) > mp->m_dirdatablk)
		return 0;
		return 0;
	}

	/*
	error = xfs_dir2_block_read(NULL, dp, &bp);
	 * Can't read the block, give up, else get dabuf in bp.
	 */
	error = xfs_da_read_buf(NULL, dp, mp->m_dirdatablk, -1,
				&bp, XFS_DATA_FORK, NULL);
	if (error)
	if (error)
		return error;
		return error;


	ASSERT(bp != NULL);
	/*
	/*
	 * Extract the byte offset we start at from the seek pointer.
	 * Extract the byte offset we start at from the seek pointer.
	 * We'll skip entries before this.
	 * We'll skip entries before this.
@@ -637,14 +672,11 @@ xfs_dir2_block_lookup_int(
	dp = args->dp;
	dp = args->dp;
	tp = args->trans;
	tp = args->trans;
	mp = dp->i_mount;
	mp = dp->i_mount;
	/*

	 * Read the buffer, return error if we can't get it.
	error = xfs_dir2_block_read(tp, dp, &bp);
	 */
	error = xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp,
				XFS_DATA_FORK, NULL);
	if (error)
	if (error)
		return error;
		return error;
	ASSERT(bp != NULL);

	hdr = bp->b_addr;
	hdr = bp->b_addr;
	xfs_dir2_data_check(dp, bp);
	xfs_dir2_data_check(dp, bp);
	btp = xfs_dir2_block_tail_p(mp, hdr);
	btp = xfs_dir2_block_tail_p(mp, hdr);