void e2fsck_hide_quota(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; ext2_filsys fs = ctx->fs; clear_problem_context(&pctx); if ((ctx->options & E2F_OPT_READONLY) || !(sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_QUOTA)) return; pctx.ino = sb->s_usr_quota_inum; if (sb->s_usr_quota_inum && (sb->s_usr_quota_inum != EXT4_USR_QUOTA_INO) && fix_problem(ctx, PR_0_HIDE_QUOTA, &pctx)) { move_quota_inode(fs, sb->s_usr_quota_inum, EXT4_USR_QUOTA_INO, USRQUOTA); sb->s_usr_quota_inum = EXT4_USR_QUOTA_INO; } pctx.ino = sb->s_grp_quota_inum; if (sb->s_grp_quota_inum && (sb->s_grp_quota_inum != EXT4_GRP_QUOTA_INO) && fix_problem(ctx, PR_0_HIDE_QUOTA, &pctx)) { move_quota_inode(fs, sb->s_grp_quota_inum, EXT4_GRP_QUOTA_INO, GRPQUOTA); sb->s_grp_quota_inum = EXT4_GRP_QUOTA_INO; } return; }
static int check_dotdot(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t ino, struct problem_context *pctx) { int rec_len, problem = 0; if (!dirent->inode) problem = PR_2_MISSING_DOT_DOT; else if (((dirent->name_len & 0xFF) != 2) || (dirent->name[0] != '.') || (dirent->name[1] != '.')) problem = PR_2_2ND_NOT_DOT_DOT; else if (dirent->name[2] != '\0') problem = PR_2_DOT_DOT_NULL_TERM; (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); if (problem) { if (fix_problem(ctx, problem, pctx)) { if (rec_len < 12) dirent->rec_len = 12; dirent->inode = EXT2_ROOT_INO; dirent->name_len = 2; dirent->name[0] = '.'; dirent->name[1] = '.'; dirent->name[2] = '\0'; return 1; } return 0; } if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) { fix_problem(ctx, PR_2_NO_DIRINFO, pctx); return -1; } return 0; }
static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, ext2_ino_t ino, struct fill_dir_struct *fd) { struct problem_context pctx; struct hash_entry *ent, *prev; int i, j; int fixed = 0; char new_name[256]; __u16 new_len; clear_problem_context(&pctx); pctx.ino = ino; for (i=1; i < fd->num_array; i++) { ent = fd->harray + i; prev = ent - 1; if (!ent->dir->inode || ((ent->dir->name_len & 0xFF) != (prev->dir->name_len & 0xFF)) || (strncmp(ent->dir->name, prev->dir->name, ent->dir->name_len & 0xFF))) continue; pctx.dirent = ent->dir; if ((ent->dir->inode == prev->dir->inode) && fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) { e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1); ent->dir->inode = 0; fixed++; continue; } memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF); new_len = ent->dir->name_len; mutate_name(new_name, &new_len); for (j=0; j < fd->num_array; j++) { if ((i==j) || ((ent->dir->name_len & 0xFF) != (fd->harray[j].dir->name_len & 0xFF)) || (strncmp(new_name, fd->harray[j].dir->name, new_len & 0xFF))) continue; mutate_name(new_name, &new_len); j = -1; } new_name[new_len & 0xFF] = 0; pctx.str = new_name; if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) { memcpy(ent->dir->name, new_name, new_len & 0xFF); ent->dir->name_len = new_len; ext2fs_dirhash(fs->super->s_def_hash_version, ent->dir->name, ent->dir->name_len & 0xFF, fs->super->s_hash_seed, &ent->hash, &ent->minor_hash); fixed++; } } return fixed; }
/* * This fuction deallocates an inode */ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; __u32 count; struct del_block del_block; e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode"); clear_problem_context(&pctx); pctx.ino = ino; /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); if (ext2fs_file_acl_block(fs, &inode) && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { pctx.errcode = ext2fs_adjust_ea_refcount3(fs, ext2fs_file_acl_block(fs, &inode), block_buf, -1, &count, ino); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; } if (pctx.errcode) { pctx.blk = ext2fs_file_acl_block(fs, &inode); fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (count == 0) { ext2fs_unmark_block_bitmap2(ctx->block_found_map, ext2fs_file_acl_block(fs, &inode)); ext2fs_block_alloc_stats2(fs, ext2fs_file_acl_block(fs, &inode), -1); } ext2fs_file_acl_block_set(fs, &inode, 0); } if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) return; if (LINUX_S_ISREG(inode.i_mode) && EXT2_I_SIZE(&inode) >= 0x80000000UL) ctx->large_files--; del_block.ctx = ctx; del_block.num = 0; pctx.errcode = ext2fs_block_iterate3(fs, ino, 0, block_buf, deallocate_inode_block, &del_block); if (pctx.errcode) { fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } }
static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; int recover = ctx->fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; int has_journal = ctx->fs->super->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL; if (has_journal || sb->s_journal_inum) { /* The journal inode is bogus, remove and force full fsck */ pctx->ino = sb->s_journal_inum; if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) { if (has_journal && sb->s_journal_inum) printf("*** ext3 journal has been deleted - " "filesystem is now ext2 only ***\n\n"); sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; sb->s_journal_inum = 0; ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */ e2fsck_clear_recover(ctx, 1); return 0; } return EXT2_ET_BAD_INODE_NUM; } else if (recover) { if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) { e2fsck_clear_recover(ctx, 1); return 0; } return EXT2_ET_UNSUPP_FEATURE; } 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, 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; }
/* * Make sure the first entry in the directory is '.', and that the * directory entry is sane. */ static int check_dot(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t ino, struct problem_context *pctx) { struct ext2_dir_entry *nextdir; unsigned int rec_len, new_len; int status = 0; int created = 0; problem_t problem = 0; if (!dirent->inode) problem = PR_2_MISSING_DOT; else if ((ext2fs_dirent_name_len(dirent) != 1) || (dirent->name[0] != '.')) problem = PR_2_1ST_NOT_DOT; else if (dirent->name[1] != '\0') problem = PR_2_DOT_NULL_TERM; (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); if (problem) { if (fix_problem(ctx, problem, pctx)) { if (rec_len < 12) rec_len = dirent->rec_len = 12; dirent->inode = ino; ext2fs_dirent_set_name_len(dirent, 1); ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); dirent->name[0] = '.'; dirent->name[1] = '\0'; status = 1; created = 1; } } if (dirent->inode != ino) { if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { dirent->inode = ino; status = 1; } } if (rec_len > 12) { new_len = rec_len - 12; if (new_len > 12) { if (created || fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { nextdir = (struct ext2_dir_entry *) ((char *) dirent + 12); dirent->rec_len = 12; (void) ext2fs_set_rec_len(ctx->fs, new_len, nextdir); nextdir->inode = 0; ext2fs_dirent_set_name_len(nextdir, 0); ext2fs_dirent_set_file_type(nextdir, EXT2_FT_UNKNOWN); status = 1; } } } return status; }
/* * 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; }
/* * Make sure the first entry in the directory is '.', and that the * directory entry is sane. */ static int check_dot(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t ino, struct problem_context *pctx) { struct ext2_dir_entry *nextdir; int status = 0; int created = 0; int new_len; int problem = 0; if (!dirent->inode) problem = PR_2_MISSING_DOT; else if (((dirent->name_len & 0xFF) != 1) || (dirent->name[0] != '.')) problem = PR_2_1ST_NOT_DOT; else if (dirent->name[1] != '\0') problem = PR_2_DOT_NULL_TERM; if (problem) { if (fix_problem(ctx, problem, pctx)) { if (dirent->rec_len < 12) dirent->rec_len = 12; dirent->inode = ino; dirent->name_len = 1; dirent->name[0] = '.'; dirent->name[1] = '\0'; status = 1; created = 1; } } if (dirent->inode != ino) { if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { dirent->inode = ino; status = 1; } } if (dirent->rec_len > 12) { new_len = dirent->rec_len - 12; if (new_len > 12) { if (created || fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { nextdir = (struct ext2_dir_entry *) ((char *) dirent + 12); dirent->rec_len = 12; nextdir->rec_len = new_len; nextdir->inode = 0; nextdir->name_len = 0; status = 1; } } } return status; }
static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; __u32 count; e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode"); clear_problem_context(&pctx); pctx.ino = ino; e2fsck_read_bitmaps(ctx); ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); if (inode.i_file_acl && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl, block_buf, -1, &count); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; } if (pctx.errcode) { pctx.blk = inode.i_file_acl; fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (count == 0) { ext2fs_unmark_block_bitmap(ctx->block_found_map, inode.i_file_acl); ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1); } inode.i_file_acl = 0; } if (!ext2fs_inode_has_valid_blocks(&inode)) return; if (LINUX_S_ISREG(inode.i_mode) && (inode.i_size_high || inode.i_size & 0x80000000UL)) ctx->large_files--; pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, deallocate_inode_block, ctx); if (pctx.errcode) { fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } }
int check_fs(FILE *fp, check_fs_config_t *config) { int res; check_context_t ctx; int bsize; omfs_super_t super; omfs_root_t root; omfs_info_t info = { .dev = fp, .super = &super, .root = &root }; ctx.config = config; if (omfs_read_super(&info)) { fix_problem(E_READ_SUPER, &ctx); return 0; } if (omfs_read_root_block(&info)) { fix_problem(E_READ_ROOT, &ctx); return 0; } ctx.omfs_info = &info; omfs_load_bitmap(&info); ctx.bitmap = info.bitmap->bmap; bsize = (swap_be64(info.super->s_num_blocks) + 7) / 8; ctx.visited = calloc(1, bsize); /* FIXME error codes are all over the place. */ res = dirscan_begin(&info, on_node, &ctx); if (res < 0) { fix_problem(E_SCAN, &ctx); return 0; } if (res != 0) return 0; res = check_bitmap(&ctx); if (ctx.bitmap) free(ctx.bitmap); free(ctx.visited); return res; }
void e2fsck_hide_quota(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; ext2_filsys fs = ctx->fs; enum quota_type qtype; ext2_ino_t quota_ino; clear_problem_context(&pctx); if ((ctx->options & E2F_OPT_READONLY) || !ext2fs_has_feature_quota(sb)) return; for (qtype = 0; qtype < MAXQUOTAS; qtype++) { pctx.ino = *quota_sb_inump(sb, qtype); quota_ino = quota_type2inum(qtype, fs->super); if (pctx.ino && (pctx.ino != quota_ino) && fix_problem(ctx, PR_0_HIDE_QUOTA, &pctx)) { move_quota_inode(fs, pctx.ino, quota_ino, qtype); *quota_sb_inump(sb, qtype) = quota_ino; } } return; }
static void print_bitmap_problem(e2fsck_t ctx, int 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; }
/* * This function makes sure the superblock hint for the external * journal is correct. */ int e2fsck_fix_ext3_journal_hint(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; char uuid[37], *journal_name; struct stat st; problem_t problem; int retval; if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) || uuid_is_null(sb->s_journal_uuid)) return 0; uuid_unparse(sb->s_journal_uuid, uuid); journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid); if (!journal_name) return 0; if (stat(journal_name, &st) < 0) return 0; if (st.st_rdev != sb->s_journal_dev) { clear_problem_context(&pctx); pctx.num = st.st_rdev; if (fix_problem(ctx, PR_0_EXTERNAL_JOURNAL_HINT, &pctx)) { sb->s_journal_dev = st.st_rdev; ext2fs_mark_super_dirty(ctx->fs); } } free(journal_name); return 0; }
/* * This routine will connect a file to lost+found */ int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino) { ext2_filsys fs = ctx->fs; errcode_t retval; char name[80]; struct problem_context pctx; struct ext2_inode inode; int file_type = 0; clear_problem_context(&pctx); pctx.ino = ino; if (!ctx->bad_lost_and_found && !ctx->lost_and_found) { if (e2fsck_get_lost_and_found(ctx, 1) == 0) ctx->bad_lost_and_found++; } if (ctx->bad_lost_and_found) { fix_problem(ctx, PR_3_NO_LPF, &pctx); return 1; } sprintf(name, "#%u", ino); if (ext2fs_read_inode(fs, ino, &inode) == 0) file_type = ext2_file_type(inode.i_mode); retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type); if (retval == EXT2_ET_DIR_NO_SPACE) { if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx)) return 1; retval = e2fsck_expand_directory(ctx, ctx->lost_and_found, 1, 0); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx); return 1; } retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type); } if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx); return 1; } e2fsck_adjust_inode_count(ctx, ino, 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_block_bitmap_checksum(e2fsck_t ctx) { struct problem_context pctx; char *buf; dgrp_t i; int nbytes; blk64_t blk_itr; errcode_t retval; if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) 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_memalign(ctx->fs->blocksize, ctx->fs->blocksize, &buf); if (retval) { com_err(ctx->program_name, 0, _("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) + (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); }
/* * Check the directory filetype (if present) */ static _INLINE_ int check_filetype(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t dir_ino EXT2FS_ATTR((unused)), struct problem_context *pctx) { int filetype = dirent->name_len >> 8; int should_be = EXT2_FT_UNKNOWN; struct ext2_inode inode; if (!(ctx->fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) { if (filetype == 0 || !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx)) return 0; dirent->name_len = dirent->name_len & 0xFF; return 1; } if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode)) { should_be = EXT2_FT_DIR; } else if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map, dirent->inode)) { should_be = EXT2_FT_REG_FILE; } else if (ctx->inode_bad_map && ext2fs_test_inode_bitmap2(ctx->inode_bad_map, dirent->inode)) should_be = 0; else { e2fsck_read_inode(ctx, dirent->inode, &inode, "check_filetype"); should_be = ext2_file_type(inode.i_mode); } if (filetype == should_be) return 0; pctx->num = should_be; if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, pctx) == 0) return 0; dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8; return 1; }
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; } }
/* * Make sure the second entry in the directory is '..', and that the * directory entry is sane. We do not check the inode number of '..' * here; this gets done in pass 3. */ static int check_dotdot(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t ino, struct problem_context *pctx) { problem_t problem = 0; unsigned int rec_len; if (!dirent->inode) problem = PR_2_MISSING_DOT_DOT; else if ((ext2fs_dirent_name_len(dirent) != 2) || (dirent->name[0] != '.') || (dirent->name[1] != '.')) problem = PR_2_2ND_NOT_DOT_DOT; else if (dirent->name[2] != '\0') problem = PR_2_DOT_DOT_NULL_TERM; (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); if (problem) { if (fix_problem(ctx, problem, pctx)) { if (rec_len < 12) dirent->rec_len = 12; /* * Note: we don't have the parent inode just * yet, so we will fill it in with the root * inode. This will get fixed in pass 3. */ dirent->inode = EXT2_ROOT_INO; ext2fs_dirent_set_name_len(dirent, 2); ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); dirent->name[0] = '.'; dirent->name[1] = '.'; dirent->name[2] = '\0'; return 1; } return 0; } if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) { fix_problem(ctx, PR_2_NO_DIRINFO, pctx); return -1; } return 0; }
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 #ifdef RESOURCE_TRACK init_resource_track(&rtrack); #endif 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; 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; #ifdef RESOURCE_TRACK if (ctx->options & E2F_OPT_TIME2) { e2fsck_clear_progbar(ctx); print_resource_track(_("Pass 5"), &rtrack); } #endif }
int end_problem_latch(e2fsck_t ctx, int mask) { struct latch_descr *ldesc; struct problem_context pctx; int answer = -1; ldesc = find_latch(mask); if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) { clear_problem_context(&pctx); answer = fix_problem(ctx, ldesc->end_message, &pctx); } ldesc->flags &= ~(PRL_VARIABLE); return answer; }
static void clear_v2_journal_fields(journal_t *journal) { e2fsck_t ctx = journal->j_dev->k_ctx; struct problem_context pctx; clear_problem_context(&pctx); if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx)) return; memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0, ctx->fs->blocksize-V1_SB_SIZE); mark_buffer_dirty(journal->j_sb_buffer); }
static void check_super_value(e2fsck_t ctx, const char *descr, unsigned long value, int flags, unsigned long min_val, unsigned long max_val) { struct problem_context pctx; if (((flags & MIN_CHECK) && (value < min_val)) || ((flags & MAX_CHECK) && (value > max_val))) { clear_problem_context(&pctx); pctx.num = value; pctx.str = descr; fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* never get here! */ } }
int check_inode(check_context_t *ctx) { int i; int ret = 1; omfs_inode_t *inode = ctx->current_inode; if (test_bit(ctx->visited, ctx->block)) { fix_problem(E_LOOP, ctx); return 0; } for (i=0; i < swap_be32(ctx->omfs_info->super->s_mirrors); i++) set_bit(ctx->visited, ctx->block + i); if (!check_sanity(ctx)) { fix_problem(E_INSANE, ctx); return 0; } if (!check_header((u8 *)inode)) { fix_problem(E_HEADER_XOR, ctx); ret = 0; } if (!check_crc((u8 *)inode)) { fix_problem(E_HEADER_CRC, ctx); ret = 0; } if (swap_be64(inode->i_head.h_self) != ctx->block) { fix_problem(E_SELF_PTR, ctx); ret = 0; } if (swap_be64(inode->i_parent) != ctx->parent) { fix_problem(E_PARENT_PTR, ctx); ret = 0; } if (omfs_compute_hash(ctx->omfs_info, inode->i_name) != ctx->hash) { fix_problem(E_HASH_WRONG, ctx); ret = 0; } if (inode->i_type == OMFS_FILE) { visit_extents(ctx); } return ret; }
/* * This fuction deallocates an inode */ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; ext2fs_icount_store(ctx->inode_link_info, ino, 0); e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); inode.i_links_count = 0; inode.i_dtime = time(0); e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode"); clear_problem_context(&pctx); pctx.ino = ino; /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); if (ctx->inode_bad_map) ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); ext2fs_unmark_inode_bitmap(fs->inode_map, ino); ext2fs_mark_ib_dirty(fs); if (!ext2fs_inode_has_valid_blocks(&inode)) return; if (!LINUX_S_ISDIR(inode.i_mode) && (inode.i_size_high || inode.i_size & 0x80000000UL)) ctx->large_files--; if (inode.i_file_acl) { ext2fs_unmark_block_bitmap(ctx->block_found_map, inode.i_file_acl); ext2fs_unmark_block_bitmap(fs->block_map, inode.i_file_acl); } ext2fs_mark_bb_dirty(fs); pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, deallocate_inode_block, ctx); if (pctx.errcode) { fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } }
/* * Check to make sure a directory entry doesn't contain any illegal * characters. */ static int check_name(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t dir_ino, struct problem_context *pctx) { int i; int fixup = -1; int ret = 0; for ( i = 0; i < (dirent->name_len & 0xFF); i++) { if (dirent->name[i] == '/' || dirent->name[i] == '\0') { if (fixup < 0) { fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx); } if (fixup) { dirent->name[i] = '.'; ret = 1; } } } return ret; }
static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx, journal_t *journal, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; int recover = ctx->fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) { if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) { e2fsck_journal_reset_super(ctx, journal->j_superblock, journal); journal->j_transaction_sequence = 1; e2fsck_clear_recover(ctx, recover); return 0; } return EXT2_ET_CORRUPT_SUPERBLOCK; } else if (e2fsck_journal_fix_bad_inode(ctx, pctx)) return EXT2_ET_CORRUPT_SUPERBLOCK; return 0; }
static errcode_t recover_ext3_journal(e2fsck_t ctx) { struct problem_context pctx; journal_t *journal; int retval; clear_problem_context(&pctx); journal_init_revoke_caches(); retval = e2fsck_get_journal(ctx, &journal); if (retval) return retval; retval = e2fsck_journal_load(journal); if (retval) goto errout; retval = journal_init_revoke(journal, 1024); if (retval) goto errout; retval = -journal_recover(journal); if (retval) goto errout; if (journal->j_failed_commit) { pctx.ino = journal->j_failed_commit; fix_problem(ctx, PR_0_JNL_TXN_CORRUPT, &pctx); journal->j_superblock->s_errno = -EINVAL; mark_buffer_dirty(journal->j_sb_buffer); } errout: journal_destroy_revoke(journal); journal_destroy_revoke_caches(); e2fsck_journal_release(ctx, journal, 1, 0); return retval; }
int check_bitmap(check_context_t *ctx) { int i; int is_ok = 1; int bsize, first_blk; omfs_super_t *super = ctx->omfs_info->super; omfs_root_t *root = ctx->omfs_info->root; if (!ctx->bitmap) return 0; bsize = (swap_be64(super->s_num_blocks) + 7) / 8; first_blk = swap_be64(root->r_bitmap) + (bsize + swap_be32(super->s_blocksize)-1) / swap_be32(super->s_blocksize); for (i=0; i < first_blk; i++) set_bit(ctx->visited, i); for (i=0; i < bsize; i++) { if (ctx->bitmap[i] != ctx->visited[i]) { is_ok = 0; if (!ctx->config->is_quiet) { printf("Wrong bitmap byte at %d (%02x,%02x)\n", i, ctx->bitmap[i], ctx->visited[i]); } } } if (!is_ok) { fix_problem(E_BITMAP, ctx); } return is_ok; }