Exemple #1
0
/*
 * This function is called by ext2fs_get_next_inode when it needs to
 * get ready to read in a new blockgroup.
 */
static errcode_t get_next_blockgroup(ext2_inode_scan scan)
{
	ext2_filsys fs = scan->fs;

	scan->current_group++;
	scan->groups_left--;

	scan->current_block = ext2fs_inode_table_loc(scan->fs,
						     scan->current_group);
	scan->current_inode = scan->current_group *
		EXT2_INODES_PER_GROUP(fs->super);

	scan->bytes_left = 0;
	scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super);
	scan->blocks_left = fs->inode_blocks_per_group;
	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
		scan->inodes_left -=
			ext2fs_bg_itable_unused(fs, scan->current_group);
		scan->blocks_left =
			(scan->inodes_left +
			 (fs->blocksize / scan->inode_size - 1)) *
			scan->inode_size / fs->blocksize;
	}

	return 0;
}
Exemple #2
0
/*
 * This function is called by ext2fs_get_next_inode when it needs to
 * get ready to read in a new blockgroup.
 */
static errcode_t get_next_blockgroup(ext2_inode_scan scan)
{
	ext2_filsys fs = scan->fs;

	scan->current_group++;
	scan->groups_left--;

	scan->current_block = ext2fs_inode_table_loc(scan->fs,
						     scan->current_group);
	scan->current_inode = scan->current_group *
		EXT2_INODES_PER_GROUP(fs->super);

	scan->bytes_left = 0;
	scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super);
	scan->blocks_left = fs->inode_blocks_per_group;
	if (ext2fs_has_group_desc_csum(fs)) {
		__u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group);
		if (scan->inodes_left > unused)
			scan->inodes_left -= unused;
		else
			scan->inodes_left = 0;
		scan->blocks_left =
			(scan->inodes_left +
			 (fs->blocksize / scan->inode_size - 1)) *
			scan->inode_size / fs->blocksize;
	}
	if (scan->current_block &&
	    ((scan->current_block < fs->super->s_first_data_block) ||
	     (scan->current_block + fs->inode_blocks_per_group - 1 >=
	      ext2fs_blocks_count(fs->super))))
		return EXT2_ET_GDESC_BAD_INODE_TABLE;
	return 0;
}
Exemple #3
0
/*
 * This function is called by ext2fs_get_next_inode when it needs to
 * get ready to read in a new blockgroup.
 */
static errcode_t get_next_blockgroup(ext2_inode_scan scan)
{
	ext2_filsys fs = scan->fs;

	scan->current_group++;
	scan->groups_left--;

	scan->current_block = ext2fs_inode_table_loc(scan->fs,
						     scan->current_group);
	scan->current_inode = scan->current_group *
		EXT2_INODES_PER_GROUP(fs->super);

	scan->bytes_left = 0;
	scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super);
	scan->blocks_left = fs->inode_blocks_per_group;
	if (ext2fs_has_group_desc_csum(fs)) {
		__u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group);
		if (scan->inodes_left > unused)
			scan->inodes_left -= unused;
		else
			scan->inodes_left = 0;
		scan->blocks_left =
			(scan->inodes_left +
			 (fs->blocksize / scan->inode_size - 1)) *
			scan->inode_size / fs->blocksize;
	}

	return 0;
}
Exemple #4
0
/*
 * Check for uninit block bitmaps and deal with them appropriately
 */
static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map,
			       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)) ||
	    !(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);
	}
	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);
}
/*
 * This will try to discard number 'count' inodes starting at
 * inode number 'start' within the 'group'. Note that 'start'
 * is 1-based, it means that we need to adjust it by -1 in this
 * function to compute right offset in the particular inode table.
 */
static void e2fsck_discard_inodes(e2fsck_t ctx, dgrp_t group,
				  ext2_ino_t start, int count)
{
	ext2_filsys fs = ctx->fs;
	blk64_t blk, num;

	/*
	 * Sanity check for 'start'
	 */
	if ((start < 1) || (start > EXT2_INODES_PER_GROUP(fs->super))) {
		printf("PROGRAMMING ERROR: Got start %d outside of group %d!"
		       " Disabling discard\n",
			start, group);
		ctx->options &= ~E2F_OPT_DISCARD;
	}

	/*
	 * Do not attempt to discard if E2F_OPT_DISCARD is not set. And also
	 * skip the discard on this group if discard does not zero data.
	 * The reason is that if the inode table is not zeroed discard would
	 * no help us since we need to zero it anyway, or if the inode table
	 * is zeroed then the read after discard would not be deterministic
	 * anyway and we would not be able to assume that this inode table
	 * was zeroed anymore so we would have to zero it again, which does
	 * not really make sense.
	 */
	if (!(ctx->options & E2F_OPT_DISCARD) ||
	    !io_channel_discard_zeroes_data(fs->io))
		return;

	/*
	 * Start is inode number within the group which starts
	 * counting from 1, so we need to adjust it.
	 */
	start -= 1;

	/*
	 * We can discard only blocks containing only unused
	 * inodes in the table.
	 */
	blk = DIV_ROUND_UP(start,
		EXT2_INODES_PER_BLOCK(fs->super));
	count -= (blk * EXT2_INODES_PER_BLOCK(fs->super) - start);
	blk += ext2fs_inode_table_loc(fs, group);
	num = count / EXT2_INODES_PER_BLOCK(fs->super);

	if (num > 0)
		e2fsck_discard_blocks(ctx, blk, num);
}
/*
 * Verify the touched map
 */
