mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2024-12-03 15:03:59 +08:00
d5d981a369
scan_extent_node() did cluster alignment check for every block in an extent. This is unnecessary and significantly slows down the runtime when hugefile is used with bigalloc. Do cluster alignment check once for each extent. Google-Bug-Id: 36886699 Signed-off-by: Tahsin Erdogan <tahsin@google.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
928 lines
25 KiB
C
928 lines
25 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 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))
|
|
|
|
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;
|
|
|
|
clear_problem_context(&pctx);
|
|
|
|
end = 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)
|
|
return;
|
|
|
|
/* 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)) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void check_block_end(e2fsck_t ctx)
|
|
{
|
|
ext2_filsys fs = ctx->fs;
|
|
blk64_t end, save_blocks_count, i;
|
|
struct problem_context pctx;
|
|
|
|
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)
|
|
return;
|
|
|
|
/* 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))) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
|