X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lib%2Fext2fs%2Falloc.c;h=3fd921679286cc0410a8032e5a8d9e483ef7c670;hb=9db53e3fec3413572a2240bd374e76353fab9cbe;hp=752c4da1681e0e8b0f4cd2169de41081dac4d531;hpb=c1a1e7fc24d6e37f931bbb8eeb29c90243f0a55d;p=tools%2Fe2fsprogs.git diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c index 752c4da..3fd9216 100644 --- a/lib/ext2fs/alloc.c +++ b/lib/ext2fs/alloc.c @@ -26,53 +26,32 @@ #include "ext2_fs.h" #include "ext2fs.h" +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#undef DEBUG + +#ifdef DEBUG +# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0) +#else +# define dbg_printf(f, a...) +#endif + /* - * Check for uninit block bitmaps and deal with them appropriately + * Clear the uninit block bitmap flag if necessary */ -static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map, - dgrp_t group) +void ext2fs_clear_block_uninit(ext2_filsys fs, dgrp_t group) { - blk_t i; - blk64_t blk, super_blk, old_desc_blk, new_desc_blk; - int old_desc_blocks; - - if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + if (group >= fs->group_desc_count || + !ext2fs_has_group_desc_csum(fs) || !(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) return; - blk = (group * fs->super->s_blocks_per_group) + - fs->super->s_first_data_block; - - ext2fs_super_and_bgd_loc2(fs, group, &super_blk, - &old_desc_blk, &new_desc_blk, 0); - - if (fs->super->s_feature_incompat & - EXT2_FEATURE_INCOMPAT_META_BG) - old_desc_blocks = fs->super->s_first_meta_bg; - else - old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; - - for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) - ext2fs_fast_unmark_block_bitmap2(map, blk); - - blk = (group * fs->super->s_blocks_per_group) + - fs->super->s_first_data_block; - for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) { - if ((blk == super_blk) || - (old_desc_blk && old_desc_blocks && - (blk >= old_desc_blk) && - (blk < old_desc_blk + old_desc_blocks)) || - (new_desc_blk && (blk == new_desc_blk)) || - (blk == ext2fs_block_bitmap_loc(fs, group)) || - (blk == ext2fs_inode_bitmap_loc(fs, group)) || - (blk >= ext2fs_inode_table_loc(fs, group) && - (blk < ext2fs_inode_table_loc(fs, group) - + fs->inode_blocks_per_group))) - ext2fs_fast_mark_block_bitmap2(map, blk); - } + /* uninit block bitmaps are now initialized in read_bitmaps() */ + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); ext2fs_group_desc_csum_set(fs, group); + ext2fs_mark_super_dirty(fs); + ext2fs_mark_bb_dirty(fs); } /* @@ -83,8 +62,8 @@ static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map, { ext2_ino_t i, ino; - if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + if (group >= fs->group_desc_count || + !ext2fs_has_group_desc_csum(fs) || !(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT))) return; @@ -93,7 +72,11 @@ static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map, ext2fs_fast_unmark_inode_bitmap2(map, ino); ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); - check_block_uninit(fs, fs->block_map, group); + /* Mimics what the kernel does */ + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, group); + ext2fs_mark_ib_dirty(fs); + ext2fs_mark_super_dirty(fs); } /* @@ -161,43 +144,67 @@ errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, * Stupid algorithm --- we now just search forward starting from the * goal. Should put in a smarter one someday.... */ -errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, - ext2fs_block_bitmap map, blk64_t *ret) +errcode_t ext2fs_new_block3(ext2_filsys fs, blk64_t goal, + ext2fs_block_bitmap map, blk64_t *ret, + struct blk_alloc_ctx *ctx) { - blk64_t i; - int c_ratio; + errcode_t retval; + blk64_t b = 0; + errcode_t (*gab)(ext2_filsys fs, blk64_t goal, blk64_t *ret); + errcode_t (*gab2)(ext2_filsys, blk64_t, blk64_t *, + struct blk_alloc_ctx *); EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + if (!map) { + /* + * In case there are clients out there whose get_alloc_block + * handlers call ext2fs_new_block2 with a NULL block map, + * temporarily swap out the function pointer so that we don't + * end up in an infinite loop. + */ + if (fs->get_alloc_block2) { + gab2 = fs->get_alloc_block2; + fs->get_alloc_block2 = NULL; + retval = gab2(fs, goal, &b, ctx); + fs->get_alloc_block2 = gab2; + goto allocated; + } else if (fs->get_alloc_block) { + gab = fs->get_alloc_block; + fs->get_alloc_block = NULL; + retval = gab(fs, goal, &b); + fs->get_alloc_block = gab; + goto allocated; + } + } if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!goal || (goal >= ext2fs_blocks_count(fs->super))) goal = fs->super->s_first_data_block; - i = goal; - c_ratio = 1 << ext2fs_get_bitmap_granularity(map); - if (c_ratio > 1) - goal &= ~EXT2FS_CLUSTER_MASK(fs); - check_block_uninit(fs, map, - (i - fs->super->s_first_data_block) / - EXT2_BLOCKS_PER_GROUP(fs->super)); - do { - if (((i - fs->super->s_first_data_block) % - EXT2_BLOCKS_PER_GROUP(fs->super)) == 0) - check_block_uninit(fs, map, - (i - fs->super->s_first_data_block) / - EXT2_BLOCKS_PER_GROUP(fs->super)); - - if (!ext2fs_fast_test_block_bitmap2(map, i)) { - *ret = i; - return 0; - } - i = (i + c_ratio) & ~(c_ratio - 1); - if (i >= ext2fs_blocks_count(fs->super)) - i = fs->super->s_first_data_block; - } while (i != goal); - return EXT2_ET_BLOCK_ALLOC_FAIL; + goal &= ~EXT2FS_CLUSTER_MASK(fs); + + retval = ext2fs_find_first_zero_block_bitmap2(map, + goal, ext2fs_blocks_count(fs->super) - 1, &b); + if ((retval == ENOENT) && (goal != fs->super->s_first_data_block)) + retval = ext2fs_find_first_zero_block_bitmap2(map, + fs->super->s_first_data_block, goal - 1, &b); +allocated: + if (retval == ENOENT) + return EXT2_ET_BLOCK_ALLOC_FAIL; + if (retval) + return retval; + + ext2fs_clear_block_uninit(fs, ext2fs_group_of_blk2(fs, b)); + *ret = b; + return 0; +} + +errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, + ext2fs_block_bitmap map, blk64_t *ret) +{ + return ext2fs_new_block3(fs, goal, map, ret, NULL); } errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, @@ -215,22 +222,17 @@ errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, * This function zeros out the allocated block, and updates all of the * appropriate filesystem records. */ -errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, - char *block_buf, blk64_t *ret) +errcode_t ext2fs_alloc_block3(ext2_filsys fs, blk64_t goal, char *block_buf, + blk64_t *ret, struct blk_alloc_ctx *ctx) { errcode_t retval; blk64_t block; - char *buf = 0; - if (!block_buf) { - retval = ext2fs_get_mem(fs->blocksize, &buf); + if (fs->get_alloc_block2) { + retval = (fs->get_alloc_block2)(fs, goal, &block, ctx); if (retval) - return retval; - block_buf = buf; - } - memset(block_buf, 0, fs->blocksize); - - if (fs->get_alloc_block) { + goto fail; + } else if (fs->get_alloc_block) { retval = (fs->get_alloc_block)(fs, goal, &block); if (retval) goto fail; @@ -241,12 +243,16 @@ errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, goto fail; } - retval = ext2fs_new_block2(fs, goal, 0, &block); + retval = ext2fs_new_block3(fs, goal, 0, &block, ctx); if (retval) goto fail; } - retval = io_channel_write_blk64(fs->io, block, 1, block_buf); + if (block_buf) { + memset(block_buf, 0, fs->blocksize); + retval = io_channel_write_blk64(fs->io, block, 1, block_buf); + } else + retval = ext2fs_zero_blocks2(fs, block, 1, NULL, NULL); if (retval) goto fail; @@ -254,20 +260,24 @@ errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, *ret = block; fail: - if (buf) - ext2fs_free_mem(&buf); return retval; } +errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, + char *block_buf, blk64_t *ret) +{ + return ext2fs_alloc_block3(fs, goal, block_buf, ret, NULL); +} + errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, char *block_buf, blk_t *ret) { errcode_t retval; - blk64_t val; - retval = ext2fs_alloc_block2(fs, goal, block_buf, &val); + blk64_t ret64, goal64 = goal; + retval = ext2fs_alloc_block3(fs, goal64, block_buf, &ret64, NULL); if (!retval) - *ret = (blk_t) val; - return retval; + *ret = (blk_t)ret64; + return retval; } errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, blk64_t finish, @@ -292,8 +302,11 @@ errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, blk64_t finish, b &= ~(c_ratio - 1); finish &= ~(c_ratio -1); do { - if (b+num-1 > ext2fs_blocks_count(fs->super)) + if (b + num - 1 >= ext2fs_blocks_count(fs->super)) { + if (finish > start) + return EXT2_ET_BLOCK_ALLOC_FAIL; b = fs->super->s_first_data_block; + } if (ext2fs_fast_test_block_bitmap_range2(map, b, num)) { *ret = b; return 0; @@ -330,3 +343,212 @@ void ext2fs_set_alloc_block_callback(ext2_filsys fs, fs->get_alloc_block = func; } + +blk64_t ext2fs_find_inode_goal(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, blk64_t lblk) +{ + dgrp_t group; + __u8 log_flex; + struct ext2fs_extent extent; + ext2_extent_handle_t handle = NULL; + errcode_t err; + + /* Make sure data stored in inode->i_block is neither fast symlink nor + * inline data. + */ + if (inode == NULL || ext2fs_is_fast_symlink(inode) || + inode->i_flags & EXT4_INLINE_DATA_FL) + goto no_blocks; + + if (inode->i_flags & EXT4_EXTENTS_FL) { + err = ext2fs_extent_open2(fs, ino, inode, &handle); + if (err) + goto no_blocks; + err = ext2fs_extent_goto2(handle, 0, lblk); + if (err) + goto no_blocks; + err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (err) + goto no_blocks; + ext2fs_extent_free(handle); + return extent.e_pblk + (lblk - extent.e_lblk); + } + + /* block mapped file; see if block zero is mapped? */ + if (inode->i_block[0]) + return inode->i_block[0]; + +no_blocks: + ext2fs_extent_free(handle); + log_flex = fs->super->s_log_groups_per_flex; + group = ext2fs_group_of_ino(fs, ino); + if (log_flex) + group = group & ~((1 << (log_flex)) - 1); + return ext2fs_group_first_block2(fs, group); +} + +/* + * Starting at _goal_, scan around the filesystem to find a run of free blocks + * that's at least _len_ blocks long. Possible flags: + * - EXT2_NEWRANGE_EXACT_GOAL: The range of blocks must start at _goal_. + * - EXT2_NEWRANGE_MIN_LENGTH: do not return a allocation shorter than _len_. + * - EXT2_NEWRANGE_ZERO_BLOCKS: Zero blocks pblk to pblk+plen before returning. + * + * The starting block is returned in _pblk_ and the length is returned via + * _plen_. The blocks are not marked in the bitmap; the caller must mark + * however much of the returned run they actually use, hopefully via + * ext2fs_block_alloc_stats_range(). + * + * This function can return a range that is longer than what was requested. + */ +errcode_t ext2fs_new_range(ext2_filsys fs, int flags, blk64_t goal, + blk64_t len, ext2fs_block_bitmap map, blk64_t *pblk, + blk64_t *plen) +{ + errcode_t retval; + blk64_t start, end, b; + int looped = 0; + blk64_t max_blocks = ext2fs_blocks_count(fs->super); + errcode_t (*nrf)(ext2_filsys fs, int flags, blk64_t goal, + blk64_t len, blk64_t *pblk, blk64_t *plen); + + dbg_printf("%s: flags=0x%x goal=%llu len=%llu\n", __func__, flags, + goal, len); + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + if (len == 0 || (flags & ~EXT2_NEWRANGE_ALL_FLAGS)) + return EXT2_ET_INVALID_ARGUMENT; + + if (!map && fs->new_range) { + /* + * In case there are clients out there whose new_range + * handlers call ext2fs_new_range with a NULL block map, + * temporarily swap out the function pointer so that we don't + * end up in an infinite loop. + */ + nrf = fs->new_range; + fs->new_range = NULL; + retval = nrf(fs, flags, goal, len, pblk, plen); + fs->new_range = nrf; + if (retval) + return retval; + start = *pblk; + end = *pblk + *plen; + goto allocated; + } + if (!map) + map = fs->block_map; + if (!map) + return EXT2_ET_NO_BLOCK_BITMAP; + if (!goal || goal >= ext2fs_blocks_count(fs->super)) + goal = fs->super->s_first_data_block; + + start = goal; + while (!looped || start <= goal) { + retval = ext2fs_find_first_zero_block_bitmap2(map, start, + max_blocks - 1, + &start); + if (retval == ENOENT) { + /* + * If there are no free blocks beyond the starting + * point, try scanning the whole filesystem, unless the + * user told us only to allocate from _goal_, or if + * we're already scanning the whole filesystem. + */ + if (flags & EXT2_NEWRANGE_FIXED_GOAL || + start == fs->super->s_first_data_block) + goto fail; + start = fs->super->s_first_data_block; + continue; + } else if (retval) + goto errout; + + if (flags & EXT2_NEWRANGE_FIXED_GOAL && start != goal) + goto fail; + + b = min(start + len - 1, max_blocks - 1); + retval = ext2fs_find_first_set_block_bitmap2(map, start, b, + &end); + if (retval == ENOENT) + end = b + 1; + else if (retval) + goto errout; + + if (!(flags & EXT2_NEWRANGE_MIN_LENGTH) || + (end - start) >= len) { + /* Success! */ + *pblk = start; + *plen = end - start; + dbg_printf("%s: new_range goal=%llu--%llu " + "blk=%llu--%llu %llu\n", + __func__, goal, goal + len - 1, + *pblk, *pblk + *plen - 1, *plen); +allocated: + for (b = start; b < end; + b += fs->super->s_blocks_per_group) + ext2fs_clear_block_uninit(fs, + ext2fs_group_of_blk2(fs, b)); + return 0; + } + + if (flags & EXT2_NEWRANGE_FIXED_GOAL) + goto fail; + start = end; + if (start >= max_blocks) { + if (looped) + goto fail; + looped = 1; + start = fs->super->s_first_data_block; + } + } + +fail: + retval = EXT2_ET_BLOCK_ALLOC_FAIL; +errout: + return retval; +} + +void ext2fs_set_new_range_callback(ext2_filsys fs, + errcode_t (*func)(ext2_filsys fs, int flags, blk64_t goal, + blk64_t len, blk64_t *pblk, blk64_t *plen), + errcode_t (**old)(ext2_filsys fs, int flags, blk64_t goal, + blk64_t len, blk64_t *pblk, blk64_t *plen)) +{ + if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS) + return; + + if (old) + *old = fs->new_range; + + fs->new_range = func; +} + +errcode_t ext2fs_alloc_range(ext2_filsys fs, int flags, blk64_t goal, + blk_t len, blk64_t *ret) +{ + int newr_flags = EXT2_NEWRANGE_MIN_LENGTH; + errcode_t retval; + blk64_t plen; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + if (len == 0 || (flags & ~EXT2_ALLOCRANGE_ALL_FLAGS)) + return EXT2_ET_INVALID_ARGUMENT; + + if (flags & EXT2_ALLOCRANGE_FIXED_GOAL) + newr_flags |= EXT2_NEWRANGE_FIXED_GOAL; + + retval = ext2fs_new_range(fs, newr_flags, goal, len, NULL, ret, &plen); + if (retval) + return retval; + + if (plen < len) + return EXT2_ET_BLOCK_ALLOC_FAIL; + + if (flags & EXT2_ALLOCRANGE_ZERO_BLOCKS) { + retval = ext2fs_zero_blocks2(fs, *ret, len, NULL, NULL); + if (retval) + return retval; + } + + ext2fs_block_alloc_stats_range(fs, *ret, len, +1); + return retval; +}