xfs_dir2_node.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,021 行 · 第 1/4 页

C
2,021
字号
/* * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like.  Any license provided herein, whether implied or * otherwise, applies only to this software file.  Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA  94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ *//* * xfs_dir2_node.c * XFS directory implementation, version 2, node form files * See data structures in xfs_dir2_node.h and xfs_da_btree.h. */#include "xfs.h"#include "xfs_macros.h"#include "xfs_types.h"#include "xfs_inum.h"#include "xfs_log.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_dir.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_bmap_btree.h"#include "xfs_attr_sf.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_bmap.h"#include "xfs_da_btree.h"#include "xfs_dir2_data.h"#include "xfs_dir2_leaf.h"#include "xfs_dir2_block.h"#include "xfs_dir2_node.h"#include "xfs_dir2_trace.h"#include "xfs_error.h"/* * Function declarations. */static void xfs_dir2_free_log_header(xfs_trans_t *tp, xfs_dabuf_t *bp);static int xfs_dir2_leafn_add(xfs_dabuf_t *bp, xfs_da_args_t *args, int index);#ifdef DEBUGstatic void xfs_dir2_leafn_check(xfs_inode_t *dp, xfs_dabuf_t *bp);#else#define	xfs_dir2_leafn_check(dp, bp)#endifstatic void xfs_dir2_leafn_moveents(xfs_da_args_t *args, xfs_dabuf_t *bp_s,				    int start_s, xfs_dabuf_t *bp_d, int start_d,				    int count);static void xfs_dir2_leafn_rebalance(xfs_da_state_t *state,				     xfs_da_state_blk_t *blk1,				     xfs_da_state_blk_t *blk2);static int xfs_dir2_leafn_remove(xfs_da_args_t *args, xfs_dabuf_t *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);/* * Log entries from a freespace block. */voidxfs_dir2_free_log_bests(	xfs_trans_t		*tp,		/* transaction pointer */	xfs_dabuf_t		*bp,		/* freespace buffer */	int			first,		/* first entry to log */	int			last)		/* last entry to log */{	xfs_dir2_free_t		*free;		/* freespace structure */	free = bp->data;	ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);	xfs_da_log_buf(tp, bp,		(uint)((char *)&free->bests[first] - (char *)free),		(uint)((char *)&free->bests[last] - (char *)free +		       sizeof(free->bests[0]) - 1));}/* * Log header from a freespace block. */static voidxfs_dir2_free_log_header(	xfs_trans_t		*tp,		/* transaction pointer */	xfs_dabuf_t		*bp)		/* freespace buffer */{	xfs_dir2_free_t		*free;		/* freespace structure */	free = bp->data;	ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);	xfs_da_log_buf(tp, bp, (uint)((char *)&free->hdr - (char *)free),		(uint)(sizeof(xfs_dir2_free_hdr_t) - 1));}/* * Convert a leaf-format directory to a node-format directory. * We need to change the magic number of the leaf block, and copy * the freespace table out of the leaf block into its own block. */int						/* error */xfs_dir2_leaf_to_node(	xfs_da_args_t		*args,		/* operation arguments */	xfs_dabuf_t		*lbp)		/* leaf buffer */{	xfs_inode_t		*dp;		/* incore directory inode */	int			error;		/* error return value */	xfs_dabuf_t		*fbp;		/* freespace buffer */	xfs_dir2_db_t		fdb;		/* freespace block number */	xfs_dir2_free_t		*free;		/* freespace structure */	xfs_dir2_data_off_t	*from;		/* pointer to freespace entry */	int			i;		/* leaf freespace index */	xfs_dir2_leaf_t		*leaf;		/* leaf structure */	xfs_dir2_leaf_tail_t	*ltp;		/* leaf tail structure */	xfs_mount_t		*mp;		/* filesystem mount point */	int			n;		/* count of live freespc ents */	xfs_dir2_data_off_t	off;		/* freespace entry value */	xfs_dir2_data_off_t	*to;		/* pointer to freespace entry */	xfs_trans_t		*tp;		/* transaction pointer */	xfs_dir2_trace_args_b("leaf_to_node", args, lbp);	dp = args->dp;	mp = dp->i_mount;	tp = args->trans;	/*	 * Add a freespace block to the directory.	 */	if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE, &fdb))) {		return error;	}	ASSERT(fdb == XFS_DIR2_FREE_FIRSTDB(mp));	/*	 * Get the buffer for the new freespace block.	 */	if ((error = xfs_da_get_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, fdb), -1, &fbp,			XFS_DATA_FORK))) {		return error;	}	ASSERT(fbp != NULL);	free = fbp->data;	leaf = lbp->data;	ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);	/*	 * Initialize the freespace block header.	 */	INT_SET(free->hdr.magic, ARCH_CONVERT, XFS_DIR2_FREE_MAGIC);	INT_ZERO(free->hdr.firstdb, ARCH_CONVERT);	ASSERT(INT_GET(ltp->bestcount, ARCH_CONVERT) <= (uint)dp->i_d.di_size / mp->m_dirblksize);	INT_COPY(free->hdr.nvalid, ltp->bestcount, ARCH_CONVERT);	/*	 * Copy freespace entries from the leaf block to the new block.	 * Count active entries.	 */	for (i = n = 0, from = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT), to = free->bests;	     i < INT_GET(ltp->bestcount, ARCH_CONVERT); i++, from++, to++) {		if ((off = INT_GET(*from, ARCH_CONVERT)) != NULLDATAOFF)			n++;		INT_SET(*to, ARCH_CONVERT, off);	}	INT_SET(free->hdr.nused, ARCH_CONVERT, n);	INT_SET(leaf->hdr.info.magic, ARCH_CONVERT, XFS_DIR2_LEAFN_MAGIC);	/*	 * Log everything.	 */	xfs_dir2_leaf_log_header(tp, lbp);	xfs_dir2_free_log_header(tp, fbp);	xfs_dir2_free_log_bests(tp, fbp, 0, INT_GET(free->hdr.nvalid, ARCH_CONVERT) - 1);	xfs_da_buf_done(fbp);	xfs_dir2_leafn_check(dp, lbp);	return 0;}/* * Add a leaf entry to a leaf block in a node-form directory. * The other work necessary is done from the caller. */static int					/* error */xfs_dir2_leafn_add(	xfs_dabuf_t		*bp,		/* leaf buffer */	xfs_da_args_t		*args,		/* operation arguments */	int			index)		/* insertion pt for new entry */{	int			compact;	/* compacting stale leaves */	xfs_inode_t		*dp;		/* incore directory inode */	int			highstale;	/* next stale entry */	xfs_dir2_leaf_t		*leaf;		/* leaf structure */	xfs_dir2_leaf_entry_t	*lep;		/* leaf entry */	int			lfloghigh;	/* high leaf entry logging */	int			lfloglow;	/* low leaf entry logging */	int			lowstale;	/* previous stale entry */	xfs_mount_t		*mp;		/* filesystem mount point */	xfs_trans_t		*tp;		/* transaction pointer */	xfs_dir2_trace_args_sb("leafn_add", args, index, bp);	dp = args->dp;	mp = dp->i_mount;	tp = args->trans;	leaf = bp->data;	/*	 * Quick check just to make sure we are not going to index	 * into other peoples memory	 */	if (index < 0)		return XFS_ERROR(EFSCORRUPTED);	/*	 * If there are already the maximum number of leaf entries in	 * the block, if there are no stale entries it won't fit.	 * Caller will do a split.  If there are stale entries we'll do	 * a compact.	 */	if (INT_GET(leaf->hdr.count, ARCH_CONVERT) == XFS_DIR2_MAX_LEAF_ENTS(mp)) {		if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT))			return XFS_ERROR(ENOSPC);		compact = INT_GET(leaf->hdr.stale, ARCH_CONVERT) > 1;	} else		compact = 0;	ASSERT(index == 0 || INT_GET(leaf->ents[index - 1].hashval, ARCH_CONVERT) <= args->hashval);	ASSERT(index == INT_GET(leaf->hdr.count, ARCH_CONVERT) ||	       INT_GET(leaf->ents[index].hashval, ARCH_CONVERT) >= args->hashval);	if (args->justcheck)		return 0;	/*	 * Compact out all but one stale leaf entry.  Leaves behind	 * the entry closest to index.	 */	if (compact) {		xfs_dir2_leaf_compact_x1(bp, &index, &lowstale, &highstale,			&lfloglow, &lfloghigh);	}	/*	 * Set impossible logging indices for this case.	 */	else if (!INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) {		lfloglow = INT_GET(leaf->hdr.count, ARCH_CONVERT);		lfloghigh = -1;	}	/*	 * No stale entries, just insert a space for the new entry.	 */	if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) {		lep = &leaf->ents[index];		if (index < INT_GET(leaf->hdr.count, ARCH_CONVERT))			memmove(lep + 1, lep,				(INT_GET(leaf->hdr.count, ARCH_CONVERT) - index) * sizeof(*lep));		lfloglow = index;		lfloghigh = INT_GET(leaf->hdr.count, ARCH_CONVERT);		INT_MOD(leaf->hdr.count, ARCH_CONVERT, +1);	}	/*	 * There are stale entries.  We'll use one for the new entry.	 */	else {		/*		 * If we didn't do a compact then we need to figure out		 * which stale entry will be used.		 */		if (compact == 0) {			/*			 * Find first stale entry before our insertion point.			 */			for (lowstale = index - 1;			     lowstale >= 0 &&				INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) !=				XFS_DIR2_NULL_DATAPTR;			     lowstale--)				continue;			/*			 * Find next stale entry after insertion point.			 * Stop looking if the answer would be worse than			 * lowstale already found.			 */			for (highstale = index;			     highstale < INT_GET(leaf->hdr.count, ARCH_CONVERT) &&				INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) !=				XFS_DIR2_NULL_DATAPTR &&				(lowstale < 0 ||				 index - lowstale - 1 >= highstale - index);			     highstale++)				continue;		}		/*		 * Using the low stale entry.		 * Shift entries up toward the stale slot.		 */		if (lowstale >= 0 &&		    (highstale == INT_GET(leaf->hdr.count, ARCH_CONVERT) ||		     index - lowstale - 1 < highstale - index)) {			ASSERT(INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) ==			       XFS_DIR2_NULL_DATAPTR);			ASSERT(index - lowstale - 1 >= 0);			if (index - lowstale - 1 > 0)				memmove(&leaf->ents[lowstale],					&leaf->ents[lowstale + 1],					(index - lowstale - 1) * sizeof(*lep));			lep = &leaf->ents[index - 1];			lfloglow = MIN(lowstale, lfloglow);			lfloghigh = MAX(index - 1, lfloghigh);		}		/*		 * Using the high stale entry.		 * Shift entries down toward the stale slot.		 */		else {			ASSERT(INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) ==			       XFS_DIR2_NULL_DATAPTR);			ASSERT(highstale - index >= 0);			if (highstale - index > 0)				memmove(&leaf->ents[index + 1],					&leaf->ents[index],					(highstale - index) * sizeof(*lep));			lep = &leaf->ents[index];			lfloglow = MIN(index, lfloglow);			lfloghigh = MAX(highstale, lfloghigh);		}		INT_MOD(leaf->hdr.stale, ARCH_CONVERT, -1);	}	/*	 * Insert the new entry, log everything.	 */	INT_SET(lep->hashval, ARCH_CONVERT, args->hashval);	INT_SET(lep->address, ARCH_CONVERT, XFS_DIR2_DB_OFF_TO_DATAPTR(mp, args->blkno, args->index));	xfs_dir2_leaf_log_header(tp, bp);	xfs_dir2_leaf_log_ents(tp, bp, lfloglow, lfloghigh);	xfs_dir2_leafn_check(dp, bp);	return 0;}#ifdef DEBUG/* * Check internal consistency of a leafn block. */voidxfs_dir2_leafn_check(	xfs_inode_t	*dp,			/* incore directory inode */	xfs_dabuf_t	*bp)			/* leaf buffer */{	int		i;			/* leaf index */	xfs_dir2_leaf_t	*leaf;			/* leaf structure */	xfs_mount_t	*mp;			/* filesystem mount point */	int		stale;			/* count of stale leaves */	leaf = bp->data;	mp = dp->i_mount;	ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);	ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) <= XFS_DIR2_MAX_LEAF_ENTS(mp));	for (i = stale = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); i++) {		if (i + 1 < INT_GET(leaf->hdr.count, ARCH_CONVERT)) {			ASSERT(INT_GET(leaf->ents[i].hashval, ARCH_CONVERT) <=			       INT_GET(leaf->ents[i + 1].hashval, ARCH_CONVERT));		}		if (INT_GET(leaf->ents[i].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)			stale++;	}	ASSERT(INT_GET(leaf->hdr.stale, ARCH_CONVERT) == stale);}#endif	/* DEBUG *//* * Return the last hash value in the leaf. * Stale entries are ok. */xfs_dahash_t					/* hash value */xfs_dir2_leafn_lasthash(	xfs_dabuf_t	*bp,			/* leaf buffer */	int		*count)			/* count of entries in leaf */{	xfs_dir2_leaf_t	*leaf;			/* leaf structure */	leaf = bp->data;	ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);	if (count)		*count = INT_GET(leaf->hdr.count, ARCH_CONVERT);	if (INT_ISZERO(leaf->hdr.count, ARCH_CONVERT))		return 0;	return INT_GET(leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);}/* * Look up a leaf entry in a node-format leaf block. * If this is an addname then the extrablk in state is a freespace block, * otherwise it's a data block. */intxfs_dir2_leafn_lookup_int(	xfs_dabuf_t		*bp,		/* leaf buffer */	xfs_da_args_t		*args,		/* operation arguments */	int			*indexp,	/* out: leaf entry index */	xfs_da_state_t		*state)		/* state to fill in */{	xfs_dabuf_t		*curbp;		/* current data/free buffer */	xfs_dir2_db_t		curdb;		/* current data block number */	xfs_dir2_db_t		curfdb;		/* current free block number */	xfs_dir2_data_entry_t	*dep;		/* data block entry */	xfs_inode_t		*dp;		/* incore directory inode */	int			error;		/* error return value */	int			fi;		/* free entry index */	xfs_dir2_free_t		*free=NULL;	/* free block structure */	int			index;		/* leaf entry index */	xfs_dir2_leaf_t		*leaf;		/* leaf structure */	int			length=0;	/* length of new data entry */	xfs_dir2_leaf_entry_t	*lep;		/* leaf entry */	xfs_mount_t		*mp;		/* filesystem mount point */	xfs_dir2_db_t		newdb;		/* new data block number */	xfs_dir2_db_t		newfdb;		/* new free block number */	xfs_trans_t		*tp;		/* transaction pointer */	dp = args->dp;	tp = args->trans;	mp = dp->i_mount;	leaf = bp->data;	ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);#ifdef __KERNEL__	ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) > 0);#endif	xfs_dir2_leafn_check(dp, bp);	/*	 * Look up the hash value in the leaf entries.	 */	index = xfs_dir2_leaf_search_hash(args, bp);	/*	 * Do we have a buffer coming in?	 */	if (state->extravalid)		curbp = state->extrablk.bp;	else		curbp = NULL;	/*	 * For addname, it's a free block buffer, get the block number.	 */	if (args->addname) {		curfdb = curbp ? state->extrablk.blkno : -1;		curdb = -1;		length = XFS_DIR2_DATA_ENTSIZE(args->namelen);		if ((free = (curbp ? curbp->data : NULL)))			ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);	}	/*	 * For others, it's a data block buffer, get the block number.	 */	else {		curfdb = -1;		curdb = curbp ? state->extrablk.blkno : -1;	}	/*	 * Loop over leaf entries with the right hash value.	 */	for (lep = &leaf->ents[index];	     index < INT_GET(leaf->hdr.count, ARCH_CONVERT) && INT_GET(lep->hashval, ARCH_CONVERT) == args->hashval;	     lep++, index++) {		/*		 * Skip stale leaf entries.		 */		if (INT_GET(lep->address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)			continue;		/*		 * Pull the data block number from the entry.		 */		newdb = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT));		/*		 * For addname, we're looking for a place to put the new entry.		 * We want to use a data block with an entry of equal		 * hash value to ours if there is one with room.		 */		if (args->addname) {			/*			 * If this block isn't the data block we already have			 * in hand, take a look at it.			 */			if (newdb != curdb) {				curdb = newdb;				/*				 * Convert the data block to the free block				 * holding its freespace information.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?