static void check_map(void)
{
	int	i, j, first=1;
	blk64_t	blk;

	for (i=0; test_vec[i]; i++) {
		if (ext2fs_test_block_bitmap2(touched_map, test_vec[i])) {
			printf("Bad block was touched --- %llu\n", test_vec[i]);
			failed++;
			first_no_comma = 1;
		}
		ext2fs_mark_block_bitmap2(touched_map, test_vec[i]);
	}
	for (i = 0; i < test_fs->group_desc_count; i++) {
		for (j=0, blk = ext2fs_inode_table_loc(test_fs, i);
		     j < test_fs->inode_blocks_per_group;
		     j++, blk++) {
			if (!ext2fs_test_block_bitmap2(touched_map, blk) &&
			    !ext2fs_test_block_bitmap2(bad_block_map, blk)) {
				printf("Missing block --- %llu\n", blk);
				failed++;
			}
		}
	}
	printf("Bad inodes: ");
	for (i=1; i <= test_fs->super->s_inodes_count; i++) {
		if (ext2fs_test_inode_bitmap2(bad_inode_map, i)) {
			if (first)
				first = 0;
			else
				printf(", ");
			printf("%u", i);
		}
	}
	printf("\n");
}
Exemple #7
0
static errcode_t mark_uninit_bg_group_blocks(ext2_filsys fs)
{
	dgrp_t			i;
	blk64_t			blk;
	ext2fs_block_bitmap	bmap = fs->block_map;

	for (i = 0; i < fs->group_desc_count; i++) {
		if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT))
			continue;

		ext2fs_reserve_super_and_bgd(fs, i, bmap);

		/*
		 * Mark the blocks used for the inode table
		 */
		blk = ext2fs_inode_table_loc(fs, i);
		if (blk)
			ext2fs_mark_block_bitmap_range2(bmap, blk,
						fs->inode_blocks_per_group);

		/*
		 * Mark block used for the block bitmap
		 */
		blk = ext2fs_block_bitmap_loc(fs, i);
		if (blk)
			ext2fs_mark_block_bitmap2(bmap, blk);

		/*
		 * Mark block used for the inode bitmap
		 */
		blk = ext2fs_inode_bitmap_loc(fs, i);
		if (blk)
			ext2fs_mark_block_bitmap2(bmap, blk);
	}
	return 0;
}
Exemple #8
0
static void list_desc (ext2_filsys fs)
{
	unsigned long i;
	blk64_t	first_block, last_block;
	blk64_t	super_blk, old_desc_blk, new_desc_blk;
	char *block_bitmap=NULL, *inode_bitmap=NULL;
	const char *units = _("blocks");
	int inode_blocks_per_group, old_desc_blocks, reserved_gdt;
	int		block_nbytes, inode_nbytes;
	int has_super;
	blk64_t		blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
	ext2_ino_t	ino_itr = 1;
	errcode_t	retval;

	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				       EXT4_FEATURE_RO_COMPAT_BIGALLOC))
		units = _("clusters");

	block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
	inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8;

	if (fs->block_map)
		block_bitmap = malloc(block_nbytes);
	if (fs->inode_map)
		inode_bitmap = malloc(inode_nbytes);

	inode_blocks_per_group = ((fs->super->s_inodes_per_group *
				   EXT2_INODE_SIZE(fs->super)) +
				  EXT2_BLOCK_SIZE(fs->super) - 1) /
				 EXT2_BLOCK_SIZE(fs->super);
	reserved_gdt = fs->super->s_reserved_gdt_blocks;
	fputc('\n', stdout);
	first_block = fs->super->s_first_data_block;
	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;
	for (i = 0; i < fs->group_desc_count; i++) {
		first_block = ext2fs_group_first_block2(fs, i);
		last_block = ext2fs_group_last_block2(fs, i);

		ext2fs_super_and_bgd_loc2(fs, i, &super_blk,
					  &old_desc_blk, &new_desc_blk, 0);

		printf (_("Group %lu: (Blocks "), i);
		print_range(first_block, last_block);
		fputs(")", stdout);
		print_bg_opts(fs, i);
		if (ext2fs_has_group_desc_csum(fs)) {
			unsigned csum = ext2fs_bg_checksum(fs, i);
			unsigned exp_csum = ext2fs_group_desc_csum(fs, i);

			printf(_("  Checksum 0x%04x"), csum);
			if (csum != exp_csum)
				printf(_(" (EXPECTED 0x%04x)"), exp_csum);
			printf(_(", unused inodes %u\n"),
			       ext2fs_bg_itable_unused(fs, i));
		}
		has_super = ((i==0) || super_blk);
		if (has_super) {
			printf (_("  %s superblock at "),
				i == 0 ? _("Primary") : _("Backup"));
			print_number(super_blk);
		}
		if (old_desc_blk) {
			printf("%s", _(", Group descriptors at "));
			print_range(old_desc_blk,
				    old_desc_blk + old_desc_blocks - 1);
			if (reserved_gdt) {
				printf("%s", _("\n  Reserved GDT blocks at "));
				print_range(old_desc_blk + old_desc_blocks,
					    old_desc_blk + old_desc_blocks +
					    reserved_gdt - 1);
			}
		} else if (new_desc_blk) {
			fputc(has_super ? ',' : ' ', stdout);
			printf("%s", _(" Group descriptor at "));
			print_number(new_desc_blk);
			has_super++;
		}
		if (has_super)
			fputc('\n', stdout);
		fputs(_("  Block bitmap at "), stdout);
		print_number(ext2fs_block_bitmap_loc(fs, i));
		print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0,
				    first_block, last_block);
		if (fs->super->s_feature_ro_compat &
		    EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
			printf(_(", csum 0x%08x"),
			       ext2fs_block_bitmap_checksum(fs, i));
		fputs(_(", Inode bitmap at "), stdout);
		print_number(ext2fs_inode_bitmap_loc(fs, i));
		print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,
				    first_block, last_block);
		if (fs->super->s_feature_ro_compat &
		    EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
			printf(_(", csum 0x%08x"),
			       ext2fs_inode_bitmap_checksum(fs, i));
		fputs(_("\n  Inode table at "), stdout);
		print_range(ext2fs_inode_table_loc(fs, i),
			    ext2fs_inode_table_loc(fs, i) +
			    inode_blocks_per_group - 1);
		print_bg_rel_offset(fs, ext2fs_inode_table_loc(fs, i), 1,
				    first_block, last_block);
		printf (_("\n  %u free %s, %u free inodes, "
			  "%u directories%s"),
			ext2fs_bg_free_blocks_count(fs, i), units,
			ext2fs_bg_free_inodes_count(fs, i),
			ext2fs_bg_used_dirs_count(fs, i),
			ext2fs_bg_itable_unused(fs, i) ? "" : "\n");
		if (ext2fs_bg_itable_unused(fs, i))
			printf (_(", %u unused inodes\n"),
				ext2fs_bg_itable_unused(fs, i));
		if (block_bitmap) {
			fputs(_("  Free blocks: "), stdout);
			retval = ext2fs_get_block_bitmap_range2(fs->block_map,
				 blk_itr, block_nbytes << 3, block_bitmap);
			if (retval)
				com_err("list_desc", retval,
					"while reading block bitmap");
			else
				print_free(i, block_bitmap,
					   fs->super->s_clusters_per_group,
					   fs->super->s_first_data_block,
					   EXT2FS_CLUSTER_RATIO(fs));
			fputc('\n', stdout);
			blk_itr += fs->super->s_clusters_per_group;
		}
		if (inode_bitmap) {
			fputs(_("  Free inodes: "), stdout);
			retval = ext2fs_get_inode_bitmap_range2(fs->inode_map,
				 ino_itr, inode_nbytes << 3, inode_bitmap);
			if (retval)
				com_err("list_desc", retval,
					"while reading inode bitmap");
			else
				print_free(i, inode_bitmap,
					   fs->super->s_inodes_per_group,
					   1, 1);
			fputc('\n', stdout);
			ino_itr += fs->super->s_inodes_per_group;
		}
	}
	if (block_bitmap)
		free(block_bitmap);
	if (inode_bitmap)
		free(inode_bitmap);
}
Exemple #9
0
errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
				  struct ext2_inode * inode, int bufsize)
{
	blk64_t block_nr;
	unsigned long group, block, offset;
	errcode_t retval = 0;
	struct ext2_inode_large *w_inode;
	char *ptr;
	unsigned i;
	int clen;
	int length = EXT2_INODE_SIZE(fs->super);

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user provided an override function */
	if (fs->write_inode) {
		retval = (fs->write_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}

	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;

	/* Prepare our shadow buffer for read/modify/byteswap/write */
	retval = ext2fs_get_mem(length, &w_inode);
	if (retval)
		return retval;

	if (bufsize < length) {
		int old_flags = fs->flags;
		fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
		retval = ext2fs_read_inode_full(fs, ino,
						(struct ext2_inode *)w_inode,
						length);
		fs->flags = (old_flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
			    (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
		if (retval)
			goto errout;
	}

	/* Check to see if the inode cache needs to be updated */
	if (fs->icache) {
		for (i=0; i < fs->icache->cache_size; i++) {
			if (fs->icache->cache[i].ino == ino) {
				memcpy(fs->icache->cache[i].inode, inode,
				       (bufsize > length) ? length : bufsize);
				break;
			}
		}
	} else {
		retval = ext2fs_create_inode_cache(fs, 4);
		if (retval)
			goto errout;
	}
	memcpy(w_inode, inode, (bufsize > length) ? length : bufsize);

	if (!(fs->flags & EXT2_FLAG_RW)) {
		retval = EXT2_ET_RO_FILSYS;
		goto errout;
	}

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif

	retval = ext2fs_inode_csum_set(fs, ino, w_inode);
	if (retval)
		goto errout;

	group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
		EXT2_INODE_SIZE(fs->super);
	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
	if (!ext2fs_inode_table_loc(fs, (unsigned) group)) {
		retval = EXT2_ET_MISSING_INODE_TABLE;
		goto errout;
	}
	block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block;

	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	ptr = (char *) w_inode;

	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (fs->icache->buffer_blk != block_nr) {
			retval = io_channel_read_blk64(fs->io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				goto errout;
			fs->icache->buffer_blk = block_nr;
		}


		memcpy((char *) fs->icache->buffer + (unsigned) offset,
		       ptr, clen);

		retval = io_channel_write_blk64(fs->io, block_nr, 1,
					      fs->icache->buffer);
		if (retval)
			goto errout;

		offset = 0;
		ptr += clen;
		length -= clen;
		block_nr++;
	}

	fs->flags |= EXT2_FLAG_CHANGED;
errout:
	ext2fs_free_mem(&w_inode);
	return retval;
}
Exemple #10
0
/*
 * Functions to read and write a single inode.
 */
errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
				 struct ext2_inode * inode, int bufsize)
{
	blk64_t		block_nr;
	unsigned long 	group, block, offset;
	char 		*ptr;
	errcode_t	retval;
	unsigned	i;
	int		clen, inodes_per_block;
	io_channel	io;
	int		length = EXT2_INODE_SIZE(fs->super);
	struct ext2_inode_large	*iptr;
	int		cache_slot, fail_csum;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user has an override function */
	if (fs->read_inode &&
	    ((bufsize == sizeof(struct ext2_inode)) ||
	     (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_inode)))) {
		retval = (fs->read_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}
	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;
	/* Create inode cache if not present */
	if (!fs->icache) {
		retval = ext2fs_create_inode_cache(fs, 4);
		if (retval)
			return retval;
	}
	/* Check to see if it's in the inode cache */
	for (i = 0; i < fs->icache->cache_size; i++) {
		if (fs->icache->cache[i].ino == ino) {
			memcpy(inode, fs->icache->cache[i].inode,
			       (bufsize > length) ? length : bufsize);
			return 0;
		}
	}
	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
		inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
		block_nr = fs->image_header->offset_inode / fs->blocksize;
		block_nr += (ino - 1) / inodes_per_block;
		offset = ((ino - 1) % inodes_per_block) *
			EXT2_INODE_SIZE(fs->super);
		io = fs->image_io;
	} else {
		group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
		if (group > fs->group_desc_count)
			return EXT2_ET_BAD_INODE_NUM;
		offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
			EXT2_INODE_SIZE(fs->super);
		block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
		if (!ext2fs_inode_table_loc(fs, (unsigned) group))
			return EXT2_ET_MISSING_INODE_TABLE;
		block_nr = ext2fs_inode_table_loc(fs, group) +
			block;
		io = fs->io;
	}
	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
	iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode;

	ptr = (char *) iptr;
	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (block_nr != fs->icache->buffer_blk) {
			retval = io_channel_read_blk64(io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				return retval;
			fs->icache->buffer_blk = block_nr;
		}

		memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
		       clen);

		offset = 0;
		length -= clen;
		ptr += clen;
		block_nr++;
	}
	length = EXT2_INODE_SIZE(fs->super);

