xfs_dir2_leaf.c

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

C
1,897
字号
/* * Copyright (c) 2000-2003 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_leaf.c * XFS directory version 2 implementation - single leaf form * see xfs_dir2_leaf.h for data structures. * These directories have multiple XFS_DIR2_DATA blocks and one * XFS_DIR2_LEAF1 block containing the hash table and freespace map. */#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_ag.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"#include "xfs_bit.h"/* * Local function declarations. */#ifdef DEBUGstatic void xfs_dir2_leaf_check(xfs_inode_t *dp, xfs_dabuf_t *bp);#else#define	xfs_dir2_leaf_check(dp, bp)#endifstatic int xfs_dir2_leaf_lookup_int(xfs_da_args_t *args, xfs_dabuf_t **lbpp,				    int *indexp, xfs_dabuf_t **dbpp);/* * Convert a block form directory to a leaf form directory. */int						/* error */xfs_dir2_block_to_leaf(	xfs_da_args_t		*args,		/* operation arguments */	xfs_dabuf_t		*dbp)		/* input block's buffer */{	xfs_dir2_data_off_t	*bestsp;	/* leaf's bestsp entries */	xfs_dablk_t		blkno;		/* leaf block's bno */	xfs_dir2_block_t	*block;		/* block structure */	xfs_dir2_leaf_entry_t	*blp;		/* block's leaf entries */	xfs_dir2_block_tail_t	*btp;		/* block's tail */	xfs_inode_t		*dp;		/* incore directory inode */	int			error;		/* error return code */	xfs_dabuf_t		*lbp;		/* leaf block's buffer */	xfs_dir2_db_t		ldb;		/* leaf block's bno */	xfs_dir2_leaf_t		*leaf;		/* leaf structure */	xfs_dir2_leaf_tail_t	*ltp;		/* leaf's tail */	xfs_mount_t		*mp;		/* filesystem mount point */	int			needlog;	/* need to log block header */	int			needscan;	/* need to rescan bestfree */	xfs_trans_t		*tp;		/* transaction pointer */	xfs_dir2_trace_args_b("block_to_leaf", args, dbp);	dp = args->dp;	mp = dp->i_mount;	tp = args->trans;	/*	 * Add the leaf block to the inode.	 * This interface will only put blocks in the leaf/node range.	 * Since that's empty now, we'll get the root (block 0 in range).	 */	if ((error = xfs_da_grow_inode(args, &blkno))) {		return error;	}	ldb = XFS_DIR2_DA_TO_DB(mp, blkno);	ASSERT(ldb == XFS_DIR2_LEAF_FIRSTDB(mp));	/*	 * Initialize the leaf block, get a buffer for it.	 */	if ((error = xfs_dir2_leaf_init(args, ldb, &lbp, XFS_DIR2_LEAF1_MAGIC))) {		return error;	}	ASSERT(lbp != NULL);	leaf = lbp->data;	block = dbp->data;	xfs_dir2_data_check(dp, dbp);	btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);	blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);	/*	 * Set the counts in the leaf header.	 */	INT_COPY(leaf->hdr.count, btp->count, ARCH_CONVERT); /* INT_: type change */	INT_COPY(leaf->hdr.stale, btp->stale, ARCH_CONVERT); /* INT_: type change */	/*	 * Could compact these but I think we always do the conversion	 * after squeezing out stale entries.	 */	memcpy(leaf->ents, blp, INT_GET(btp->count, ARCH_CONVERT) * sizeof(xfs_dir2_leaf_entry_t));	xfs_dir2_leaf_log_ents(tp, lbp, 0, INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1);	needscan = 0;	needlog = 1;	/*	 * Make the space formerly occupied by the leaf entries and block	 * tail be free.	 */	xfs_dir2_data_make_free(tp, dbp,		(xfs_dir2_data_aoff_t)((char *)blp - (char *)block),		(xfs_dir2_data_aoff_t)((char *)block + mp->m_dirblksize -				       (char *)blp),		&needlog, &needscan);	/*	 * Fix up the block header, make it a data block.	 */	INT_SET(block->hdr.magic, ARCH_CONVERT, XFS_DIR2_DATA_MAGIC);	if (needscan)		xfs_dir2_data_freescan(mp, (xfs_dir2_data_t *)block, &needlog,			NULL);	/*	 * Set up leaf tail and bests table.	 */	ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);	INT_SET(ltp->bestcount, ARCH_CONVERT, 1);	bestsp = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT);	INT_COPY(bestsp[0], block->hdr.bestfree[0].length, ARCH_CONVERT);	/*	 * Log the data header and leaf bests table.	 */	if (needlog)		xfs_dir2_data_log_header(tp, dbp);	xfs_dir2_leaf_check(dp, lbp);	xfs_dir2_data_check(dp, dbp);	xfs_dir2_leaf_log_bests(tp, lbp, 0, 0);	xfs_da_buf_done(lbp);	return 0;}/* * Add an entry to a leaf form directory. */int						/* error */xfs_dir2_leaf_addname(	xfs_da_args_t		*args)		/* operation arguments */{	xfs_dir2_data_off_t	*bestsp;	/* freespace table in leaf */	int			compact;	/* need to compact leaves */	xfs_dir2_data_t		*data;		/* data block structure */	xfs_dabuf_t		*dbp;		/* data block buffer */	xfs_dir2_data_entry_t	*dep;		/* data block entry */	xfs_inode_t		*dp;		/* incore directory inode */	xfs_dir2_data_unused_t	*dup;		/* data unused entry */	int			error;		/* error return value */	int			grown;		/* allocated new data block */	int			highstale;	/* index of next stale leaf */	int			i;		/* temporary, index */	int			index;		/* leaf table position */	xfs_dabuf_t		*lbp;		/* leaf's buffer */	xfs_dir2_leaf_t		*leaf;		/* leaf structure */	int			length;		/* length of new entry */	xfs_dir2_leaf_entry_t	*lep;		/* leaf entry table pointer */	int			lfloglow;	/* low leaf logging index */	int			lfloghigh;	/* high leaf logging index */	int			lowstale;	/* index of prev stale leaf */	xfs_dir2_leaf_tail_t	*ltp;		/* leaf tail pointer */	xfs_mount_t		*mp;		/* filesystem mount point */	int			needbytes;	/* leaf block bytes needed */	int			needlog;	/* need to log data header */	int			needscan;	/* need to rescan data free */	xfs_dir2_data_off_t	*tagp;		/* end of data entry */	xfs_trans_t		*tp;		/* transaction pointer */	xfs_dir2_db_t		use_block;	/* data block number */	xfs_dir2_trace_args("leaf_addname", args);	dp = args->dp;	tp = args->trans;	mp = dp->i_mount;	/*	 * Read the leaf block.	 */	error = xfs_da_read_buf(tp, dp, mp->m_dirleafblk, -1, &lbp,		XFS_DATA_FORK);	if (error) {		return error;	}	ASSERT(lbp != NULL);	/*	 * Look up the entry by hash value and name.	 * We know it's not there, our caller has already done a lookup.	 * So the index is of the entry to insert in front of.	 * But if there are dup hash values the index is of the first of those.	 */	index = xfs_dir2_leaf_search_hash(args, lbp);	leaf = lbp->data;	ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);	bestsp = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT);	length = XFS_DIR2_DATA_ENTSIZE(args->namelen);	/*	 * See if there are any entries with the same hash value	 * and space in their block for the new entry.	 * This is good because it puts multiple same-hash value entries	 * in a data block, improving the lookup of those entries.	 */	for (use_block = -1, lep = &leaf->ents[index];	     index < INT_GET(leaf->hdr.count, ARCH_CONVERT) && INT_GET(lep->hashval, ARCH_CONVERT) == args->hashval;	     index++, lep++) {		if (INT_GET(lep->address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)			continue;		i = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT));		ASSERT(i < INT_GET(ltp->bestcount, ARCH_CONVERT));		ASSERT(INT_GET(bestsp[i], ARCH_CONVERT) != NULLDATAOFF);		if (INT_GET(bestsp[i], ARCH_CONVERT) >= length) {			use_block = i;			break;		}	}	/*	 * Didn't find a block yet, linear search all the data blocks.	 */	if (use_block == -1) {		for (i = 0; i < INT_GET(ltp->bestcount, ARCH_CONVERT); i++) {			/*			 * Remember a block we see that's missing.			 */			if (INT_GET(bestsp[i], ARCH_CONVERT) == NULLDATAOFF && use_block == -1)				use_block = i;			else if (INT_GET(bestsp[i], ARCH_CONVERT) >= length) {				use_block = i;				break;			}		}	}	/*	 * How many bytes do we need in the leaf block?	 */	needbytes =		(!INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT) ? 0 : (uint)sizeof(leaf->ents[0])) +		(use_block != -1 ? 0 : (uint)sizeof(leaf->bests[0]));	/*	 * Now kill use_block if it refers to a missing block, so we	 * can use it as an indication of allocation needed.	 */	if (use_block != -1 && INT_GET(bestsp[use_block], ARCH_CONVERT) == NULLDATAOFF)		use_block = -1;	/*	 * If we don't have enough free bytes but we can make enough	 * by compacting out stale entries, we'll do that.	 */	if ((char *)bestsp - (char *)&leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT)] < needbytes &&	    INT_GET(leaf->hdr.stale, ARCH_CONVERT) > 1) {		compact = 1;	}	/*	 * Otherwise if we don't have enough free bytes we need to	 * convert to node form.	 */	else if ((char *)bestsp - (char *)&leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT)] <		 needbytes) {		/*		 * Just checking or no space reservation, give up.		 */		if (args->justcheck || args->total == 0) {			xfs_da_brelse(tp, lbp);			return XFS_ERROR(ENOSPC);		}		/*		 * Convert to node form.		 */		error = xfs_dir2_leaf_to_node(args, lbp);		xfs_da_buf_done(lbp);		if (error)			return error;		/*		 * Then add the new entry.		 */		return xfs_dir2_node_addname(args);	}	/*	 * Otherwise it will fit without compaction.	 */	else		compact = 0;	/*	 * If just checking, then it will fit unless we needed to allocate	 * a new data block.	 */	if (args->justcheck) {		xfs_da_brelse(tp, lbp);		return use_block == -1 ? XFS_ERROR(ENOSPC) : 0;	}	/*	 * If no allocations are allowed, return now before we've	 * changed anything.	 */	if (args->total == 0 && use_block == -1) {		xfs_da_brelse(tp, lbp);		return XFS_ERROR(ENOSPC);	}	/*	 * Need to compact the leaf entries, removing stale ones.	 * Leave one stale entry behind - the one closest to our	 * insertion index - and we'll shift that one to our insertion	 * point later.	 */	if (compact) {		xfs_dir2_leaf_compact_x1(lbp, &index, &lowstale, &highstale,			&lfloglow, &lfloghigh);	}	/*	 * There are stale entries, so we'll need log-low and log-high	 * impossibly bad values later.	 */	else if (INT_GET(leaf->hdr.stale, ARCH_CONVERT)) {		lfloglow = INT_GET(leaf->hdr.count, ARCH_CONVERT);		lfloghigh = -1;	}	/*	 * If there was no data block space found, we need to allocate	 * a new one.	 */	if (use_block == -1) {		/*		 * Add the new data block.		 */		if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE,				&use_block))) {			xfs_da_brelse(tp, lbp);			return error;		}		/*		 * Initialize the block.		 */		if ((error = xfs_dir2_data_init(args, use_block, &dbp))) {			xfs_da_brelse(tp, lbp);			return error;		}		/*		 * If we're adding a new data block on the end we need to		 * extend the bests table.  Copy it up one entry.		 */		if (use_block >= INT_GET(ltp->bestcount, ARCH_CONVERT)) {			bestsp--;			memmove(&bestsp[0], &bestsp[1],				INT_GET(ltp->bestcount, ARCH_CONVERT) * sizeof(bestsp[0]));			INT_MOD(ltp->bestcount, ARCH_CONVERT, +1);			xfs_dir2_leaf_log_tail(tp, lbp);			xfs_dir2_leaf_log_bests(tp, lbp, 0, INT_GET(ltp->bestcount, ARCH_CONVERT) - 1);		}		/*		 * If we're filling in a previously empty block just log it.		 */		else			xfs_dir2_leaf_log_bests(tp, lbp, use_block, use_block);		data = dbp->data;		INT_COPY(bestsp[use_block], data->hdr.bestfree[0].length, ARCH_CONVERT);		grown = 1;	}	/*	 * Already had space in some data block.	 * Just read that one in.	 */	else {		if ((error =		    xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, use_block),			    -1, &dbp, XFS_DATA_FORK))) {			xfs_da_brelse(tp, lbp);			return error;		}		data = dbp->data;		grown = 0;	}	xfs_dir2_data_check(dp, dbp);	/*	 * Point to the biggest freespace in our data block.	 */	dup = (xfs_dir2_data_unused_t *)	      ((char *)data + INT_GET(data->hdr.bestfree[0].offset, ARCH_CONVERT));	ASSERT(INT_GET(dup->length, ARCH_CONVERT) >= length);	needscan = needlog = 0;	/*	 * Mark the initial part of our freespace in use for the new entry.	 */	xfs_dir2_data_use_free(tp, dbp, dup,		(xfs_dir2_data_aoff_t)((char *)dup - (char *)data), length,		&needlog, &needscan);	/*	 * Initialize our new entry (at last).	 */	dep = (xfs_dir2_data_entry_t *)dup;	INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);	dep->namelen = args->namelen;	memcpy(dep->name, args->name, dep->namelen);	tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);	INT_SET(*tagp, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dep - (char *)data));	/*	 * Need to scan fix up the bestfree table.	 */	if (needscan)		xfs_dir2_data_freescan(mp, data, &needlog, NULL);	/*	 * Need to log the data block's header.	 */	if (needlog)		xfs_dir2_data_log_header(tp, dbp);	xfs_dir2_data_log_entry(tp, dbp, dep);	/*	 * If the bests table needs to be changed, do it.	 * Log the change unless we've already done that.	 */	if (INT_GET(bestsp[use_block], ARCH_CONVERT) != INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT)) {		INT_COPY(bestsp[use_block], data->hdr.bestfree[0].length, ARCH_CONVERT);		if (!grown)			xfs_dir2_leaf_log_bests(tp, lbp, use_block, use_block);	}	/*	 * Now we need to make room to insert the leaf entry.	 * If there are no stale entries, we just insert a hole at index.	 */	if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) {		/*		 * lep is still good as the index leaf entry.		 */		if (index < INT_GET(leaf->hdr.count, ARCH_CONVERT))			memmove(lep + 1, lep,				(INT_GET(leaf->hdr.count, ARCH_CONVERT) - index) * sizeof(*lep));		/*		 * Record low and high logging indices for the leaf.		 */		lfloglow = index;		lfloghigh = INT_GET(leaf->hdr.count, ARCH_CONVERT);		INT_MOD(leaf->hdr.count, ARCH_CONVERT, +1);	}	/*	 * There are stale entries.	 * We will use one of them for the new entry.

⌨️ 快捷键说明

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