namei.c

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

C
2,370
字号
	 * Find the next leaf page by incrementing the frame pointer.	 * If we run out of entries in the interior node, loop around and	 * increment pointer in the parent node.  When we break out of	 * this loop, num_frames indicates the number of interior	 * nodes need to be read.	 */	while (1) {		if (++(p->at) < p->entries + dx_get_count(p->entries))			break;		if (p == frames)			return 0;		num_frames++;		p--;	}	/*	 * If the hash is 1, then continue only if the next page has a	 * continuation hash of any value.  This is used for readdir	 * handling.  Otherwise, check to see if the hash matches the	 * desired contiuation hash.  If it doesn't, return since	 * there's no point to read in the successive index pages.	 */	bhash = dx_get_hash(p->at);	if (start_hash)		*start_hash = bhash;	if ((hash & 1) == 0) {		if ((bhash & ~1) != hash)			return 0;	}	/*	 * If the hash is HASH_NB_ALWAYS, we always go to the next	 * block so no check is necessary	 */	while (num_frames--) {		if (!(bh = ext3_bread(NULL, dir, dx_get_block(p->at),				      0, &err)))			return err; /* Failure */		p++;		brelse (p->bh);		p->bh = bh;		p->at = p->entries = ((struct dx_node *) bh->b_data)->entries;	}	return 1;}/* * p is at least 6 bytes before the end of page */static inline struct ext3_dir_entry_2 *ext3_next_entry(struct ext3_dir_entry_2 *p){	return (struct ext3_dir_entry_2 *)((char*)p + le16_to_cpu(p->rec_len));}/* * This function fills a red-black tree with information from a * directory block.  It returns the number directory entries loaded * into the tree.  If there is an error it is returned in err. */static int htree_dirblock_to_tree(struct file *dir_file,				  struct inode *dir, int block,				  struct dx_hash_info *hinfo,				  __u32 start_hash, __u32 start_minor_hash){	struct buffer_head *bh;	struct ext3_dir_entry_2 *de, *top;	int err, count = 0;	dxtrace(printk("In htree dirblock_to_tree: block %d\n", block));	if (!(bh = ext3_bread (NULL, dir, block, 0, &err)))		return err;	de = (struct ext3_dir_entry_2 *) bh->b_data;	top = (struct ext3_dir_entry_2 *) ((char *) de +					   dir->i_sb->s_blocksize -					   EXT3_DIR_REC_LEN(0));	for (; de < top; de = ext3_next_entry(de)) {		ext3fs_dirhash(de->name, de->name_len, hinfo);		if ((hinfo->hash < start_hash) ||		    ((hinfo->hash == start_hash) &&		     (hinfo->minor_hash < start_minor_hash)))			continue;		if (de->inode == 0)			continue;		if ((err = ext3_htree_store_dirent(dir_file,				   hinfo->hash, hinfo->minor_hash, de)) != 0) {			brelse(bh);			return err;		}		count++;	}	brelse(bh);	return count;}/* * This function fills a red-black tree with information from a * directory.  We start scanning the directory in hash order, starting * at start_hash and start_minor_hash. * * This function returns the number of entries inserted into the tree, * or a negative error code. */int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,			 __u32 start_minor_hash, __u32 *next_hash){	struct dx_hash_info hinfo;	struct ext3_dir_entry_2 *de;	struct dx_frame frames[2], *frame;	struct inode *dir;	int block, err;	int count = 0;	int ret;	__u32 hashval;	dxtrace(printk("In htree_fill_tree, start hash: %x:%x\n", start_hash,		       start_minor_hash));	dir = dir_file->f_dentry->d_inode;	if (!(EXT3_I(dir)->i_flags & EXT3_INDEX_FL)) {		hinfo.hash_version = EXT3_SB(dir->i_sb)->s_def_hash_version;		hinfo.seed = EXT3_SB(dir->i_sb)->s_hash_seed;		count = htree_dirblock_to_tree(dir_file, dir, 0, &hinfo,					       start_hash, start_minor_hash);		*next_hash = ~0;		return count;	}	hinfo.hash = start_hash;	hinfo.minor_hash = 0;	frame = dx_probe(NULL, dir_file->f_dentry->d_inode, &hinfo, frames, &err);	if (!frame)		return err;	/* Add '.' and '..' from the htree header */	if (!start_hash && !start_minor_hash) {		de = (struct ext3_dir_entry_2 *) frames[0].bh->b_data;		if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0)			goto errout;		de = ext3_next_entry(de);		if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0)			goto errout;		count += 2;	}	while (1) {		block = dx_get_block(frame->at);		ret = htree_dirblock_to_tree(dir_file, dir, block, &hinfo,					     start_hash, start_minor_hash);		if (ret < 0) {			err = ret;			goto errout;		}		count += ret;		hashval = ~0;		ret = ext3_htree_next_block(dir, HASH_NB_ALWAYS, 					    frame, frames, &hashval);		*next_hash = hashval;		if (ret < 0) {			err = ret;			goto errout;		}		/*		 * Stop if:  (a) there are no more entries, or		 * (b) we have inserted at least one entry and the		 * next hash value is not a continuation		 */		if ((ret == 0) ||		    (count && ((hashval & 1) == 0)))			break;	}	dx_release(frames);	dxtrace(printk("Fill tree: returned %d entries, next hash: %x\n", 		       count, *next_hash));	return count;errout:	dx_release(frames);	return (err);}/* * Directory block splitting, compacting */static int dx_make_map (struct ext3_dir_entry_2 *de, int size,			struct dx_hash_info *hinfo, struct dx_map_entry *map_tail){	int count = 0;	char *base = (char *) de;	struct dx_hash_info h = *hinfo;	while ((char *) de < base + size)	{		if (de->name_len && de->inode) {			ext3fs_dirhash(de->name, de->name_len, &h);			map_tail--;			map_tail->hash = h.hash;			map_tail->offs = (u32) ((char *) de - base);			count++;		}		/* XXX: do we need to check rec_len == 0 case? -Chris */		de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));	}	return count;}static void dx_sort_map (struct dx_map_entry *map, unsigned count){        struct dx_map_entry *p, *q, *top = map + count - 1;        int more;        /* Combsort until bubble sort doesn't suck */        while (count > 2)	{                count = count*10/13;                if (count - 9 < 2) /* 9, 10 -> 11 */                        count = 11;                for (p = top, q = p - count; q >= map; p--, q--)                        if (p->hash < q->hash)                                swap(*p, *q);        }        /* Garden variety bubble sort */        do {                more = 0;                q = top;                while (q-- > map)		{                        if (q[1].hash >= q[0].hash)				continue;                        swap(*(q+1), *q);                        more = 1;		}	} while(more);}static void dx_insert_block(struct dx_frame *frame, u32 hash, u32 block){	struct dx_entry *entries = frame->entries;	struct dx_entry *old = frame->at, *new = old + 1;	int count = dx_get_count(entries);	assert(count < dx_get_limit(entries));	assert(old < entries + count);	memmove(new + 1, new, (char *)(entries + count) - (char *)(new));	dx_set_hash(new, hash);	dx_set_block(new, block);	dx_set_count(entries, count + 1);}#endifstatic void ext3_update_dx_flag(struct inode *inode){	if (!EXT3_HAS_COMPAT_FEATURE(inode->i_sb,				     EXT3_FEATURE_COMPAT_DIR_INDEX))		EXT3_I(inode)->i_flags &= ~EXT3_INDEX_FL;}/* * NOTE! unlike strncmp, ext3_match returns 1 for success, 0 for failure. * * `len <= EXT3_NAME_LEN' is guaranteed by caller. * `de != NULL' is guaranteed by caller. */static inline int ext3_match (int len, const char * const name,			      struct ext3_dir_entry_2 * de){	if (len != de->name_len)		return 0;	if (!de->inode)		return 0;	return !memcmp(name, de->name, len);}/* * Returns 0 if not found, -1 on failure, and 1 on success */static inline int search_dirblock(struct buffer_head * bh,				  struct inode *dir,				  struct dentry *dentry,				  unsigned long offset,				  struct ext3_dir_entry_2 ** res_dir){	struct ext3_dir_entry_2 * de;	char * dlimit;	int de_len;	const char *name = dentry->d_name.name;	int namelen = dentry->d_name.len;	de = (struct ext3_dir_entry_2 *) bh->b_data;	dlimit = bh->b_data + dir->i_sb->s_blocksize;	while ((char *) de < dlimit) {		/* this code is executed quadratically often */		/* do minimal checking `by hand' */		if ((char *) de + namelen <= dlimit &&		    ext3_match (namelen, name, de)) {			/* found a match - just to be sure, do a full check */			if (!ext3_check_dir_entry("ext3_find_entry",						  dir, de, bh, offset))				return -1;			*res_dir = de;			return 1;		}		/* prevent looping on a bad block */		de_len = le16_to_cpu(de->rec_len);		if (de_len <= 0)			return -1;		offset += de_len;		de = (struct ext3_dir_entry_2 *) ((char *) de + de_len);	}	return 0;}/* *	ext3_find_entry() * * finds an entry in the specified directory with the wanted name. It * returns the cache buffer in which the entry was found, and the entry * itself (as a parameter - res_dir). It does NOT read the inode of the * entry - you'll have to do that yourself if you want to. * * The returned buffer_head has ->b_count elevated.  The caller is expected * to brelse() it when appropriate. */static struct buffer_head * ext3_find_entry (struct dentry *dentry,					struct ext3_dir_entry_2 ** res_dir){	struct super_block * sb;	struct buffer_head * bh_use[NAMEI_RA_SIZE];	struct buffer_head * bh, *ret = NULL;	unsigned long start, block, b;	int ra_max = 0;		/* Number of bh's in the readahead				   buffer, bh_use[] */	int ra_ptr = 0;		/* Current index into readahead				   buffer */	int num = 0;	int nblocks, i, err;	struct inode *dir = dentry->d_parent->d_inode;	int namelen;	const u8 *name;	unsigned blocksize;	*res_dir = NULL;	sb = dir->i_sb;	blocksize = sb->s_blocksize;	namelen = dentry->d_name.len;	name = dentry->d_name.name;	if (namelen > EXT3_NAME_LEN)		return NULL;#ifdef CONFIG_EXT3_INDEX	if (is_dx(dir)) {		bh = ext3_dx_find_entry(dentry, res_dir, &err);		/*		 * On success, or if the error was file not found,		 * return.  Otherwise, fall back to doing a search the		 * old fashioned way.		 */		if (bh || (err != ERR_BAD_DX_DIR))			return bh;		dxtrace(printk("ext3_find_entry: dx failed, falling back\n"));	}#endif	nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb);	start = EXT3_I(dir)->i_dir_start_lookup;	if (start >= nblocks)		start = 0;	block = start;restart:	do {		/*		 * We deal with the read-ahead logic here.		 */		if (ra_ptr >= ra_max) {			/* Refill the readahead buffer */			ra_ptr = 0;			b = block;			for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) {				/*				 * Terminate if we reach the end of the				 * directory and must wrap, or if our				 * search has finished at this block.				 */				if (b >= nblocks || (num && block == start)) {					bh_use[ra_max] = NULL;					break;				}				num++;				bh = ext3_getblk(NULL, dir, b++, 0, &err);				bh_use[ra_max] = bh;				if (bh)					ll_rw_block(READ, 1, &bh);			}		}		if ((bh = bh_use[ra_ptr++]) == NULL)			goto next;		wait_on_buffer(bh);		if (!buffer_uptodate(bh)) {			/* read error, skip block & hope for the best */			ext3_error(sb, __FUNCTION__, "reading directory #%lu "				   "offset %lu\n", dir->i_ino, block);			brelse(bh);			goto next;		}		i = search_dirblock(bh, dir, dentry,			    block << EXT3_BLOCK_SIZE_BITS(sb), res_dir);		if (i == 1) {			EXT3_I(dir)->i_dir_start_lookup = block;			ret = bh;			goto cleanup_and_exit;		} else {			brelse(bh);			if (i < 0)				goto cleanup_and_exit;		}	next:		if (++block >= nblocks)			block = 0;	} while (block != start);	/*	 * If the directory has grown while we were searching, then	 * search the last part of the directory before giving up.	 */	block = nblocks;	nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb);	if (block < nblocks) {		start = 0;		goto restart;	}cleanup_and_exit:	/* Clean up the read-ahead blocks */	for (; ra_ptr < ra_max; ra_ptr++)		brelse (bh_use[ra_ptr]);	return ret;}#ifdef CONFIG_EXT3_INDEXstatic struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,		       struct ext3_dir_entry_2 **res_dir, int *err){	struct super_block * sb;	struct dx_hash_info	hinfo;	u32 hash;	struct dx_frame frames[2], *frame;	struct ext3_dir_entry_2 *de, *top;	struct buffer_head *bh;	unsigned long block;	int retval;	int namelen = dentry->d_name.len;	const u8 *name = dentry->d_name.name;	struct inode *dir = dentry->d_parent->d_inode;	sb = dir->i_sb;	if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))		return NULL;	hash = hinfo.hash;	do {		block = dx_get_block(frame->at);		if (!(bh = ext3_bread (NULL,dir, block, 0, err)))			goto errout;		de = (struct ext3_dir_entry_2 *) bh->b_data;		top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize -				       EXT3_DIR_REC_LEN(0));		for (; de < top; de = ext3_next_entry(de))		if (ext3_match (namelen, name, de)) {			if (!ext3_check_dir_entry("ext3_find_entry",						  dir, de, bh,				  (block<<EXT3_BLOCK_SIZE_BITS(sb))					  +((char *)de - bh->b_data))) {				brelse (bh);				goto errout;			}

⌨️ 快捷键说明

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