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

📄 inode.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 3 页
字号:
 *	This function verifies that chain (up to the missing link) had not *	changed, fills the missing link and does all housekeeping needed in *	inode (->i_blocks, etc.). In case of success we end up with the full *	chain to new block and return 0. Otherwise (== chain had been changed) *	we free the new blocks (forgetting their buffer_heads, indeed) and *	return -EAGAIN. */static inline int ext2_splice_branch(struct inode *inode,				     long block,				     Indirect chain[4],				     Indirect *where,				     int num){	int i;	/* Verify that place we are splicing to is still there and vacant */	/* Writer: pointers, ->i_next_alloc*, ->i_blocks */	if (!verify_chain(chain, where-1) || *where->p)		/* Writer: end */		goto changed;	/* That's it */	*where->p = where->key;	inode->u.ext2_i.i_next_alloc_block = block;	inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);	inode->i_blocks += num * inode->i_sb->s_blocksize/512;	/* Writer: end */	/* We are done with atomic stuff, now do the rest of housekeeping */	inode->i_ctime = CURRENT_TIME;	/* had we spliced it onto indirect block? */	if (where->bh) {		mark_buffer_dirty_inode(where->bh, inode);		if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {			ll_rw_block (WRITE, 1, &where->bh);			wait_on_buffer(where->bh);		}	}	if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)		ext2_sync_inode (inode);	else		mark_inode_dirty(inode);	return 0;changed:	for (i = 1; i < num; i++)		bforget(where[i].bh);	for (i = 0; i < num; i++)		ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1);	return -EAGAIN;}/* * Allocation strategy is simple: if we have to allocate something, we will * have to go the whole way to leaf. So let's do it before attaching anything * to tree, set linkage between the newborn blocks, write them if sync is * required, recheck the path, free and repeat if check fails, otherwise * set the last missing link (that will protect us from any truncate-generated * removals - all blocks on the path are immune now) and possibly force the * write on the parent block. * That has a nice additional property: no special recovery from the failed * allocations is needed - we simply release blocks and do not touch anything * reachable from inode. */static int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create){	int err = -EIO;	int offsets[4];	Indirect chain[4];	Indirect *partial;	unsigned long goal;	int left;	int depth = ext2_block_to_path(inode, iblock, offsets);	if (depth == 0)		goto out;	lock_kernel();reread:	partial = ext2_get_branch(inode, depth, offsets, chain, &err);	/* Simplest case - block found, no allocation needed */	if (!partial) {got_it:		bh_result->b_dev = inode->i_dev;		bh_result->b_blocknr = le32_to_cpu(chain[depth-1].key);		bh_result->b_state |= (1UL << BH_Mapped);		/* Clean up and exit */		partial = chain+depth-1; /* the whole chain */		goto cleanup;	}	/* Next simple case - plain lookup or failed read of indirect block */	if (!create || err == -EIO) {cleanup:		while (partial > chain) {			brelse(partial->bh);			partial--;		}		unlock_kernel();out:		return err;	}	/*	 * Indirect block might be removed by truncate while we were	 * reading it. Handling of that case (forget what we've got and	 * reread) is taken out of the main path.	 */	if (err == -EAGAIN)		goto changed;	if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0)		goto changed;	left = (chain + depth) - partial;	err = ext2_alloc_branch(inode, left, goal,					offsets+(partial-chain), partial);	if (err)		goto cleanup;	if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)		goto changed;	bh_result->b_state |= (1UL << BH_New);	goto got_it;changed:	while (partial > chain) {		bforget(partial->bh);		partial--;	}	goto reread;}struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, int * err){	struct buffer_head dummy;	int error;	dummy.b_state = 0;	dummy.b_blocknr = -1000;	error = ext2_get_block(inode, block, &dummy, create);	*err = error;	if (!error && buffer_mapped(&dummy)) {		struct buffer_head *bh;		bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize);		if (buffer_new(&dummy)) {			if (!buffer_uptodate(bh))				wait_on_buffer(bh);			memset(bh->b_data, 0, inode->i_sb->s_blocksize);			mark_buffer_uptodate(bh, 1);			mark_buffer_dirty_inode(bh, inode);		}		return bh;	}	return NULL;}struct buffer_head * ext2_bread (struct inode * inode, int block, 				 int create, int *err){	struct buffer_head * bh;	int prev_blocks;		prev_blocks = inode->i_blocks;		bh = ext2_getblk (inode, block, create, err);	if (!bh)		return bh;		/*	 * If the inode has grown, and this is a directory, then perform	 * preallocation of a few more blocks to try to keep directory	 * fragmentation down.	 */	if (create && 	    S_ISDIR(inode->i_mode) && 	    inode->i_blocks > prev_blocks &&	    EXT2_HAS_COMPAT_FEATURE(inode->i_sb,				    EXT2_FEATURE_COMPAT_DIR_PREALLOC)) {		int i;		struct buffer_head *tmp_bh;				for (i = 1;		     i < EXT2_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;		     i++) {			/* 			 * ext2_getblk will zero out the contents of the			 * directory for us			 */			tmp_bh = ext2_getblk(inode, block+i, create, err);			if (!tmp_bh) {				brelse (bh);				return 0;			}			brelse (tmp_bh);		}	}		if (buffer_uptodate(bh))		return bh;	ll_rw_block (READ, 1, &bh);	wait_on_buffer (bh);	if (buffer_uptodate(bh))		return bh;	brelse (bh);	*err = -EIO;	return NULL;}static int ext2_writepage(struct page *page){	return block_write_full_page(page,ext2_get_block);}static int ext2_readpage(struct file *file, struct page *page){	return block_read_full_page(page,ext2_get_block);}static int ext2_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to){	return block_prepare_write(page,from,to,ext2_get_block);}static int ext2_bmap(struct address_space *mapping, long block){	return generic_block_bmap(mapping,block,ext2_get_block);}struct address_space_operations ext2_aops = {	readpage: ext2_readpage,	writepage: ext2_writepage,	sync_page: block_sync_page,	prepare_write: ext2_prepare_write,	commit_write: generic_commit_write,	bmap: ext2_bmap};/* * Probably it should be a library function... search for first non-zero word * or memcmp with zero_page, whatever is better for particular architecture. * Linus? */static inline int all_zeroes(u32 *p, u32 *q){	while (p < q)		if (*p++)			return 0;	return 1;}/** *	ext2_find_shared - find the indirect blocks for partial truncation. *	@inode:	  inode in question *	@depth:	  depth of the affected branch *	@offsets: offsets of pointers in that branch (see ext2_block_to_path) *	@chain:	  place to store the pointers to partial indirect blocks *	@top:	  place to the (detached) top of branch * *	This is a helper function used by ext2_truncate(). * *	When we do truncate() we may have to clean the ends of several indirect *	blocks but leave the blocks themselves alive. Block is partially *	truncated if some data below the new i_size is refered from it (and *	it is on the path to the first completely truncated data block, indeed). *	We have to free the top of that path along with everything to the right *	of the path. Since no allocation past the truncation point is possible *	until ext2_truncate() finishes, we may safely do the latter, but top *	of branch may require special attention - pageout below the truncation *	point might try to populate it. * *	We atomically detach the top of branch from the tree, store the block *	number of its root in *@top, pointers to buffer_heads of partially *	truncated blocks - in @chain[].bh and pointers to their last elements *	that should not be removed - in @chain[].p. Return value is the pointer *	to last filled element of @chain. * *	The work left to caller to do the actual freeing of subtrees: *		a) free the subtree starting from *@top *		b) free the subtrees whose roots are stored in *			(@chain[i].p+1 .. end of @chain[i].bh->b_data) *		c) free the subtrees growing from the inode past the @chain[0].p *			(no partially truncated stuff there). */static Indirect *ext2_find_shared(struct inode *inode,				int depth,				int offsets[4],				Indirect chain[4],				u32 *top){	Indirect *partial, *p;	int k, err;	*top = 0;	for (k = depth; k > 1 && !offsets[k-1]; k--)		;	partial = ext2_get_branch(inode, k, offsets, chain, &err);	/* Writer: pointers */	if (!partial)		partial = chain + k-1;	/*	 * If the branch acquired continuation since we've looked at it -	 * fine, it should all survive and (new) top doesn't belong to us.	 */	if (!partial->key && *partial->p)		/* Writer: end */		goto no_top;	for (p=partial; p>chain && all_zeroes((u32*)p->bh->b_data,p->p); p--)		;	/*	 * OK, we've found the last block that must survive. The rest of our	 * branch should be detached before unlocking. However, if that rest	 * of branch is all ours and does not grow immediately from the inode	 * it's easier to cheat and just decrement partial->p.	 */	if (p == chain + k - 1 && p > chain) {		p->p--;	} else {		*top = *p->p;		*p->p = 0;	}	/* Writer: end */	while(partial > p)	{		brelse(partial->bh);		partial--;	}no_top:	return partial;}/** *	ext2_free_data - free a list of data blocks *	@inode:	inode we are dealing with *	@p:	array of block numbers *	@q:	points immediately past the end of array * *	We are freeing all blocks refered from that array (numbers are *	stored as little-endian 32-bit) and updating @inode->i_blocks *	appropriately. */static inline void ext2_free_data(struct inode *inode, u32 *p, u32 *q){	int blocks = inode->i_sb->s_blocksize / 512;	unsigned long block_to_free = 0, count = 0;	unsigned long nr;	for ( ; p < q ; p++) {		nr = le32_to_cpu(*p);		if (nr) {			*p = 0;			/* accumulate blocks to free if they're contiguous */			if (count == 0)				goto free_this;			else if (block_to_free == nr - count)				count++;			else {				/* Writer: ->i_blocks */				inode->i_blocks -= blocks * count;				/* Writer: end */				ext2_free_blocks (inode, block_to_free, count);				mark_inode_dirty(inode);			free_this:				block_to_free = nr;				count = 1;			}		}	}	if (count > 0) {		/* Writer: ->i_blocks */		inode->i_blocks -= blocks * count;		/* Writer: end */		ext2_free_blocks (inode, block_to_free, count);		mark_inode_dirty(inode);	}}/** *	ext2_free_branches - free an array of branches *	@inode:	inode we are dealing with *	@p:	array of block numbers *	@q:	pointer immediately past the end of array *	@depth:	depth of the branches to free * *	We are freeing all blocks refered from these branches (numbers are *	stored as little-endian 32-bit) and updating @inode->i_blocks *	appropriately. */static void ext2_free_branches(struct inode *inode, u32 *p, u32 *q, int depth){	struct buffer_head * bh;	unsigned long nr;	if (depth--) {		int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);		for ( ; p < q ; p++) {			nr = le32_to_cpu(*p);			if (!nr)				continue;			*p = 0;			bh = bread (inode->i_dev, nr, inode->i_sb->s_blocksize);			/*			 * A read failure? Report error and clear slot			 * (should be rare).			 */ 			if (!bh) {				ext2_error(inode->i_sb, "ext2_free_branches",					"Read failure, inode=%ld, block=%ld",					inode->i_ino, nr);				continue;			}			ext2_free_branches(inode,					   (u32*)bh->b_data,					   (u32*)bh->b_data + addr_per_block,					   depth);			bforget(bh);			/* Writer: ->i_blocks */			inode->i_blocks -= inode->i_sb->s_blocksize / 512;			/* Writer: end */			ext2_free_blocks(inode, nr, 1);			mark_inode_dirty(inode);		}	} else		ext2_free_data(inode, p, q);}

⌨️ 快捷键说明

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