Esempio n. 1
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);
}
Esempio n. 2
0
void e2fsck_move_ext3_journal(e2fsck_t ctx)
{
	struct ext2_super_block *sb = ctx->fs->super;
	struct problem_context	pctx;
	struct ext2_inode 	inode;
	ext2_filsys		fs = ctx->fs;
	ext2_ino_t		ino;
	errcode_t		retval;
	const char * const *	cpp;
	int			group, mount_flags;

	clear_problem_context(&pctx);

	/*
	 * If the filesystem is opened read-only, or there is no
	 * journal, then do nothing.
	 */
	if ((ctx->options & E2F_OPT_READONLY) ||
	    (sb->s_journal_inum == 0) ||
	    !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
		return;

	/*
	 * Read in the journal inode
	 */
	if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
		return;

	/*
	 * If it's necessary to backup the journal inode, do so.
	 */
	if ((sb->s_jnl_backup_type == 0) ||
	    ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
	     memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
		if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
			memcpy(sb->s_jnl_blocks, inode.i_block,
			       EXT2_N_BLOCKS*4);
			sb->s_jnl_blocks[15] = inode.i_size_high;
			sb->s_jnl_blocks[16] = inode.i_size;
			sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
			ext2fs_mark_super_dirty(fs);
			fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
		}
	}

	/*
	 * If the journal is already the hidden inode, then do nothing
	 */
	if (sb->s_journal_inum == EXT2_JOURNAL_INO)
		return;

	/*
	 * The journal inode had better have only one link and not be readable.
	 */
	if (inode.i_links_count != 1)
		return;

	/*
	 * If the filesystem is mounted, or we can't tell whether
	 * or not it's mounted, do nothing.
	 */
	retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
	if (retval || (mount_flags & EXT2_MF_MOUNTED))
		return;

	/*
	 * If we can't find the name of the journal inode, then do
	 * nothing.
	 */
	for (cpp = journal_names; *cpp; cpp++) {
		retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
				       strlen(*cpp), 0, &ino);
		if ((retval == 0) && (ino == sb->s_journal_inum))
			break;
	}
	if (*cpp == 0)
		return;

	/* We need the inode bitmap to be loaded */
	retval = ext2fs_read_bitmaps(fs);
	if (retval)
		return;

	pctx.str = *cpp;
	if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
		return;

	/*
	 * OK, we've done all the checks, let's actually move the
	 * journal inode.  Errors at this point mean we need to force
	 * an ext2 filesystem check.
	 */
	if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
		goto err_out;
	if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
		goto err_out;
	sb->s_journal_inum = EXT2_JOURNAL_INO;
	ext2fs_mark_super_dirty(fs);
	fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
	inode.i_links_count = 0;
	inode.i_dtime = ctx->now;
	if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
		goto err_out;

	group = ext2fs_group_of_ino(fs, ino);
	ext2fs_unmark_inode_bitmap2(fs->inode_map, ino);
	ext2fs_mark_ib_dirty(fs);
	ext2fs_bg_free_inodes_count_set(fs, group, ext2fs_bg_free_inodes_count(fs, group) + 1);
	ext2fs_group_desc_csum_set(fs, group);
	fs->super->s_free_inodes_count++;
	return;

