📄 inode.c
字号:
*/ for (i=1; i < num; i++) *(branch[n].p + i) = cpu_to_le32(++current_block); } set_buffer_uptodate(bh); unlock_buffer(bh); mark_buffer_dirty_inode(bh, inode); /* We used to sync bh here if IS_SYNC(inode). * But we now rely upon generic_osync_inode() * and b_inode_buffers. But not for directories. */ if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) sync_dirty_buffer(bh); } *blks = num; return err;}/** * ext2_splice_branch - splice the allocated branch onto inode. * @inode: owner * @block: (logical) number of block we are adding * @chain: chain of indirect blocks (with a missing link - see * ext2_alloc_branch) * @where: location of missing link * @num: number of indirect blocks we are adding * @blks: number of direct blocks we are adding * * This function 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. */static void ext2_splice_branch(struct inode *inode, long block, Indirect *where, int num, int blks){ int i; struct ext2_block_alloc_info *block_i; ext2_fsblk_t current_block; block_i = EXT2_I(inode)->i_block_alloc_info; /* XXX LOCKING probably should have i_meta_lock ?*/ /* That's it */ *where->p = where->key; /* * Update the host buffer_head or inode to point to more just allocated * direct blocks blocks */ if (num == 0 && blks > 1) { current_block = le32_to_cpu(where->key) + 1; for (i = 1; i < blks; i++) *(where->p + i ) = cpu_to_le32(current_block++); } /* * update the most recently allocated logical & physical block * in i_block_alloc_info, to assist find the proper goal block for next * allocation */ if (block_i) { block_i->last_alloc_logical_block = block + blks - 1; block_i->last_alloc_physical_block = le32_to_cpu(where[num].key) + blks - 1; } /* We are done with atomic stuff, now do the rest of housekeeping */ /* had we spliced it onto indirect block? */ if (where->bh) mark_buffer_dirty_inode(where->bh, inode); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode);}/* * 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. * * `handle' can be NULL if create == 0. * * The BKL may not be held on entry here. Be sure to take it early. * return > 0, # of blocks mapped or allocated. * return = 0, if plain lookup failed. * return < 0, error case. */static int ext2_get_blocks(struct inode *inode, sector_t iblock, unsigned long maxblocks, struct buffer_head *bh_result, int create){ int err = -EIO; int offsets[4]; Indirect chain[4]; Indirect *partial; ext2_fsblk_t goal; int indirect_blks; int blocks_to_boundary = 0; int depth; struct ext2_inode_info *ei = EXT2_I(inode); int count = 0; ext2_fsblk_t first_block = 0; depth = ext2_block_to_path(inode,iblock,offsets,&blocks_to_boundary); if (depth == 0) return (err);reread: partial = ext2_get_branch(inode, depth, offsets, chain, &err); /* Simplest case - block found, no allocation needed */ if (!partial) { first_block = le32_to_cpu(chain[depth - 1].key); clear_buffer_new(bh_result); /* What's this do? */ count++; /*map more blocks*/ while (count < maxblocks && count <= blocks_to_boundary) { ext2_fsblk_t blk; if (!verify_chain(chain, partial)) { /* * Indirect block might be removed by * truncate while we were reading it. * Handling of that case: forget what we've * got now, go to reread. */ count = 0; goto changed; } blk = le32_to_cpu(*(chain[depth-1].p + count)); if (blk == first_block + count) count++; else break; } goto got_it; } /* Next simple case - plain lookup or failed read of indirect block */ if (!create || err == -EIO) goto cleanup; mutex_lock(&ei->truncate_mutex); /* * Okay, we need to do block allocation. Lazily initialize the block * allocation info here if necessary */ if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info)) ext2_init_block_alloc_info(inode); goal = ext2_find_goal(inode, iblock, chain, partial); /* the number of blocks need to allocate for [d,t]indirect blocks */ indirect_blks = (chain + depth) - partial - 1; /* * Next look up the indirect map to count the totoal number of * direct blocks to allocate for this branch. */ count = ext2_blks_to_allocate(partial, indirect_blks, maxblocks, blocks_to_boundary); /* * XXX ???? Block out ext2_truncate while we alter the tree */ err = ext2_alloc_branch(inode, indirect_blks, &count, goal, offsets + (partial - chain), partial); if (err) { mutex_unlock(&ei->truncate_mutex); goto cleanup; } if (ext2_use_xip(inode->i_sb)) { /* * we need to clear the block */ err = ext2_clear_xip_target (inode, le32_to_cpu(chain[depth-1].key)); if (err) { mutex_unlock(&ei->truncate_mutex); goto cleanup; } } ext2_splice_branch(inode, iblock, partial, indirect_blks, count); mutex_unlock(&ei->truncate_mutex); set_buffer_new(bh_result);got_it: map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); if (count > blocks_to_boundary) set_buffer_boundary(bh_result); err = count; /* Clean up and exit */ partial = chain + depth - 1; /* the whole chain */cleanup: while (partial > chain) { brelse(partial->bh); partial--; } return err;changed: while (partial > chain) { brelse(partial->bh); partial--; } goto reread;}int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create){ unsigned max_blocks = bh_result->b_size >> inode->i_blkbits; int ret = ext2_get_blocks(inode, iblock, max_blocks, bh_result, create); if (ret > 0) { bh_result->b_size = (ret << inode->i_blkbits); ret = 0; } return ret;}static int ext2_writepage(struct page *page, struct writeback_control *wbc){ return block_write_full_page(page, ext2_get_block, wbc);}static int ext2_readpage(struct file *file, struct page *page){ return mpage_readpage(page, ext2_get_block);}static intext2_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages){ return mpage_readpages(mapping, pages, nr_pages, ext2_get_block);}int __ext2_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata){ return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, ext2_get_block);}static intext2_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata){ *pagep = NULL; return __ext2_write_begin(file, mapping, pos, len, flags, pagep,fsdata);}static intext2_nobh_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata){ /* * Dir-in-pagecache still uses ext2_write_begin. Would have to rework * directory handling code to pass around offsets rather than struct * pages in order to make this work easily. */ return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata, ext2_get_block);}static int ext2_nobh_writepage(struct page *page, struct writeback_control *wbc){ return nobh_writepage(page, ext2_get_block, wbc);}static sector_t ext2_bmap(struct address_space *mapping, sector_t block){ return generic_block_bmap(mapping,block,ext2_get_block);}static ssize_text2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs){ struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, ext2_get_block, NULL);}static intext2_writepages(struct address_space *mapping, struct writeback_control *wbc){ return mpage_writepages(mapping, wbc, ext2_get_block);}const struct address_space_operations ext2_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_writepage, .sync_page = block_sync_page, .write_begin = ext2_write_begin, .write_end = generic_write_end, .bmap = ext2_bmap, .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, .migratepage = buffer_migrate_page,};const struct address_space_operations ext2_aops_xip = { .bmap = ext2_bmap, .get_xip_page = ext2_get_xip_page,};const struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_nobh_writepage, .sync_page = block_sync_page, .write_begin = ext2_nobh_write_begin, .write_end = nobh_write_end, .bmap = ext2_bmap, .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, .migratepage = buffer_migrate_page,};/* * 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(__le32 *p, __le32 *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], __le32 *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); 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. */ write_lock(&EXT2_I(inode)->i_meta_lock); if (!partial->key && *partial->p) { write_unlock(&EXT2_I(inode)->i_meta_lock); goto no_top; } for (p=partial; p>chain && all_zeroes((__le32*)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; } write_unlock(&EXT2_I(inode)->i_meta_lock); 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, __le32 *p, __le32 *q){ 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 { mark_inode_dirty(inode); ext2_free_blocks (inode, block_to_free, count); free_this: block_to_free = nr; count = 1; } } } if (count > 0) { mark_inode_dirty(inode); ext2_free_blocks (inode, block_to_free, count); }}/** * 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 *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -