/* * This routine is called when an inode is not connected to the * directory tree. * * This subroutine returns 1 then the caller shouldn't bother with the * rest of the pass 4 tests. */ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, struct ext2_inode *inode) { ext2_filsys fs = ctx->fs; struct problem_context pctx; __u32 eamagic = 0; int extra_size = 0; if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) { e2fsck_read_inode_full(ctx, i, inode,EXT2_INODE_SIZE(fs->super), "pass4: disconnect_inode"); extra_size = ((struct ext2_inode_large *)inode)->i_extra_isize; } else { e2fsck_read_inode(ctx, i, inode, "pass4: disconnect_inode"); } clear_problem_context(&pctx); pctx.ino = i; pctx.inode = inode; if (EXT2_INODE_SIZE(fs->super) -EXT2_GOOD_OLD_INODE_SIZE -extra_size >0) eamagic = *(__u32 *)(((char *)inode) +EXT2_GOOD_OLD_INODE_SIZE + extra_size); /* * Offer to delete any zero-length files that does not have * blocks. If there is an EA block, it might have useful * information, so we won't prompt to delete it, but let it be * reconnected to lost+found. */ if (!inode->i_blocks && eamagic != EXT2_EXT_ATTR_MAGIC && (LINUX_S_ISREG(inode->i_mode) || LINUX_S_ISDIR(inode->i_mode))) { if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) { e2fsck_clear_inode(ctx, i, inode, 0, "disconnect_inode"); /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_inode_alloc_stats2(fs, i, -1, LINUX_S_ISDIR(inode->i_mode)); return 0; } } /* * Prompt to reconnect. */ if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) { if (e2fsck_reconnect_file(ctx, i)) ext2fs_unmark_valid(fs); } else { /* * If we don't attach the inode, then skip the * i_links_test since there's no point in trying to * force i_links_count to zero. */ ext2fs_unmark_valid(fs); return 1; } return 0; }
/* * This routine is called when an inode is not connected to the * directory tree. * * This subroutine returns 1 then the caller shouldn't bother with the * rest of the pass 4 tests. */ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode"); clear_problem_context(&pctx); pctx.ino = i; pctx.inode = &inode; /* * Offer to delete any zero-length files that does not have * blocks. If there is an EA block, it might have useful * information, so we won't prompt to delete it, but let it be * reconnected to lost+found. */ if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) || LINUX_S_ISDIR(inode.i_mode))) { if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) { ext2fs_icount_store(ctx->inode_link_info, i, 0); inode.i_links_count = 0; inode.i_dtime = ctx->now; e2fsck_write_inode(ctx, i, &inode, "disconnect_inode"); /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i); ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i); ext2fs_inode_alloc_stats2(fs, i, -1, LINUX_S_ISDIR(inode.i_mode)); return 0; } } /* * Prompt to reconnect. */ if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) { if (e2fsck_reconnect_file(ctx, i)) ext2fs_unmark_valid(fs); } else { /* * If we don't attach the inode, then skip the * i_links_test since there's no point in trying to * force i_links_count to zero. */ ext2fs_unmark_valid(fs); return 1; } return 0; }
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) + ((blk64_t)EXT2_CLUSTERS_PER_GROUP(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; } }
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; } }
int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx) { ext2_filsys fs = ctx->fs; const struct e2fsck_problem *ptr; struct latch_descr *ldesc = 0; const char *message; int def_yn, answer, ans; int print_answer = 0; int suppress = 0; ptr = find_problem(code); if (!ptr) { printf(_("Unhandled error code (%d)!\n"), code); return 0; } def_yn = 1; if ((ptr->flags & PR_NO_DEFAULT) || ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) || (ctx->options & E2F_OPT_NO)) def_yn= 0; /* * Do special latch processing. This is where we ask the * latch question, if it exists */ if (ptr->flags & PR_LATCH_MASK) { ldesc = find_latch(ptr->flags & PR_LATCH_MASK); if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) { ans = fix_problem(ctx, ldesc->question, pctx); if (ans == 1) ldesc->flags |= PRL_YES; if (ans == 0) ldesc->flags |= PRL_NO; ldesc->flags |= PRL_LATCHED; } if (ldesc->flags & PRL_SUPPRESS) suppress++; } if ((ptr->flags & PR_PREEN_NOMSG) && (ctx->options & E2F_OPT_PREEN)) suppress++; if ((ptr->flags & PR_NO_NOMSG) && (ctx->options & E2F_OPT_NO)) suppress++; if (!suppress) { message = ptr->e2p_description; if (ctx->options & E2F_OPT_PREEN) { printf("%s: ", ctx->device_name); #if 0 if (ptr->e2p_preen_msg) message = ptr->e2p_preen_msg; #endif } print_e2fsck_message(ctx, _(message), pctx, 1); } if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE)) preenhalt(ctx); if (ptr->flags & PR_FATAL) fatal_error(ctx, 0); if (ptr->prompt == PROMPT_NONE) { if (ptr->flags & PR_NOCOLLATE) answer = -1; else answer = def_yn; } else { if (ctx->options & E2F_OPT_PREEN) { answer = def_yn; if (!(ptr->flags & PR_PREEN_NOMSG)) print_answer = 1; } else if ((ptr->flags & PR_LATCH_MASK) && (ldesc->flags & (PRL_YES | PRL_NO))) { if (!suppress) print_answer = 1; if (ldesc->flags & PRL_YES) answer = 1; else answer = 0; } else answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn); if (!answer && !(ptr->flags & PR_NO_OK)) ext2fs_unmark_valid(fs); if (print_answer) printf("%s.\n", answer ? _(preen_msg[(int) ptr->prompt]) : _("IGNORED")); } if ((ptr->prompt == PROMPT_ABORT) && answer) fatal_error(ctx, 0); if (ptr->flags & PR_AFTER_CODE) answer = fix_problem(ctx, ptr->second_code, pctx); return answer; }
/* * This subroutine is responsible for making sure that a particular * directory is connected to the root; if it isn't we trace it up as * far as we can go, and then offer to connect the resulting parent to * the lost+found. We have to do loop detection; if we ever discover * a loop, we treat that as a disconnected directory and offer to * reparent it to lost+found. * * However, loop detection is expensive, because for very large * filesystems, the inode_loop_detect bitmap is huge, and clearing it * is non-trivial. Loops in filesystems are also a rare error case, * and we shouldn't optimize for error cases. So we try two passes of * the algorithm. The first time, we ignore loop detection and merely * increment a counter; if the counter exceeds some extreme threshold, * then we try again with the loop detection bitmap enabled. */ static int check_directory(e2fsck_t ctx, ext2_ino_t dir, struct problem_context *pctx) { ext2_filsys fs = ctx->fs; ext2_ino_t ino = dir, parent; int loop_pass = 0, parent_count = 0; while (1) { /* * Mark this inode as being "done"; by the time we * return from this function, the inode we either be * verified as being connected to the directory tree, * or we will have offered to reconnect this to * lost+found. * * If it was marked done already, then we've reached a * parent we've already checked. */ if (ext2fs_mark_inode_bitmap2(inode_done_map, ino)) break; if (e2fsck_dir_info_get_parent(ctx, ino, &parent)) { fix_problem(ctx, PR_3_NO_DIRINFO, pctx); return 0; } /* * If this directory doesn't have a parent, or we've * seen the parent once already, then offer to * reparent it to lost+found */ if (!parent || (loop_pass && (ext2fs_test_inode_bitmap2(inode_loop_detect, parent)))) { pctx->ino = ino; if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) { if (e2fsck_reconnect_file(ctx, pctx->ino)) ext2fs_unmark_valid(fs); else { fix_dotdot(ctx, pctx->ino, ctx->lost_and_found); parent = ctx->lost_and_found; } } break; } ino = parent; if (loop_pass) { ext2fs_mark_inode_bitmap2(inode_loop_detect, ino); } else if (parent_count++ > 2048) { /* * If we've run into a path depth that's * greater than 2048, try again with the inode * loop bitmap turned on and start from the * top. */ loop_pass = 1; if (inode_loop_detect) ext2fs_clear_inode_bitmap(inode_loop_detect); else { pctx->errcode = e2fsck_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), EXT2FS_BMAP64_AUTODIR, "inode_loop_detect", &inode_loop_detect); if (pctx->errcode) { pctx->num = 1; fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, pctx); ctx->flags |= E2F_FLAG_ABORT; return -1; } } ino = dir; } } /* * Make sure that .. and the parent directory are the same; * offer to fix it if not. */ pctx->ino = dir; if (e2fsck_dir_info_get_dotdot(ctx, dir, &pctx->ino2) || e2fsck_dir_info_get_parent(ctx, dir, &pctx->dir)) { fix_problem(ctx, PR_3_NO_DIRINFO, pctx); return 0; } if (pctx->ino2 != pctx->dir) { if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx)) fix_dotdot(ctx, dir, pctx->dir); } return 0; }
static int check_dir_block(ext2_filsys fs, struct ext2_db_entry2 *db, void *priv_data) { struct dx_dir_info *dx_dir; #ifdef ENABLE_HTREE struct dx_dirblock_info *dx_db = 0; #endif /* ENABLE_HTREE */ struct ext2_dir_entry *dirent, *prev; ext2_dirhash_t hash; unsigned int offset = 0; int dir_modified = 0; int dot_state; unsigned int rec_len; blk64_t block_nr = db->blk; ext2_ino_t ino = db->ino; ext2_ino_t subdir_parent; __u16 links; struct check_dir_struct *cd; char *buf; e2fsck_t ctx; int problem; struct ext2_dx_root_info *root; struct ext2_dx_countlimit *limit; static dict_t de_dict; struct problem_context pctx; int dups_found = 0; int ret; int dx_csum_size = 0, de_csum_size = 0; int failed_csum = 0; int is_leaf = 1; cd = (struct check_dir_struct *) priv_data; buf = cd->buf; ctx = cd->ctx; if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) return DIRENT_ABORT; if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) return DIRENT_ABORT; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { dx_csum_size = sizeof(struct ext2_dx_tail); de_csum_size = sizeof(struct ext2_dir_entry_tail); } /* * Make sure the inode is still in use (could have been * deleted in the duplicate/bad blocks pass. */ if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, ino))) return 0; cd->pctx.ino = ino; cd->pctx.blk = block_nr; cd->pctx.blkcount = db->blockcnt; cd->pctx.ino2 = 0; cd->pctx.dirent = 0; cd->pctx.num = 0; if (db->blk == 0) { if (allocate_dir_block(ctx, db, buf, &cd->pctx)) return 0; block_nr = db->blk; } if (db->blockcnt) dot_state = 2; else dot_state = 0; if (ctx->dirs_to_hash && ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) dups_found++; #if 0 printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, db->blockcnt, ino); #endif ehandler_operation(_("reading directory block")); cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino); ehandler_operation(0); if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) cd->pctx.errcode = 0; /* We'll handle this ourselves */ else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) { cd->pctx.errcode = 0; /* We'll handle this ourselves */ failed_csum = 1; } if (cd->pctx.errcode) { char *buf2; if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0, EXT2_ROOT_INO, &buf2); memcpy(buf, buf2, fs->blocksize); ext2fs_free_mem(&buf2); } #ifdef ENABLE_HTREE dx_dir = e2fsck_get_dx_dir_info(ctx, ino); if (dx_dir && dx_dir->numblocks) { if (db->blockcnt >= dx_dir->numblocks) { if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK, &pctx)) { clear_htree(ctx, ino); dx_dir->numblocks = 0; dx_db = 0; goto out_htree; } fatal_error(ctx, _("Can not continue.")); } dx_db = &dx_dir->dx_block[db->blockcnt]; dx_db->type = DX_DIRBLOCK_LEAF; dx_db->phys = block_nr; dx_db->min_hash = ~0; dx_db->max_hash = 0; dirent = (struct ext2_dir_entry *) buf; (void) ext2fs_get_rec_len(fs, dirent, &rec_len); limit = (struct ext2_dx_countlimit *) (buf+8); if (db->blockcnt == 0) { root = (struct ext2_dx_root_info *) (buf + 24); dx_db->type = DX_DIRBLOCK_ROOT; dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; if ((root->reserved_zero || root->info_length < 8 || root->indirect_levels > 1) && fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { clear_htree(ctx, ino); dx_dir->numblocks = 0; dx_db = 0; } dx_dir->hashversion = root->hash_version; if ((dx_dir->hashversion <= EXT2_HASH_TEA) && (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) dx_dir->hashversion += 3; dx_dir->depth = root->indirect_levels + 1; } else if ((dirent->inode == 0) && (rec_len == fs->blocksize) && (dirent->name_len == 0) && (ext2fs_le16_to_cpu(limit->limit) == ((fs->blocksize - (8 + dx_csum_size)) / sizeof(struct ext2_dx_entry)))) dx_db->type = DX_DIRBLOCK_NODE; is_leaf = 0; } out_htree: #endif /* ENABLE_HTREE */ /* Verify checksum. */ if (is_leaf && de_csum_size) { /* No space for csum? Rebuild dirs in pass 3A. */ if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) { de_csum_size = 0; if (e2fsck_dir_will_be_rehashed(ctx, ino)) goto skip_checksum; if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM, &cd->pctx)) goto skip_checksum; e2fsck_rehash_dir_later(ctx, ino); goto skip_checksum; } if (failed_csum) { char *buf2; if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID, &cd->pctx)) goto skip_checksum; ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0, EXT2_ROOT_INO, &buf2); memcpy(buf, buf2, fs->blocksize); ext2fs_free_mem(&buf2); dir_modified++; failed_csum = 0; } } /* htree nodes don't use fake dirents to store checksums */ if (!is_leaf) de_csum_size = 0; skip_checksum: dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); prev = 0; do { int group; ext2_ino_t first_unused_inode; problem = 0; dirent = (struct ext2_dir_entry *) (buf + offset); (void) ext2fs_get_rec_len(fs, dirent, &rec_len); cd->pctx.dirent = dirent; cd->pctx.num = offset; if (((offset + rec_len) > fs->blocksize) || (rec_len < 12) || ((rec_len % 4) != 0) || (((dirent->name_len & (unsigned) 0xFF)+8) > rec_len)) { if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { salvage_directory(fs, dirent, prev, &offset); dir_modified++; continue; } else goto abort_free_dict; } if (dot_state == 0) { if (check_dot(ctx, dirent, ino, &cd->pctx)) dir_modified++; } else if (dot_state == 1) { ret = check_dotdot(ctx, dirent, ino, &cd->pctx); if (ret < 0) goto abort_free_dict; if (ret) dir_modified++; } else if (dirent->inode == ino) { problem = PR_2_LINK_DOT; if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } } if (!dirent->inode) goto next; /* * Make sure the inode listed is a legal one. */ if (((dirent->inode != EXT2_ROOT_INO) && (dirent->inode < EXT2_FIRST_INODE(fs->super))) || (dirent->inode > fs->super->s_inodes_count)) { problem = PR_2_BAD_INO; } else if (ctx->inode_bb_map && (ext2fs_test_inode_bitmap2(ctx->inode_bb_map, dirent->inode))) { /* * If the inode is in a bad block, offer to * clear it. */ problem = PR_2_BB_INODE; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.')) { /* * If there's a '.' entry in anything other * than the first directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { /* * If there's a '..' entry in anything other * than the second directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT_DOT; } else if ((dot_state > 1) && (dirent->inode == EXT2_ROOT_INO)) { /* * Don't allow links to the root directory. * We check this specially to make sure we * catch this error case even if the root * directory hasn't been created yet. */ problem = PR_2_LINK_ROOT; } else if ((dot_state > 1) && (dirent->name_len & 0xFF) == 0) { /* * Don't allow zero-length directory names. */ problem = PR_2_NULL_NAME; } if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } /* * If the inode was marked as having bad fields in * pass1, process it and offer to fix/clear it. * (We wait until now so that we can display the * pathname to the user.) */ if (ctx->inode_bad_map && ext2fs_test_inode_bitmap2(ctx->inode_bad_map, dirent->inode)) { if (e2fsck_process_bad_inode(ctx, ino, dirent->inode, buf + fs->blocksize)) { dirent->inode = 0; dir_modified++; goto next; } if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return DIRENT_ABORT; } group = ext2fs_group_of_ino(fs, dirent->inode); first_unused_inode = group * fs->super->s_inodes_per_group + 1 + fs->super->s_inodes_per_group - ext2fs_bg_itable_unused(fs, group); cd->pctx.group = group; /* * Check if the inode was missed out because * _INODE_UNINIT flag was set or bg_itable_unused was * incorrect. If so, clear the _INODE_UNINIT flag and * restart e2fsck. In the future it would be nice if * we could call a function in pass1.c that checks the * newly visible inodes. */ if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)) { pctx.num = dirent->inode; if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT, &cd->pctx)){ ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); ext2fs_mark_super_dirty(fs); ctx->flags |= E2F_FLAG_RESTART_LATER; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } else if (dirent->inode >= first_unused_inode) { pctx.num = dirent->inode; if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){ ext2fs_bg_itable_unused_set(fs, group, 0); ext2fs_mark_super_dirty(fs); ctx->flags |= E2F_FLAG_RESTART_LATER; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } /* * Offer to clear unused inodes; if we are going to be * restarting the scan due to bg_itable_unused being * wrong, then don't clear any inodes to avoid zapping * inodes that were skipped during pass1 due to an * incorrect bg_itable_unused; we'll get any real * problems after we restart. */ if (!(ctx->flags & E2F_FLAG_RESTART_LATER) && !(ext2fs_test_inode_bitmap2(ctx->inode_used_map, dirent->inode))) problem = PR_2_UNUSED_INODE; if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } if (check_name(ctx, dirent, ino, &cd->pctx)) dir_modified++; if (check_filetype(ctx, dirent, ino, &cd->pctx)) dir_modified++; #ifdef ENABLE_HTREE if (dx_db) { ext2fs_dirhash(dx_dir->hashversion, dirent->name, (dirent->name_len & 0xFF), fs->super->s_hash_seed, &hash, 0); if (hash < dx_db->min_hash) dx_db->min_hash = hash; if (hash > dx_db->max_hash) dx_db->max_hash = hash; } #endif /* * If this is a directory, then mark its parent in its * dir_info structure. If the parent field is already * filled in, then this directory has more than one * hard link. We assume the first link is correct, * and ask the user if he/she wants to clear this one. */ if ((dot_state > 1) && (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode))) { if (e2fsck_dir_info_get_parent(ctx, dirent->inode, &subdir_parent)) { cd->pctx.ino = dirent->inode; fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); goto abort_free_dict; } if (subdir_parent) { cd->pctx.ino2 = subdir_parent; if (fix_problem(ctx, PR_2_LINK_DIR, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } cd->pctx.ino2 = 0; } else { (void) e2fsck_dir_info_set_parent(ctx, dirent->inode, ino); } } if (dups_found) { ; } else if (dict_lookup(&de_dict, dirent)) { clear_problem_context(&pctx); pctx.ino = ino; pctx.dirent = dirent; fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); e2fsck_rehash_dir_later(ctx, ino); dups_found++; } else dict_alloc_insert(&de_dict, dirent, dirent); ext2fs_icount_increment(ctx->inode_count, dirent->inode, &links); if (links > 1) ctx->fs_links_count++; ctx->fs_total_count++; next: prev = dirent; if (dir_modified) (void) ext2fs_get_rec_len(fs, dirent, &rec_len); offset += rec_len; dot_state++; } while (offset < fs->blocksize - de_csum_size); #if 0 printf("\n"); #endif #ifdef ENABLE_HTREE if (dx_db) { #ifdef DX_DEBUG printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n", db->blockcnt, dx_db->type, dx_db->min_hash, dx_db->max_hash); #endif cd->pctx.dir = cd->pctx.ino; if ((dx_db->type == DX_DIRBLOCK_ROOT) || (dx_db->type == DX_DIRBLOCK_NODE)) parse_int_node(fs, db, cd, dx_dir, buf, failed_csum); } #endif /* ENABLE_HTREE */ if (offset != fs->blocksize - de_csum_size) { cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) + offset; if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { dirent->rec_len = cd->pctx.num; dir_modified++; } } if (dir_modified) { /* leaf block with no tail? Rehash dirs later. */ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && is_leaf && !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) e2fsck_rehash_dir_later(ctx, ino); write_and_fix: if (e2fsck_dir_will_be_rehashed(ctx, ino)) ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf, 0, ino); if (e2fsck_dir_will_be_rehashed(ctx, ino)) ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) goto abort_free_dict; } ext2fs_mark_changed(fs); } else if (is_leaf && failed_csum && !dir_modified) { /* * If a leaf node that fails csum makes it this far without * alteration, ask the user if the checksum should be fixed. */ if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID, &cd->pctx)) goto write_and_fix; } dict_free_nodes(&de_dict); return 0; abort_free_dict: ctx->flags |= E2F_FLAG_ABORT; dict_free_nodes(&de_dict); return DIRENT_ABORT; }
static int check_dir_block(ext2_filsys fs, struct ext2_db_entry *db, void *priv_data) { struct dx_dir_info *dx_dir; #ifdef ENABLE_HTREE struct dx_dirblock_info *dx_db = 0; #endif struct ext2_dir_entry *dirent, *prev; ext2_dirhash_t hash; unsigned int offset = 0; const char * old_op; int dir_modified = 0; int dot_state; unsigned int rec_len; blk_t block_nr = db->blk; ext2_ino_t ino = db->ino; ext2_ino_t subdir_parent; __u16 links; struct check_dir_struct *cd; char *buf; e2fsck_t ctx; int problem; struct ext2_dx_root_info *root; struct ext2_dx_countlimit *limit; static dict_t de_dict; struct problem_context pctx; int dups_found = 0; int ret; cd = (struct check_dir_struct *) priv_data; buf = cd->buf; ctx = cd->ctx; if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) return DIRENT_ABORT; if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) return DIRENT_ABORT; if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))) return 0; cd->pctx.ino = ino; cd->pctx.blk = block_nr; cd->pctx.blkcount = db->blockcnt; cd->pctx.ino2 = 0; cd->pctx.dirent = 0; cd->pctx.num = 0; if (db->blk == 0) { if (allocate_dir_block(ctx, db, buf, &cd->pctx)) return 0; block_nr = db->blk; } if (db->blockcnt) dot_state = 2; else dot_state = 0; if (ctx->dirs_to_hash && ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) dups_found++; #if 0 printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, db->blockcnt, ino); #endif old_op = ehandler_operation(_("reading directory block")); cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf); ehandler_operation(0); if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) cd->pctx.errcode = 0; if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } memset(buf, 0, fs->blocksize); } #ifdef ENABLE_HTREE dx_dir = e2fsck_get_dx_dir_info(ctx, ino); if (dx_dir && dx_dir->numblocks) { if (db->blockcnt >= dx_dir->numblocks) { if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK, &pctx)) { clear_htree(ctx, ino); dx_dir->numblocks = 0; dx_db = 0; goto out_htree; } fatal_error(ctx, _("Can not continue.")); } dx_db = &dx_dir->dx_block[db->blockcnt]; dx_db->type = DX_DIRBLOCK_LEAF; dx_db->phys = block_nr; dx_db->min_hash = ~0; dx_db->max_hash = 0; dirent = (struct ext2_dir_entry *) buf; (void) ext2fs_get_rec_len(fs, dirent, &rec_len); limit = (struct ext2_dx_countlimit *) (buf+8); if (db->blockcnt == 0) { root = (struct ext2_dx_root_info *) (buf + 24); dx_db->type = DX_DIRBLOCK_ROOT; dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; if ((root->reserved_zero || root->info_length < 8 || root->indirect_levels > 1) && fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { clear_htree(ctx, ino); dx_dir->numblocks = 0; dx_db = 0; } dx_dir->hashversion = root->hash_version; if ((dx_dir->hashversion <= EXT2_HASH_TEA) && (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) dx_dir->hashversion += 3; dx_dir->depth = root->indirect_levels + 1; } else if ((dirent->inode == 0) && (rec_len == fs->blocksize) && (dirent->name_len == 0) && (ext2fs_le16_to_cpu(limit->limit) == ((fs->blocksize-8) / sizeof(struct ext2_dx_entry)))) dx_db->type = DX_DIRBLOCK_NODE; } out_htree: #endif dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); prev = 0; do { int group; ext2_ino_t first_unused_inode; problem = 0; dirent = (struct ext2_dir_entry *) (buf + offset); (void) ext2fs_get_rec_len(fs, dirent, &rec_len); cd->pctx.dirent = dirent; cd->pctx.num = offset; if (((offset + rec_len) > fs->blocksize) || (rec_len < 12) || ((rec_len % 4) != 0) || (((dirent->name_len & (unsigned) 0xFF)+8) > rec_len)) { if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { salvage_directory(fs, dirent, prev, &offset); dir_modified++; continue; } else goto abort_free_dict; } if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) { if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) { dirent->name_len = EXT2_NAME_LEN; dir_modified++; } } if (dot_state == 0) { if (check_dot(ctx, dirent, ino, &cd->pctx)) dir_modified++; } else if (dot_state == 1) { ret = check_dotdot(ctx, dirent, ino, &cd->pctx); if (ret < 0) goto abort_free_dict; if (ret) dir_modified++; } else if (dirent->inode == ino) { problem = PR_2_LINK_DOT; if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } } if (!dirent->inode) goto next; if (((dirent->inode != EXT2_ROOT_INO) && (dirent->inode < EXT2_FIRST_INODE(fs->super))) || (dirent->inode > fs->super->s_inodes_count)) { problem = PR_2_BAD_INO; } else if (ctx->inode_bb_map && (ext2fs_test_inode_bitmap(ctx->inode_bb_map, dirent->inode))) { problem = PR_2_BB_INODE; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.')) { problem = PR_2_DUP_DOT; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { problem = PR_2_DUP_DOT_DOT; } else if ((dot_state > 1) && (dirent->inode == EXT2_ROOT_INO)) { problem = PR_2_LINK_ROOT; } else if ((dot_state > 1) && (dirent->name_len & 0xFF) == 0) { problem = PR_2_NULL_NAME; } if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } if (ctx->inode_bad_map && ext2fs_test_inode_bitmap(ctx->inode_bad_map, dirent->inode)) { if (e2fsck_process_bad_inode(ctx, ino, dirent->inode, buf + fs->blocksize)) { dirent->inode = 0; dir_modified++; goto next; } if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return DIRENT_ABORT; } group = ext2fs_group_of_ino(fs, dirent->inode); first_unused_inode = group * fs->super->s_inodes_per_group + 1 + fs->super->s_inodes_per_group - fs->group_desc[group].bg_itable_unused; cd->pctx.group = group; if (fs->group_desc[group].bg_flags & EXT2_BG_INODE_UNINIT) { pctx.num = dirent->inode; if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT, &cd->pctx)){ fs->group_desc[group].bg_flags &= ~EXT2_BG_INODE_UNINIT; ext2fs_mark_super_dirty(fs); ctx->flags |= E2F_FLAG_RESTART_LATER; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } else if (dirent->inode >= first_unused_inode) { pctx.num = dirent->inode; if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){ fs->group_desc[group].bg_itable_unused = 0; ext2fs_mark_super_dirty(fs); ctx->flags |= E2F_FLAG_RESTART_LATER; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } if (!(ctx->flags & E2F_FLAG_RESTART_LATER) && !(ext2fs_test_inode_bitmap(ctx->inode_used_map, dirent->inode))) problem = PR_2_UNUSED_INODE; if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } if (check_name(ctx, dirent, ino, &cd->pctx)) dir_modified++; if (check_filetype(ctx, dirent, ino, &cd->pctx)) dir_modified++; #ifdef ENABLE_HTREE if (dx_db) { ext2fs_dirhash(dx_dir->hashversion, dirent->name, (dirent->name_len & 0xFF), fs->super->s_hash_seed, &hash, 0); if (hash < dx_db->min_hash) dx_db->min_hash = hash; if (hash > dx_db->max_hash) dx_db->max_hash = hash; } #endif if ((dot_state > 1) && (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode))) { if (e2fsck_dir_info_get_parent(ctx, dirent->inode, &subdir_parent)) { cd->pctx.ino = dirent->inode; fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); goto abort_free_dict; } if (subdir_parent) { cd->pctx.ino2 = subdir_parent; if (fix_problem(ctx, PR_2_LINK_DIR, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } cd->pctx.ino2 = 0; } else { (void) e2fsck_dir_info_set_parent(ctx, dirent->inode, ino); } } if (dups_found) { ; } else if (dict_lookup(&de_dict, dirent)) { clear_problem_context(&pctx); pctx.ino = ino; pctx.dirent = dirent; fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); if (!ctx->dirs_to_hash) ext2fs_u32_list_create(&ctx->dirs_to_hash, 50); if (ctx->dirs_to_hash) ext2fs_u32_list_add(ctx->dirs_to_hash, ino); dups_found++; } else dict_alloc_insert(&de_dict, dirent, dirent); ext2fs_icount_increment(ctx->inode_count, dirent->inode, &links); if (links > 1) ctx->fs_links_count++; ctx->fs_total_count++; next: prev = dirent; if (dir_modified) (void) ext2fs_get_rec_len(fs, dirent, &rec_len); offset += rec_len; dot_state++; } while (offset < fs->blocksize); #if 0 printf("\n"); #endif #ifdef ENABLE_HTREE if (dx_db) { #ifdef DX_DEBUG printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n", db->blockcnt, dx_db->type, dx_db->min_hash, dx_db->max_hash); #endif cd->pctx.dir = cd->pctx.ino; if ((dx_db->type == DX_DIRBLOCK_ROOT) || (dx_db->type == DX_DIRBLOCK_NODE)) parse_int_node(fs, db, cd, dx_dir, buf); } #endif if (offset != fs->blocksize) { cd->pctx.num = rec_len - fs->blocksize + offset; if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { dirent->rec_len = cd->pctx.num; dir_modified++; } } if (dir_modified) { cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf); if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) goto abort_free_dict; } ext2fs_mark_changed(fs); } dict_free_nodes(&de_dict); return 0; abort_free_dict: ctx->flags |= E2F_FLAG_ABORT; dict_free_nodes(&de_dict); return DIRENT_ABORT; }
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; int 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; int problem, save_problem, 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 = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM); 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_block_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk64_t i; unsigned int *free_array; int 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; int problem, save_problem, fixit, had_problem; errcode_t retval; int csum_flag; int skip_group = 0; int old_desc_blocks = 0; int count = 0; int cmp_block = 0; int redo_flag = 0; blk64_t super_blk, old_desc_blk, new_desc_blk; 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; } csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM); redo_counts: had_problem = 0; save_problem = 0; pctx.blk = pctx.blk2 = NO_BLK; if (csum_flag && (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) skip_group++; for (i = B2C(fs->super->s_first_data_block); i < ext2fs_blocks_count(fs->super); i += EXT2FS_CLUSTER_RATIO(fs)) { actual = ext2fs_fast_test_block_bitmap2(ctx->block_found_map, i); if (skip_group) { if ((B2C(i) - B2C(fs->super->s_first_data_block)) % fs->super->s_clusters_per_group == 0) { super_blk = 0; old_desc_blk = 0; new_desc_blk = 0; ext2fs_super_and_bgd_loc2(fs, group, &super_blk, &old_desc_blk, &new_desc_blk, 0); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; count = 0; cmp_block = fs->super->s_clusters_per_group; if (group == (int)fs->group_desc_count - 1) cmp_block = EXT2FS_NUM_B2C(fs, ext2fs_group_blocks_count(fs, group)); } bitmap = 0; if (EQ_CLSTR(i, super_blk) || (old_desc_blk && old_desc_blocks && GE_CLSTR(i, old_desc_blk) && LE_CLSTR(i, old_desc_blk + old_desc_blocks-1)) || (new_desc_blk && EQ_CLSTR(i, new_desc_blk)) || EQ_CLSTR(i, ext2fs_block_bitmap_loc(fs, group)) || EQ_CLSTR(i, ext2fs_inode_bitmap_loc(fs, group)) || (GE_CLSTR(i, ext2fs_inode_table_loc(fs, group)) && LE_CLSTR(i, (ext2fs_inode_table_loc(fs, group) + fs->inode_blocks_per_group - 1)))) { bitmap = 1; actual = (actual != 0); count++; cmp_block--; } else if ((EXT2FS_B2C(fs, i) - count - EXT2FS_B2C(fs, fs->super->s_first_data_block)) % fs->super->s_clusters_per_group == 0) { /* * When the compare data blocks in block bitmap * are 0, count the free block, * skip the current block group. */ if (ext2fs_test_block_bitmap_range2( ctx->block_found_map, EXT2FS_B2C(fs, i), cmp_block)) { /* * -1 means to skip the current block * group. */ blocks = fs->super->s_clusters_per_group - 1; group_free = cmp_block; free_blocks += cmp_block; /* * The current block group's last block * is set to i. */ i += EXT2FS_C2B(fs, cmp_block - 1); bitmap = 1; goto do_counts; } } } else 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 (skip_group) { 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); skip_group = 0; } } } if (pctx.blk == NO_BLK) { pctx.blk = pctx.blk2 = i; save_problem = problem; } else { if ((problem == save_problem) && (pctx.blk2 == i-1)) pctx.blk2++; 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); first_free = ext2fs_blocks_count(fs->super); free_array[group] = group_free; group ++; blocks = 0; group_free = 0; skip_group = 0; if (ctx->progress) if ((ctx->progress)(ctx, 5, group, fs->group_desc_count*2)) goto errout; if (csum_flag && (i != ext2fs_blocks_count(fs->super)-1) && ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)) skip_group++; } } 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 (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != ext2fs_bg_free_blocks_count(fs, i)) { pctx.group = i; pctx.blk = ext2fs_bg_free_blocks_count(fs, i); pctx.blk2 = free_array[i]; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP, &pctx)) { ext2fs_bg_free_blocks_count_set(fs, i, free_array[i]); 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); }
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 old_desc_blocks = 0; int count = 0; int cmp_block = 0; int redo_flag = 0; blk64_t super_blk, old_desc_blk, new_desc_blk; 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 == (int)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_block_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk_t i, super; int *free_array; int group = 0; unsigned int blocks = 0; unsigned int free_blocks = 0; int group_free = 0; int actual, bitmap; struct problem_context pctx; int problem, save_problem, fixit, had_problem; errcode_t retval; int lazy_bg = 0; int skip_group = 0; clear_problem_context(&pctx); free_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "free block count array"); if ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start(ctx->block_found_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end(ctx->block_found_map))) { pctx.num = 1; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -1; pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map); pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ goto errout; } if ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start(fs->block_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end(fs->block_map))) { pctx.num = 2; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -1; pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map); pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ goto errout; } if (EXT2_HAS_COMPAT_FEATURE(fs->super, EXT2_FEATURE_COMPAT_LAZY_BG)) lazy_bg++; redo_counts: had_problem = 0; save_problem = 0; pctx.blk = pctx.blk2 = NO_BLK; if (lazy_bg && (fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT)) skip_group++; super = fs->super->s_first_data_block; for (i = fs->super->s_first_data_block; i < fs->super->s_blocks_count; i++) { actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i); if (skip_group) { if ((i >= super) && (i <= super + fs->desc_blocks) && ext2fs_bg_has_super(fs, group)) bitmap = 1; else if (i == fs->group_desc[group].bg_block_bitmap) bitmap = 1; else if (i == fs->group_desc[group].bg_inode_bitmap) bitmap = 1; else if (i >= fs->group_desc[group].bg_inode_table && (i < fs->group_desc[group].bg_inode_table + fs->inode_blocks_per_group)) bitmap = 1; else bitmap = 0; actual = (actual != 0); } else bitmap = ext2fs_fast_test_block_bitmap(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 (pctx.blk == NO_BLK) { pctx.blk = pctx.blk2 = i; save_problem = problem; } else { if ((problem == save_problem) && (pctx.blk2 == i-1)) pctx.blk2++; else { print_bitmap_problem(ctx, save_problem, &pctx); pctx.blk = pctx.blk2 = i; save_problem = problem; } } ctx->flags |= E2F_FLAG_PROG_SUPPRESS; had_problem++; do_counts: if (!bitmap && !skip_group) { group_free++; free_blocks++; } blocks ++; if ((blocks == fs->super->s_blocks_per_group) || (i == fs->super->s_blocks_count-1)) { free_array[group] = group_free; group ++; blocks = 0; group_free = 0; skip_group = 0; super += fs->super->s_blocks_per_group; if (ctx->progress) if ((ctx->progress)(ctx, 5, group, fs->group_desc_count*2)) goto errout; if (lazy_bg && (i != fs->super->s_blocks_count-1) && (fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT)) skip_group++; } } 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)); goto redo_counts; } else if (fixit == 0) ext2fs_unmark_valid(fs); for (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) { pctx.group = i; pctx.blk = fs->group_desc[i].bg_free_blocks_count; pctx.blk2 = free_array[i]; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_free_blocks_count = free_array[i]; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } } if (free_blocks != fs->super->s_free_blocks_count) { pctx.group = 0; pctx.blk = fs->super->s_free_blocks_count; pctx.blk2 = free_blocks; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) { fs->super->s_free_blocks_count = free_blocks; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } errout: ext2fs_free_mem(&free_array); }
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; int group = 0; unsigned int inodes = 0; int *free_array; int *dir_array; int actual, bitmap; errcode_t retval; struct problem_context pctx; int problem, save_problem, fixit, had_problem; int lazy_bg = 0; int skip_group = 0; clear_problem_context(&pctx); free_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "free inode count array"); dir_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "directory count array"); if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) || (fs->super->s_inodes_count > ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) { pctx.num = 3; pctx.blk = 1; pctx.blk2 = fs->super->s_inodes_count; pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map); pctx.ino2 = ext2fs_get_inode_bitmap_end(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_start(fs->inode_map)) || (fs->super->s_inodes_count > ext2fs_get_inode_bitmap_end(fs->inode_map))) { pctx.num = 4; pctx.blk = 1; pctx.blk2 = fs->super->s_inodes_count; pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map); pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ goto errout; } if (EXT2_HAS_COMPAT_FEATURE(fs->super, EXT2_FEATURE_COMPAT_LAZY_BG)) lazy_bg++; redo_counts: had_problem = 0; save_problem = 0; pctx.ino = pctx.ino2 = 0; if (lazy_bg && (fs->group_desc[group].bg_flags & 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++) { actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i); if (skip_group) bitmap = 0; else bitmap = ext2fs_fast_test_inode_bitmap(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; } 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++; do_counts: if (bitmap) { if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i)) dirs_count++; } else if (!skip_group) { group_free++; free_inodes++; } inodes++; if ((inodes == fs->super->s_inodes_per_group) || (i == fs->super->s_inodes_count)) { 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 (lazy_bg && (i != fs->super->s_inodes_count) && (fs->group_desc[group].bg_flags & 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)); goto redo_counts; } else if (fixit == 0) ext2fs_unmark_valid(fs); for (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) { pctx.group = i; pctx.ino = fs->group_desc[i].bg_free_inodes_count; pctx.ino2 = free_array[i]; if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_free_inodes_count = free_array[i]; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) { pctx.group = i; pctx.ino = fs->group_desc[i].bg_used_dirs_count; pctx.ino2 = dir_array[i]; if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_used_dirs_count = 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); } else ext2fs_unmark_valid(fs); } errout: ext2fs_free_mem(&free_array); ext2fs_free_mem(&dir_array); }
static void check_block_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk64_t i; int *free_array; int group = 0; blk_t blocks = 0; blk_t free_blocks = 0; int group_free = 0; int actual, bitmap; struct problem_context pctx; int problem, save_problem, fixit, had_problem; errcode_t retval; int csum_flag; int skip_group = 0; int old_desc_blocks = 0; int count = 0; int cmp_block = 0; int redo_flag = 0; blk64_t super_blk, old_desc_blk, new_desc_blk; clear_problem_context(&pctx); free_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "free block count array"); if ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start2(ctx->block_found_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end2(ctx->block_found_map))) { pctx.num = 1; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -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 ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start2(fs->block_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end2(fs->block_map))) { pctx.num = 2; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -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; } csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM); redo_counts: had_problem = 0; save_problem = 0; pctx.blk = pctx.blk2 = NO_BLK; if (csum_flag && (fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT)) skip_group++; for (i = fs->super->s_first_data_block; i < fs->super->s_blocks_count; i++) { actual = ext2fs_fast_test_block_bitmap2(ctx->block_found_map, i); if (skip_group) { if ((i - fs->super->s_first_data_block) % fs->super->s_blocks_per_group == 0) { super_blk = 0; old_desc_blk = 0; new_desc_blk = 0; ext2fs_super_and_bgd_loc2(fs, group, &super_blk, &old_desc_blk, &new_desc_blk, 0); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; count = 0; cmp_block = fs->super->s_blocks_per_group; if (group == (int)fs->group_desc_count - 1) cmp_block = fs->super->s_blocks_count % fs->super->s_blocks_per_group; } bitmap = 0; if ((i == super_blk) || (old_desc_blk && old_desc_blocks && (i >= old_desc_blk) && (i < old_desc_blk + old_desc_blocks)) || (new_desc_blk && (i == new_desc_blk)) || (i == fs->group_desc[group].bg_block_bitmap) || (i == fs->group_desc[group].bg_inode_bitmap) || (i >= fs->group_desc[group].bg_inode_table && (i < fs->group_desc[group].bg_inode_table + fs->inode_blocks_per_group))) { bitmap = 1; actual = (actual != 0); count++; cmp_block--; } else if ((i - count - fs->super->s_first_data_block) % fs->super->s_blocks_per_group == 0) { /* * When the compare data blocks in block bitmap * are 0, count the free block, * skip the current block group. */ if (ext2fs_test_block_bitmap_range2( ctx->block_found_map, i, cmp_block)) { /* * -1 means to skip the current block * group. */ blocks = fs->super->s_blocks_per_group - 1; group_free = cmp_block; free_blocks += cmp_block; /* * The current block group's last block * is set to i. */ i += cmp_block - 1; bitmap = 1; goto do_counts; } } } else 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 (skip_group) { struct problem_context pctx2; pctx2.blk = i; pctx2.group = group; if (fix_problem(ctx, PR_5_BLOCK_UNINIT,&pctx2)){ fs->group_desc[group].bg_flags &= ~EXT2_BG_BLOCK_UNINIT; skip_group = 0; } } } if (pctx.blk == NO_BLK) { pctx.blk = pctx.blk2 = i; save_problem = problem; } else { if ((problem == save_problem) && (pctx.blk2 == i-1)) pctx.blk2++; else { print_bitmap_problem(ctx, save_problem, &pctx); pctx.blk = pctx.blk2 = i; save_problem = problem; } } ctx->flags |= E2F_FLAG_PROG_SUPPRESS; had_problem++; do_counts: if (!bitmap && (!skip_group || csum_flag)) { group_free++; free_blocks++; } blocks ++; if ((blocks == fs->super->s_blocks_per_group) || (i == fs->super->s_blocks_count-1)) { free_array[group] = group_free; group ++; blocks = 0; group_free = 0; skip_group = 0; if (ctx->progress) if ((ctx->progress)(ctx, 5, group, fs->group_desc_count*2)) goto errout; if (csum_flag && (i != fs->super->s_blocks_count-1) && (fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT)) skip_group++; } } 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 (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) { pctx.group = i; pctx.blk = fs->group_desc[i].bg_free_blocks_count; pctx.blk2 = free_array[i]; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_free_blocks_count = free_array[i]; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } } if (free_blocks != fs->super->s_free_blocks_count) { pctx.group = 0; pctx.blk = fs->super->s_free_blocks_count; pctx.blk2 = free_blocks; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) { fs->super->s_free_blocks_count = free_blocks; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } errout: ext2fs_free_mem(&free_array); }
static int check_dir_block(ext2_filsys fs, struct ext2_db_entry *db, void *priv_data) { struct dir_info *subdir, *dir; struct ext2_dir_entry *dirent; int offset = 0; int dir_modified = 0; int dot_state; blk_t block_nr = db->blk; ext2_ino_t ino = db->ino; __u16 links; struct check_dir_struct *cd; char *buf; e2fsck_t ctx; int problem; cd = (struct check_dir_struct *) priv_data; buf = cd->buf; ctx = cd->ctx; if (ctx->progress) if ((ctx->progress)(ctx, 2, cd->count++, cd->max)) return DIRENT_ABORT; /* * Make sure the inode is still in use (could have been * deleted in the duplicate/bad blocks pass. */ if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))) return 0; cd->pctx.ino = ino; cd->pctx.blk = block_nr; cd->pctx.blkcount = db->blockcnt; cd->pctx.ino2 = 0; cd->pctx.dirent = 0; cd->pctx.num = 0; if (db->blk == 0) { if (allocate_dir_block(ctx, db, buf, &cd->pctx)) return 0; block_nr = db->blk; } if (db->blockcnt) dot_state = 2; else dot_state = 0; #if 0 printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, db->blockcnt, ino); #endif cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf); if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) cd->pctx.errcode = 0; /* We'll handle this ourselves */ if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } memset(buf, 0, fs->blocksize); } do { dot_state++; problem = 0; dirent = (struct ext2_dir_entry *) (buf + offset); cd->pctx.dirent = dirent; cd->pctx.num = offset; if (((offset + dirent->rec_len) > fs->blocksize) || (dirent->rec_len < 12) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { dirent->rec_len = fs->blocksize - offset; dirent->name_len = 0; dirent->inode = 0; dir_modified++; } else return DIRENT_ABORT; } if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) { if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) { dirent->name_len = EXT2_NAME_LEN; dir_modified++; } } if (dot_state == 1) { if (check_dot(ctx, dirent, ino, &cd->pctx)) dir_modified++; } else if (dot_state == 2) { dir = e2fsck_get_dir_info(ctx, ino); if (!dir) { fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } if (check_dotdot(ctx, dirent, dir, &cd->pctx)) dir_modified++; } else if (dirent->inode == ino) { problem = PR_2_LINK_DOT; if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } } if (!dirent->inode) goto next; /* * Make sure the inode listed is a legal one. */ if (((dirent->inode != EXT2_ROOT_INO) && (dirent->inode < EXT2_FIRST_INODE(fs->super))) || (dirent->inode > fs->super->s_inodes_count)) { problem = PR_2_BAD_INO; } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, dirent->inode))) { /* * If the inode is unused, offer to clear it. */ problem = PR_2_UNUSED_INODE; } else if (ctx->inode_bb_map && (ext2fs_test_inode_bitmap(ctx->inode_bb_map, dirent->inode))) { /* * If the inode is in a bad block, offer to * clear it. */ problem = PR_2_BB_INODE; } else if ((dot_state > 2) && ((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.')) { /* * If there's a '.' entry in anything other * than the first directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT; } else if ((dot_state > 2) && ((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { /* * If there's a '..' entry in anything other * than the second directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT_DOT; } else if ((dot_state > 2) && (dirent->inode == EXT2_ROOT_INO)) { /* * Don't allow links to the root directory. * We check this specially to make sure we * catch this error case even if the root * directory hasn't been created yet. */ problem = PR_2_LINK_ROOT; } else if ((dot_state > 2) && (dirent->name_len & 0xFF) == 0) { /* * Don't allow zero-length directory names. */ problem = PR_2_NULL_NAME; } if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } /* * If the inode was marked as having bad fields in * pass1, process it and offer to fix/clear it. * (We wait until now so that we can display the * pathname to the user.) */ if (ctx->inode_bad_map && ext2fs_test_inode_bitmap(ctx->inode_bad_map, dirent->inode)) { if (e2fsck_process_bad_inode(ctx, ino, dirent->inode)) { dirent->inode = 0; dir_modified++; goto next; } if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return DIRENT_ABORT; } if (check_name(ctx, dirent, ino, &cd->pctx)) dir_modified++; if (check_filetype(ctx, dirent, ino, &cd->pctx)) dir_modified++; /* * If this is a directory, then mark its parent in its * dir_info structure. If the parent field is already * filled in, then this directory has more than one * hard link. We assume the first link is correct, * and ask the user if he/she wants to clear this one. */ if ((dot_state > 2) && (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode))) { subdir = e2fsck_get_dir_info(ctx, dirent->inode); if (!subdir) { cd->pctx.ino = dirent->inode; fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } if (subdir->parent) { cd->pctx.ino2 = subdir->parent; if (fix_problem(ctx, PR_2_LINK_DIR, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } cd->pctx.ino2 = 0; } else subdir->parent = ino; } ext2fs_icount_increment(ctx->inode_count, dirent->inode, &links); if (links > 1) ctx->fs_links_count++; ctx->fs_total_count++; next: offset += dirent->rec_len; } while (offset < fs->blocksize); #if 0 printf("\n"); #endif if (offset != fs->blocksize) { cd->pctx.num = dirent->rec_len - fs->blocksize + offset; if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { dirent->rec_len = cd->pctx.num; dir_modified++; } } if (dir_modified) { cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf); if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) { ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } } ext2fs_mark_changed(fs); } return 0; }