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

📄 namei.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
static struct buffer_head * ext4_dx_find_entry(struct dentry *dentry,		       struct ext4_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 ext4_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;	/* NFS may look up ".." - look at dx_root directory block */	if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){		if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))			return NULL;	} else {		frame = frames;		frame->bh = NULL;			/* for dx_release() */		frame->at = (struct dx_entry *)frames;	/* hack for zero entry*/		dx_set_block(frame->at, 0);		/* dx_root block is 0 */	}	hash = hinfo.hash;	do {		block = dx_get_block(frame->at);		if (!(bh = ext4_bread (NULL,dir, block, 0, err)))			goto errout;		de = (struct ext4_dir_entry_2 *) bh->b_data;		top = (struct ext4_dir_entry_2 *) ((char *) de + sb->s_blocksize -				       EXT4_DIR_REC_LEN(0));		for (; de < top; de = ext4_next_entry(de))		if (ext4_match (namelen, name, de)) {			if (!ext4_check_dir_entry("ext4_find_entry",						  dir, de, bh,				  (block<<EXT4_BLOCK_SIZE_BITS(sb))					  +((char *)de - bh->b_data))) {				brelse (bh);				*err = ERR_BAD_DX_DIR;				goto errout;			}			*res_dir = de;			dx_release (frames);			return bh;		}		brelse (bh);		/* Check to see if we should continue to search */		retval = ext4_htree_next_block(dir, hash, frame,					       frames, NULL);		if (retval < 0) {			ext4_warning(sb, __FUNCTION__,			     "error reading index page in directory #%lu",			     dir->i_ino);			*err = retval;			goto errout;		}	} while (retval == 1);	*err = -ENOENT;errout:	dxtrace(printk("%s not found\n", name));	dx_release (frames);	return NULL;}static struct dentry *ext4_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd){	struct inode * inode;	struct ext4_dir_entry_2 * de;	struct buffer_head * bh;	if (dentry->d_name.len > EXT4_NAME_LEN)		return ERR_PTR(-ENAMETOOLONG);	bh = ext4_find_entry(dentry, &de);	inode = NULL;	if (bh) {		unsigned long ino = le32_to_cpu(de->inode);		brelse (bh);		if (!ext4_valid_inum(dir->i_sb, ino)) {			ext4_error(dir->i_sb, "ext4_lookup",				   "bad inode number: %lu", ino);			inode = NULL;		} else			inode = iget(dir->i_sb, ino);		if (!inode)			return ERR_PTR(-EACCES);		if (is_bad_inode(inode)) {			iput(inode);			return ERR_PTR(-ENOENT);		}	}	return d_splice_alias(inode, dentry);}struct dentry *ext4_get_parent(struct dentry *child){	unsigned long ino;	struct dentry *parent;	struct inode *inode;	struct dentry dotdot;	struct ext4_dir_entry_2 * de;	struct buffer_head *bh;	dotdot.d_name.name = "..";	dotdot.d_name.len = 2;	dotdot.d_parent = child; /* confusing, isn't it! */	bh = ext4_find_entry(&dotdot, &de);	inode = NULL;	if (!bh)		return ERR_PTR(-ENOENT);	ino = le32_to_cpu(de->inode);	brelse(bh);	if (!ext4_valid_inum(child->d_inode->i_sb, ino)) {		ext4_error(child->d_inode->i_sb, "ext4_get_parent",			   "bad inode number: %lu", ino);		inode = NULL;	} else		inode = iget(child->d_inode->i_sb, ino);	if (!inode)		return ERR_PTR(-EACCES);	if (is_bad_inode(inode)) {		iput(inode);		return ERR_PTR(-ENOENT);	}	parent = d_alloc_anon(inode);	if (!parent) {		iput(inode);		parent = ERR_PTR(-ENOMEM);	}	return parent;}#define S_SHIFT 12static unsigned char ext4_type_by_mode[S_IFMT >> S_SHIFT] = {	[S_IFREG >> S_SHIFT]	= EXT4_FT_REG_FILE,	[S_IFDIR >> S_SHIFT]	= EXT4_FT_DIR,	[S_IFCHR >> S_SHIFT]	= EXT4_FT_CHRDEV,	[S_IFBLK >> S_SHIFT]	= EXT4_FT_BLKDEV,	[S_IFIFO >> S_SHIFT]	= EXT4_FT_FIFO,	[S_IFSOCK >> S_SHIFT]	= EXT4_FT_SOCK,	[S_IFLNK >> S_SHIFT]	= EXT4_FT_SYMLINK,};static inline void ext4_set_de_type(struct super_block *sb,				struct ext4_dir_entry_2 *de,				umode_t mode) {	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE))		de->file_type = ext4_type_by_mode[(mode & S_IFMT)>>S_SHIFT];}/* * Move count entries from end of map between two memory locations. * Returns pointer to last entry moved. */static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count){	unsigned rec_len = 0;	while (count--) {		struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *) (from + map->offs);		rec_len = EXT4_DIR_REC_LEN(de->name_len);		memcpy (to, de, rec_len);		((struct ext4_dir_entry_2 *) to)->rec_len =				cpu_to_le16(rec_len);		de->inode = 0;		map++;		to += rec_len;	}	return (struct ext4_dir_entry_2 *) (to - rec_len);}/* * Compact each dir entry in the range to the minimal rec_len. * Returns pointer to last entry in range. */static struct ext4_dir_entry_2* dx_pack_dirents(char *base, int size){	struct ext4_dir_entry_2 *next, *to, *prev, *de = (struct ext4_dir_entry_2 *) base;	unsigned rec_len = 0;	prev = to = de;	while ((char*)de < base + size) {		next = (struct ext4_dir_entry_2 *) ((char *) de +						    le16_to_cpu(de->rec_len));		if (de->inode && de->name_len) {			rec_len = EXT4_DIR_REC_LEN(de->name_len);			if (de > to)				memmove(to, de, rec_len);			to->rec_len = cpu_to_le16(rec_len);			prev = to;			to = (struct ext4_dir_entry_2 *) (((char *) to) + rec_len);		}		de = next;	}	return prev;}/* * Split a full leaf block to make room for a new dir entry. * Allocate a new block, and move entries so that they are approx. equally full. * Returns pointer to de in block into which the new entry will be inserted. */static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,			struct buffer_head **bh,struct dx_frame *frame,			struct dx_hash_info *hinfo, int *error){	unsigned blocksize = dir->i_sb->s_blocksize;	unsigned count, continued;	struct buffer_head *bh2;	u32 newblock;	u32 hash2;	struct dx_map_entry *map;	char *data1 = (*bh)->b_data, *data2;	unsigned split, move, size, i;	struct ext4_dir_entry_2 *de = NULL, *de2;	int	err = 0;	bh2 = ext4_append (handle, dir, &newblock, &err);	if (!(bh2)) {		brelse(*bh);		*bh = NULL;		goto errout;	}	BUFFER_TRACE(*bh, "get_write_access");	err = ext4_journal_get_write_access(handle, *bh);	if (err)		goto journal_error;	BUFFER_TRACE(frame->bh, "get_write_access");	err = ext4_journal_get_write_access(handle, frame->bh);	if (err)		goto journal_error;	data2 = bh2->b_data;	/* create map in the end of data2 block */	map = (struct dx_map_entry *) (data2 + blocksize);	count = dx_make_map ((struct ext4_dir_entry_2 *) data1,			     blocksize, hinfo, map);	map -= count;	dx_sort_map (map, count);	/* Split the existing block in the middle, size-wise */	size = 0;	move = 0;	for (i = count-1; i >= 0; i--) {		/* is more than half of this entry in 2nd half of the block? */		if (size + map[i].size/2 > blocksize/2)			break;		size += map[i].size;		move++;	}	/* map index at which we will split */	split = count - move;	hash2 = map[split].hash;	continued = hash2 == map[split - 1].hash;	dxtrace(printk("Split block %i at %x, %i/%i\n",		dx_get_block(frame->at), hash2, split, count-split));	/* Fancy dance to stay within two buffers */	de2 = dx_move_dirents(data1, data2, map + split, count - split);	de = dx_pack_dirents(data1,blocksize);	de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);	de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2);	dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1));	dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1));	/* Which block gets the new entry? */	if (hinfo->hash >= hash2)	{		swap(*bh, bh2);		de = de2;	}	dx_insert_block (frame, hash2 + continued, newblock);	err = ext4_journal_dirty_metadata (handle, bh2);	if (err)		goto journal_error;	err = ext4_journal_dirty_metadata (handle, frame->bh);	if (err)		goto journal_error;	brelse (bh2);	dxtrace(dx_show_index ("frame", frame->entries));	return de;journal_error:	brelse(*bh);	brelse(bh2);	*bh = NULL;	ext4_std_error(dir->i_sb, err);errout:	*error = err;	return NULL;}/* * Add a new entry into a directory (leaf) block.  If de is non-NULL, * it points to a directory entry which is guaranteed to be large * enough for new directory entry.  If de is NULL, then * add_dirent_to_buf will attempt search the directory block for * space.  It will return -ENOSPC if no space is available, and -EIO * and -EEXIST if directory entry already exists. * * NOTE!  bh is NOT released in the case where ENOSPC is returned.  In * all other cases bh is released. */static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,			     struct inode *inode, struct ext4_dir_entry_2 *de,			     struct buffer_head * bh){	struct inode	*dir = dentry->d_parent->d_inode;	const char	*name = dentry->d_name.name;	int		namelen = dentry->d_name.len;	unsigned long	offset = 0;	unsigned short	reclen;	int		nlen, rlen, err;	char		*top;	reclen = EXT4_DIR_REC_LEN(namelen);	if (!de) {		de = (struct ext4_dir_entry_2 *)bh->b_data;		top = bh->b_data + dir->i_sb->s_blocksize - reclen;		while ((char *) de <= top) {			if (!ext4_check_dir_entry("ext4_add_entry", dir, de,						  bh, offset)) {				brelse (bh);				return -EIO;			}			if (ext4_match (namelen, name, de)) {				brelse (bh);				return -EEXIST;			}			nlen = EXT4_DIR_REC_LEN(de->name_len);			rlen = le16_to_cpu(de->rec_len);			if ((de->inode? rlen - nlen: rlen) >= reclen)				break;			de = (struct ext4_dir_entry_2 *)((char *)de + rlen);			offset += rlen;		}		if ((char *) de > top)			return -ENOSPC;	}	BUFFER_TRACE(bh, "get_write_access");	err = ext4_journal_get_write_access(handle, bh);	if (err) {		ext4_std_error(dir->i_sb, err);		brelse(bh);		return err;	}	/* By now the buffer is marked for journaling */	nlen = EXT4_DIR_REC_LEN(de->name_len);	rlen = le16_to_cpu(de->rec_len);	if (de->inode) {		struct ext4_dir_entry_2 *de1 = (struct ext4_dir_entry_2 *)((char *)de + nlen);		de1->rec_len = cpu_to_le16(rlen - nlen);		de->rec_len = cpu_to_le16(nlen);		de = de1;	}	de->file_type = EXT4_FT_UNKNOWN;	if (inode) {		de->inode = cpu_to_le32(inode->i_ino);		ext4_set_de_type(dir->i_sb, de, inode->i_mode);	} else		de->inode = 0;	de->name_len = namelen;	memcpy (de->name, name, namelen);	/*	 * XXX shouldn't update any times until successful	 * completion of syscall, but too many callers depend	 * on this.	 *	 * XXX similarly, too many callers depend on	 * ext4_new_inode() setting the times, but error	 * recovery deletes the inode, so the worst that can	 * happen is that the times are slightly out of date	 * and/or different from the directory change time.	 */	dir->i_mtime = dir->i_ctime = ext4_current_time(dir);	ext4_update_dx_flag(dir);	dir->i_version++;	ext4_mark_inode_dirty(handle, dir);	BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata");	err = ext4_journal_dirty_metadata(handle, bh);	if (err)		ext4_std_error(dir->i_sb, err);	brelse(bh);	return 0;}/* * This converts a one block unindexed directory to a 3 block indexed * directory, and adds the dentry to the indexed directory. */static int make_indexed_dir(handle_t *handle, struct dentry *dentry,			    struct inode *inode, struct buffer_head *bh){	struct inode	*dir = dentry->d_parent->d_inode;	const char	*name = dentry->d_name.name;	int		namelen = dentry->d_name.len;	struct buffer_head *bh2;	struct dx_root	*root;	struct dx_frame	frames[2], *frame;	struct dx_entry *entries;	struct ext4_dir_entry_2	*de, *de2;	char		*data1, *top;	unsigned	len;	int		retval;	unsigned	blocksize;	struct dx_hash_info hinfo;	u32		block;	struct fake_dirent *fde;	blocksize =  dir->i_sb->s_blocksize;	dxtrace(printk("Creating index\n"));	retval = ext4_journal_get_write_access(handle, bh);	if (retval) {		ext4_std_error(dir->i_sb, retval);		brelse(bh);		return retval;	}	root = (struct dx_root *) bh->b_data;	bh2 = ext4_append (handle, dir, &block, &retval);	if (!(bh2)) {		brelse(bh);		return retval;	}	EXT4_I(dir)->i_flags |= EXT4_INDEX_FL;	data1 = bh2->b_data;	/* The 0th block becomes the root, move the dirents out */	fde = &root->dotdot;	de = (struct ext4_dir_entry_2 *)((char *)fde + le16_to_cpu(fde->rec_len));	len = ((char *) root) + blocksize - (char *) de;	memcpy (data1, de, len);	de = (struct ext4_dir_entry_2 *) data1;	top = data1 + len;	while ((char *)(de2=(void*)de+le16_to_cpu(de->rec_len)) < top)		de = de2;	de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);	/* Initialize the root; the dot dirents already exist */	de = (struct ext4_dir_entry_2 *) (&root->dotdot);	de->rec_len = cpu_to_le16(blocksize - EXT4_DIR_REC_LEN(2));	memset (&root->info, 0, sizeof(root->info));	root->info.info_length = sizeof(root->info);	root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;	entries = root->entries;	dx_set_block (entries, 1);	dx_set_count (entries, 1);	dx_set_limit (entries, dx_root_limit(dir, sizeof(root->info)));	/* Initialize as for dx_probe */	hinfo.hash_version = root->info.hash_version;	hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;	ext4fs_dirhash(name, namelen, &hinfo);	frame = frames;	frame->entries = entries;	frame->at = entries;	frame->bh = bh;	bh = bh2;	de = do_split(handle,dir, &bh, frame, &hinfo, &retval);	dx_release (frames);	if (!(de))		return retval;

⌨️ 快捷键说明

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