	/* Verify the inode checksum. */
	fail_csum = !ext2fs_inode_csum_verify(fs, ino, iptr);

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
			       (struct ext2_inode_large *) iptr,
			       0, length);
#endif

	/* Update the inode cache bookkeeping */
	if (!fail_csum) {
		fs->icache->cache_last = cache_slot;
		fs->icache->cache[cache_slot].ino = ino;
	}
	memcpy(inode, iptr, (bufsize > length) ? length : bufsize);

	if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && fail_csum)
		return EXT2_ET_INODE_CSUM_INVALID;

	return 0;
}
/*
 * This routine sanity checks the group descriptors
 */
errcode_t ext2fs_check_desc(ext2_filsys fs)
{
	ext2fs_block_bitmap bmap;
	errcode_t retval;
	dgrp_t i;
	blk64_t first_block = fs->super->s_first_data_block;
	blk64_t last_block = ext2fs_blocks_count(fs->super)-1;
	blk64_t blk, b;
	unsigned int j;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	if (EXT2_DESC_SIZE(fs->super) & (EXT2_DESC_SIZE(fs->super) - 1))
		return EXT2_ET_BAD_DESC_SIZE;

	retval = ext2fs_allocate_subcluster_bitmap(fs, "check_desc map", &bmap);
	if (retval)
		return retval;

	for (i = 0; i < fs->group_desc_count; i++)
		ext2fs_reserve_super_and_bgd(fs, i, bmap);

	for (i = 0; i < fs->group_desc_count; i++) {
		if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
					       EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
			first_block = ext2fs_group_first_block2(fs, i);
			last_block = ext2fs_group_last_block2(fs, i);
		}

		/*
		 * Check to make sure the block bitmap for group is sane
		 */
		blk = ext2fs_block_bitmap_loc(fs, i);
		if (blk < first_block || blk > last_block ||
		    ext2fs_test_block_bitmap2(bmap, blk)) {
			retval = EXT2_ET_GDESC_BAD_BLOCK_MAP;
			goto errout;
		}
		ext2fs_mark_block_bitmap2(bmap, blk);

		/*
		 * Check to make sure the inode bitmap for group is sane
		 */
		blk = ext2fs_inode_bitmap_loc(fs, i);
		if (blk < first_block || blk > last_block ||
		    ext2fs_test_block_bitmap2(bmap, blk)) {
			retval = EXT2_ET_GDESC_BAD_INODE_MAP;
			goto errout;
		}
		ext2fs_mark_block_bitmap2(bmap, blk);

		/*
		 * Check to make sure the inode table for group is sane
		 */
		blk = ext2fs_inode_table_loc(fs, i);
		if (blk < first_block ||
		    ((blk + fs->inode_blocks_per_group - 1) > last_block)) {
			retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
			goto errout;
		}
		for (j = 0, b = blk; j < fs->inode_blocks_per_group;
		     j++, b++) {
			if (ext2fs_test_block_bitmap2(bmap, b)) {
				retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
				goto errout;
			}
			ext2fs_mark_block_bitmap2(bmap, b);
		}
	}
errout:
	ext2fs_free_block_bitmap(bmap);
	return retval;
}
Exemple #12
0
errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
				 ext2_inode_scan *ret_scan)
{
	ext2_inode_scan	scan;
	errcode_t	retval;
	errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/*
	 * If fs->badblocks isn't set, then set it --- since the inode
	 * scanning functions require it.
	 */
	if (fs->badblocks == 0) {
		/*
		 * Temporarly save fs->get_blocks and set it to zero,
		 * for compatibility with old e2fsck's.
		 */
		save_get_blocks = fs->get_blocks;
		fs->get_blocks = 0;
		retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
		if (retval && fs->badblocks) {
			ext2fs_badblocks_list_free(fs->badblocks);
			fs->badblocks = 0;
		}
		fs->get_blocks = save_get_blocks;
	}

	retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
	if (retval)
		return retval;
	memset(scan, 0, sizeof(struct ext2_struct_inode_scan));

	scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
	scan->fs = fs;
	scan->inode_size = EXT2_INODE_SIZE(fs->super);
	scan->bytes_left = 0;
	scan->current_group = 0;
	scan->groups_left = fs->group_desc_count - 1;
	scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks :
				    EXT2_INODE_SCAN_DEFAULT_BUFFER_BLOCKS;
	scan->current_block = ext2fs_inode_table_loc(scan->fs,
						     scan->current_group);
	scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
	scan->blocks_left = scan->fs->inode_blocks_per_group;
	if (ext2fs_has_group_desc_csum(fs)) {
		__u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group);
		if (scan->inodes_left > unused)
			scan->inodes_left -= unused;
		else
			scan->inodes_left = 0;
		scan->blocks_left =
			(scan->inodes_left +
			 (fs->blocksize / scan->inode_size - 1)) *
			scan->inode_size / fs->blocksize;
	}
	retval = io_channel_alloc_buf(fs->io, scan->inode_buffer_blocks,
				      &scan->inode_buffer);
	scan->done_group = 0;
	scan->done_group_data = 0;
	scan->bad_block_ptr = 0;
	if (retval) {
		ext2fs_free_mem(&scan);
		return retval;
	}
	retval = ext2fs_get_mem(scan->inode_size + scan->inode_buffer_blocks,
				&scan->temp_buffer);
	if (retval) {
		ext2fs_free_mem(&scan->inode_buffer);
		ext2fs_free_mem(&scan);
		return retval;
	}
	memset(SCAN_BLOCK_STATUS(scan), 0, scan->inode_buffer_blocks);
	if (scan->fs->badblocks && scan->fs->badblocks->num)
		scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
	if (ext2fs_has_group_desc_csum(fs))
		scan->scan_flags |= EXT2_SF_DO_LAZY;
	*ret_scan = scan;
	return 0;
}
errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
				  struct ext2_inode * inode, int bufsize)
{
	blk64_t block_nr;
	unsigned long group, block, offset;
	errcode_t retval = 0;
	struct ext2_inode_large temp_inode, *w_inode;
	char *ptr;
	int clen, i, length;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user provided an override function */
	if (fs->write_inode) {
		retval = (fs->write_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}

	/* Check to see if the inode cache needs to be updated */
	if (fs->icache) {
		for (i=0; i < fs->icache->cache_size; i++) {
			if (fs->icache->cache[i].ino == ino) {
				fs->icache->cache[i].inode = *inode;
				break;
			}
		}
	} else {
		retval = create_icache(fs);
		if (retval)
			return retval;
	}

	if (!(fs->flags & EXT2_FLAG_RW))
		return EXT2_ET_RO_FILSYS;

	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;

	length = bufsize;
	if (length < EXT2_INODE_SIZE(fs->super))
		length = EXT2_INODE_SIZE(fs->super);

	if (length > (int) sizeof(struct ext2_inode_large)) {
		w_inode = malloc(length);
		if (!w_inode) {
			retval = ENOMEM;
			goto errout;
		}
	} else
		w_inode = &temp_inode;
	memset(w_inode, 0, length);

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, w_inode,
			       (struct ext2_inode_large *) inode,
			       1, bufsize);
#else
	memcpy(w_inode, inode, bufsize);
#endif

	group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
		EXT2_INODE_SIZE(fs->super);
	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
	if (!ext2fs_inode_table_loc(fs, (unsigned) group)) {
		retval = EXT2_ET_MISSING_INODE_TABLE;
		goto errout;
	}
	block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block;

	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	length = EXT2_INODE_SIZE(fs->super);
	if (length > bufsize)
		length = bufsize;

	ptr = (char *) w_inode;

	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (fs->icache->buffer_blk != block_nr) {
			retval = io_channel_read_blk64(fs->io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				goto errout;
			fs->icache->buffer_blk = block_nr;
		}


		memcpy((char *) fs->icache->buffer + (unsigned) offset,
		       ptr, clen);

		retval = io_channel_write_blk64(fs->io, block_nr, 1,
					      fs->icache->buffer);
		if (retval)
			goto errout;

		offset = 0;
		ptr += clen;
		length -= clen;
		block_nr++;
	}

	fs->flags |= EXT2_FLAG_CHANGED;
errout:
	if (w_inode && w_inode != &temp_inode)
		free(w_inode);
	return retval;
}
Exemple #14
0
void do_logdump(int argc, char **argv)
{
	int		c;
	int		retval;
	char		*out_fn;
	FILE		*out_file;

	char		*inode_spec = NULL;
	char		*journal_fn = NULL;
	int		journal_fd = 0;
	int		use_sb = 0;
	ext2_ino_t	journal_inum;
	struct ext2_inode journal_inode;
	ext2_file_t 	journal_file;
	char		*tmp;
	struct journal_source journal_source;
	struct ext2_super_block *es = NULL;

	journal_source.where = JOURNAL_IS_INTERNAL;
	journal_source.fd = 0;
	journal_source.file = 0;
	dump_all = 0;
	dump_old = 0;
	dump_contents = 0;
	dump_descriptors = 1;
	block_to_dump = ANY_BLOCK;
	bitmap_to_dump = -1;
	inode_block_to_dump = ANY_BLOCK;
	inode_to_dump = -1;

	reset_getopt();
	while ((c = getopt (argc, argv, "ab:ci:f:Os")) != EOF) {
		switch (c) {
		case 'a':
			dump_all++;
			break;
		case 'b':
			block_to_dump = strtoul(optarg, &tmp, 0);
			if (*tmp) {
				com_err(argv[0], 0,
					"Bad block number - %s", optarg);
				return;
			}
			dump_descriptors = 0;
			break;
		case 'c':
			dump_contents++;
			break;
		case 'f':
			journal_fn = optarg;
			break;
		case 'i':
			inode_spec = optarg;
			dump_descriptors = 0;
			break;
		case 'O':
			dump_old++;
			break;
		case 's':
			use_sb++;
			break;
		default:
			goto print_usage;
		}
	}
	if (optind != argc && optind != argc-1) {
		goto print_usage;
	}

	if (current_fs)
		es = current_fs->super;

	if (inode_spec) {
		int inode_group, group_offset, inodes_per_block;

		if (check_fs_open(argv[0]))
			return;

		inode_to_dump = string_to_inode(inode_spec);
		if (!inode_to_dump)
			return;

		inode_group = ((inode_to_dump - 1)
			       / es->s_inodes_per_group);
		group_offset = ((inode_to_dump - 1)
				% es->s_inodes_per_group);
		inodes_per_block = (current_fs->blocksize
				    / sizeof(struct ext2_inode));

		inode_block_to_dump =
			ext2fs_inode_table_loc(current_fs, inode_group) +
			(group_offset / inodes_per_block);
		inode_offset_to_dump = ((group_offset % inodes_per_block)
					* sizeof(struct ext2_inode));
		printf("Inode %u is at group %u, block %llu, offset %u\n",
		       inode_to_dump, inode_group,
		       inode_block_to_dump, inode_offset_to_dump);
	}

	if (optind == argc) {
		out_file = stdout;
	} else {
		out_fn = argv[optind];
		out_file = fopen(out_fn, "w");
		if (!out_file) {
			com_err(argv[0], errno, "while opening %s for logdump",
				out_fn);
			goto errout;
		}
	}

	if (block_to_dump != ANY_BLOCK && current_fs != NULL) {
		group_to_dump = ((block_to_dump -
				  es->s_first_data_block)
				 / es->s_blocks_per_group);
		bitmap_to_dump = ext2fs_block_bitmap_loc(current_fs, group_to_dump);
	}

	if (!journal_fn && check_fs_open(argv[0]))
		goto errout;

	if (journal_fn) {
		/* Set up to read journal from a regular file somewhere */
		journal_fd = open(journal_fn, O_RDONLY, 0);
		if (journal_fd < 0) {
			com_err(argv[0], errno, "while opening %s for logdump",
				journal_fn);
			goto errout;
		}

		journal_source.where = JOURNAL_IS_EXTERNAL;
		journal_source.fd = journal_fd;
	} else if ((journal_inum = es->s_journal_inum)) {
		if (use_sb) {
			if (es->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS) {
				com_err(argv[0], 0,
					"no journal backup in super block\n");
				goto errout;
			}
			memset(&journal_inode, 0, sizeof(struct ext2_inode));
			memcpy(&journal_inode.i_block[0], es->s_jnl_blocks,
			       EXT2_N_BLOCKS*4);
			journal_inode.i_size_high = es->s_jnl_blocks[15];
			journal_inode.i_size = es->s_jnl_blocks[16];
			journal_inode.i_links_count = 1;
			journal_inode.i_mode = LINUX_S_IFREG | 0600;
		} else {
			if (debugfs_read_inode(journal_inum, &journal_inode,
					       argv[0]))
				goto errout;
		}

		retval = ext2fs_file_open2(current_fs, journal_inum,
					   &journal_inode, 0, &journal_file);
		if (retval) {
			com_err(argv[0], retval, "while opening ext2 file");
			goto errout;
		}
		journal_source.where = JOURNAL_IS_INTERNAL;
		journal_source.file = journal_file;
	} else {
		char uuid[37];

		uuid_unparse(es->s_journal_uuid, uuid);
		journal_fn = blkid_get_devname(NULL, "UUID", uuid);
		if (!journal_fn)
				journal_fn = blkid_devno_to_devname(es->s_journal_dev);
		if (!journal_fn) {
			com_err(argv[0], 0, "filesystem has no journal");
			goto errout;
		}
		journal_fd = open(journal_fn, O_RDONLY, 0);
		if (journal_fd < 0) {
			com_err(argv[0], errno, "while opening %s for logdump",
				journal_fn);
			free(journal_fn);
			goto errout;
		}
		fprintf(out_file, "Using external journal found at %s\n",
			journal_fn);
		free(journal_fn);
		journal_source.where = JOURNAL_IS_EXTERNAL;
		journal_source.fd = journal_fd;
	}

	dump_journal(argv[0], out_file, &journal_source);

	if (journal_source.where == JOURNAL_IS_INTERNAL)
		ext2fs_file_close(journal_file);
	else
		close(journal_fd);

errout:
	if (out_file && (out_file != stdout))
		fclose(out_file);

	return;

print_usage:
	fprintf(stderr, "%s: Usage: logdump [-acsO] [-b<block>] [-i<filespec>]\n\t"
		"[-f<journal_file>] [output_file]\n", argv[0]);
}
/*
 * Functions to read and write a single inode.
 */
errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
				 struct ext2_inode * inode, int bufsize)
{
	blk64_t		block_nr;
	unsigned long 	group, block, offset;
	char 		*ptr;
	errcode_t	retval;
	int 		clen, i, inodes_per_block, length;
	io_channel	io;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user has an override function */
	if (fs->read_inode &&
	    ((bufsize == sizeof(struct ext2_inode)) ||
	     (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_inode)))) {
		retval = (fs->read_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}
	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;
	/* Create inode cache if not present */
	if (!fs->icache) {
		retval = create_icache(fs);
		if (retval)
			return retval;
	}
	/* Check to see if it's in the inode cache */
	if (bufsize == sizeof(struct ext2_inode)) {
		/* only old good inode can be retrieved from the cache */
		for (i=0; i < fs->icache->cache_size; i++) {
			if (fs->icache->cache[i].ino == ino) {
				*inode = fs->icache->cache[i].inode;
				return 0;
			}
		}
	}
	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
		inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
		block_nr = fs->image_header->offset_inode / fs->blocksize;
		block_nr += (ino - 1) / inodes_per_block;
		offset = ((ino - 1) % inodes_per_block) *
			EXT2_INODE_SIZE(fs->super);
		io = fs->image_io;
	} else {
		group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
		if (group > fs->group_desc_count)
			return EXT2_ET_BAD_INODE_NUM;
		offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
			EXT2_INODE_SIZE(fs->super);
		block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
		if (!ext2fs_inode_table_loc(fs, (unsigned) group))
			return EXT2_ET_MISSING_INODE_TABLE;
		block_nr = ext2fs_inode_table_loc(fs, group) +
			block;
		io = fs->io;
	}
	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	length = EXT2_INODE_SIZE(fs->super);
	if (bufsize < length)
		length = bufsize;

	ptr = (char *) inode;
	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (block_nr != fs->icache->buffer_blk) {
			retval = io_channel_read_blk64(io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				return retval;
			fs->icache->buffer_blk = block_nr;
		}

		memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
		       clen);

		offset = 0;
		length -= clen;
		ptr += clen;
		block_nr++;
	}

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
			       (struct ext2_inode_large *) inode,
			       0, bufsize);
