+ io_options = strchr(argv[optind], '?');
+ if (io_options)
+ *io_options++ = 0;
+ device_name = blkid_get_devname(NULL, argv[optind], NULL);
+ if (!device_name) {
+ com_err("tune2fs", 0, _("Unable to resolve '%s'"),
+ argv[optind]);
+ exit(1);
+ }
+}
+
+#ifdef CONFIG_BUILD_FINDFS
+void do_findfs(int argc, char **argv)
+{
+ char *dev;
+
+ if ((argc != 2) ||
+ (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
+ fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
+ exit(2);
+ }
+ dev = blkid_get_devname(NULL, argv[1], NULL);
+ if (!dev) {
+ com_err("findfs", 0, _("Unable to resolve '%s'"),
+ argv[1]);
+ exit(1);
+ }
+ puts(dev);
+ exit(0);
+}
+#endif
+
+static void parse_extended_opts(ext2_filsys fs, const char *opts)
+{
+ char *buf, *token, *next, *p, *arg;
+ int len, hash_alg;
+ int r_usage = 0;
+
+ len = strlen(opts);
+ buf = malloc(len+1);
+ if (!buf) {
+ fprintf(stderr,
+ _("Couldn't allocate memory to parse options!\n"));
+ exit(1);
+ }
+ strcpy(buf, opts);
+ for (token = buf; token && *token; token = next) {
+ p = strchr(token, ',');
+ next = 0;
+ if (p) {
+ *p = 0;
+ next = p+1;
+ }
+ arg = strchr(token, '=');
+ if (arg) {
+ *arg = 0;
+ arg++;
+ }
+ if (!strcmp(token, "test_fs")) {
+ fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS;
+ printf("Setting test filesystem flag\n");
+ ext2fs_mark_super_dirty(fs);
+ } else if (!strcmp(token, "^test_fs")) {
+ fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
+ printf("Clearing test filesystem flag\n");
+ ext2fs_mark_super_dirty(fs);
+ } else if (strcmp(token, "stride") == 0) {
+ if (!arg) {
+ r_usage++;
+ continue;
+ }
+ stride = strtoul(arg, &p, 0);
+ if (*p || (stride == 0)) {
+ fprintf(stderr,
+ _("Invalid RAID stride: %s\n"),
+ arg);
+ r_usage++;
+ continue;
+ }
+ stride_set = 1;
+ } else if (strcmp(token, "stripe-width") == 0 ||
+ strcmp(token, "stripe_width") == 0) {
+ if (!arg) {
+ r_usage++;
+ continue;
+ }
+ stripe_width = strtoul(arg, &p, 0);
+ if (*p || (stripe_width == 0)) {
+ fprintf(stderr,
+ _("Invalid RAID stripe-width: %s\n"),
+ arg);
+ r_usage++;
+ continue;
+ }
+ stripe_width_set = 1;
+ } else if (strcmp(token, "hash_alg") == 0 ||
+ strcmp(token, "hash-alg") == 0) {
+ if (!arg) {
+ r_usage++;
+ continue;
+ }
+ hash_alg = e2p_string2hash(arg);
+ if (hash_alg < 0) {
+ fprintf(stderr,
+ _("Invalid hash algorithm: %s\n"),
+ arg);
+ r_usage++;
+ continue;
+ }
+ fs->super->s_def_hash_version = hash_alg;
+ printf(_("Setting default hash algorithm "
+ "to %s (%d)\n"),
+ arg, hash_alg);
+ ext2fs_mark_super_dirty(fs);
+ } else
+ r_usage++;
+ }
+ if (r_usage) {
+ fprintf(stderr, _("\nBad options specified.\n\n"
+ "Extended options are separated by commas, "
+ "and may take an argument which\n"
+ "\tis set off by an equals ('=') sign.\n\n"
+ "Valid extended options are:\n"
+ "\tstride=<RAID per-disk chunk size in blocks>\n"
+ "\tstripe_width=<RAID stride*data disks in blocks>\n"
+ "\thash_alg=<hash algorithm>\n"
+ "\ttest_fs\n"
+ "\t^test_fs\n"));
+ free(buf);
+ exit(1);
+ }
+ free(buf);
+}
+
+/*
+ * Fill in the block bitmap bmap with the information regarding the
+ * blocks to be moved
+ */
+static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp,
+ ext2fs_block_bitmap bmap)
+{
+ dgrp_t i;
+ int retval;
+ ext2_badblocks_list bb_list = 0;
+ blk_t j, needed_blocks = 0;
+ blk_t start_blk, end_blk;
+
+ retval = ext2fs_read_bb_inode(fs, &bb_list);
+ if (retval)
+ return retval;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ start_blk = fs->group_desc[i].bg_inode_table +
+ fs->inode_blocks_per_group;
+
+ end_blk = fs->group_desc[i].bg_inode_table +
+ new_ino_blks_per_grp;
+
+ for (j = start_blk; j < end_blk; j++) {
+ if (ext2fs_test_block_bitmap(fs->block_map, j)) {
+ /*
+ * IF the block is a bad block we fail
+ */
+ if (ext2fs_badblocks_list_test(bb_list, j)) {
+ ext2fs_badblocks_list_free(bb_list);
+ return ENOSPC;
+ }
+
+ ext2fs_mark_block_bitmap(bmap, j);
+ } else {
+ /*
+ * We are going to use this block for
+ * inode table. So mark them used.
+ */
+ ext2fs_mark_block_bitmap(fs->block_map, j);
+ }
+ }
+ needed_blocks += end_blk - start_blk;
+ }
+
+ ext2fs_badblocks_list_free(bb_list);
+ if (needed_blocks > fs->super->s_free_blocks_count)
+ return ENOSPC;
+
+ return 0;
+}
+
+static int ext2fs_is_meta_block(ext2_filsys fs, blk_t blk)
+{
+ dgrp_t group;
+ group = ext2fs_group_of_blk(fs, blk);
+ if (fs->group_desc[group].bg_block_bitmap == blk)
+ return 1;
+ if (fs->group_desc[group].bg_inode_bitmap == blk)
+ return 1;
+ return 0;
+}
+
+static int ext2fs_is_block_in_group(ext2_filsys fs, dgrp_t group, blk_t blk)
+{
+ blk_t start_blk, end_blk;
+ start_blk = fs->super->s_first_data_block +
+ EXT2_BLOCKS_PER_GROUP(fs->super) * group;
+ /*
+ * We cannot get new block beyond end_blk for for the last block group
+ * so we can check with EXT2_BLOCKS_PER_GROUP even for last block group
+ */
+ end_blk = start_blk + EXT2_BLOCKS_PER_GROUP(fs->super);
+ if (blk >= start_blk && blk <= end_blk)
+ return 1;
+ return 0;
+}
+
+static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap)
+{
+
+ char *buf;
+ dgrp_t group;
+ errcode_t retval;
+ int meta_data = 0;
+ blk_t blk, new_blk, goal;
+ struct blk_move *bmv;
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+
+ for (new_blk = blk = fs->super->s_first_data_block;
+ blk < fs->super->s_blocks_count; blk++) {
+ if (!ext2fs_test_block_bitmap(bmap, blk))
+ continue;
+
+ if (ext2fs_is_meta_block(fs, blk)) {
+ /*
+ * If the block is mapping a fs meta data block
+ * like group desc/block bitmap/inode bitmap. We
+ * should find a block in the same group and fix
+ * the respective fs metadata pointers. Otherwise
+ * fail
+ */
+ group = ext2fs_group_of_blk(fs, blk);
+ goal = ext2fs_group_first_block(fs, group);
+ meta_data = 1;
+
+ } else {
+ goal = new_blk;
+ }
+ retval = ext2fs_new_block(fs, goal, NULL, &new_blk);
+ if (retval)
+ goto err_out;
+
+ /* new fs meta data block should be in the same group */
+ if (meta_data && !ext2fs_is_block_in_group(fs, group, new_blk)) {
+ retval = ENOSPC;
+ goto err_out;
+ }
+
+ /* Mark this block as allocated */
+ ext2fs_mark_block_bitmap(fs->block_map, new_blk);
+
+ /* Add it to block move list */
+ retval = ext2fs_get_mem(sizeof(struct blk_move), &bmv);
+ if (retval)
+ goto err_out;
+
+ bmv->old_loc = blk;
+ bmv->new_loc = new_blk;
+
+ list_add(&(bmv->list), &blk_move_list);
+
+ retval = io_channel_read_blk(fs->io, blk, 1, buf);
+ if (retval)
+ goto err_out;
+
+ retval = io_channel_write_blk(fs->io, new_blk, 1, buf);
+ if (retval)
+ goto err_out;
+ }
+
+err_out:
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+static blk_t translate_block(blk_t blk)
+{
+ struct list_head *entry;
+ struct blk_move *bmv;
+
+ list_for_each(entry, &blk_move_list) {
+ bmv = list_entry(entry, struct blk_move, list);
+ if (bmv->old_loc == blk)
+ return bmv->new_loc;
+ }
+
+ return 0;
+}
+
+static int process_block(ext2_filsys fs EXT2FS_ATTR((unused)),
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ int ret = 0;
+ blk_t new_blk;
+ ext2fs_block_bitmap bmap = (ext2fs_block_bitmap) priv_data;
+
+ if (!ext2fs_test_block_bitmap(bmap, *block_nr))
+ return 0;
+ new_blk = translate_block(*block_nr);
+ if (new_blk) {
+ *block_nr = new_blk;
+ /*
+ * This will force the ext2fs_write_inode in the iterator
+ */
+ ret |= BLOCK_CHANGED;
+ }
+
+ return ret;
+}
+
+static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
+{
+ errcode_t retval = 0;
+ ext2_ino_t ino;
+ blk_t blk;
+ char *block_buf = 0;
+ struct ext2_inode inode;
+ ext2_inode_scan scan = NULL;
+
+ retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ goto err_out;
+
+ while (1) {
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval)
+ goto err_out;
+
+ if (!ino)
+ break;
+
+ if (inode.i_links_count == 0)
+ continue; /* inode not in use */
+
+ /* FIXME!!
+ * If we end up modifying the journal inode
+ * the sb->s_jnl_blocks will differ. But a
+ * subsequent e2fsck fixes that.
+ * Do we need to fix this ??
+ */
+
+ if (inode.i_file_acl &&
+ ext2fs_test_block_bitmap(bmap, inode.i_file_acl)) {
+ blk = translate_block(inode.i_file_acl);
+ if (!blk)
+ continue;
+
+ inode.i_file_acl = blk;
+
+ /*
+ * Write the inode to disk so that inode table
+ * resizing can work
+ */
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval)
+ goto err_out;
+ }
+
+ if (!ext2fs_inode_has_valid_blocks(&inode))
+ continue;
+
+ retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+ process_block, bmap);
+ if (retval)
+ goto err_out;
+
+ }
+
+err_out:
+ ext2fs_free_mem(&block_buf);
+
+ return retval;
+}
+
+/*
+ * We need to scan for inode and block bitmaps that may need to be
+ * moved. This can take place if the filesystem was formatted for
+ * RAID arrays using the mke2fs's extended option "stride".
+ */
+static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
+{
+ dgrp_t i;
+ blk_t blk, new_blk;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ blk = fs->group_desc[i].bg_block_bitmap;
+ if (ext2fs_test_block_bitmap(bmap, blk)) {
+ new_blk = translate_block(blk);
+ if (!new_blk)
+ continue;
+ fs->group_desc[i].bg_block_bitmap = new_blk;
+ }
+
+ blk = fs->group_desc[i].bg_inode_bitmap;
+ if (ext2fs_test_block_bitmap(bmap, blk)) {
+ new_blk = translate_block(blk);
+ if (!new_blk)
+ continue;
+ fs->group_desc[i].bg_inode_bitmap = new_blk;
+ }
+ }
+ return 0;
+}
+
+static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size)
+{
+ dgrp_t i;
+ blk_t blk;
+ errcode_t retval;
+ int new_ino_blks_per_grp;
+ unsigned int j;
+ char *old_itable = NULL, *new_itable = NULL;
+ char *tmp_old_itable = NULL, *tmp_new_itable = NULL;
+ unsigned long old_ino_size;
+ int old_itable_size, new_itable_size;
+
+ old_itable_size = fs->inode_blocks_per_group * fs->blocksize;
+ old_ino_size = EXT2_INODE_SIZE(fs->super);
+
+ new_ino_blks_per_grp = ext2fs_div_ceil(
+ EXT2_INODES_PER_GROUP(fs->super) *
+ new_ino_size,
+ fs->blocksize);
+
+ new_itable_size = new_ino_blks_per_grp * fs->blocksize;
+
+ retval = ext2fs_get_mem(old_itable_size, &old_itable);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_get_mem(new_itable_size, &new_itable);
+ if (retval)
+ goto err_out;
+
+ tmp_old_itable = old_itable;
+ tmp_new_itable = new_itable;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ blk = fs->group_desc[i].bg_inode_table;
+ retval = io_channel_read_blk(fs->io, blk,
+ fs->inode_blocks_per_group, old_itable);
+ if (retval)
+ goto err_out;
+
+ for (j = 0; j < EXT2_INODES_PER_GROUP(fs->super); j++) {
+ memcpy(new_itable, old_itable, old_ino_size);
+
+ memset(new_itable+old_ino_size, 0,
+ new_ino_size - old_ino_size);
+
+ new_itable += new_ino_size;
+ old_itable += old_ino_size;
+ }
+
+ /* reset the pointer */
+ old_itable = tmp_old_itable;
+ new_itable = tmp_new_itable;
+
+ retval = io_channel_write_blk(fs->io, blk,
+ new_ino_blks_per_grp, new_itable);
+ if (retval)
+ goto err_out;
+ }