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

📄 dir.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
static int ocfs2_dir_foreach_blk_id(struct inode *inode,				    u64 *f_version,				    loff_t *f_pos, void *priv,				    filldir_t filldir, int *filldir_err){	int ret, i, filldir_ret;	unsigned long offset = *f_pos;	struct buffer_head *di_bh = NULL;	struct ocfs2_dinode *di;	struct ocfs2_inline_data *data;	struct ocfs2_dir_entry *de;	ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,			       &di_bh, OCFS2_BH_CACHED, inode);	if (ret) {		mlog(ML_ERROR, "Unable to read inode block for dir %llu\n",		     (unsigned long long)OCFS2_I(inode)->ip_blkno);		goto out;	}	di = (struct ocfs2_dinode *)di_bh->b_data;	data = &di->id2.i_data;	while (*f_pos < i_size_read(inode)) {revalidate:		/* If the dir block has changed since the last call to		 * readdir(2), then we might be pointing to an invalid		 * dirent right now.  Scan from the start of the block		 * to make sure. */		if (*f_version != inode->i_version) {			for (i = 0; i < i_size_read(inode) && i < offset; ) {				de = (struct ocfs2_dir_entry *)					(data->id_data + i);				/* It's too expensive to do a full				 * dirent test each time round this				 * loop, but we do have to test at				 * least that it is non-zero.  A				 * failure will be detected in the				 * dirent test below. */				if (le16_to_cpu(de->rec_len) <				    OCFS2_DIR_REC_LEN(1))					break;				i += le16_to_cpu(de->rec_len);			}			*f_pos = offset = i;			*f_version = inode->i_version;		}		de = (struct ocfs2_dir_entry *) (data->id_data + *f_pos);		if (!ocfs2_check_dir_entry(inode, de, di_bh, *f_pos)) {			/* On error, skip the f_pos to the end. */			*f_pos = i_size_read(inode);			goto out;		}		offset += le16_to_cpu(de->rec_len);		if (le64_to_cpu(de->inode)) {			/* We might block in the next section			 * if the data destination is			 * currently swapped out.  So, use a			 * version stamp to detect whether or			 * not the directory has been modified			 * during the copy operation.			 */			u64 version = *f_version;			unsigned char d_type = DT_UNKNOWN;			if (de->file_type < OCFS2_FT_MAX)				d_type = ocfs2_filetype_table[de->file_type];			filldir_ret = filldir(priv, de->name,					      de->name_len,					      *f_pos,					      le64_to_cpu(de->inode),					      d_type);			if (filldir_ret) {				if (filldir_err)					*filldir_err = filldir_ret;				break;			}			if (version != *f_version)				goto revalidate;		}		*f_pos += le16_to_cpu(de->rec_len);	}out:	brelse(di_bh);	return 0;}static int ocfs2_dir_foreach_blk_el(struct inode *inode,				    u64 *f_version,				    loff_t *f_pos, void *priv,				    filldir_t filldir, int *filldir_err){	int error = 0;	unsigned long offset, blk, last_ra_blk = 0;	int i, stored;	struct buffer_head * bh, * tmp;	struct ocfs2_dir_entry * de;	int err;	struct super_block * sb = inode->i_sb;	unsigned int ra_sectors = 16;	stored = 0;	bh = NULL;	offset = (*f_pos) & (sb->s_blocksize - 1);	while (!error && !stored && *f_pos < i_size_read(inode)) {		blk = (*f_pos) >> sb->s_blocksize_bits;		bh = ocfs2_bread(inode, blk, &err, 0);		if (!bh) {			mlog(ML_ERROR,			     "directory #%llu contains a hole at offset %lld\n",			     (unsigned long long)OCFS2_I(inode)->ip_blkno,			     *f_pos);			*f_pos += sb->s_blocksize - offset;			continue;		}		/* The idea here is to begin with 8k read-ahead and to stay		 * 4k ahead of our current position.		 *		 * TODO: Use the pagecache for this. We just need to		 * make sure it's cluster-safe... */		if (!last_ra_blk		    || (((last_ra_blk - blk) << 9) <= (ra_sectors / 2))) {			for (i = ra_sectors >> (sb->s_blocksize_bits - 9);			     i > 0; i--) {				tmp = ocfs2_bread(inode, ++blk, &err, 1);				if (tmp)					brelse(tmp);			}			last_ra_blk = blk;			ra_sectors = 8;		}revalidate:		/* If the dir block has changed since the last call to		 * readdir(2), then we might be pointing to an invalid		 * dirent right now.  Scan from the start of the block		 * to make sure. */		if (*f_version != inode->i_version) {			for (i = 0; i < sb->s_blocksize && i < offset; ) {				de = (struct ocfs2_dir_entry *) (bh->b_data + i);				/* It's too expensive to do a full				 * dirent test each time round this				 * loop, but we do have to test at				 * least that it is non-zero.  A				 * failure will be detected in the				 * dirent test below. */				if (le16_to_cpu(de->rec_len) <				    OCFS2_DIR_REC_LEN(1))					break;				i += le16_to_cpu(de->rec_len);			}			offset = i;			*f_pos = ((*f_pos) & ~(sb->s_blocksize - 1))				| offset;			*f_version = inode->i_version;		}		while (!error && *f_pos < i_size_read(inode)		       && offset < sb->s_blocksize) {			de = (struct ocfs2_dir_entry *) (bh->b_data + offset);			if (!ocfs2_check_dir_entry(inode, de, bh, offset)) {				/* On error, skip the f_pos to the				   next block. */				*f_pos = ((*f_pos) | (sb->s_blocksize - 1)) + 1;				brelse(bh);				goto out;			}			offset += le16_to_cpu(de->rec_len);			if (le64_to_cpu(de->inode)) {				/* We might block in the next section				 * if the data destination is				 * currently swapped out.  So, use a				 * version stamp to detect whether or				 * not the directory has been modified				 * during the copy operation.				 */				unsigned long version = *f_version;				unsigned char d_type = DT_UNKNOWN;				if (de->file_type < OCFS2_FT_MAX)					d_type = ocfs2_filetype_table[de->file_type];				error = filldir(priv, de->name,						de->name_len,						*f_pos,						le64_to_cpu(de->inode),						d_type);				if (error) {					if (filldir_err)						*filldir_err = error;					break;				}				if (version != *f_version)					goto revalidate;				stored ++;			}			*f_pos += le16_to_cpu(de->rec_len);		}		offset = 0;		brelse(bh);	}	stored = 0;out:	return stored;}static int ocfs2_dir_foreach_blk(struct inode *inode, u64 *f_version,				 loff_t *f_pos, void *priv, filldir_t filldir,				 int *filldir_err){	if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)		return ocfs2_dir_foreach_blk_id(inode, f_version, f_pos, priv,						filldir, filldir_err);	return ocfs2_dir_foreach_blk_el(inode, f_version, f_pos, priv, filldir,					filldir_err);}/* * This is intended to be called from inside other kernel functions, * so we fake some arguments. */int ocfs2_dir_foreach(struct inode *inode, loff_t *f_pos, void *priv,		      filldir_t filldir){	int ret = 0, filldir_err = 0;	u64 version = inode->i_version;	while (*f_pos < i_size_read(inode)) {		ret = ocfs2_dir_foreach_blk(inode, &version, f_pos, priv,					    filldir, &filldir_err);		if (ret || filldir_err)			break;	}	if (ret > 0)		ret = -EIO;	return 0;}/* * ocfs2_readdir() * */int ocfs2_readdir(struct file * filp, void * dirent, filldir_t filldir){	int error = 0;	struct inode *inode = filp->f_path.dentry->d_inode;	int lock_level = 0;	mlog_entry("dirino=%llu\n",		   (unsigned long long)OCFS2_I(inode)->ip_blkno);	error = ocfs2_meta_lock_atime(inode, filp->f_vfsmnt, &lock_level);	if (lock_level && error >= 0) {		/* We release EX lock which used to update atime		 * and get PR lock again to reduce contention		 * on commonly accessed directories. */		ocfs2_meta_unlock(inode, 1);		lock_level = 0;		error = ocfs2_meta_lock(inode, NULL, 0);	}	if (error < 0) {		if (error != -ENOENT)			mlog_errno(error);		/* we haven't got any yet, so propagate the error. */		goto bail_nolock;	}	error = ocfs2_dir_foreach_blk(inode, &filp->f_version, &filp->f_pos,				      dirent, filldir, NULL);	ocfs2_meta_unlock(inode, lock_level);bail_nolock:	mlog_exit(error);	return error;}/* * NOTE: this should always be called with parent dir i_mutex taken. */int ocfs2_find_files_on_disk(const char *name,			     int namelen,			     u64 *blkno,			     struct inode *inode,			     struct buffer_head **dirent_bh,			     struct ocfs2_dir_entry **dirent){	int status = -ENOENT;	mlog_entry("(name=%.*s, blkno=%p, inode=%p, dirent_bh=%p, dirent=%p)\n",		   namelen, name, blkno, inode, dirent_bh, dirent);	*dirent_bh = ocfs2_find_entry(name, namelen, inode, dirent);	if (!*dirent_bh || !*dirent) {		status = -ENOENT;		goto leave;	}	*blkno = le64_to_cpu((*dirent)->inode);	status = 0;leave:	if (status < 0) {		*dirent = NULL;		if (*dirent_bh) {			brelse(*dirent_bh);			*dirent_bh = NULL;		}	}	mlog_exit(status);	return status;}/* * Convenience function for callers which just want the block number * mapped to a name and don't require the full dirent info, etc. */int ocfs2_lookup_ino_from_name(struct inode *dir, const char *name,			       int namelen, u64 *blkno){	int ret;	struct buffer_head *bh = NULL;	struct ocfs2_dir_entry *dirent = NULL;	ret = ocfs2_find_files_on_disk(name, namelen, blkno, dir, &bh, &dirent);	brelse(bh);	return ret;}/* Check for a name within a directory. * * Return 0 if the name does not exist * Return -EEXIST if the directory contains the name * * Callers should have i_mutex + a cluster lock on dir */int ocfs2_check_dir_for_entry(struct inode *dir,			      const char *name,			      int namelen){	int ret;	struct buffer_head *dirent_bh = NULL;	struct ocfs2_dir_entry *dirent = NULL;	mlog_entry("dir %llu, name '%.*s'\n",		   (unsigned long long)OCFS2_I(dir)->ip_blkno, namelen, name);	ret = -EEXIST;	dirent_bh = ocfs2_find_entry(name, namelen, dir, &dirent);	if (dirent_bh)		goto bail;	ret = 0;bail:	if (dirent_bh)		brelse(dirent_bh);	mlog_exit(ret);	return ret;}struct ocfs2_empty_dir_priv {	unsigned seen_dot;	unsigned seen_dot_dot;	unsigned seen_other;};static int ocfs2_empty_dir_filldir(void *priv, const char *name, int name_len,				   loff_t pos, u64 ino, unsigned type){	struct ocfs2_empty_dir_priv *p = priv;	/*	 * Check the positions of "." and ".." records to be sure	 * they're in the correct place.	 */	if (name_len == 1 && !strncmp(".", name, 1) && pos == 0) {		p->seen_dot = 1;		return 0;	}	if (name_len == 2 && !strncmp("..", name, 2) &&	    pos == OCFS2_DIR_REC_LEN(1)) {		p->seen_dot_dot = 1;		return 0;	}	p->seen_other = 1;	return 1;}/* * routine to check that the specified directory is empty (for rmdir) * * Returns 1 if dir is empty, zero otherwise. */int ocfs2_empty_dir(struct inode *inode){	int ret;	loff_t start = 0;	struct ocfs2_empty_dir_priv priv;	memset(&priv, 0, sizeof(priv));	ret = ocfs2_dir_foreach(inode, &start, &priv, ocfs2_empty_dir_filldir);	if (ret)		mlog_errno(ret);	if (!priv.seen_dot || !priv.seen_dot_dot) {		mlog(ML_ERROR, "bad directory (dir #%llu) - no `.' or `..'\n",		     (unsigned long long)OCFS2_I(inode)->ip_blkno);		/*		 * XXX: Is it really safe to allow an unlink to continue?		 */		return 1;	}	return !priv.seen_other;}static void ocfs2_fill_initial_dirents(struct inode *inode,				       struct inode *parent,				       char *start, unsigned int size){	struct ocfs2_dir_entry *de = (struct ocfs2_dir_entry *)start;	de->inode = cpu_to_le64(OCFS2_I(inode)->ip_blkno);	de->name_len = 1;	de->rec_len =		cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len));	strcpy(de->name, ".");	ocfs2_set_de_type(de, S_IFDIR);	de = (struct ocfs2_dir_entry *) ((char *)de + le16_to_cpu(de->rec_len));	de->inode = cpu_to_le64(OCFS2_I(parent)->ip_blkno);	de->rec_len = cpu_to_le16(size - OCFS2_DIR_REC_LEN(1));	de->name_len = 2;	strcpy(de->name, "..");	ocfs2_set_de_type(de, S_IFDIR);}/* * This works together with code in ocfs2_mknod_locked() which sets * the inline-data flag and initializes the inline-data section. */static int ocfs2_fill_new_dir_id(struct ocfs2_super *osb,				 handle_t *handle,				 struct inode *parent,				 struct inode *inode,				 struct buffer_head *di_bh){	int ret;	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;	struct ocfs2_inline_data *data = &di->id2.i_data;	unsigned int size = le16_to_cpu(data->id_count);	ret = ocfs2_journal_access(handle, inode, di_bh,				   OCFS2_JOURNAL_ACCESS_WRITE);	if (ret) {		mlog_errno(ret);		goto out;	}	ocfs2_fill_initial_dirents(inode, parent, data->id_data, size);	ocfs2_journal_dirty(handle, di_bh);	if (ret) {		mlog_errno(ret);		goto out;	}	i_size_write(inode, size);	inode->i_nlink = 2;	inode->i_blocks = ocfs2_inode_sector_count(inode);	ret = ocfs2_mark_inode_dirty(handle, inode, di_bh);	if (ret < 0)		mlog_errno(ret);out:	return ret;}static int ocfs2_fill_new_dir_el(struct ocfs2_super *osb,				 handle_t *handle,				 struct inode *parent,				 struct inode *inode,				 struct buffer_head *fe_bh,				 struct ocfs2_alloc_context *data_ac){	int status;	struct buffer_head *new_bh = NULL;	mlog_entry_void();	status = ocfs2_do_extend_dir(osb->sb, handle, inode, fe_bh,				     data_ac, NULL, &new_bh);	if (status < 0) {		mlog_errno(status);		goto bail;	}	ocfs2_set_new_buffer_uptodate(inode, new_bh);	status = ocfs2_journal_access(handle, inode, new_bh,				      OCFS2_JOURNAL_ACCESS_CREATE);	if (status < 0) {		mlog_errno(status);		goto bail;	}	memset(new_bh->b_data, 0, osb->sb->s_blocksize);	ocfs2_fill_initial_dirents(inode, parent, new_bh->b_data,				   osb->sb->s_blocksize);	status = ocfs2_journal_dirty(handle, new_bh);	if (status < 0) {		mlog_errno(status);		goto bail;	}	i_size_write(inode, inode->i_sb->s_blocksize);	inode->i_nlink = 2;	inode->i_blocks = ocfs2_inode_sector_count(inode);	status = ocfs2_mark_inode_dirty(handle, inode, fe_bh);	if (status < 0) {		mlog_errno(status);		goto bail;	}	status = 0;bail:	if (new_bh)		brelse(new_bh);	mlog_exit(status);	return status;}int ocfs2_fill_new_dir(struct ocfs2_super *osb,		       handle_t *handle,		       struct inode *parent,		       struct inode *inode,		       struct buffer_head *fe_bh,		       struct ocfs2_alloc_context *data_ac){	BUG_ON(!ocfs2_supports_inline_data(osb) && data_ac == NULL);	if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)		return ocfs2_fill_new_dir_id(osb, handle, parent, inode, fe_bh);	return ocfs2_fill_new_dir_el(osb, handle, parent, inode, fe_bh,				     data_ac);}static void ocfs2_expand_last_dirent(char *start, unsigned int old_size,				     unsigned int new_size){	struct ocfs2_dir_entry *de;	struct ocfs2_dir_entry *prev_de;	char *de_buf, *limit;	unsigned int bytes = new_size - old_size;	limit = start + old_size;	de_buf = start;	de = (struct ocfs2_dir_entry *)de_buf;	do {		prev_de = de;		de_buf += le16_to_cpu(de->rec_len);		de = (struct ocfs2_dir_entry *)de_buf;	} while (de_buf < limit);	le16_add_cpu(&prev_de->rec_len, bytes);}/*

⌨️ 快捷键说明

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