#endif

	/* Update the inode cache */
	fs->icache->cache_last = (fs->icache->cache_last + 1) %
		fs->icache->cache_size;
	fs->icache->cache[fs->icache->cache_last].ino = ino;
	fs->icache->cache[fs->icache->cache_last].inode = *inode;

	return 0;
}
Exemple #16
0
errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt,
			   blk64_t *new_size, int flags EXT2FS_ATTR((unused)))
{
#ifdef __linux__
	struct ext2_new_group_input input;
	struct ext4_new_group_input input64;
	struct ext2_super_block *sb = fs->super;
	unsigned long		new_desc_blocks;
	ext2_filsys 		new_fs;
	errcode_t 		retval;
	double			percent;
	dgrp_t			i;
	blk64_t			size;
	int			fd, overhead;
	int			use_old_ioctl = 1;

	printf(_("Filesystem at %s is mounted on %s; "
		 "on-line resizing required\n"), fs->device_name, mtpt);

	if (*new_size < ext2fs_blocks_count(sb)) {
		com_err(program_name, 0, _("On-line shrinking not supported"));
		exit(1);
	}

	/*
	 * If the number of descriptor blocks is going to increase,
	 * the on-line resizing inode must be present.
	 */
	new_desc_blocks = ext2fs_div_ceil(
		ext2fs_div64_ceil(*new_size -
				  fs->super->s_first_data_block,
				  EXT2_BLOCKS_PER_GROUP(fs->super)),
		EXT2_DESC_PER_BLOCK(fs->super));
	printf("old desc_blocks = %lu, new_desc_blocks = %lu\n",
	       fs->desc_blocks, new_desc_blocks);
	if (!(fs->super->s_feature_compat &
	      EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
	    new_desc_blocks != fs->desc_blocks) {
		com_err(program_name, 0,
			_("Filesystem does not support online resizing"));
		exit(1);
	}

	fd = open(mtpt, O_RDONLY);
	if (fd < 0) {
		com_err(program_name, errno,
			_("while trying to open mountpoint %s"), mtpt);
		exit(1);
	}

	size=ext2fs_blocks_count(sb);
	if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) {
		if (errno == EPERM)
			com_err(program_name, 0,
				_("Permission denied to resize filesystem"));
		else if (errno == ENOTTY)
			com_err(program_name, 0,
			_("Kernel does not support online resizing"));
		else
			com_err(program_name, errno,
			_("While checking for on-line resizing support"));
		exit(1);
	}

	percent = (ext2fs_r_blocks_count(sb) * 100.0) /
		ext2fs_blocks_count(sb);

	retval = ext2fs_read_bitmaps(fs);
	if (retval)
		return retval;

	retval = ext2fs_dup_handle(fs, &new_fs);
	if (retval)
		return retval;

	/* The current method of adding one block group at a time to a
	 * mounted filesystem means it is impossible to accomodate the
	 * flex_bg allocation method of placing the metadata together
	 * in a single block group.  For now we "fix" this issue by
	 * using the traditional layout for new block groups, where
	 * each block group is self-contained and contains its own
	 * bitmap blocks and inode tables.  This means we don't get
	 * the layout advantages of flex_bg in the new block groups,
	 * but at least it allows on-line resizing to function.
	 */
	new_fs->super->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_FLEX_BG;
	retval = adjust_fs_info(new_fs, fs, 0, *new_size);
	if (retval)
		return retval;

	printf(_("Performing an on-line resize of %s to %llu (%dk) blocks.\n"),
	       fs->device_name, *new_size, fs->blocksize / 1024);

	size = fs->group_desc_count * sb->s_blocks_per_group +
		sb->s_first_data_block;
	if (size > *new_size)
		size = *new_size;

	if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) {
		com_err(program_name, errno,
			_("While trying to extend the last group"));
		exit(1);
	}

	for (i = fs->group_desc_count;
	     i < new_fs->group_desc_count; i++) {

		overhead = (int) (2 + new_fs->inode_blocks_per_group);

		if (ext2fs_bg_has_super(new_fs, new_fs->group_desc_count - 1))
			overhead += 1 + new_fs->desc_blocks +
				new_fs->super->s_reserved_gdt_blocks;

		input.group = i;
		input.block_bitmap = ext2fs_block_bitmap_loc(new_fs, i);
		input.inode_bitmap = ext2fs_inode_bitmap_loc(new_fs, i);
		input.inode_table = ext2fs_inode_table_loc(new_fs, i);
		input.blocks_count = sb->s_blocks_per_group;
		if (i == new_fs->group_desc_count-1) {
			input.blocks_count = ext2fs_blocks_count(new_fs->super) -
				sb->s_first_data_block -
				(i * sb->s_blocks_per_group);
		}
		input.reserved_blocks = (blk_t) (percent * input.blocks_count
						 / 100.0);

#if 0
		printf("new block bitmap is at 0x%04x\n", input.block_bitmap);
		printf("new inode bitmap is at 0x%04x\n", input.inode_bitmap);
		printf("new inode table is at 0x%04x-0x%04x\n",
		       input.inode_table,
		       input.inode_table + new_fs->inode_blocks_per_group-1);
		printf("new group has %u blocks\n", input.blocks_count);
		printf("new group will reserve %d blocks\n",
		       input.reserved_blocks);
		printf("new group has %d free blocks\n",
		       ext2fs_bg_free_blocks_count(new_fs, i),
		printf("new group has %d free inodes (%d blocks)\n",
		       ext2fs_bg_free_inodes_count(new_fs, i),
		       new_fs->inode_blocks_per_group);
		printf("Adding group #%d\n", input.group);
#endif

		if (use_old_ioctl &&
		    ioctl(fd, EXT2_IOC_GROUP_ADD, &input) == 0)
			continue;
		else
			use_old_ioctl = 0;

		input64.group = input.group;
		input64.block_bitmap = input.block_bitmap;
		input64.inode_bitmap = input.inode_bitmap;
		input64.inode_table = input.inode_table;
		input64.blocks_count = input.blocks_count;
		input64.reserved_blocks = input.reserved_blocks;
		input64.unused = input.unused;

		if (ioctl(fd, EXT4_IOC_GROUP_ADD, &input64) < 0) {
			com_err(program_name, errno,
				_("While trying to add group #%d"),
				input.group);
			exit(1);
		}
	}

	ext2fs_free(new_fs);
	close(fd);

	return 0;
#else
	printf(_("Filesystem at %s is mounted on %s, and on-line resizing is "
		 "not supported on this system.\n"), fs->device_name, mtpt);
	exit(1);
#endif
}
Exemple #17
0
errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt,
			   blk64_t *new_size, int flags EXT2FS_ATTR((unused)))
{
#ifdef __linux__
	struct ext2_new_group_input input;
	struct ext4_new_group_input input64;
	struct ext2_super_block *sb = fs->super;
	unsigned long		new_desc_blocks;
	ext2_filsys 		new_fs;
	errcode_t 		retval;
	double			percent;
	dgrp_t			i;
	blk_t			size;
	int			fd, overhead;
	int			use_old_ioctl = 1;
	int			no_meta_bg_resize = 0;
	int			no_resize_ioctl = 0;

	if (getenv("RESIZE2FS_KERNEL_VERSION")) {
		char *version_to_emulate = getenv("RESIZE2FS_KERNEL_VERSION");
		int kvers = parse_version_number(version_to_emulate);

		if (kvers < VERSION_CODE(3, 7, 0))
			no_meta_bg_resize = 1;
		if (kvers < VERSION_CODE(3, 3, 0))
			no_resize_ioctl = 1;
	}

	if (ext2fs_has_feature_sparse_super2(fs->super) &&
	    (access("/sys/fs/ext4/features/sparse_super2", R_OK) != 0)) {
		com_err(program_name, 0, _("kernel does not support online "
					   "resize with sparse_super2"));
		exit(1);
	}

	printf(_("Filesystem at %s is mounted on %s; "
		 "on-line resizing required\n"), fs->device_name, mtpt);

	if (*new_size < ext2fs_blocks_count(sb)) {
		com_err(program_name, 0, _("On-line shrinking not supported"));
		exit(1);
	}

	/*
	 * If the number of descriptor blocks is going to increase,
	 * the on-line resizing inode must be present.
	 */
	new_desc_blocks = ext2fs_div_ceil(
		ext2fs_div64_ceil(*new_size -
				  fs->super->s_first_data_block,
				  EXT2_BLOCKS_PER_GROUP(fs->super)),
		EXT2_DESC_PER_BLOCK(fs->super));
	printf("old_desc_blocks = %lu, new_desc_blocks = %lu\n",
	       fs->desc_blocks, new_desc_blocks);

	/*
	 * Do error checking to make sure the resize will be successful.
	 */
	if ((access("/sys/fs/ext4/features/meta_bg_resize", R_OK) != 0) ||
	    no_meta_bg_resize) {
		if (!ext2fs_has_feature_resize_inode(fs->super) &&
		    (new_desc_blocks != fs->desc_blocks)) {
			com_err(program_name, 0,
				_("Filesystem does not support online resizing"));
			exit(1);
		}

		if (ext2fs_has_feature_resize_inode(fs->super) &&
		    new_desc_blocks > (fs->desc_blocks +
				       fs->super->s_reserved_gdt_blocks)) {
			com_err(program_name, 0,
				_("Not enough reserved gdt blocks for resizing"));
			exit(1);
		}

		if ((ext2fs_blocks_count(sb) > MAX_32_NUM) ||
		    (*new_size > MAX_32_NUM)) {
			com_err(program_name, 0,
				_("Kernel does not support resizing a file system this large"));
			exit(1);
		}
	}

	fd = open(mtpt, O_RDONLY);
	if (fd < 0) {
		com_err(program_name, errno,
			_("while trying to open mountpoint %s"), mtpt);
		exit(1);
	}

	if (no_resize_ioctl) {
		printf(_("Old resize interface requested.\n"));
	} else if (ioctl(fd, EXT4_IOC_RESIZE_FS, new_size)) {
		/*
		 * If kernel does not support EXT4_IOC_RESIZE_FS, use the
		 * old online resize. Note that the old approach does not
		 * handle >32 bit file systems
		 *
		 * Sigh, if we are running a 32-bit binary on a 64-bit
		 * kernel (which happens all the time on the MIPS
		 * architecture in Debian, but can happen on other CPU
		 * architectures as well) we will get EINVAL returned
		 * when an ioctl doesn't exist, at least up to Linux
		 * 3.1.  See compat_sys_ioctl() in fs/compat_ioctl.c
		 * in the kernel sources.  This is probably a kernel
		 * bug, but work around it here.
		 */
		if ((errno != ENOTTY) && (errno != EINVAL)) {
			if (errno == EPERM)
				com_err(program_name, 0,
				_("Permission denied to resize filesystem"));
			else
				com_err(program_name, errno,
				_("While checking for on-line resizing "
				  "support"));
			exit(1);
		}
	} else {
		close(fd);
		return 0;
	}

	size = ext2fs_blocks_count(sb);

	if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) {
		if (errno == EPERM)
			com_err(program_name, 0,
				_("Permission denied to resize filesystem"));
		else if (errno == ENOTTY)
			com_err(program_name, 0,
			_("Kernel does not support online resizing"));
		else
			com_err(program_name, errno,
			_("While checking for on-line resizing support"));
		exit(1);
	}

	percent = (ext2fs_r_blocks_count(sb) * 100.0) /
		ext2fs_blocks_count(sb);

	retval = ext2fs_read_bitmaps(fs);
	if (retval) {
		close(fd);
		return retval;
	}

	retval = ext2fs_dup_handle(fs, &new_fs);
	if (retval) {
		close(fd);
		return retval;
	}

	/* The current method of adding one block group at a time to a
	 * mounted filesystem means it is impossible to accommodate the
	 * flex_bg allocation method of placing the metadata together
	 * in a single block group.  For now we "fix" this issue by
	 * using the traditional layout for new block groups, where
	 * each block group is self-contained and contains its own
	 * bitmap blocks and inode tables.  This means we don't get
	 * the layout advantages of flex_bg in the new block groups,
	 * but at least it allows on-line resizing to function.
	 */
	ext2fs_clear_feature_flex_bg(new_fs->super);
	retval = adjust_fs_info(new_fs, fs, 0, *new_size);
	if (retval) {
		close(fd);
		return retval;
	}

	printf(_("Performing an on-line resize of %s to %llu (%dk) blocks.\n"),
	       fs->device_name, *new_size, fs->blocksize / 1024);

	size = fs->group_desc_count * sb->s_blocks_per_group +
		sb->s_first_data_block;
	if (size > *new_size)
		size = *new_size;

	if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) {
		com_err(program_name, errno,
			_("While trying to extend the last group"));
		exit(1);
	}

	for (i = fs->group_desc_count;
	     i < new_fs->group_desc_count; i++) {

		overhead = (int) (2 + new_fs->inode_blocks_per_group);

		if (ext2fs_bg_has_super(new_fs, new_fs->group_desc_count - 1))
			overhead += 1 + new_fs->desc_blocks +
				new_fs->super->s_reserved_gdt_blocks;

		input.group = i;
		input.block_bitmap = ext2fs_block_bitmap_loc(new_fs, i);
		input.inode_bitmap = ext2fs_inode_bitmap_loc(new_fs, i);
		input.inode_table = ext2fs_inode_table_loc(new_fs, i);
		input.blocks_count = ext2fs_group_blocks_count(new_fs, i);
		input.reserved_blocks = (blk_t) (percent * input.blocks_count
						 / 100.0);

#if 0
		printf("new block bitmap is at 0x%04x\n", input.block_bitmap);
		printf("new inode bitmap is at 0x%04x\n", input.inode_bitmap);
		printf("new inode table is at 0x%04x-0x%04x\n",
		       input.inode_table,
		       input.inode_table + new_fs->inode_blocks_per_group-1);
		printf("new group has %u blocks\n", input.blocks_count);
		printf("new group will reserve %d blocks\n",
		       input.reserved_blocks);
		printf("new group has %d free blocks\n",
		       ext2fs_bg_free_blocks_count(new_fs, i),
		printf("new group has %d free inodes (%d blocks)\n",
		       ext2fs_bg_free_inodes_count(new_fs, i),
		       new_fs->inode_blocks_per_group);
		printf("Adding group #%d\n", input.group);
#endif

		if (use_old_ioctl &&
		    ioctl(fd, EXT2_IOC_GROUP_ADD, &input) == 0)
			continue;
		else
			use_old_ioctl = 0;

		input64.group = input.group;
		input64.block_bitmap = input.block_bitmap;
		input64.inode_bitmap = input.inode_bitmap;
		input64.inode_table = input.inode_table;
		input64.blocks_count = input.blocks_count;
		input64.reserved_blocks = input.reserved_blocks;
		input64.unused = input.unused;

		if (ioctl(fd, EXT4_IOC_GROUP_ADD, &input64) < 0) {
			com_err(program_name, errno,
				_("While trying to add group #%d"),
				input.group);
			exit(1);
		}
	}

	ext2fs_free(new_fs);
	close(fd);

	return 0;
#else
	printf(_("Filesystem at %s is mounted on %s, and on-line resizing is "
		 "not supported on this system.\n"), fs->device_name, mtpt);
	exit(1);
#endif
}
static void check_block_bitmaps(e2fsck_t ctx)
{
	ext2_filsys fs = ctx->fs;
	blk64_t	i;
	unsigned int	*free_array;
	int	group = 0;
	unsigned int	blocks = 0;
	blk64_t	free_blocks = 0;
	blk64_t first_free = ext2fs_blocks_count(fs->super);
	unsigned int	group_free = 0;
	int	actual, bitmap;
	struct problem_context	pctx;
	int	problem, save_problem, fixit, had_problem;
	errcode_t	retval;
	int		csum_flag;
	int		skip_group = 0;
	int	old_desc_blocks = 0;
	int	count = 0;
	int	cmp_block = 0;
	int	redo_flag = 0;
	blk64_t	super_blk, old_desc_blk, new_desc_blk;

	clear_problem_context(&pctx);
	free_array = (unsigned int *) e2fsck_allocate_memory(ctx,
	    fs->group_desc_count * sizeof(unsigned int), "free block count array");

	if ((B2C(fs->super->s_first_data_block) <
	     ext2fs_get_block_bitmap_start2(ctx->block_found_map)) ||
	    (B2C(ext2fs_blocks_count(fs->super)-1) >
	     ext2fs_get_block_bitmap_end2(ctx->block_found_map))) {
		pctx.num = 1;
		pctx.blk = B2C(fs->super->s_first_data_block);
		pctx.blk2 = B2C(ext2fs_blocks_count(fs->super) - 1);
		pctx.ino = ext2fs_get_block_bitmap_start2(ctx->block_found_map);
		pctx.ino2 = ext2fs_get_block_bitmap_end2(ctx->block_found_map);
		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);

		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
		goto errout;
	}

	if ((B2C(fs->super->s_first_data_block) <
	     ext2fs_get_block_bitmap_start2(fs->block_map)) ||
	    (B2C(ext2fs_blocks_count(fs->super)-1) >
	     ext2fs_get_block_bitmap_end2(fs->block_map))) {
		pctx.num = 2;
		pctx.blk = B2C(fs->super->s_first_data_block);
		pctx.blk2 = B2C(ext2fs_blocks_count(fs->super) - 1);
		pctx.ino = ext2fs_get_block_bitmap_start2(fs->block_map);
		pctx.ino2 = ext2fs_get_block_bitmap_end2(fs->block_map);
		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);

		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
		goto errout;
	}

	csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