err_out:
	pctx.errcode = retval;
	fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
	fs->super->s_state &= ~EXT2_VALID_FS;
	ext2fs_mark_super_dirty(fs);
	return;
}
Esempio n. 3
0
static void check_inode_bitmaps(e2fsck_t ctx)
{
	ext2_filsys fs = ctx->fs;
	ext2_ino_t	i;
	unsigned int	free_inodes = 0;
	int		group_free = 0;
	int		dirs_count = 0;
	int		group = 0;
	unsigned int	inodes = 0;
	ext2_ino_t	*free_array;
	ext2_ino_t	*dir_array;
	int		actual, bitmap;
	errcode_t	retval;
	struct problem_context	pctx;
	int		problem, save_problem, fixit, had_problem;
	int		csum_flag;
	int		skip_group = 0;
	int		redo_flag = 0;
	ext2_ino_t		first_free = fs->super->s_inodes_per_group + 1;

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

	dir_array = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
	   fs->group_desc_count * sizeof(ext2_ino_t), "directory count array");

	if ((1 < ext2fs_get_inode_bitmap_start2(ctx->inode_used_map)) ||
	    (fs->super->s_inodes_count >
	     ext2fs_get_inode_bitmap_end2(ctx->inode_used_map))) {
		pctx.num = 3;
		pctx.blk = 1;
		pctx.blk2 = fs->super->s_inodes_count;
		pctx.ino = ext2fs_get_inode_bitmap_start2(ctx->inode_used_map);
		pctx.ino2 = ext2fs_get_inode_bitmap_end2(ctx->inode_used_map);
		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);

		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
		goto errout;
	}
	if ((1 < ext2fs_get_inode_bitmap_start2(fs->inode_map)) ||
	    (fs->super->s_inodes_count >
	     ext2fs_get_inode_bitmap_end2(fs->inode_map))) {
		pctx.num = 4;
		pctx.blk = 1;
		pctx.blk2 = fs->super->s_inodes_count;
		pctx.ino = ext2fs_get_inode_bitmap_start2(fs->inode_map);
		pctx.ino2 = ext2fs_get_inode_bitmap_end2(fs->inode_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.ino = pctx.ino2 = 0;
	if (csum_flag &&
	    (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)))
		skip_group++;

	/* Protect loop from wrap-around if inodes_count is maxed */
	for (i = 1; i <= fs->super->s_inodes_count && i > 0; i++) {
		bitmap = 0;
		if (skip_group &&
		    i % fs->super->s_inodes_per_group == 1) {
			/*
			 * Current inode is the first inode
			 * in the current block group.
			 */
			if (ext2fs_test_inode_bitmap_range(
				    ctx->inode_used_map, i,
				    fs->super->s_inodes_per_group)) {
				/*
				 * When the compared inodes in inodes bitmap
				 * are 0, count the free inode,
				 * skip the current block group.
				 */
				first_free = 1;
				inodes = fs->super->s_inodes_per_group - 1;
				group_free = inodes;
				free_inodes += inodes;
				i += inodes;
				skip_group = 0;
				goto do_counts;
			}
		}

		actual = ext2fs_fast_test_inode_bitmap2(ctx->inode_used_map, i);
		if (redo_flag)
			bitmap = actual;
		else if (!skip_group)
			bitmap = ext2fs_fast_test_inode_bitmap2(fs->inode_map, i);
		if (!actual == !bitmap)
			goto do_counts;

		if (!actual && bitmap) {
			/*
			 * Inode wasn't used, but marked in bitmap
			 */
			problem = PR_5_INODE_UNUSED;
		} else /* if (actual && !bitmap) */ {
			/*
			 * Inode used, but not in bitmap
			 */
			problem = PR_5_INODE_USED;

			/* We should never hit this, because it means that
			 * inodes were marked in use that weren't noticed
			 * in pass1 or pass 2. It is easier to fix the problem
			 * than to kill e2fsck and leave the user stuck. */
			if (skip_group) {
				struct problem_context pctx2;
				pctx2.blk = i;
				pctx2.group = group;
				if (fix_problem(ctx, PR_5_INODE_UNINIT,&pctx2)){
					ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT);
					skip_group = 0;
				}
			}
		}
		if (pctx.ino == 0) {
			pctx.ino = pctx.ino2 = i;
			save_problem = problem;
		} else {
			if ((problem == save_problem) &&
			    (pctx.ino2 == i-1))
				pctx.ino2++;
			else {
				print_bitmap_problem(ctx, save_problem, &pctx);
				pctx.ino = pctx.ino2 = 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:
		inodes++;
		if (bitmap) {
			if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, i))
				dirs_count++;
			if (inodes > first_free) {
				e2fsck_discard_inodes(ctx, group, first_free,
						      inodes - first_free);
				first_free = fs->super->s_inodes_per_group + 1;
			}
		} else {
			group_free++;
			free_inodes++;
			if (first_free > inodes)
				first_free = inodes;
		}

		if ((inodes == fs->super->s_inodes_per_group) ||
		    (i == fs->super->s_inodes_count)) {
			/*
			 * If the last inode is free, we can discard it as well.
			 */
			if (!bitmap && inodes >= first_free)
				e2fsck_discard_inodes(ctx, group, first_free,
						      inodes - first_free + 1);
			/*
			 * If discard zeroes data and the group inode table
			 * was not zeroed yet, set itable as zeroed
			 */
			if ((ctx->options & E2F_OPT_DISCARD) &&
			    io_channel_discard_zeroes_data(fs->io) &&
			    !(ext2fs_bg_flags_test(fs, group,
						   EXT2_BG_INODE_ZEROED))) {
				ext2fs_bg_flags_set(fs, group,
						    EXT2_BG_INODE_ZEROED);
				ext2fs_group_desc_csum_set(fs, group);
			}

			first_free = fs->super->s_inodes_per_group + 1;
			free_array[group] = group_free;
			dir_array[group] = dirs_count;
			group ++;
			inodes = 0;
			skip_group = 0;
			group_free = 0;
			dirs_count = 0;
			if (ctx->progress)
				if ((ctx->progress)(ctx, 5,
					    group + fs->group_desc_count,
					    fs->group_desc_count*2))
					goto errout;
			if (csum_flag &&
			    (i != fs->super->s_inodes_count) &&
			    (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)
			     ))
				skip_group++;
		}
	}
	if (pctx.ino)
		print_bitmap_problem(ctx, save_problem, &pctx);

	if (had_problem)
		fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
	else
		fixit = -1;
	ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;

	if (fixit == 1) {
		ext2fs_free_inode_bitmap(fs->inode_map);
		retval = ext2fs_copy_bitmap(ctx->inode_used_map,
						  &fs->inode_map);
		if (retval) {
			clear_problem_context(&pctx);
			fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
			ctx->flags |= E2F_FLAG_ABORT;
			goto errout;
		}
		ext2fs_set_bitmap_padding(fs->inode_map);
		ext2fs_mark_ib_dirty(fs);

		/* redo counts */
		inodes = 0; free_inodes = 0; group_free = 0;
		dirs_count = 0; group = 0;
		memset(free_array, 0, fs->group_desc_count * sizeof(int));
		memset(dir_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_inodes_count(fs, i)) {
			pctx.group = i;
			pctx.ino = ext2fs_bg_free_inodes_count(fs, i);
			pctx.ino2 = free_array[i];
			if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
					&pctx)) {
				ext2fs_bg_free_inodes_count_set(fs, i, free_array[i]);
				ext2fs_mark_super_dirty(fs);
			} else
				ext2fs_unmark_valid(fs);
		}
		if (dir_array[i] != ext2fs_bg_used_dirs_count(fs, i)) {
			pctx.group = i;
			pctx.ino = ext2fs_bg_used_dirs_count(fs, i);
			pctx.ino2 = dir_array[i];

			if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
					&pctx)) {
				ext2fs_bg_used_dirs_count_set(fs, i, dir_array[i]);
				ext2fs_mark_super_dirty(fs);
			} else
				ext2fs_unmark_valid(fs);
		}
	}
	if (free_inodes != fs->super->s_free_inodes_count) {
		pctx.group = -1;
		pctx.ino = fs->super->s_free_inodes_count;
		pctx.ino2 = free_inodes;

		if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
			fs->super->s_free_inodes_count = free_inodes;
			ext2fs_mark_super_dirty(fs);
		}
	}
errout:
	ext2fs_free_mem(&free_array);
	ext2fs_free_mem(&dir_array);
}
Esempio n. 4
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
}
Esempio n. 5
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
}