+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;
+
+ /*
+ * 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);
+}
+
+#define NO_BLK ((blk64_t) -1)
+
+static void print_bitmap_problem(e2fsck_t ctx, problem_t problem,
+ struct problem_context *pctx)
+{
+ switch (problem) {
+ case PR_5_BLOCK_UNUSED:
+ if (pctx->blk == pctx->blk2)
+ pctx->blk2 = 0;
+ else
+ problem = PR_5_BLOCK_RANGE_UNUSED;
+ break;
+ case PR_5_BLOCK_USED:
+ if (pctx->blk == pctx->blk2)
+ pctx->blk2 = 0;
+ else
+ problem = PR_5_BLOCK_RANGE_USED;
+ break;
+ case PR_5_INODE_UNUSED:
+ if (pctx->ino == pctx->ino2)
+ pctx->ino2 = 0;
+ else
+ problem = PR_5_INODE_RANGE_UNUSED;
+ break;
+ case PR_5_INODE_USED:
+ if (pctx->ino == pctx->ino2)
+ pctx->ino2 = 0;
+ else
+ problem = PR_5_INODE_RANGE_USED;
+ break;
+ }
+ fix_problem(ctx, problem, pctx);
+ pctx->blk = pctx->blk2 = NO_BLK;
+ pctx->ino = pctx->ino2 = 0;
+}
+
+/* Just to be more succint */
+#define B2C(x) EXT2FS_B2C(fs, (x))
+#define EQ_CLSTR(x, y) (B2C(x) == B2C(y))
+#define LE_CLSTR(x, y) (B2C(x) <= B2C(y))
+#define GE_CLSTR(x, y) (B2C(x) >= B2C(y))
+