redo_counts:
	had_problem = 0;
	save_problem = 0;
	pctx.blk = pctx.blk2 = NO_BLK;
	if (csum_flag &&
	    (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
		skip_group++;
	for (i = B2C(fs->super->s_first_data_block);
	     i < ext2fs_blocks_count(fs->super);
	     i += EXT2FS_CLUSTER_RATIO(fs)) {
		actual = ext2fs_fast_test_block_bitmap2(ctx->block_found_map, i);

		if (skip_group) {
			if ((B2C(i) - B2C(fs->super->s_first_data_block)) %
			    fs->super->s_clusters_per_group == 0) {
				super_blk = 0;
				old_desc_blk = 0;
				new_desc_blk = 0;
				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;

				count = 0;
				cmp_block = fs->super->s_clusters_per_group;
				if (group == (int)fs->group_desc_count - 1)
					cmp_block = EXT2FS_NUM_B2C(fs,
						    ext2fs_group_blocks_count(fs, group));
			}

			bitmap = 0;
			if (EQ_CLSTR(i, super_blk) ||
			    (old_desc_blk && old_desc_blocks &&
			     GE_CLSTR(i, old_desc_blk) &&
			     LE_CLSTR(i, old_desc_blk + old_desc_blocks-1)) ||
			    (new_desc_blk && EQ_CLSTR(i, new_desc_blk)) ||
			    EQ_CLSTR(i, ext2fs_block_bitmap_loc(fs, group)) ||
			    EQ_CLSTR(i, ext2fs_inode_bitmap_loc(fs, group)) ||
			    (GE_CLSTR(i, ext2fs_inode_table_loc(fs, group)) &&
			     LE_CLSTR(i, (ext2fs_inode_table_loc(fs, group) +
					  fs->inode_blocks_per_group - 1)))) {
				bitmap = 1;
				actual = (actual != 0);
				count++;
				cmp_block--;
			} else if ((EXT2FS_B2C(fs, i) - count -
				    EXT2FS_B2C(fs, fs->super->s_first_data_block)) %
				   fs->super->s_clusters_per_group == 0) {
				/*
				 * When the compare data blocks in block bitmap
				 * are 0, count the free block,
				 * skip the current block group.
				 */
				if (ext2fs_test_block_bitmap_range2(
					    ctx->block_found_map,
					    EXT2FS_B2C(fs, i),
					    cmp_block)) {
					/*
					 * -1 means to skip the current block
					 * group.
					 */
					blocks = fs->super->s_clusters_per_group - 1;
					group_free = cmp_block;
					free_blocks += cmp_block;
					/*
					 * The current block group's last block
					 * is set to i.
					 */
					i += EXT2FS_C2B(fs, cmp_block - 1);
					bitmap = 1;
					goto do_counts;
				}
			}
		} else if (redo_flag)
			bitmap = actual;
		else
			bitmap = ext2fs_fast_test_block_bitmap2(fs->block_map, i);

		if (!actual == !bitmap)
			goto do_counts;

		if (!actual && bitmap) {
			/*
			 * Block not used, but marked in use in the bitmap.
			 */
			problem = PR_5_BLOCK_UNUSED;
		} else {
			/*
			 * Block used, but not marked in use in the bitmap.
			 */
			problem = PR_5_BLOCK_USED;

			if (skip_group) {
				struct problem_context pctx2;
				pctx2.blk = i;
				pctx2.group = group;
				if (fix_problem(ctx, PR_5_BLOCK_UNINIT,&pctx2)){
					ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
					skip_group = 0;
				}
			}
		}
		if (pctx.blk == NO_BLK) {
			pctx.blk = pctx.blk2 = i;
			save_problem = problem;
		} else {
			if ((problem == save_problem) &&
			    (pctx.blk2 == i-1))
				pctx.blk2++;
			else {
				print_bitmap_problem(ctx, save_problem, &pctx);
				pctx.blk = pctx.blk2 = i;
				save_problem = problem;
			}
		}
		ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
		had_problem++;

		/*
		 * If there a problem we should turn off the discard so we
		 * do not compromise the filesystem.
		 */
		ctx->options &= ~E2F_OPT_DISCARD;

	do_counts:
		if (!bitmap) {
			group_free++;
			free_blocks++;
			if (first_free > i)
				first_free = i;
		} else if (i > first_free) {
			e2fsck_discard_blocks(ctx, first_free,
					      (i - first_free));
			first_free = ext2fs_blocks_count(fs->super);
		}
		blocks ++;
		if ((blocks == fs->super->s_clusters_per_group) ||
		    (EXT2FS_B2C(fs, i) ==
		     EXT2FS_B2C(fs, ext2fs_blocks_count(fs->super)-1))) {
			/*
			 * If the last block of this group is free, then we can
			 * discard it as well.
			 */
			if (!bitmap && i >= first_free)
				e2fsck_discard_blocks(ctx, first_free,
						      (i - first_free) + 1);
			first_free = ext2fs_blocks_count(fs->super);

			free_array[group] = group_free;
			group ++;
			blocks = 0;
			group_free = 0;
			skip_group = 0;
			if (ctx->progress)
				if ((ctx->progress)(ctx, 5, group,
						    fs->group_desc_count*2))
					goto errout;
			if (csum_flag &&
			    (i != ext2fs_blocks_count(fs->super)-1) &&
			    ext2fs_bg_flags_test(fs, group, 
						EXT2_BG_BLOCK_UNINIT))
				skip_group++;
		}
	}
	if (pctx.blk != NO_BLK)
		print_bitmap_problem(ctx, save_problem, &pctx);
	if (had_problem)
		fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
	else
		fixit = -1;
	ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;

	if (fixit == 1) {
		ext2fs_free_block_bitmap(fs->block_map);
		retval = ext2fs_copy_bitmap(ctx->block_found_map,
						  &fs->block_map);
		if (retval) {
			clear_problem_context(&pctx);
			fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
			ctx->flags |= E2F_FLAG_ABORT;
			goto errout;
		}
		ext2fs_set_bitmap_padding(fs->block_map);
		ext2fs_mark_bb_dirty(fs);

		/* Redo the counts */
		blocks = 0; free_blocks = 0; group_free = 0; group = 0;
		memset(free_array, 0, fs->group_desc_count * sizeof(int));
		redo_flag++;
		goto redo_counts;
	} else if (fixit == 0)
		ext2fs_unmark_valid(fs);

	for (i = 0; i < fs->group_desc_count; i++) {
		if (free_array[i] != ext2fs_bg_free_blocks_count(fs, i)) {
			pctx.group = i;
			pctx.blk = ext2fs_bg_free_blocks_count(fs, i);
			pctx.blk2 = free_array[i];

			if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
					&pctx)) {
				ext2fs_bg_free_blocks_count_set(fs, i, free_array[i]);
				ext2fs_mark_super_dirty(fs);
			} else
				ext2fs_unmark_valid(fs);
		}
	}
	free_blocks = EXT2FS_C2B(fs, free_blocks);
	if (free_blocks != ext2fs_free_blocks_count(fs->super)) {
		pctx.group = 0;
		pctx.blk = ext2fs_free_blocks_count(fs->super);
		pctx.blk2 = free_blocks;

		if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
			ext2fs_free_blocks_count_set(fs->super, free_blocks);
			ext2fs_mark_super_dirty(fs);
		}
	}
