e2fsprogs/e2fsck/pass5.c
Andreas Dilger 102993ec1c e2fsck: fix overflow if more than 4B inodes
Even though we don't have support for filesystems with over 4B inodes
in the current e2fsprogs, this may happen in the future.  There are
latent overflow bugs when calculating the number of inodes in the
filesystem that can trivially be fixed now, rather than waiting for
them to be hit at some point in the future.  The block number calcs
are already correct in this code.

Signed-off-by: Andreas Dilger <adilger@dilger.ca>
Lustre-bug-id: https://jira.whamcloud.com/browse/LU-13197
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2020-02-29 18:34:54 -05:00

959 lines
26 KiB
C

/*
* pass5.c --- check block and inode bitmaps against on-disk bitmaps
*
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*
*/
#include "config.h"
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include "e2fsck.h"
#include "problem.h"
static void check_block_bitmaps(e2fsck_t ctx);
static void check_inode_bitmaps(e2fsck_t ctx);
static void check_inode_end(e2fsck_t ctx);
static void check_block_end(e2fsck_t ctx);
static void check_inode_bitmap_checksum(e2fsck_t ctx);
static void check_block_bitmap_checksum(e2fsck_t ctx);
void e2fsck_pass5(e2fsck_t ctx)
{
#ifdef RESOURCE_TRACK
struct resource_track rtrack;
#endif
struct problem_context pctx;
#ifdef MTRACE
mtrace_print("Pass 5");
#endif
init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&pctx);
if (!(ctx->options & E2F_OPT_PREEN))
fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
if (ctx->progress)
if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
return;
e2fsck_read_bitmaps(ctx);
check_block_bitmaps(ctx);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
check_inode_bitmaps(ctx);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
check_inode_end(ctx);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
check_block_end(ctx);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
check_inode_bitmap_checksum(ctx);
check_block_bitmap_checksum(ctx);
ext2fs_free_inode_bitmap(ctx->inode_used_map);
ctx->inode_used_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_dir_map);
ctx->inode_dir_map = 0;
ext2fs_free_block_bitmap(ctx->block_found_map);
ctx->block_found_map = 0;
ext2fs_free_block_bitmap(ctx->block_metadata_map);
ctx->block_metadata_map = 0;
print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
}
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;
/*
* 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 succinct */
#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))
static void check_block_bitmaps(e2fsck_t ctx)
{
ext2_filsys fs = ctx->fs;
blk64_t i;
unsigned int *free_array;
dgrp_t g, 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;
problem_t problem, save_problem;
int fixit, had_problem;
errcode_t retval;
int redo_flag = 0;
char *actual_buf, *bitmap_buf;
actual_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize,
"actual bitmap buffer");
bitmap_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize,
"bitmap block buffer");
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;
}
redo_counts:
had_problem = 0;
save_problem = 0;
pctx.blk = pctx.blk2 = NO_BLK;
for (i = B2C(fs->super->s_first_data_block);
i < ext2fs_blocks_count(fs->super);
i += EXT2FS_CLUSTER_RATIO(fs)) {
int first_block_in_bg = (B2C(i) -
B2C(fs->super->s_first_data_block)) %
fs->super->s_clusters_per_group == 0;
int n, nbytes = fs->super->s_clusters_per_group / 8;
actual = ext2fs_fast_test_block_bitmap2(ctx->block_found_map, i);
/*
* Try to optimize pass5 by extracting a bitmap block
* as expected from what we have on disk, and then
* comparing the two. If they are identical, then
* update the free block counts and go on to the next
* block group. This is much faster than doing the
* individual bit-by-bit comparison. The one downside
* is that this doesn't work if we are asking e2fsck
* to do a discard operation.
*/
if (!first_block_in_bg ||
(group == fs->group_desc_count - 1) ||
(ctx->options & E2F_OPT_DISCARD))
goto no_optimize;
retval = ext2fs_get_block_bitmap_range2(ctx->block_found_map,
B2C(i), fs->super->s_clusters_per_group,
actual_buf);
if (retval)
goto no_optimize;
retval = ext2fs_get_block_bitmap_range2(fs->block_map,
B2C(i), fs->super->s_clusters_per_group,
bitmap_buf);
if (retval)
goto no_optimize;
if (memcmp(actual_buf, bitmap_buf, nbytes) != 0)
goto no_optimize;
n = ext2fs_bitcount(actual_buf, nbytes);
group_free = fs->super->s_clusters_per_group - n;
free_blocks += group_free;
i += EXT2FS_C2B(fs, fs->super->s_clusters_per_group - 1);
goto next_group;
no_optimize:
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 (ext2fs_bg_flags_test(fs, group,
EXT2_BG_BLOCK_UNINIT)) {
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);
}
}
if (pctx.blk == NO_BLK) {
pctx.blk = pctx.blk2 = i;
save_problem = problem;
} else {
if ((problem == save_problem) &&
(pctx.blk2 == i - EXT2FS_CLUSTER_RATIO(fs)))
pctx.blk2 += EXT2FS_CLUSTER_RATIO(fs);
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);
next_group:
first_free = ext2fs_blocks_count(fs->super);
free_array[group] = group_free;
group ++;
blocks = 0;
group_free = 0;
if (ctx->progress)
if ((ctx->progress)(ctx, 5, group,
fs->group_desc_count*2))
goto errout;
}
}
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 (g = 0; g < fs->group_desc_count; g++) {
if (free_array[g] != ext2fs_bg_free_blocks_count(fs, g)) {
pctx.group = g;
pctx.blk = ext2fs_bg_free_blocks_count(fs, g);
pctx.blk2 = free_array[g];
if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
&pctx)) {
ext2fs_bg_free_blocks_count_set(fs, g, free_array[g]);
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);
ext2fs_free_mem(&actual_buf);
ext2fs_free_mem(&bitmap_buf);
}
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;
dgrp_t 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;
problem_t problem, save_problem;
int 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 = ext2fs_has_group_desc_csum(fs);
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);
}
static void check_inode_end(e2fsck_t ctx)
{
ext2_filsys fs = ctx->fs;
ext2_ino_t end, save_inodes_count, i;
struct problem_context pctx;
int asked = 0;
clear_problem_context(&pctx);
end = (__u64)EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
&save_inodes_count);
if (pctx.errcode) {
pctx.num = 1;
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
return;
}
if (save_inodes_count == end)
goto check_intra_bg_tail;
/* protect loop from wrap-around if end is maxed */
for (i = save_inodes_count + 1; i <= end && i > save_inodes_count; i++) {
if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
asked = 1;
if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
for (; i <= end; i++)
ext2fs_mark_inode_bitmap(fs->inode_map,
i);
ext2fs_mark_ib_dirty(fs);
} else
ext2fs_unmark_valid(fs);
break;
}
}
pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
save_inodes_count, 0);
if (pctx.errcode) {
pctx.num = 2;
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
return;
}
/*
* If the number of inodes per block group != blocksize, we
* can also have a potential problem with the tail bits in
* each individual inode bitmap block. If there is a problem,
* it would have been noticed when the bitmap was loaded. And
* fixing this is easy; all we need to do force the bitmap to
* be written back to disk.
*/
check_intra_bg_tail:
if (!asked && fs->flags & EXT2_FLAG_IBITMAP_TAIL_PROBLEM) {
if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx))
ext2fs_mark_ib_dirty(fs);
else
ext2fs_unmark_valid(fs);
}
}
static void check_block_end(e2fsck_t ctx)
{
ext2_filsys fs = ctx->fs;
blk64_t end, save_blocks_count, i;
struct problem_context pctx;
int asked = 0;
clear_problem_context(&pctx);
end = ext2fs_get_block_bitmap_start2(fs->block_map) +
EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count) - 1;
pctx.errcode = ext2fs_fudge_block_bitmap_end2(fs->block_map, end,
&save_blocks_count);
if (pctx.errcode) {
pctx.num = 3;
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
return;
}
if (save_blocks_count == end)
goto check_intra_bg_tail;
/* Protect loop from wrap-around if end is maxed */
for (i = save_blocks_count + 1; i <= end && i > save_blocks_count; i++) {
if (!ext2fs_test_block_bitmap2(fs->block_map,
EXT2FS_C2B(fs, i))) {
asked = 1;
if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
for (; i <= end; i++)
ext2fs_mark_block_bitmap2(fs->block_map,
EXT2FS_C2B(fs, i));
ext2fs_mark_bb_dirty(fs);
} else
ext2fs_unmark_valid(fs);
break;
}
}
pctx.errcode = ext2fs_fudge_block_bitmap_end2(fs->block_map,
save_blocks_count, 0);
if (pctx.errcode) {
pctx.num = 4;
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
return;
}
/*
* If the number of blocks per block group != blocksize, we
* can also have a potential problem with the tail bits in
* each individual block bitmap block. If there is a problem,
* it would have been noticed when the bitmap was loaded. And
* fixing this is easy; all we need to do force the bitmap to
* be written back to disk.
*/
check_intra_bg_tail:
if (!asked && fs->flags & EXT2_FLAG_BBITMAP_TAIL_PROBLEM) {
if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx))
ext2fs_mark_bb_dirty(fs);
else
ext2fs_unmark_valid(fs);
}
}