X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=resize%2Fresize2fs.c;h=5eeb7d44624929178e9930c293c1c6366b31c046;hb=6cde9c20dcea7f1a6085e742da94863636522cb1;hp=36da253fccfcf0bdd67aec3f8e263656da25b967;hpb=f3745728bc254892da4c569ba3fd8801895f3524;p=tools%2Fe2fsprogs.git diff --git a/resize/resize2fs.c b/resize/resize2fs.c index 36da253..5eeb7d4 100644 --- a/resize/resize2fs.c +++ b/resize/resize2fs.c @@ -4,7 +4,7 @@ * Copyright (C) 1997, 1998 by Theodore Ts'o and * PowerQuest, Inc. * - * Copyright (C) 1999, 2000 by Theosore Ts'o + * Copyright (C) 1999, 2000 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public @@ -49,7 +49,7 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs); static errcode_t inode_ref_fix(ext2_resize_t rfs); static errcode_t move_itables(ext2_resize_t rfs); static errcode_t fix_resize_inode(ext2_filsys fs); -static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs); +static errcode_t resize2fs_calculate_summary_stats(ext2_filsys fs); static errcode_t fix_sb_journal_backup(ext2_filsys fs); static errcode_t mark_table_blocks(ext2_filsys fs, ext2fs_block_bitmap bmap); @@ -73,14 +73,14 @@ static inline int is_inode_bm(ext2_filsys fs, unsigned int grp, blk64_t blk) return blk == ext2fs_inode_bitmap_loc(fs, grp); } -static inline int is_inode_tb(ext2_filsys fs, unsigned int grp, blk64_t blk) +static int is_inode_tb(ext2_filsys fs, unsigned int grp, blk64_t blk) { return blk >= ext2fs_inode_table_loc(fs, grp) && blk < (ext2fs_inode_table_loc(fs, grp) + fs->inode_blocks_per_group); } -/* Some bigalloc helper macros which are more succint... */ +/* Some bigalloc helper macros which are more succinct... */ #define B2C(x) EXT2FS_B2C(fs, (x)) #define C2B(x) EXT2FS_C2B(fs, (x)) #define EQ_CLSTR(x, y) (B2C(x) == B2C(y)) @@ -177,9 +177,9 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags, #ifdef RESIZE2FS_DEBUG if (rfs->flags & RESIZE_DEBUG_BMOVE) printf("Number of free blocks: %llu/%llu, Needed: %llu\n", - ext2fs_free_blocks_count(rfs->old_fs->super), - ext2fs_free_blocks_count(rfs->new_fs->super), - rfs->needed_blocks); + (unsigned long long) ext2fs_free_blocks_count(rfs->old_fs->super), + (unsigned long long) ext2fs_free_blocks_count(rfs->new_fs->super), + (unsigned long long) rfs->needed_blocks); #endif init_resource_track(&rtrack, "block_mover", fs->io); @@ -206,8 +206,12 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags, goto errout; print_resource_track(rfs, &rtrack, fs->io); + retval = clear_sparse_super2_last_group(rfs); + if (retval) + goto errout; + init_resource_track(&rtrack, "calculate_summary_stats", fs->io); - retval = ext2fs_calculate_summary_stats(rfs->new_fs); + retval = resize2fs_calculate_summary_stats(rfs->new_fs); if (retval) goto errout; print_resource_track(rfs, &rtrack, fs->io); @@ -224,10 +228,6 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags, goto errout; print_resource_track(rfs, &rtrack, fs->io); - retval = clear_sparse_super2_last_group(rfs); - if (retval) - goto errout; - retval = ext2fs_set_gdt_csum(rfs->new_fs); if (retval) goto errout; @@ -329,8 +329,8 @@ static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size) copy_size = EXT2_DESC_SIZE(rfs->new_fs->super); for (i = 0; i < rfs->old_fs->group_desc_count; i++) { memcpy(n, o, copy_size); - n += EXT2_DESC_SIZE(rfs->new_fs->super); - o += EXT2_DESC_SIZE(rfs->old_fs->super); + n = (char *)n + EXT2_DESC_SIZE(rfs->new_fs->super); + o = (char *)o + EXT2_DESC_SIZE(rfs->old_fs->super); } ext2fs_free_mem(&rfs->new_fs->group_desc); @@ -521,7 +521,7 @@ static errcode_t zero_high_bits_in_inodes(ext2_resize_t rfs) if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT))) return 0; - if (fs->super->s_creator_os != EXT2_OS_LINUX) + if (fs->super->s_creator_os == EXT2_OS_HURD) return 0; retval = ext2fs_open_inode_scan(fs, 0, &scan); @@ -578,13 +578,13 @@ out: } /* - * Clean up the bitmaps for unitialized bitmaps + * Clean up the bitmaps for uninitialized bitmaps */ static void fix_uninit_block_bitmaps(ext2_filsys fs) { blk64_t blk, lblk; dgrp_t g; - int i; + unsigned int i; if (!ext2fs_has_group_desc_csum(fs)) return; @@ -604,7 +604,7 @@ static void fix_uninit_block_bitmaps(ext2_filsys fs) ext2fs_mark_block_bitmap2(fs->block_map, ext2fs_inode_bitmap_loc(fs, g)); for (i = 0, blk = ext2fs_inode_table_loc(fs, g); - i < (unsigned int) fs->inode_blocks_per_group; + i < fs->inode_blocks_per_group; i++, blk++) ext2fs_mark_block_bitmap2(fs->block_map, blk); } @@ -631,12 +631,12 @@ static errcode_t free_gdp_blocks(ext2_filsys fs, ext2_filsys old_fs, dgrp_t group) { - blk64_t blk; - int j; - dgrp_t i; + blk64_t blk; + unsigned int j; + dgrp_t i; ext2fs_block_bitmap bg_map = NULL; - errcode_t retval = 0; - dgrp_t count = old_fs->group_desc_count - fs->group_desc_count; + errcode_t retval = 0; + dgrp_t count = old_fs->group_desc_count - fs->group_desc_count; /* If bigalloc, don't free metadata living in the same cluster */ if (EXT2FS_CLUSTER_RATIO(fs) > 1) { @@ -698,11 +698,12 @@ errcode_t adjust_fs_info(ext2_filsys fs, ext2_filsys old_fs, blk64_t old_numblocks, numblocks, adjblocks; unsigned long i, j, old_desc_blocks; unsigned int meta_bg, meta_bg_size; - int has_super, csum_flag; + int has_super, csum_flag, has_bg; unsigned long long new_inodes; /* u64 to check for overflow */ double percent; ext2fs_blocks_count_set(fs->super, new_size); + fs->super->s_overhead_clusters = 0; retry: fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) - @@ -721,7 +722,19 @@ retry: */ overhead = (int) (2 + fs->inode_blocks_per_group); - if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) + has_bg = 0; + if (ext2fs_has_feature_sparse_super2(fs->super)) { + /* + * We have to do this manually since + * super->s_backup_bgs hasn't been set up yet. + */ + if (fs->group_desc_count == 2) + has_bg = fs->super->s_backup_bgs[0] != 0; + else + has_bg = fs->super->s_backup_bgs[1] != 0; + } else + has_bg = ext2fs_bg_has_super(fs, fs->group_desc_count - 1); + if (has_bg) overhead += 1 + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; @@ -744,8 +757,17 @@ retry: */ new_inodes =(unsigned long long) fs->super->s_inodes_per_group * fs->group_desc_count; if (new_inodes > ~0U) { - fprintf(stderr, _("inodes (%llu) must be less than %u"), - new_inodes, ~0U); + new_inodes = (unsigned long long) fs->super->s_inodes_per_group * (fs->group_desc_count - 1); + if (new_inodes <= ~0U) { + unsigned long long new_blocks = + ((unsigned long long) fs->super->s_blocks_per_group * + (fs->group_desc_count - 1)) + fs->super->s_first_data_block; + + ext2fs_blocks_count_set(fs->super, new_blocks); + goto retry; + } + fprintf(stderr, _("inodes (%llu) must be less than %u\n"), + (unsigned long long) new_inodes, ~0U); return EXT2_ET_TOO_MANY_INODES; } fs->super->s_inodes_count = fs->super->s_inodes_per_group * @@ -843,10 +865,9 @@ retry: if (last_bg > old_last_bg) { if (old_fs->group_desc_count == 1) fs->super->s_backup_bgs[0] = 1; - if (old_fs->group_desc_count == 1 && - fs->super->s_backup_bgs[0]) - fs->super->s_backup_bgs[0] = last_bg; - else if (fs->super->s_backup_bgs[1]) + if ((old_fs->group_desc_count < 3 && + fs->group_desc_count > 2) || + fs->super->s_backup_bgs[1]) fs->super->s_backup_bgs[1] = last_bg; } else if (last_bg < old_last_bg) { if (fs->super->s_backup_bgs[0] > last_bg) @@ -909,8 +930,9 @@ retry: group_block = ext2fs_group_first_block2(fs, old_fs->group_desc_count); csum_flag = ext2fs_has_group_desc_csum(fs); - if (!getenv("RESIZE2FS_FORCE_ITABLE_INIT") && - access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0) + if (getenv("RESIZE2FS_FORCE_LAZY_ITABLE_INIT") || + (!getenv("RESIZE2FS_FORCE_ITABLE_INIT") && + access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0)) lazy_itable_init = 1; if (ext2fs_has_feature_meta_bg(fs->super)) old_desc_blocks = fs->super->s_first_meta_bg; @@ -921,7 +943,7 @@ retry: /* * If we changed the number of block_group descriptor blocks, * we need to make sure they are all marked as reserved in the - * file systems's block allocation map. + * filesystem's block allocation map. */ for (i = 0; i < old_fs->group_desc_count; i++) ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); @@ -1007,6 +1029,85 @@ errout: } /* + * Replicate the first part of adjust_fs_info to determine what the + * new size of the file system should be. This allows resize2fs to + * exit early if we aren't going to make any changes to the file + * system. + */ +void adjust_new_size(ext2_filsys fs, blk64_t *sizep) +{ + blk64_t size, rem, overhead = 0; + unsigned long desc_blocks; + dgrp_t group_desc_count; + int has_bg; + unsigned long long new_inodes; /* u64 to check for overflow */ + + size = *sizep; +retry: + group_desc_count = ext2fs_div64_ceil(size - + fs->super->s_first_data_block, + EXT2_BLOCKS_PER_GROUP(fs->super)); + if (group_desc_count == 0) + return; + desc_blocks = ext2fs_div_ceil(group_desc_count, + EXT2_DESC_PER_BLOCK(fs->super)); + + /* + * Overhead is the number of bookkeeping blocks per group. It + * includes the superblock backup, the group descriptor + * backups, the inode bitmap, the block bitmap, and the inode + * table. + */ + overhead = (int) (2 + fs->inode_blocks_per_group); + + has_bg = 0; + if (ext2fs_has_feature_sparse_super2(fs->super)) { + /* + * We have to do this manually since + * super->s_backup_bgs hasn't been set up yet. + */ + if (group_desc_count == 2) + has_bg = fs->super->s_backup_bgs[0] != 0; + else + has_bg = fs->super->s_backup_bgs[1] != 0; + } else + has_bg = ext2fs_bg_has_super(fs, group_desc_count - 1); + if (has_bg) + overhead += 1 + desc_blocks + + fs->super->s_reserved_gdt_blocks; + + /* + * See if the last group is big enough to support the + * necessary data structures. If not, we need to get rid of + * it. + */ + rem = (size - fs->super->s_first_data_block) % + fs->super->s_blocks_per_group; + if ((group_desc_count == 1) && rem && (rem < overhead)) + return; + if ((group_desc_count > 1) && rem && (rem < overhead+50)) { + size -= rem; + goto retry; + } + + /* + * If we need to reduce the size by no more than a block + * group to avoid overrunning the max inode limit, do it. + */ + new_inodes =(unsigned long long) fs->super->s_inodes_per_group * group_desc_count; + if (new_inodes > ~0U) { + new_inodes = (unsigned long long) fs->super->s_inodes_per_group * (group_desc_count - 1); + if (new_inodes > ~0U) + return; + size = ((unsigned long long) fs->super->s_blocks_per_group * + (group_desc_count - 1)) + fs->super->s_first_data_block; + + goto retry; + } + *sizep = size; +} + +/* * This routine adjusts the superblock and other data structures, both * in disk as well as in memory... */ @@ -1015,7 +1116,6 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size) ext2_filsys fs = rfs->new_fs; int adj = 0; errcode_t retval; - blk64_t group_block; unsigned long i; unsigned long max_group; @@ -1080,8 +1180,6 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size) goto errout; memset(rfs->itable_buf, 0, fs->blocksize * fs->inode_blocks_per_group); - group_block = ext2fs_group_first_block2(fs, - rfs->old_fs->group_desc_count); adj = rfs->old_fs->group_desc_count; max_group = fs->group_desc_count - adj; if (rfs->progress) { @@ -1108,7 +1206,6 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size) if (retval) goto errout; } - group_block += fs->super->s_blocks_per_group; } io_channel_flush(fs->io); retval = 0; @@ -1164,6 +1261,11 @@ static errcode_t mark_table_blocks(ext2_filsys fs, if (blk) ext2fs_mark_block_bitmap2(bmap, blk); } + /* Reserve the MMP block */ + if (ext2fs_has_feature_mmp(fs->super) && + fs->super->s_mmp_block > fs->super->s_first_data_block && + fs->super->s_mmp_block < ext2fs_blocks_count(fs->super)) + ext2fs_mark_block_bitmap2(bmap, fs->super->s_mmp_block); return 0; } @@ -1230,7 +1332,8 @@ static void mark_fs_metablock(ext2_resize_t rfs, * nothing other than standard metadata in use. */ return; - } else if (ext2fs_test_block_bitmap2(rfs->old_fs->block_map, blk) && + } else if (blk < ext2fs_blocks_count(rfs->old_fs->super) && + ext2fs_test_block_bitmap2(rfs->old_fs->block_map, blk) && !ext2fs_test_block_bitmap2(meta_bmap, blk)) { ext2fs_mark_block_bitmap2(rfs->move_blocks, blk); rfs->needed_blocks++; @@ -1245,7 +1348,8 @@ static void mark_fs_metablock(ext2_resize_t rfs, */ static errcode_t blocks_to_move(ext2_resize_t rfs) { - int j, has_super; + unsigned int j; + int has_super; dgrp_t i, max_groups, g; blk64_t blk, group_blk; blk64_t old_blocks, new_blocks, group_end, cluster_freed; @@ -1491,12 +1595,13 @@ static errcode_t blocks_to_move(ext2_resize_t rfs) /* * For those structures that have changed, we need to - * do bookkeepping. + * do bookkeeping. */ if (ext2fs_block_bitmap_loc(old_fs, i) != (blk = ext2fs_block_bitmap_loc(fs, i))) { ext2fs_block_alloc_stats2(fs, blk, +1); - if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) && + if (blk < ext2fs_blocks_count(old_fs->super) && + ext2fs_test_block_bitmap2(old_fs->block_map, blk) && !ext2fs_test_block_bitmap2(meta_bmap, blk)) ext2fs_mark_block_bitmap2(rfs->move_blocks, blk); @@ -1504,7 +1609,8 @@ static errcode_t blocks_to_move(ext2_resize_t rfs) if (ext2fs_inode_bitmap_loc(old_fs, i) != (blk = ext2fs_inode_bitmap_loc(fs, i))) { ext2fs_block_alloc_stats2(fs, blk, +1); - if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) && + if (blk < ext2fs_blocks_count(old_fs->super) && + ext2fs_test_block_bitmap2(old_fs->block_map, blk) && !ext2fs_test_block_bitmap2(meta_bmap, blk)) ext2fs_mark_block_bitmap2(rfs->move_blocks, blk); @@ -1530,7 +1636,8 @@ static errcode_t blocks_to_move(ext2_resize_t rfs) for (blk = ext2fs_inode_table_loc(fs, i), j=0; j < fs->inode_blocks_per_group ; j++, blk++) { ext2fs_block_alloc_stats2(fs, blk, +1); - if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) && + if (blk < ext2fs_blocks_count(old_fs->super) && + ext2fs_test_block_bitmap2(old_fs->block_map, blk) && !ext2fs_test_block_bitmap2(meta_bmap, blk)) ext2fs_mark_block_bitmap2(rfs->move_blocks, blk); @@ -1630,7 +1737,8 @@ static errcode_t resize2fs_get_alloc_block(ext2_filsys fs, #ifdef RESIZE2FS_DEBUG if (rfs->flags & 0xF) - printf("get_alloc_block allocating %llu\n", blk); + printf("get_alloc_block allocating %llu\n", + (unsigned long long) blk); #endif ext2fs_mark_block_bitmap2(rfs->old_fs->block_map, blk); @@ -1651,8 +1759,7 @@ static errcode_t block_mover(ext2_resize_t rfs) ext2_filsys fs = rfs->new_fs; ext2_filsys old_fs = rfs->old_fs; errcode_t retval; - __u64 size; - int c; + __u64 c, size; int to_move, moved; ext2_badblocks_list badblock_list = 0; int bb_modified = 0; @@ -1670,11 +1777,11 @@ static errcode_t block_mover(ext2_resize_t rfs) fs->inode_blocks_per_group, &rfs->itable_buf); if (retval) - return retval; + goto errout; } retval = ext2fs_create_extent_table(&rfs->bmap, 0); if (retval) - return retval; + goto errout; /* * The first step is to figure out where all of the blocks @@ -1737,7 +1844,9 @@ static errcode_t block_mover(ext2_resize_t rfs) #ifdef RESIZE2FS_DEBUG if (rfs->flags & RESIZE_DEBUG_BMOVE) printf("Moving %llu blocks %llu->%llu\n", - size, old_blk, new_blk); + (unsigned long long) size, + (unsigned long long) old_blk, + (unsigned long long) new_blk); #endif do { c = size; @@ -1830,8 +1939,9 @@ static int process_block(ext2_filsys fs, blk64_t *block_nr, #ifdef RESIZE2FS_DEBUG if (pb->rfs->flags & RESIZE_DEBUG_BMOVE) printf("ino=%u, blockcnt=%lld, %llu->%llu\n", - pb->old_ino, blockcnt, block, - new_block); + pb->old_ino, (long long) blockcnt, + (unsigned long long) block, + (unsigned long long) new_block); #endif block = new_block; } @@ -1884,7 +1994,7 @@ static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino, errcode_t err = 0; /* No EA block or no remapping? Quit early. */ - if (ext2fs_file_acl_block(rfs->old_fs, inode) == 0 && !rfs->bmap) + if (ext2fs_file_acl_block(rfs->old_fs, inode) == 0 || !rfs->bmap) return 0; new_block = extent_translate(rfs->old_fs, rfs->bmap, ext2fs_file_acl_block(rfs->old_fs, inode)); @@ -1915,66 +2025,153 @@ out: return err; } -/* Rewrite extents */ -static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino) +static void quiet_com_err_proc(const char *whoami EXT2FS_ATTR((unused)), + errcode_t code EXT2FS_ATTR((unused)), + const char *fmt EXT2FS_ATTR((unused)), + va_list args EXT2FS_ATTR((unused))) { - ext2_extent_handle_t handle; - struct ext2fs_extent extent; - errcode_t errcode; - struct ext2_extent_info info; +} - errcode = ext2fs_extent_open(fs, ino, &handle); - if (errcode) - return errcode; +static int fix_ea_entries(ext2_extent imap, struct ext2_ext_attr_entry *entry, + struct ext2_ext_attr_entry *end, ext2_ino_t last_ino) +{ + int modified = 0; + ext2_ino_t new_ino; + + while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) { + if (entry->e_value_inum > last_ino) { + new_ino = ext2fs_extent_translate(imap, + entry->e_value_inum); + entry->e_value_inum = new_ino; + modified = 1; + } + entry = EXT2_EXT_ATTR_NEXT(entry); + } + return modified; +} - errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent); - if (errcode) +static int fix_ea_ibody_entries(ext2_extent imap, + struct ext2_inode_large *inode, int inode_size, + ext2_ino_t last_ino) +{ + struct ext2_ext_attr_entry *start, *end; + __u32 *ea_magic; + + if (inode->i_extra_isize == 0) + return 0; + + ea_magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize); + if (*ea_magic != EXT2_EXT_ATTR_MAGIC) + return 0; + + start = (struct ext2_ext_attr_entry *)(ea_magic + 1); + end = (struct ext2_ext_attr_entry *)((char *)inode + inode_size); + + return fix_ea_entries(imap, start, end, last_ino); +} + +static int fix_ea_block_entries(ext2_extent imap, char *block_buf, + unsigned int blocksize, ext2_ino_t last_ino) +{ + struct ext2_ext_attr_header *header; + struct ext2_ext_attr_entry *start, *end; + + header = (struct ext2_ext_attr_header *)block_buf; + start = (struct ext2_ext_attr_entry *)(header+1); + end = (struct ext2_ext_attr_entry *)(block_buf + blocksize); + + return fix_ea_entries(imap, start, end, last_ino); +} + +/* A simple LRU cache to check recently processed blocks. */ +struct blk_cache { + int cursor; + blk64_t blks[4]; +}; + +#define BLK_IN_CACHE(b,c) ((b) == (c).blks[0] || (b) == (c).blks[1] || \ + (b) == (c).blks[2] || (b) == (c).blks[3]) +#define BLK_ADD_CACHE(b,c) { \ + (c).blks[(c).cursor] = (b); \ + (c).cursor = ((c).cursor + 1) % 4; \ +} + +static errcode_t fix_ea_inode_refs(ext2_resize_t rfs, struct ext2_inode *inode, + char *block_buf, ext2_ino_t last_ino) +{ + ext2_filsys fs = rfs->new_fs; + ext2_inode_scan scan = NULL; + ext2_ino_t ino; + int inode_size = EXT2_INODE_SIZE(fs->super); + blk64_t blk; + int modified; + struct blk_cache blk_cache; + struct ext2_ext_attr_header *header; + errcode_t retval; + + memset(&blk_cache, 0, sizeof(blk_cache)); + + header = (struct ext2_ext_attr_header *)block_buf; + + retval = ext2fs_open_inode_scan(fs, 0, &scan); + if (retval) goto out; - do { - errcode = ext2fs_extent_get_info(handle, &info); - if (errcode) + while (1) { + retval = ext2fs_get_next_inode_full(scan, &ino, inode, + inode_size); + if (retval) + goto out; + if (!ino) break; - /* - * If this is the first extent in an extent block that we - * haven't visited, rewrite the extent to force the ETB - * checksum to be rewritten. - */ - if (info.curr_entry == 1 && info.curr_level != 0 && - !(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)) { - errcode = ext2fs_extent_replace(handle, 0, &extent); - if (errcode) - break; - } + if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO) + continue; /* inode not in use */ - /* Skip to the end of a block of leaf nodes */ - if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) { - errcode = ext2fs_extent_get(handle, - EXT2_EXTENT_LAST_SIB, - &extent); - if (errcode) - break; + if (inode_size != EXT2_GOOD_OLD_INODE_SIZE) { + modified = fix_ea_ibody_entries(rfs->imap, + (struct ext2_inode_large *)inode, + inode_size, last_ino); + if (modified) { + retval = ext2fs_write_inode_full(fs, ino, inode, + inode_size); + if (retval) + goto out; + } } - errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent); - } while (errcode == 0); + blk = ext2fs_file_acl_block(fs, inode); + if (blk && !BLK_IN_CACHE(blk, blk_cache)) { + retval = ext2fs_read_ext_attr3(fs, blk, block_buf, ino); + if (retval) + goto out; + modified = fix_ea_block_entries(rfs->imap, block_buf, + fs->blocksize, + last_ino); + if (modified) { + retval = ext2fs_write_ext_attr3(fs, blk, + block_buf, ino); + if (retval) + goto out; + /* + * If refcount is greater than 1, we might see + * the same block referenced by other inodes + * later. + */ + if (header->h_refcount > 1) + BLK_ADD_CACHE(blk, blk_cache); + } + } + } + retval = 0; out: - /* Ok if we run off the end */ - if (errcode == EXT2_ET_EXTENT_NO_NEXT) - errcode = 0; - ext2fs_extent_free(handle); - return errcode; -} + if (scan) + ext2fs_close_inode_scan(scan); + return retval; -static void quiet_com_err_proc(const char *whoami EXT2FS_ATTR((unused)), - errcode_t code EXT2FS_ATTR((unused)), - const char *fmt EXT2FS_ATTR((unused)), - va_list args EXT2FS_ATTR((unused))) -{ } - static errcode_t inode_scan_and_fix(ext2_resize_t rfs) { struct process_block_struct pb; @@ -1985,6 +2182,7 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs) char *block_buf = 0; ext2_ino_t start_to_move; int inode_size; + int update_ea_inode_refs = 0; if ((rfs->old_fs->group_desc_count <= rfs->new_fs->group_desc_count) && @@ -2057,7 +2255,16 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs) ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1, pb.is_dir); - inode->i_ctime = time(0); + /* + * i_ctime field in xattr inodes contain a portion of the ref + * count, do not overwrite. + */ + if (inode->i_flags & EXT4_EA_INODE_FL) + update_ea_inode_refs = 1; + else + inode->i_ctime = rfs->old_fs->now ? + rfs->old_fs->now : time(0); + retval = ext2fs_write_inode_full(rfs->old_fs, new_inode, inode, inode_size); if (retval) @@ -2083,31 +2290,20 @@ remap_blocks: if (retval) goto errout; - /* Rewrite extent block checksums with new inode number */ - if (ext2fs_has_feature_metadata_csum(rfs->old_fs->super) && - (inode->i_flags & EXT4_EXTENTS_FL)) { - rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; - retval = rewrite_extents(rfs->old_fs, new_inode); - rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; - if (retval) - goto errout; - } - /* * Update inodes to point to new blocks; schedule directory * blocks for inode remapping. Need to write out dir blocks * with new inode numbers if we have metadata_csum enabled. */ + rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) && (rfs->bmap || pb.is_dir)) { pb.ino = new_inode; pb.old_ino = ino; pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL; - rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; retval = ext2fs_block_iterate3(rfs->old_fs, new_inode, 0, block_buf, process_block, &pb); - rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (retval) goto errout; if (pb.error) { @@ -2122,11 +2318,29 @@ remap_blocks: if (retval) goto errout; } + + /* Fix up extent block checksums with the new inode number */ + if (ext2fs_has_feature_metadata_csum(rfs->old_fs->super) && + (inode->i_flags & EXT4_EXTENTS_FL)) { + retval = ext2fs_fix_extents_checksums(rfs->old_fs, + new_inode, NULL); + if (retval) + goto errout; + } + } + + if (update_ea_inode_refs && + ext2fs_has_feature_ea_inode(rfs->new_fs->super)) { + retval = fix_ea_inode_refs(rfs, inode, block_buf, + start_to_move); + if (retval) + goto errout; } io_channel_flush(rfs->old_fs->io); errout: reset_com_err_hook(); + rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (rfs->bmap) { ext2fs_free_extent_table(rfs->bmap); rfs->bmap = 0; @@ -2202,7 +2416,8 @@ static int check_and_change_inodes(ext2_ino_t dir, /* Update the directory mtime and ctime */ retval = ext2fs_read_inode(is->rfs->old_fs, dir, &inode); if (retval == 0) { - inode.i_mtime = inode.i_ctime = time(0); + inode.i_mtime = inode.i_ctime = is->rfs->old_fs->now ? + is->rfs->old_fs->now : time(0); is->err = ext2fs_write_inode(is->rfs->old_fs, dir, &inode); if (is->err) return ret | DIRENT_ABORT; @@ -2287,7 +2502,8 @@ static errcode_t move_itables(ext2_resize_t rfs) char *cp; blk64_t old_blk, new_blk, blk, cluster_freed; errcode_t retval; - int j, to_move, moved; + int to_move, moved; + unsigned int j; ext2fs_block_bitmap new_bmap = NULL; max_groups = fs->group_desc_count; @@ -2343,7 +2559,8 @@ static errcode_t move_itables(ext2_resize_t rfs) #ifdef RESIZE2FS_DEBUG if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) printf("Itable move group %d block %llu->%llu (diff %lld)\n", - i, old_blk, new_blk, diff); + i, (unsigned long long) old_blk, + (unsigned long long) new_blk, diff); #endif if (!diff) @@ -2466,6 +2683,9 @@ static errcode_t clear_sparse_super2_last_group(ext2_resize_t rfs) fs->super->s_backup_bgs[1] == old_last_bg) return 0; + if (old_last_bg == 0) + return 0; + retval = ext2fs_super_and_bgd_loc2(rfs->old_fs, old_last_bg, &sb, &old_desc, NULL, &num); if (retval) @@ -2620,71 +2840,42 @@ errout: /* * Finally, recalculate the summary information */ -static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs) +static errcode_t resize2fs_calculate_summary_stats(ext2_filsys fs) { - blk64_t blk; + errcode_t retval; + blk64_t blk = fs->super->s_first_data_block; ext2_ino_t ino; - unsigned int group = 0; - unsigned int count = 0; - blk64_t total_blocks_free = 0; + unsigned int n, group, count; + blk64_t total_clusters_free = 0; int total_inodes_free = 0; int group_free = 0; int uninit = 0; - blk64_t super_blk, old_desc_blk, new_desc_blk; - int old_desc_blocks; + char *bitmap_buf; /* * First calculate the block statistics */ - uninit = ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT); - ext2fs_super_and_bgd_loc2(fs, group, &super_blk, &old_desc_blk, - &new_desc_blk, 0); - if (ext2fs_has_feature_meta_bg(fs->super)) - old_desc_blocks = fs->super->s_first_meta_bg; - else - old_desc_blocks = fs->desc_blocks + - fs->super->s_reserved_gdt_blocks; - for (blk = B2C(fs->super->s_first_data_block); - blk < ext2fs_blocks_count(fs->super); - blk += EXT2FS_CLUSTER_RATIO(fs)) { - if ((uninit && - !(EQ_CLSTR(blk, super_blk) || - ((old_desc_blk && old_desc_blocks && - GE_CLSTR(blk, old_desc_blk) && - LT_CLSTR(blk, old_desc_blk + old_desc_blocks))) || - ((new_desc_blk && EQ_CLSTR(blk, new_desc_blk))) || - EQ_CLSTR(blk, ext2fs_block_bitmap_loc(fs, group)) || - EQ_CLSTR(blk, ext2fs_inode_bitmap_loc(fs, group)) || - ((GE_CLSTR(blk, ext2fs_inode_table_loc(fs, group)) && - LT_CLSTR(blk, ext2fs_inode_table_loc(fs, group) - + fs->inode_blocks_per_group))))) || - (!ext2fs_fast_test_block_bitmap2(fs->block_map, blk))) { - group_free++; - total_blocks_free++; - } - count++; - if ((count == fs->super->s_clusters_per_group) || - EQ_CLSTR(blk, ext2fs_blocks_count(fs->super)-1)) { - ext2fs_bg_free_blocks_count_set(fs, group, group_free); - ext2fs_group_desc_csum_set(fs, group); - group++; - if (group >= fs->group_desc_count) - break; - count = 0; - group_free = 0; - uninit = ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT); - ext2fs_super_and_bgd_loc2(fs, group, &super_blk, - &old_desc_blk, - &new_desc_blk, 0); - if (ext2fs_has_feature_meta_bg(fs->super)) - old_desc_blocks = fs->super->s_first_meta_bg; - else - old_desc_blocks = fs->desc_blocks + - fs->super->s_reserved_gdt_blocks; + bitmap_buf = malloc(fs->blocksize); + if (!bitmap_buf) + return ENOMEM; + for (group = 0; group < fs->group_desc_count; + group++) { + retval = ext2fs_get_block_bitmap_range2(fs->block_map, + B2C(blk), fs->super->s_clusters_per_group, bitmap_buf); + if (retval) { + free(bitmap_buf); + return retval; } + n = ext2fs_bitcount(bitmap_buf, + fs->super->s_clusters_per_group / 8); + group_free = fs->super->s_clusters_per_group - n; + total_clusters_free += group_free; + ext2fs_bg_free_blocks_count_set(fs, group, group_free); + ext2fs_group_desc_csum_set(fs, group); + blk += fs->super->s_blocks_per_group; } - total_blocks_free = C2B(total_blocks_free); - ext2fs_free_blocks_count_set(fs->super, total_blocks_free); + free(bitmap_buf); + ext2fs_free_blocks_count_set(fs->super, C2B(total_clusters_free)); /* * Next, calculate the inode statistics @@ -2768,7 +2959,7 @@ static int calc_group_overhead(ext2_filsys fs, blk64_t grp, /* - * calcluate the minimum number of blocks the given fs can be resized to + * calculate the minimum number of blocks the given fs can be resized to */ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) { @@ -2778,7 +2969,7 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) blk64_t grp, data_needed, last_start; blk64_t overhead = 0; int old_desc_blocks; - int flexbg_size = 1 << fs->super->s_log_groups_per_flex; + unsigned flexbg_size = 1U << fs->super->s_log_groups_per_flex; /* * first figure out how many group descriptors we need to @@ -2807,14 +2998,24 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) fs->super->s_reserved_gdt_blocks; /* calculate how many blocks are needed for data */ - data_needed = ext2fs_blocks_count(fs->super) - - ext2fs_free_blocks_count(fs->super); - - for (grp = 0; grp < fs->group_desc_count; grp++) - data_needed -= calc_group_overhead(fs, grp, old_desc_blocks); + data_needed = ext2fs_blocks_count(fs->super); + for (grp = 0; grp < fs->group_desc_count; grp++) { + __u32 n = ext2fs_bg_free_blocks_count(fs, grp); + + if (n > EXT2_BLOCKS_PER_GROUP(fs->super)) + n = EXT2_BLOCKS_PER_GROUP(fs->super); + n += calc_group_overhead(fs, grp, old_desc_blocks); + if (data_needed < n) { + if (flags & RESIZE_DEBUG_MIN_CALC) + printf("file system appears inconsistent?!?\n"); + return ext2fs_blocks_count(fs->super); + } + data_needed -= n; + } #ifdef RESIZE2FS_DEBUG if (flags & RESIZE_DEBUG_MIN_CALC) - printf("fs requires %llu data blocks.\n", data_needed); + printf("fs requires %llu data blocks.\n", + (unsigned long long) data_needed); #endif /* @@ -2857,11 +3058,11 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) #ifdef RESIZE2FS_DEBUG if (flags & RESIZE_DEBUG_MIN_CALC) printf("With %d group(s), we have %llu blocks available.\n", - groups, data_blocks); + groups, (unsigned long long) data_blocks); #endif /* - * if we need more group descriptors in order to accomodate our data + * if we need more group descriptors in order to accommodate our data * then we need to add them here */ blks_needed = data_needed; @@ -2910,8 +3111,10 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) if (flags & RESIZE_DEBUG_MIN_CALC) printf("Added %d extra group(s), " "blks_needed %llu, data_blocks %llu, " - "last_start %llu\n", extra_grps, blks_needed, - data_blocks, last_start); + "last_start %llu\n", extra_grps, + (unsigned long long) blks_needed, + (unsigned long long) data_blocks, + (unsigned long long) last_start); #endif } @@ -2926,7 +3129,8 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) #ifdef RESIZE2FS_DEBUG if (flags & RESIZE_DEBUG_MIN_CALC) - printf("Last group's overhead is %llu\n", overhead); + printf("Last group's overhead is %llu\n", + (unsigned long long) overhead); #endif /* @@ -2939,7 +3143,7 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) #ifdef RESIZE2FS_DEBUG if (flags & RESIZE_DEBUG_MIN_CALC) printf("Need %llu data blocks in last group\n", - remainder); + (unsigned long long) remainder); #endif /* * 50 is a magic number that mkfs/resize uses to see if its @@ -2957,7 +3161,8 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) overhead += fs->super->s_first_data_block; #ifdef RESIZE2FS_DEBUG if (flags & RESIZE_DEBUG_MIN_CALC) - printf("Final size of last group is %lld\n", overhead); + printf("Final size of last group is %llu\n", + (unsigned long long) overhead); #endif /* Add extra slack for bigalloc file systems */ @@ -2984,7 +3189,8 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) #ifdef RESIZE2FS_DEBUG if (flags & RESIZE_DEBUG_MIN_CALC) - printf("Estimated blocks needed: %llu\n", blks_needed); + printf("Estimated blocks needed: %llu\n", + (unsigned long long) blks_needed); #endif /* @@ -3019,7 +3225,8 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags) #ifdef RESIZE2FS_DEBUG if (flags & RESIZE_DEBUG_MIN_CALC) - printf("Extents safety margin: %llu\n", safe_margin); + printf("Extents safety margin: %llu\n", + (unsigned long long) safe_margin); #endif blks_needed += safe_margin; }