errout:
	ext2fs_free_mem(&free_array);
}
Exemple #19
0
errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
				      ext2fs_block_bitmap bmap)
{
	errcode_t	retval;
	blk64_t		group_blk, start_blk, last_blk, new_blk;
	dgrp_t		last_grp = 0;
	int		rem_grps = 0, flexbg_size = 0;

	group_blk = ext2fs_group_first_block2(fs, group);
	last_blk = ext2fs_group_last_block2(fs, group);

	if (!bmap)
		bmap = fs->block_map;

	if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
				      EXT4_FEATURE_INCOMPAT_FLEX_BG) &&
	    fs->super->s_log_groups_per_flex) {
		flexbg_size = 1 << fs->super->s_log_groups_per_flex;
		last_grp = group | (flexbg_size - 1);
		if (last_grp > fs->group_desc_count-1)
			last_grp = fs->group_desc_count-1;
		rem_grps = last_grp - group + 1;
	}

	/*
	 * Allocate the block and inode bitmaps, if necessary
	 */
	if (fs->stride) {
		retval = ext2fs_get_free_blocks2(fs, group_blk, last_blk,
						 1, bmap, &start_blk);
		if (retval)
			return retval;
		start_blk += fs->inode_blocks_per_group;
		start_blk += ((fs->stride * group) %
			      (last_blk - start_blk + 1));
		if (start_blk >= last_blk)
			start_blk = group_blk;
	} else
		start_blk = group_blk;

	if (flexbg_size) {
		blk64_t prev_block = 0;

		if (group % flexbg_size)
			prev_block = ext2fs_block_bitmap_loc(fs, group - 1) + 1;
		start_blk = flexbg_offset(fs, group, prev_block, bmap,
					  rem_grps, 1);
		last_blk = ext2fs_group_last_block2(fs, last_grp);
	}

	if (!ext2fs_block_bitmap_loc(fs, group)) {
		retval = ext2fs_get_free_blocks2(fs, start_blk, last_blk,
						 1, bmap, &new_blk);
		if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
			retval = ext2fs_get_free_blocks2(fs, group_blk,
					last_blk, 1, bmap, &new_blk);
		if (retval)
			return retval;
		ext2fs_mark_block_bitmap2(bmap, new_blk);
		ext2fs_block_bitmap_loc_set(fs, group, new_blk);
		if (flexbg_size) {
			dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk);
			ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1);
			ext2fs_free_blocks_count_add(fs->super, -1);
			ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT);
			ext2fs_group_desc_csum_set(fs, gr);
		}
	}

	if (flexbg_size) {
		blk64_t prev_block = 0;
		if (group % flexbg_size)
			prev_block = ext2fs_inode_bitmap_loc(fs, group - 1) + 1;
		else
			prev_block = ext2fs_block_bitmap_loc(fs, group) +
				flexbg_size;
		start_blk = flexbg_offset(fs, group, prev_block, bmap,
					  rem_grps, 1);
		last_blk = ext2fs_group_last_block2(fs, last_grp);
	}

	if (!ext2fs_inode_bitmap_loc(fs, group)) {
		retval = ext2fs_get_free_blocks2(fs, start_blk, last_blk,
						 1, bmap, &new_blk);
		if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
			retval = ext2fs_get_free_blocks2(fs, group_blk,
					 last_blk, 1, bmap, &new_blk);
		if (retval)
			return retval;
		ext2fs_mark_block_bitmap2(bmap, new_blk);
		ext2fs_inode_bitmap_loc_set(fs, group, new_blk);
		if (flexbg_size) {
			dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk);
			ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1);
			ext2fs_free_blocks_count_add(fs->super, -1);
			ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT);
			ext2fs_group_desc_csum_set(fs, gr);
		}
	}

	/*
	 * Allocate the inode table
	 */
	if (flexbg_size) {
		blk64_t prev_block = 0;

		if (group % flexbg_size)
			prev_block = ext2fs_inode_table_loc(fs, group - 1) +
				fs->inode_blocks_per_group;
		else
			prev_block = ext2fs_inode_bitmap_loc(fs, group) +
				flexbg_size;

		group_blk = flexbg_offset(fs, group, prev_block, bmap,
					  rem_grps, fs->inode_blocks_per_group);
		last_blk = ext2fs_group_last_block2(fs, last_grp);
	}

	if (!ext2fs_inode_table_loc(fs, group)) {
		retval = ext2fs_get_free_blocks2(fs, group_blk, last_blk,
						fs->inode_blocks_per_group,
						bmap, &new_blk);
		if (retval)
			return retval;
		if (flexbg_size)
			ext2fs_block_alloc_stats_range(fs, new_blk,
				       fs->inode_blocks_per_group, +1);
		else
			ext2fs_mark_block_bitmap_range2(fs->block_map,
					new_blk, fs->inode_blocks_per_group);
		ext2fs_inode_table_loc_set(fs, group, new_blk);
	}
	ext2fs_group_desc_csum_set(fs, group);
	return 0;
}