⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 namei.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * controlled by the hash parameter.  If the hash value is even, then * the search is only continued if the next block starts with that * hash value.  This is used if we are searching for a specific file. * * If the hash value is HASH_NB_ALWAYS, then always go to the next block. * * This function returns 1 if the caller should continue to search, * or 0 if it should not.  If there is an error reading one of the * index blocks, it will a negative error code. * * If start_hash is non-null, it will be filled in with the starting * hash of the next page. */static int ext4_htree_next_block(struct inode *dir, __u32 hash,				 struct dx_frame *frame,				 struct dx_frame *frames,				 __u32 *start_hash){	struct dx_frame *p;	struct buffer_head *bh;	int err, num_frames = 0;	__u32 bhash;	p = frame;	/*	 * 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 = ext4_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 ext4_dir_entry_2 *ext4_next_entry(struct ext4_dir_entry_2 *p){	return (struct ext4_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 ext4_dir_entry_2 *de, *top;	int err, count = 0;	dxtrace(printk("In htree dirblock_to_tree: block %d\n", block));	if (!(bh = ext4_bread (NULL, dir, block, 0, &err)))		return err;	de = (struct ext4_dir_entry_2 *) bh->b_data;	top = (struct ext4_dir_entry_2 *) ((char *) de +					   dir->i_sb->s_blocksize -					   EXT4_DIR_REC_LEN(0));	for (; de < top; de = ext4_next_entry(de)) {		if (!ext4_check_dir_entry("htree_dirblock_to_tree", dir, de, bh,					(block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))						+((char *)de - bh->b_data))) {			/* On error, skip the f_pos to the next block. */			dir_file->f_pos = (dir_file->f_pos |					(dir->i_sb->s_blocksize - 1)) + 1;			brelse (bh);			return count;		}		ext4fs_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 = ext4_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 ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,			 __u32 start_minor_hash, __u32 *next_hash){	struct dx_hash_info hinfo;	struct ext4_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_path.dentry->d_inode;	if (!(EXT4_I(dir)->i_flags & EXT4_INDEX_FL)) {		hinfo.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;		hinfo.seed = EXT4_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_path.dentry->d_inode, &hinfo, frames, &err);	if (!frame)		return err;	/* Add '.' and '..' from the htree header */	if (!start_hash && !start_minor_hash) {		de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data;		if ((err = ext4_htree_store_dirent(dir_file, 0, 0, de)) != 0)			goto errout;		count++;	}	if (start_hash < 2 || (start_hash ==2 && start_minor_hash==0)) {		de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data;		de = ext4_next_entry(de);		if ((err = ext4_htree_store_dirent(dir_file, 2, 0, de)) != 0)			goto errout;		count++;	}	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 = ext4_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 *//* * Create map of hash values, offsets, and sizes, stored at end of block. * Returns number of entries mapped. */static int dx_make_map (struct ext4_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) {			ext4fs_dirhash(de->name, de->name_len, &h);			map_tail--;			map_tail->hash = h.hash;			map_tail->offs = (u16) ((char *) de - base);			map_tail->size = le16_to_cpu(de->rec_len);			count++;			cond_resched();		}		/* XXX: do we need to check rec_len == 0 case? -Chris */		de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));	}	return count;}/* Sort map by hash value */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);}static void ext4_update_dx_flag(struct inode *inode){	if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb,				     EXT4_FEATURE_COMPAT_DIR_INDEX))		EXT4_I(inode)->i_flags &= ~EXT4_INDEX_FL;}/* * NOTE! unlike strncmp, ext4_match returns 1 for success, 0 for failure. * * `len <= EXT4_NAME_LEN' is guaranteed by caller. * `de != NULL' is guaranteed by caller. */static inline int ext4_match (int len, const char * const name,			      struct ext4_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 ext4_dir_entry_2 ** res_dir){	struct ext4_dir_entry_2 * de;	char * dlimit;	int de_len;	const char *name = dentry->d_name.name;	int namelen = dentry->d_name.len;	de = (struct ext4_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 &&		    ext4_match (namelen, name, de)) {			/* found a match - just to be sure, do a full check */			if (!ext4_check_dir_entry("ext4_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 ext4_dir_entry_2 *) ((char *) de + de_len);	}	return 0;}/* *	ext4_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 * ext4_find_entry (struct dentry *dentry,					struct ext4_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 > EXT4_NAME_LEN)		return NULL;	if (is_dx(dir)) {		bh = ext4_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("ext4_find_entry: dx failed, falling back\n"));	}	nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb);	start = EXT4_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 = ext4_getblk(NULL, dir, b++, 0, &err);				bh_use[ra_max] = bh;				if (bh)					ll_rw_block(READ_META, 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 */			ext4_error(sb, __FUNCTION__, "reading directory #%lu "				   "offset %lu", dir->i_ino, block);			brelse(bh);			goto next;		}		i = search_dirblock(bh, dir, dentry,			    block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);		if (i == 1) {			EXT4_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 >> EXT4_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;}

⌨️ 快捷键说明

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