+static void check_inode_bitmap_checksum(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ char *buf = NULL;
+ dgrp_t i;
+ int nbytes;
+ ext2_ino_t ino_itr;
+ errcode_t retval;
+
+ if (!ext2fs_has_feature_metadata_csum(ctx->fs->super))
+ return;
+
+ /* If bitmap is dirty from being fixed, checksum will be corrected */
+ if (ext2fs_test_ib_dirty(ctx->fs))
+ return;
+
+ nbytes = (size_t)(EXT2_INODES_PER_GROUP(ctx->fs->super) / 8);
+ retval = ext2fs_get_mem(ctx->fs->blocksize, &buf);
+ if (retval) {
+ com_err(ctx->program_name, 0, "%s",
+ _("check_inode_bitmap_checksum: Memory allocation error"));
+ fatal_error(ctx, 0);
+ }
+
+ clear_problem_context(&pctx);
+ for (i = 0; i < ctx->fs->group_desc_count; i++) {
+ if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_INODE_UNINIT))
+ continue;
+
+ ino_itr = 1 + (i * (nbytes << 3));
+ retval = ext2fs_get_inode_bitmap_range2(ctx->fs->inode_map,
+ ino_itr, nbytes << 3,
+ buf);
+ if (retval)
+ break;
+
+ if (ext2fs_inode_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+ continue;
+ pctx.group = i;
+ if (!fix_problem(ctx, PR_5_INODE_BITMAP_CSUM_INVALID, &pctx))
+ continue;
+
+ /*
+ * Fixing one checksum will rewrite all of them. The bitmap
+ * will be checked against the one we made during pass1 for
+ * discrepancies, and fixed if need be.
+ */
+ ext2fs_mark_ib_dirty(ctx->fs);
+ break;
+ }
+
+ ext2fs_free_mem(&buf);
+}
+
+static void check_block_bitmap_checksum(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ char *buf = NULL;
+ dgrp_t i;
+ int nbytes;
+ blk64_t blk_itr;
+ errcode_t retval;
+
+ if (!ext2fs_has_feature_metadata_csum(ctx->fs->super))
+ return;
+
+ /* If bitmap is dirty from being fixed, checksum will be corrected */
+ if (ext2fs_test_bb_dirty(ctx->fs))
+ return;
+
+ nbytes = (size_t)(EXT2_CLUSTERS_PER_GROUP(ctx->fs->super) / 8);
+ retval = ext2fs_get_mem(ctx->fs->blocksize, &buf);
+ if (retval) {
+ com_err(ctx->program_name, 0, "%s",
+ _("check_block_bitmap_checksum: Memory allocation error"));
+ fatal_error(ctx, 0);
+ }
+
+ clear_problem_context(&pctx);
+ for (i = 0; i < ctx->fs->group_desc_count; i++) {
+ if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_BLOCK_UNINIT))
+ continue;
+
+ blk_itr = EXT2FS_B2C(ctx->fs,
+ ctx->fs->super->s_first_data_block) +
+ ((blk64_t) i * (nbytes << 3));
+ retval = ext2fs_get_block_bitmap_range2(ctx->fs->block_map,
+ blk_itr, nbytes << 3,
+ buf);
+ if (retval)
+ break;
+
+ if (ext2fs_block_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+ continue;
+ pctx.group = i;
+ if (!fix_problem(ctx, PR_5_BLOCK_BITMAP_CSUM_INVALID, &pctx))
+ continue;
+
+ /*
+ * Fixing one checksum will rewrite all of them. The bitmap
+ * will be checked against the one we made during pass1 for
+ * discrepancies, and fixed if need be.
+ */
+ ext2fs_mark_bb_dirty(ctx->fs);
+ break;
+ }
+
+ ext2fs_free_mem(&buf);
+}
+
+static void e2fsck_discard_blocks(e2fsck_t ctx, blk64_t start,
+ blk64_t count)
+{
+ ext2_filsys fs = ctx->fs;
+
+ /*
+ * If the filesystem has changed it means that there was an corruption
+ * which should be repaired, but in some cases just one e2fsck run is
+ * not enough to fix the problem, hence it is not safe to run discard
+ * in this case.
+ */
+ if (ext2fs_test_changed(fs))
+ ctx->options &= ~E2F_OPT_DISCARD;
+
+ if ((ctx->options & E2F_OPT_DISCARD) &&
+ (io_channel_discard(fs->io, start, count)))
+ ctx->options &= ~E2F_OPT_DISCARD;
+}
+
+/*
+ * 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;