?? inode.c
字號:
* 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 + -