diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_dir2_node.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_dir2_node.c | 678 | 
1 files changed, 323 insertions, 355 deletions
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c index 1fc44efc344d..705c4f562758 100644 --- a/fs/xfs/libxfs/xfs_dir2_node.c +++ b/fs/xfs/libxfs/xfs_dir2_node.c @@ -32,8 +32,6 @@ static void xfs_dir2_leafn_rebalance(xfs_da_state_t *state,  static int xfs_dir2_leafn_remove(xfs_da_args_t *args, struct xfs_buf *bp,  				 int index, xfs_da_state_blk_t *dblk,  				 int *rval); -static int xfs_dir2_node_addname_int(xfs_da_args_t *args, -				     xfs_da_state_blk_t *fblk);  /*   * Check internal consistency of a leafn block. @@ -1611,113 +1609,152 @@ xfs_dir2_leafn_unbalance(  }  /* - * Top-level node form directory addname routine. + * Add a new data block to the directory at the free space index that the caller + * has specified.   */ -int						/* error */ -xfs_dir2_node_addname( -	xfs_da_args_t		*args)		/* operation arguments */ +static int +xfs_dir2_node_add_datablk( +	struct xfs_da_args	*args, +	struct xfs_da_state_blk	*fblk, +	xfs_dir2_db_t		*dbno, +	struct xfs_buf		**dbpp, +	struct xfs_buf		**fbpp, +	int			*findex)  { -	xfs_da_state_blk_t	*blk;		/* leaf block for insert */ -	int			error;		/* error return value */ -	int			rval;		/* sub-return value */ -	xfs_da_state_t		*state;		/* btree cursor */ +	struct xfs_inode	*dp = args->dp; +	struct xfs_trans	*tp = args->trans; +	struct xfs_mount	*mp = dp->i_mount; +	struct xfs_dir3_icfree_hdr freehdr; +	struct xfs_dir2_data_free *bf; +	struct xfs_dir2_data_hdr *hdr; +	struct xfs_dir2_free	*free = NULL; +	xfs_dir2_db_t		fbno; +	struct xfs_buf		*fbp; +	struct xfs_buf		*dbp; +	__be16			*bests = NULL; +	int			error; -	trace_xfs_dir2_node_addname(args); +	/* Not allowed to allocate, return failure. */ +	if (args->total == 0) +		return -ENOSPC; + +	/* Allocate and initialize the new data block.  */ +	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, dbno); +	if (error) +		return error; +	error = xfs_dir3_data_init(args, *dbno, &dbp); +	if (error) +		return error;  	/* -	 * Allocate and initialize the state (btree cursor). -	 */ -	state = xfs_da_state_alloc(); -	state->args = args; -	state->mp = args->dp->i_mount; -	/* -	 * Look up the name.  We're not supposed to find it, but -	 * this gives us the insertion point. +	 * Get the freespace block corresponding to the data block +	 * that was just allocated.  	 */ -	error = xfs_da3_node_lookup_int(state, &rval); +	fbno = dp->d_ops->db_to_fdb(args->geo, *dbno); +	error = xfs_dir2_free_try_read(tp, dp, +			       xfs_dir2_db_to_da(args->geo, fbno), &fbp);  	if (error) -		rval = error; -	if (rval != -ENOENT) { -		goto done; -	} +		return error; +  	/* -	 * Add the data entry to a data block. -	 * Extravalid is set to a freeblock found by lookup. +	 * If there wasn't a freespace block, the read will +	 * return a NULL fbp.  Allocate and initialize a new one.  	 */ -	rval = xfs_dir2_node_addname_int(args, -		state->extravalid ? &state->extrablk : NULL); -	if (rval) { -		goto done; +	if (!fbp) { +		error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE, &fbno); +		if (error) +			return error; + +		if (dp->d_ops->db_to_fdb(args->geo, *dbno) != fbno) { +			xfs_alert(mp, +"%s: dir ino %llu needed freesp block %lld for data block %lld, got %lld", +				__func__, (unsigned long long)dp->i_ino, +				(long long)dp->d_ops->db_to_fdb(args->geo, *dbno), +				(long long)*dbno, (long long)fbno); +			if (fblk) { +				xfs_alert(mp, +			" fblk "PTR_FMT" blkno %llu index %d magic 0x%x", +					fblk, (unsigned long long)fblk->blkno, +					fblk->index, fblk->magic); +			} else { +				xfs_alert(mp, " ... fblk is NULL"); +			} +			XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); +			return -EFSCORRUPTED; +		} + +		/* Get a buffer for the new block. */ +		error = xfs_dir3_free_get_buf(args, fbno, &fbp); +		if (error) +			return error; +		free = fbp->b_addr; +		bests = dp->d_ops->free_bests_p(free); +		dp->d_ops->free_hdr_from_disk(&freehdr, free); + +		/* Remember the first slot as our empty slot. */ +		freehdr.firstdb = (fbno - xfs_dir2_byte_to_db(args->geo, +							XFS_DIR2_FREE_OFFSET)) * +				dp->d_ops->free_max_bests(args->geo); +	} else { +		free = fbp->b_addr; +		bests = dp->d_ops->free_bests_p(free); +		dp->d_ops->free_hdr_from_disk(&freehdr, free);  	} -	blk = &state->path.blk[state->path.active - 1]; -	ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC); + +	/* Set the freespace block index from the data block number. */ +	*findex = dp->d_ops->db_to_fdindex(args->geo, *dbno); + +	/* Extend the freespace table if the new data block is off the end. */ +	if (*findex >= freehdr.nvalid) { +		ASSERT(*findex < dp->d_ops->free_max_bests(args->geo)); +		freehdr.nvalid = *findex + 1; +		bests[*findex] = cpu_to_be16(NULLDATAOFF); +	} +  	/* -	 * Add the new leaf entry. +	 * If this entry was for an empty data block (this should always be +	 * true) then update the header.  	 */ -	rval = xfs_dir2_leafn_add(blk->bp, args, blk->index); -	if (rval == 0) { -		/* -		 * It worked, fix the hash values up the btree. -		 */ -		if (!(args->op_flags & XFS_DA_OP_JUSTCHECK)) -			xfs_da3_fixhashpath(state, &state->path); -	} else { -		/* -		 * It didn't work, we need to split the leaf block. -		 */ -		if (args->total == 0) { -			ASSERT(rval == -ENOSPC); -			goto done; -		} -		/* -		 * Split the leaf block and insert the new entry. -		 */ -		rval = xfs_da3_split(state); +	if (bests[*findex] == cpu_to_be16(NULLDATAOFF)) { +		freehdr.nused++; +		dp->d_ops->free_hdr_to_disk(fbp->b_addr, &freehdr); +		xfs_dir2_free_log_header(args, fbp);  	} -done: -	xfs_da_state_free(state); -	return rval; + +	/* Update the freespace value for the new block in the table. */ +	hdr = dbp->b_addr; +	bf = dp->d_ops->data_bestfree_p(hdr); +	bests[*findex] = bf[0].length; + +	*dbpp = dbp; +	*fbpp = fbp; +	return 0;  } -/* - * Add the data entry for a node-format directory name addition. - * The leaf entry is added in xfs_dir2_leafn_add. - * We may enter with a freespace block that the lookup found. - */ -static int					/* error */ -xfs_dir2_node_addname_int( -	xfs_da_args_t		*args,		/* operation arguments */ -	xfs_da_state_blk_t	*fblk)		/* optional freespace block */ +static int +xfs_dir2_node_find_freeblk( +	struct xfs_da_args	*args, +	struct xfs_da_state_blk	*fblk, +	xfs_dir2_db_t		*dbnop, +	struct xfs_buf		**fbpp, +	int			*findexp, +	int			length)  { -	xfs_dir2_data_hdr_t	*hdr;		/* data block header */ -	xfs_dir2_db_t		dbno;		/* data block number */ -	struct xfs_buf		*dbp;		/* data block buffer */ -	xfs_dir2_data_entry_t	*dep;		/* data entry pointer */ -	xfs_inode_t		*dp;		/* incore directory inode */ -	xfs_dir2_data_unused_t	*dup;		/* data unused entry pointer */ -	int			error;		/* error return value */ -	xfs_dir2_db_t		fbno;		/* freespace block number */ -	struct xfs_buf		*fbp;		/* freespace buffer */ -	int			findex;		/* freespace entry index */ -	xfs_dir2_free_t		*free=NULL;	/* freespace block structure */ -	xfs_dir2_db_t		ifbno;		/* initial freespace block no */ -	xfs_dir2_db_t		lastfbno=0;	/* highest freespace block no */ -	int			length;		/* length of the new entry */ -	int			logfree;	/* need to log free entry */ -	xfs_mount_t		*mp;		/* filesystem mount point */ -	int			needlog;	/* need to log data header */ -	int			needscan;	/* need to rescan data frees */ -	__be16			*tagp;		/* data entry tag pointer */ -	xfs_trans_t		*tp;		/* transaction pointer */ -	__be16			*bests;  	struct xfs_dir3_icfree_hdr freehdr; -	struct xfs_dir2_data_free *bf; -	xfs_dir2_data_aoff_t	aoff; +	struct xfs_dir2_free	*free = NULL; +	struct xfs_inode	*dp = args->dp; +	struct xfs_trans	*tp = args->trans; +	struct xfs_buf		*fbp = NULL; +	xfs_dir2_db_t		firstfbno; +	xfs_dir2_db_t		lastfbno; +	xfs_dir2_db_t		ifbno = -1; +	xfs_dir2_db_t		dbno = -1; +	xfs_dir2_db_t		fbno; +	xfs_fileoff_t		fo; +	__be16			*bests = NULL; +	int			findex = 0; +	int			error; -	dp = args->dp; -	mp = dp->i_mount; -	tp = args->trans; -	length = dp->d_ops->data_entsize(args->namelen);  	/*  	 * If we came in with a freespace block that means that lookup  	 * found an entry with our hash value.  This is the freespace @@ -1725,288 +1762,157 @@ xfs_dir2_node_addname_int(  	 */  	if (fblk) {  		fbp = fblk->bp; -		/* -		 * Remember initial freespace block number. -		 */ -		ifbno = fblk->blkno;  		free = fbp->b_addr;  		findex = fblk->index; -		bests = dp->d_ops->free_bests_p(free); -		dp->d_ops->free_hdr_from_disk(&freehdr, free); - -		/* -		 * This means the free entry showed that the data block had -		 * space for our entry, so we remembered it. -		 * Use that data block. -		 */  		if (findex >= 0) { +			/* caller already found the freespace for us. */ +			bests = dp->d_ops->free_bests_p(free); +			dp->d_ops->free_hdr_from_disk(&freehdr, free); +  			ASSERT(findex < freehdr.nvalid);  			ASSERT(be16_to_cpu(bests[findex]) != NULLDATAOFF);  			ASSERT(be16_to_cpu(bests[findex]) >= length);  			dbno = freehdr.firstdb + findex; -		} else { -			/* -			 * The data block looked at didn't have enough room. -			 * We'll start at the beginning of the freespace entries. -			 */ -			dbno = -1; -			findex = 0; +			goto found_block;  		} -	} else { +  		/* -		 * Didn't come in with a freespace block, so no data block. +		 * The data block looked at didn't have enough room. +		 * We'll start at the beginning of the freespace entries.  		 */ -		ifbno = dbno = -1; +		ifbno = fblk->blkno; +		xfs_trans_brelse(tp, fbp);  		fbp = NULL; -		findex = 0; +		fblk->bp = NULL;  	}  	/* -	 * If we don't have a data block yet, we're going to scan the -	 * freespace blocks looking for one.  Figure out what the -	 * highest freespace block number is. -	 */ -	if (dbno == -1) { -		xfs_fileoff_t	fo;		/* freespace block number */ - -		if ((error = xfs_bmap_last_offset(dp, &fo, XFS_DATA_FORK))) -			return error; -		lastfbno = xfs_dir2_da_to_db(args->geo, (xfs_dablk_t)fo); -		fbno = ifbno; -	} -	/* -	 * While we haven't identified a data block, search the freeblock -	 * data for a good data block.  If we find a null freeblock entry, -	 * indicating a hole in the data blocks, remember that. +	 * If we don't have a data block yet, we're going to scan the freespace +	 * data for a data block with enough free space in it.  	 */ -	while (dbno == -1) { -		/* -		 * If we don't have a freeblock in hand, get the next one. -		 */ -		if (fbp == NULL) { -			/* -			 * Happens the first time through unless lookup gave -			 * us a freespace block to start with. -			 */ -			if (++fbno == 0) -				fbno = xfs_dir2_byte_to_db(args->geo, -							XFS_DIR2_FREE_OFFSET); -			/* -			 * If it's ifbno we already looked at it. -			 */ -			if (fbno == ifbno) -				fbno++; -			/* -			 * If it's off the end we're done. -			 */ -			if (fbno >= lastfbno) -				break; -			/* -			 * Read the block.  There can be holes in the -			 * freespace blocks, so this might not succeed. -			 * This should be really rare, so there's no reason -			 * to avoid it. -			 */ -			error = xfs_dir2_free_try_read(tp, dp, -					xfs_dir2_db_to_da(args->geo, fbno), -					&fbp); -			if (error) -				return error; -			if (!fbp) -				continue; -			free = fbp->b_addr; -			findex = 0; -		} -		/* -		 * Look at the current free entry.  Is it good enough? -		 * -		 * The bests initialisation should be where the bufer is read in -		 * the above branch. But gcc is too stupid to realise that bests -		 * and the freehdr are actually initialised if they are placed -		 * there, so we have to do it here to avoid warnings. Blech. -		 */ -		bests = dp->d_ops->free_bests_p(free); -		dp->d_ops->free_hdr_from_disk(&freehdr, free); -		if (be16_to_cpu(bests[findex]) != NULLDATAOFF && -		    be16_to_cpu(bests[findex]) >= length) -			dbno = freehdr.firstdb + findex; -		else { -			/* -			 * Are we done with the freeblock? -			 */ -			if (++findex == freehdr.nvalid) { -				/* -				 * Drop the block. -				 */ -				xfs_trans_brelse(tp, fbp); -				fbp = NULL; -				if (fblk && fblk->bp) -					fblk->bp = NULL; -			} -		} -	} -	/* -	 * If we don't have a data block, we need to allocate one and make -	 * the freespace entries refer to it. -	 */ -	if (unlikely(dbno == -1)) { -		/* -		 * Not allowed to allocate, return failure. -		 */ -		if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0) -			return -ENOSPC; - -		/* -		 * Allocate and initialize the new data block. -		 */ -		if (unlikely((error = xfs_dir2_grow_inode(args, -							 XFS_DIR2_DATA_SPACE, -							 &dbno)) || -		    (error = xfs_dir3_data_init(args, dbno, &dbp)))) -			return error; +	error = xfs_bmap_last_offset(dp, &fo, XFS_DATA_FORK); +	if (error) +		return error; +	lastfbno = xfs_dir2_da_to_db(args->geo, (xfs_dablk_t)fo); +	firstfbno = xfs_dir2_byte_to_db(args->geo, XFS_DIR2_FREE_OFFSET); -		/* -		 * If (somehow) we have a freespace block, get rid of it. -		 */ -		if (fbp) -			xfs_trans_brelse(tp, fbp); -		if (fblk && fblk->bp) -			fblk->bp = NULL; +	for (fbno = lastfbno - 1; fbno >= firstfbno; fbno--) { +		/* If it's ifbno we already looked at it. */ +		if (fbno == ifbno) +			continue;  		/* -		 * Get the freespace block corresponding to the data block -		 * that was just allocated. +		 * Read the block.  There can be holes in the freespace blocks, +		 * so this might not succeed.  This should be really rare, so +		 * there's no reason to avoid it.  		 */ -		fbno = dp->d_ops->db_to_fdb(args->geo, dbno);  		error = xfs_dir2_free_try_read(tp, dp, -				       xfs_dir2_db_to_da(args->geo, fbno), -				       &fbp); +				xfs_dir2_db_to_da(args->geo, fbno), +				&fbp);  		if (error)  			return error; +		if (!fbp) +			continue; -		/* -		 * If there wasn't a freespace block, the read will -		 * return a NULL fbp.  Allocate and initialize a new one. -		 */ -		if (!fbp) { -			error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE, -						    &fbno); -			if (error) -				return error; +		free = fbp->b_addr; +		bests = dp->d_ops->free_bests_p(free); +		dp->d_ops->free_hdr_from_disk(&freehdr, free); -			if (dp->d_ops->db_to_fdb(args->geo, dbno) != fbno) { -				xfs_alert(mp, -"%s: dir ino %llu needed freesp block %lld for data block %lld, got %lld ifbno %llu lastfbno %d", -					__func__, (unsigned long long)dp->i_ino, -					(long long)dp->d_ops->db_to_fdb( -								args->geo, dbno), -					(long long)dbno, (long long)fbno, -					(unsigned long long)ifbno, lastfbno); -				if (fblk) { -					xfs_alert(mp, -				" fblk "PTR_FMT" blkno %llu index %d magic 0x%x", -						fblk, -						(unsigned long long)fblk->blkno, -						fblk->index, -						fblk->magic); -				} else { -					xfs_alert(mp, " ... fblk is NULL"); -				} -				XFS_ERROR_REPORT("xfs_dir2_node_addname_int", -						 XFS_ERRLEVEL_LOW, mp); -				return -EFSCORRUPTED; +		/* Scan the free entry array for a large enough free space. */ +		for (findex = freehdr.nvalid - 1; findex >= 0; findex--) { +			if (be16_to_cpu(bests[findex]) != NULLDATAOFF && +			    be16_to_cpu(bests[findex]) >= length) { +				dbno = freehdr.firstdb + findex; +				goto found_block;  			} - -			/* -			 * Get a buffer for the new block. -			 */ -			error = xfs_dir3_free_get_buf(args, fbno, &fbp); -			if (error) -				return error; -			free = fbp->b_addr; -			bests = dp->d_ops->free_bests_p(free); -			dp->d_ops->free_hdr_from_disk(&freehdr, free); - -			/* -			 * Remember the first slot as our empty slot. -			 */ -			freehdr.firstdb = -				(fbno - xfs_dir2_byte_to_db(args->geo, -							XFS_DIR2_FREE_OFFSET)) * -					dp->d_ops->free_max_bests(args->geo); -		} else { -			free = fbp->b_addr; -			bests = dp->d_ops->free_bests_p(free); -			dp->d_ops->free_hdr_from_disk(&freehdr, free);  		} -		/* -		 * Set the freespace block index from the data block number. -		 */ -		findex = dp->d_ops->db_to_fdindex(args->geo, dbno); -		/* -		 * If it's after the end of the current entries in the -		 * freespace block, extend that table. -		 */ -		if (findex >= freehdr.nvalid) { -			ASSERT(findex < dp->d_ops->free_max_bests(args->geo)); -			freehdr.nvalid = findex + 1; -			/* -			 * Tag new entry so nused will go up. -			 */ -			bests[findex] = cpu_to_be16(NULLDATAOFF); -		} -		/* -		 * If this entry was for an empty data block -		 * (this should always be true) then update the header. -		 */ -		if (bests[findex] == cpu_to_be16(NULLDATAOFF)) { -			freehdr.nused++; -			dp->d_ops->free_hdr_to_disk(fbp->b_addr, &freehdr); -			xfs_dir2_free_log_header(args, fbp); -		} -		/* -		 * Update the real value in the table. -		 * We haven't allocated the data entry yet so this will -		 * change again. -		 */ -		hdr = dbp->b_addr; -		bf = dp->d_ops->data_bestfree_p(hdr); -		bests[findex] = bf[0].length; -		logfree = 1; +		/* Didn't find free space, go on to next free block */ +		xfs_trans_brelse(tp, fbp);  	} + +found_block: +	*dbnop = dbno; +	*fbpp = fbp; +	*findexp = findex; +	return 0; +} + + +/* + * Add the data entry for a node-format directory name addition. + * The leaf entry is added in xfs_dir2_leafn_add. + * We may enter with a freespace block that the lookup found. + */ +static int +xfs_dir2_node_addname_int( +	struct xfs_da_args	*args,		/* operation arguments */ +	struct xfs_da_state_blk	*fblk)		/* optional freespace block */ +{ +	struct xfs_dir2_data_unused *dup;	/* data unused entry pointer */ +	struct xfs_dir2_data_entry *dep;	/* data entry pointer */ +	struct xfs_dir2_data_hdr *hdr;		/* data block header */ +	struct xfs_dir2_data_free *bf; +	struct xfs_dir2_free	*free = NULL;	/* freespace block structure */ +	struct xfs_trans	*tp = args->trans; +	struct xfs_inode	*dp = args->dp; +	struct xfs_buf		*dbp;		/* data block buffer */ +	struct xfs_buf		*fbp;		/* freespace buffer */ +	xfs_dir2_data_aoff_t	aoff; +	xfs_dir2_db_t		dbno;		/* data block number */ +	int			error;		/* error return value */ +	int			findex;		/* freespace entry index */ +	int			length;		/* length of the new entry */ +	int			logfree = 0;	/* need to log free entry */ +	int			needlog = 0;	/* need to log data header */ +	int			needscan = 0;	/* need to rescan data frees */ +	__be16			*tagp;		/* data entry tag pointer */ +	__be16			*bests; + +	length = dp->d_ops->data_entsize(args->namelen); +	error = xfs_dir2_node_find_freeblk(args, fblk, &dbno, &fbp, &findex, +					   length); +	if (error) +		return error; +  	/* -	 * We had a data block so we don't have to make a new one. +	 * Now we know if we must allocate blocks, so if we are checking whether +	 * we can insert without allocation then we can return now.  	 */ -	else { -		/* -		 * If just checking, we succeeded. -		 */ -		if (args->op_flags & XFS_DA_OP_JUSTCHECK) -			return 0; +	if (args->op_flags & XFS_DA_OP_JUSTCHECK) { +		if (dbno == -1) +			return -ENOSPC; +		return 0; +	} -		/* -		 * Read the data block in. -		 */ +	/* +	 * If we don't have a data block, we need to allocate one and make +	 * the freespace entries refer to it. +	 */ +	if (dbno == -1) { +		/* we're going to have to log the free block index later */ +		logfree = 1; +		error = xfs_dir2_node_add_datablk(args, fblk, &dbno, &dbp, &fbp, +						  &findex); +	} else { +		/* Read the data block in. */  		error = xfs_dir3_data_read(tp, dp,  					   xfs_dir2_db_to_da(args->geo, dbno),  					   -1, &dbp); -		if (error) -			return error; -		hdr = dbp->b_addr; -		bf = dp->d_ops->data_bestfree_p(hdr); -		logfree = 0;  	} +	if (error) +		return error; + +	/* setup for data block up now */ +	hdr = dbp->b_addr; +	bf = dp->d_ops->data_bestfree_p(hdr);  	ASSERT(be16_to_cpu(bf[0].length) >= length); -	/* -	 * Point to the existing unused space. -	 */ + +	/* Point to the existing unused space. */  	dup = (xfs_dir2_data_unused_t *)  	      ((char *)hdr + be16_to_cpu(bf[0].offset)); -	needscan = needlog = 0; -	/* -	 * Mark the first part of the unused space, inuse for us. -	 */ + +	/* Mark the first part of the unused space, inuse for us. */  	aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);  	error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,  			&needlog, &needscan); @@ -2014,9 +1920,8 @@ xfs_dir2_node_addname_int(  		xfs_trans_brelse(tp, dbp);  		return error;  	} -	/* -	 * Fill in the new entry and log it. -	 */ + +	/* Fill in the new entry and log it. */  	dep = (xfs_dir2_data_entry_t *)dup;  	dep->inumber = cpu_to_be64(args->inumber);  	dep->namelen = args->namelen; @@ -2025,38 +1930,101 @@ xfs_dir2_node_addname_int(  	tagp = dp->d_ops->data_entry_tag_p(dep);  	*tagp = cpu_to_be16((char *)dep - (char *)hdr);  	xfs_dir2_data_log_entry(args, dbp, dep); -	/* -	 * Rescan the block for bestfree if needed. -	 */ + +	/* Rescan the freespace and log the data block if needed. */  	if (needscan)  		xfs_dir2_data_freescan(dp, hdr, &needlog); -	/* -	 * Log the data block header if needed. -	 */  	if (needlog)  		xfs_dir2_data_log_header(args, dbp); -	/* -	 * If the freespace entry is now wrong, update it. -	 */ -	bests = dp->d_ops->free_bests_p(free); /* gcc is so stupid */ -	if (be16_to_cpu(bests[findex]) != be16_to_cpu(bf[0].length)) { + +	/* If the freespace block entry is now wrong, update it. */ +	free = fbp->b_addr; +	bests = dp->d_ops->free_bests_p(free); +	if (bests[findex] != bf[0].length) {  		bests[findex] = bf[0].length;  		logfree = 1;  	} -	/* -	 * Log the freespace entry if needed. -	 */ + +	/* Log the freespace entry if needed. */  	if (logfree)  		xfs_dir2_free_log_bests(args, fbp, findex, findex); -	/* -	 * Return the data block and offset in args, then drop the data block. -	 */ + +	/* Return the data block and offset in args. */  	args->blkno = (xfs_dablk_t)dbno;  	args->index = be16_to_cpu(*tagp);  	return 0;  }  /* + * Top-level node form directory addname routine. + */ +int						/* error */ +xfs_dir2_node_addname( +	xfs_da_args_t		*args)		/* operation arguments */ +{ +	xfs_da_state_blk_t	*blk;		/* leaf block for insert */ +	int			error;		/* error return value */ +	int			rval;		/* sub-return value */ +	xfs_da_state_t		*state;		/* btree cursor */ + +	trace_xfs_dir2_node_addname(args); + +	/* +	 * Allocate and initialize the state (btree cursor). +	 */ +	state = xfs_da_state_alloc(); +	state->args = args; +	state->mp = args->dp->i_mount; +	/* +	 * Look up the name.  We're not supposed to find it, but +	 * this gives us the insertion point. +	 */ +	error = xfs_da3_node_lookup_int(state, &rval); +	if (error) +		rval = error; +	if (rval != -ENOENT) { +		goto done; +	} +	/* +	 * Add the data entry to a data block. +	 * Extravalid is set to a freeblock found by lookup. +	 */ +	rval = xfs_dir2_node_addname_int(args, +		state->extravalid ? &state->extrablk : NULL); +	if (rval) { +		goto done; +	} +	blk = &state->path.blk[state->path.active - 1]; +	ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC); +	/* +	 * Add the new leaf entry. +	 */ +	rval = xfs_dir2_leafn_add(blk->bp, args, blk->index); +	if (rval == 0) { +		/* +		 * It worked, fix the hash values up the btree. +		 */ +		if (!(args->op_flags & XFS_DA_OP_JUSTCHECK)) +			xfs_da3_fixhashpath(state, &state->path); +	} else { +		/* +		 * It didn't work, we need to split the leaf block. +		 */ +		if (args->total == 0) { +			ASSERT(rval == -ENOSPC); +			goto done; +		} +		/* +		 * Split the leaf block and insert the new entry. +		 */ +		rval = xfs_da3_split(state); +	} +done: +	xfs_da_state_free(state); +	return rval; +} + +/*   * Lookup an entry in a node-format directory.   * All the real work happens in xfs_da3_node_lookup_int.   * The only real output is the inode number of the entry.  |