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

📄 dir.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
 * We allocate enough clusters to fulfill "blocks_wanted", but set * i_size to exactly one block. Ocfs2_extend_dir() will handle the * rest automatically for us. * * *first_block_bh is a pointer to the 1st data block allocated to the *  directory. */static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,				   unsigned int blocks_wanted,				   struct buffer_head **first_block_bh){	int ret, credits = OCFS2_INLINE_TO_EXTENTS_CREDITS;	u32 alloc, bit_off, len;	struct super_block *sb = dir->i_sb;	u64 blkno, bytes = blocks_wanted << sb->s_blocksize_bits;	struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);	struct ocfs2_inode_info *oi = OCFS2_I(dir);	struct ocfs2_alloc_context *data_ac;	struct buffer_head *dirdata_bh = NULL;	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;	handle_t *handle;	alloc = ocfs2_clusters_for_bytes(sb, bytes);	/*	 * We should never need more than 2 clusters for this -	 * maximum dirent size is far less than one block. In fact,	 * the only time we'd need more than one cluster is if	 * blocksize == clustersize and the dirent won't fit in the	 * extra space that the expansion to a single block gives. As	 * of today, that only happens on 4k/4k file systems.	 */	BUG_ON(alloc > 2);	ret = ocfs2_reserve_clusters(osb, alloc, &data_ac);	if (ret) {		mlog_errno(ret);		goto out;	}	down_write(&oi->ip_alloc_sem);	/*	 * Prepare for worst case allocation scenario of two seperate	 * extents.	 */	if (alloc == 2)		credits += OCFS2_SUBALLOC_ALLOC;	handle = ocfs2_start_trans(osb, credits);	if (IS_ERR(handle)) {		ret = PTR_ERR(handle);		mlog_errno(ret);		goto out_sem;	}	/*	 * Try to claim as many clusters as the bitmap can give though	 * if we only get one now, that's enough to continue. The rest	 * will be claimed after the conversion to extents.	 */	ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, &len);	if (ret) {		mlog_errno(ret);		goto out_commit;	}	/*	 * Operations are carefully ordered so that we set up the new	 * data block first. The conversion from inline data to	 * extents follows.	 */	blkno = ocfs2_clusters_to_blocks(dir->i_sb, bit_off);	dirdata_bh = sb_getblk(sb, blkno);	if (!dirdata_bh) {		ret = -EIO;		mlog_errno(ret);		goto out_commit;	}	ocfs2_set_new_buffer_uptodate(dir, dirdata_bh);	ret = ocfs2_journal_access(handle, dir, dirdata_bh,				   OCFS2_JOURNAL_ACCESS_CREATE);	if (ret) {		mlog_errno(ret);		goto out_commit;	}	memcpy(dirdata_bh->b_data, di->id2.i_data.id_data, i_size_read(dir));	memset(dirdata_bh->b_data + i_size_read(dir), 0,	       sb->s_blocksize - i_size_read(dir));	ocfs2_expand_last_dirent(dirdata_bh->b_data, i_size_read(dir),				 sb->s_blocksize);	ret = ocfs2_journal_dirty(handle, dirdata_bh);	if (ret) {		mlog_errno(ret);		goto out_commit;	}	/*	 * Set extent, i_size, etc on the directory. After this, the	 * inode should contain the same exact dirents as before and	 * be fully accessible from system calls.	 *	 * We let the later dirent insert modify c/mtime - to the user	 * the data hasn't changed.	 */	ret = ocfs2_journal_access(handle, dir, di_bh,				   OCFS2_JOURNAL_ACCESS_CREATE);	if (ret) {		mlog_errno(ret);		goto out_commit;	}	spin_lock(&oi->ip_lock);	oi->ip_dyn_features &= ~OCFS2_INLINE_DATA_FL;	di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);	spin_unlock(&oi->ip_lock);	ocfs2_dinode_new_extent_list(dir, di);	i_size_write(dir, sb->s_blocksize);	dir->i_mtime = dir->i_ctime = CURRENT_TIME;	di->i_size = cpu_to_le64(sb->s_blocksize);	di->i_ctime = di->i_mtime = cpu_to_le64(dir->i_ctime.tv_sec);	di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(dir->i_ctime.tv_nsec);	dir->i_blocks = ocfs2_inode_sector_count(dir);	/*	 * This should never fail as our extent list is empty and all	 * related blocks have been journaled already.	 */	ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 0, blkno, len, 0,				  NULL);	if (ret) {		mlog_errno(ret);		goto out;	}	ret = ocfs2_journal_dirty(handle, di_bh);	if (ret) {		mlog_errno(ret);		goto out_commit;	}	/*	 * We asked for two clusters, but only got one in the 1st	 * pass. Claim the 2nd cluster as a separate extent.	 */	if (alloc > len) {		ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off,					   &len);		if (ret) {			mlog_errno(ret);			goto out_commit;		}		blkno = ocfs2_clusters_to_blocks(dir->i_sb, bit_off);		ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 1, blkno,					  len, 0, NULL);		if (ret) {			mlog_errno(ret);			goto out;		}	}	*first_block_bh = dirdata_bh;	dirdata_bh = NULL;out_commit:	ocfs2_commit_trans(osb, handle);out_sem:	up_write(&oi->ip_alloc_sem);out:	if (data_ac)		ocfs2_free_alloc_context(data_ac);	brelse(dirdata_bh);	return ret;}/* returns a bh of the 1st new block in the allocation. */static int ocfs2_do_extend_dir(struct super_block *sb,			       handle_t *handle,			       struct inode *dir,			       struct buffer_head *parent_fe_bh,			       struct ocfs2_alloc_context *data_ac,			       struct ocfs2_alloc_context *meta_ac,			       struct buffer_head **new_bh){	int status;	int extend;	u64 p_blkno, v_blkno;	spin_lock(&OCFS2_I(dir)->ip_lock);	extend = (i_size_read(dir) == ocfs2_clusters_to_bytes(sb, OCFS2_I(dir)->ip_clusters));	spin_unlock(&OCFS2_I(dir)->ip_lock);	if (extend) {		u32 offset = OCFS2_I(dir)->ip_clusters;		status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, &offset,						    1, 0, parent_fe_bh, handle,						    data_ac, meta_ac, NULL);		BUG_ON(status == -EAGAIN);		if (status < 0) {			mlog_errno(status);			goto bail;		}	}	v_blkno = ocfs2_blocks_for_bytes(sb, i_size_read(dir));	status = ocfs2_extent_map_get_blocks(dir, v_blkno, &p_blkno, NULL, NULL);	if (status < 0) {		mlog_errno(status);		goto bail;	}	*new_bh = sb_getblk(sb, p_blkno);	if (!*new_bh) {		status = -EIO;		mlog_errno(status);		goto bail;	}	status = 0;bail:	mlog_exit(status);	return status;}/* * Assumes you already have a cluster lock on the directory. * * 'blocks_wanted' is only used if we have an inline directory which * is to be turned into an extent based one. The size of the dirent to * insert might be larger than the space gained by growing to just one * block, so we may have to grow the inode by two blocks in that case. */static int ocfs2_extend_dir(struct ocfs2_super *osb,			    struct inode *dir,			    struct buffer_head *parent_fe_bh,			    unsigned int blocks_wanted,			    struct buffer_head **new_de_bh){	int status = 0;	int credits, num_free_extents, drop_alloc_sem = 0;	loff_t dir_i_size;	struct ocfs2_dinode *fe = (struct ocfs2_dinode *) parent_fe_bh->b_data;	struct ocfs2_alloc_context *data_ac = NULL;	struct ocfs2_alloc_context *meta_ac = NULL;	handle_t *handle = NULL;	struct buffer_head *new_bh = NULL;	struct ocfs2_dir_entry * de;	struct super_block *sb = osb->sb;	mlog_entry_void();	if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {		status = ocfs2_expand_inline_dir(dir, parent_fe_bh,						 blocks_wanted, &new_bh);		if (status) {			mlog_errno(status);			goto bail;		}		if (blocks_wanted == 1) {			/*			 * If the new dirent will fit inside the space			 * created by pushing out to one block, then			 * we can complete the operation			 * here. Otherwise we have to expand i_size			 * and format the 2nd block below.			 */			BUG_ON(new_bh == NULL);			goto bail_bh;		}		/*		 * Get rid of 'new_bh' - we want to format the 2nd		 * data block and return that instead.		 */		brelse(new_bh);		new_bh = NULL;		dir_i_size = i_size_read(dir);		credits = OCFS2_SIMPLE_DIR_EXTEND_CREDITS;		goto do_extend;	}	dir_i_size = i_size_read(dir);	mlog(0, "extending dir %llu (i_size = %lld)\n",	     (unsigned long long)OCFS2_I(dir)->ip_blkno, dir_i_size);	/* dir->i_size is always block aligned. */	spin_lock(&OCFS2_I(dir)->ip_lock);	if (dir_i_size == ocfs2_clusters_to_bytes(sb, OCFS2_I(dir)->ip_clusters)) {		spin_unlock(&OCFS2_I(dir)->ip_lock);		num_free_extents = ocfs2_num_free_extents(osb, dir, fe);		if (num_free_extents < 0) {			status = num_free_extents;			mlog_errno(status);			goto bail;		}		if (!num_free_extents) {			status = ocfs2_reserve_new_metadata(osb, fe, &meta_ac);			if (status < 0) {				if (status != -ENOSPC)					mlog_errno(status);				goto bail;			}		}		status = ocfs2_reserve_clusters(osb, 1, &data_ac);		if (status < 0) {			if (status != -ENOSPC)				mlog_errno(status);			goto bail;		}		credits = ocfs2_calc_extend_credits(sb, fe, 1);	} else {		spin_unlock(&OCFS2_I(dir)->ip_lock);		credits = OCFS2_SIMPLE_DIR_EXTEND_CREDITS;	}do_extend:	down_write(&OCFS2_I(dir)->ip_alloc_sem);	drop_alloc_sem = 1;	handle = ocfs2_start_trans(osb, credits);	if (IS_ERR(handle)) {		status = PTR_ERR(handle);		handle = NULL;		mlog_errno(status);		goto bail;	}	status = ocfs2_do_extend_dir(osb->sb, handle, dir, parent_fe_bh,				     data_ac, meta_ac, &new_bh);	if (status < 0) {		mlog_errno(status);		goto bail;	}	ocfs2_set_new_buffer_uptodate(dir, new_bh);	status = ocfs2_journal_access(handle, dir, new_bh,				      OCFS2_JOURNAL_ACCESS_CREATE);	if (status < 0) {		mlog_errno(status);		goto bail;	}	memset(new_bh->b_data, 0, sb->s_blocksize);	de = (struct ocfs2_dir_entry *) new_bh->b_data;	de->inode = 0;	de->rec_len = cpu_to_le16(sb->s_blocksize);	status = ocfs2_journal_dirty(handle, new_bh);	if (status < 0) {		mlog_errno(status);		goto bail;	}	dir_i_size += dir->i_sb->s_blocksize;	i_size_write(dir, dir_i_size);	dir->i_blocks = ocfs2_inode_sector_count(dir);	status = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh);	if (status < 0) {		mlog_errno(status);		goto bail;	}bail_bh:	*new_de_bh = new_bh;	get_bh(*new_de_bh);bail:	if (drop_alloc_sem)		up_write(&OCFS2_I(dir)->ip_alloc_sem);	if (handle)		ocfs2_commit_trans(osb, handle);	if (data_ac)		ocfs2_free_alloc_context(data_ac);	if (meta_ac)		ocfs2_free_alloc_context(meta_ac);	if (new_bh)		brelse(new_bh);	mlog_exit(status);	return status;}static int ocfs2_find_dir_space_id(struct inode *dir, struct buffer_head *di_bh,				   const char *name, int namelen,				   struct buffer_head **ret_de_bh,				   unsigned int *blocks_wanted){	int ret;	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;	struct ocfs2_dir_entry *de, *last_de = NULL;	char *de_buf, *limit;	unsigned long offset = 0;	unsigned int rec_len, new_rec_len;	de_buf = di->id2.i_data.id_data;	limit = de_buf + i_size_read(dir);	rec_len = OCFS2_DIR_REC_LEN(namelen);	while (de_buf < limit) {		de = (struct ocfs2_dir_entry *)de_buf;		if (!ocfs2_check_dir_entry(dir, de, di_bh, offset)) {			ret = -ENOENT;			goto out;		}		if (ocfs2_match(namelen, name, de)) {			ret = -EEXIST;			goto out;		}		if (ocfs2_dirent_would_fit(de, rec_len)) {			/* Ok, we found a spot. Return this bh and let			 * the caller actually fill it in. */			*ret_de_bh = di_bh;			get_bh(*ret_de_bh);			ret = 0;			goto out;		}		last_de = de;		de_buf += le16_to_cpu(de->rec_len);		offset += le16_to_cpu(de->rec_len);	}	/*	 * We're going to require expansion of the directory - figure	 * out how many blocks we'll need so that a place for the	 * dirent can be found.	 */	*blocks_wanted = 1;	new_rec_len = le16_to_cpu(last_de->rec_len) + (dir->i_sb->s_blocksize - i_size_read(dir));	if (new_rec_len < (rec_len + OCFS2_DIR_REC_LEN(last_de->name_len)))		*blocks_wanted = 2;	ret = -ENOSPC;out:	return ret;}static int ocfs2_find_dir_space_el(struct inode *dir, const char *name,				   int namelen, struct buffer_head **ret_de_bh){	unsigned long offset;	struct buffer_head *bh = NULL;	unsigned short rec_len;	struct ocfs2_dir_entry *de;	struct super_block *sb = dir->i_sb;	int status;	bh = ocfs2_bread(dir, 0, &status, 0);	if (!bh) {		mlog_errno(status);		goto bail;	}	rec_len = OCFS2_DIR_REC_LEN(namelen);	offset = 0;	de = (struct ocfs2_dir_entry *) bh->b_data;	while (1) {		if ((char *)de >= sb->s_blocksize + bh->b_data) {			brelse(bh);			bh = NULL;			if (i_size_read(dir) <= offset) {				/*				 * Caller will have to expand this				 * directory.				 */				status = -ENOSPC;				goto bail;			}			bh = ocfs2_bread(dir,					 offset >> sb->s_blocksize_bits,					 &status,					 0);			if (!bh) {				mlog_errno(status);				goto bail;			}			/* move to next block */			de = (struct ocfs2_dir_entry *) bh->b_data;		}		if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {			status = -ENOENT;			goto bail;		}		if (ocfs2_match(namelen, name, de)) {			status = -EEXIST;			goto bail;		}		if (ocfs2_dirent_would_fit(de, rec_len)) {			/* Ok, we found a spot. Return this bh and let			 * the caller actually fill it in. */			*ret_de_bh = bh;			get_bh(*ret_de_bh);			status = 0;			goto bail;		}		offset += le16_to_cpu(de->rec_len);		de = (struct ocfs2_dir_entry *)((char *) de + le16_to_cpu(de->rec_len));	}	status = 0;bail:	if (bh)		brelse(bh);	mlog_exit(status);	return status;}int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb,				 struct inode *dir,				 struct buffer_head *parent_fe_bh,				 const char *name,				 int namelen,				 struct buffer_head **ret_de_bh){	int ret;	unsigned int blocks_wanted = 1;	struct buffer_head *bh = NULL;	mlog(0, "getting ready to insert namelen %d into dir %llu\n",	     namelen, (unsigned long long)OCFS2_I(dir)->ip_blkno);	*ret_de_bh = NULL;	if (!namelen) {		ret = -EINVAL;		mlog_errno(ret);		goto out;	}	if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {		ret = ocfs2_find_dir_space_id(dir, parent_fe_bh, name,					      namelen, &bh, &blocks_wanted);	} else		ret = ocfs2_find_dir_space_el(dir, name, namelen, &bh);	if (ret && ret != -ENOSPC) {		mlog_errno(ret);		goto out;	}	if (ret == -ENOSPC) {		/*		 * We have to expand the directory to add this name.		 */		BUG_ON(bh);		ret = ocfs2_extend_dir(osb, dir, parent_fe_bh, blocks_wanted,				       &bh);		if (ret) {			if (ret != -ENOSPC)				mlog_errno(ret);			goto out;		}		BUG_ON(!bh);	}	*ret_de_bh = bh;	bh = NULL;out:	if (bh)		brelse(bh);	return ret;}

⌨️ 快捷键说明

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