static void setup_db(e2fsck_t ctx) { struct dir_info_db *db; ext2_ino_t num_dirs; errcode_t retval; db = (struct dir_info_db *) e2fsck_allocate_memory(ctx, sizeof(struct dir_info_db), "directory map db"); db->count = db->size = 0; db->array = 0; ctx->dir_info = db; retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs); if (retval) num_dirs = 1024; /* Guess */ setup_tdb(ctx, num_dirs); if (db->tdb) { #ifdef DIRINFO_DEBUG printf("Note: using tdb!\n"); #endif return; } db->size = num_dirs + 10; db->array = (struct dir_info *) e2fsck_allocate_memory(ctx, db->size * sizeof (struct dir_info), "directory map"); }
/* * This subroutine is called during pass1 to create a directory info * entry. During pass1, the passed-in parent is 0; it will get filled * in during pass2. */ void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks) { struct dx_dir_info *dir; int i, j; errcode_t retval; unsigned long old_size; #if 0 printf("add_dx_dir_info for inode %lu...\n", ino); #endif if (!ctx->dx_dir_info) { ctx->dx_dir_info_count = 0; ctx->dx_dir_info_size = 100; /* Guess */ ctx->dx_dir_info = (struct dx_dir_info *) e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size * sizeof (struct dx_dir_info), "directory map"); } if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) { old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info); ctx->dx_dir_info_size += 10; retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size * sizeof(struct dx_dir_info), (void **) &ctx->dx_dir_info); if (retval) { ctx->dx_dir_info_size -= 10; return; } } /* * Normally, add_dx_dir_info is called with each inode in * sequential order; but once in a while (like when pass 3 * needs to recreate the root directory or lost+found * directory) it is called out of order. In those cases, we * need to move the dx_dir_info entries down to make room, since * the dx_dir_info array needs to be sorted by inode number for * get_dx_dir_info()'s sake. */ if (ctx->dx_dir_info_count && ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) { for (i = ctx->dx_dir_info_count-1; i > 0; i--) if (ctx->dx_dir_info[i-1].ino < ino) break; dir = &ctx->dx_dir_info[i]; if (dir->ino != ino) for (j = ctx->dx_dir_info_count++; j > i; j--) ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1]; } else dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++]; dir->ino = ino; dir->numblocks = num_blocks; dir->hashversion = 0; dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks * sizeof (struct dx_dirblock_info), "dx_block info array"); }
void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks) { struct dx_dir_info *dir; int i, j; errcode_t retval; unsigned long old_size; #if 0 printf("add_dx_dir_info for inode %lu...\n", ino); #endif if (!ctx->dx_dir_info) { ctx->dx_dir_info_count = 0; ctx->dx_dir_info_size = 100; ctx->dx_dir_info = (struct dx_dir_info *) e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size * sizeof (struct dx_dir_info), "directory map"); } if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) { old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info); ctx->dx_dir_info_size += 10; retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size * sizeof(struct dx_dir_info), &ctx->dx_dir_info); if (retval) { ctx->dx_dir_info_size -= 10; return; } } if (ctx->dx_dir_info_count && ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) { for (i = ctx->dx_dir_info_count-1; i > 0; i--) if (ctx->dx_dir_info[i-1].ino < ino) break; dir = &ctx->dx_dir_info[i]; if (dir->ino != ino) for (j = ctx->dx_dir_info_count++; j > i; j--) ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1]; } else dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++]; dir->ino = ino; dir->numblocks = num_blocks; dir->hashversion = 0; dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks * sizeof (struct dx_dirblock_info), "dx_block info array"); }
/* * Add a duplicate block record */ static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk64_t cluster, struct ext2_inode *inode) { dnode_t *n; struct dup_cluster *db; struct dup_inode *di; struct cluster_el *cluster_el; struct inode_el *ino_el; n = dict_lookup(&clstr_dict, INT_TO_VOIDPTR(cluster)); if (n) db = (struct dup_cluster *) dnode_get(n); else { db = (struct dup_cluster *) e2fsck_allocate_memory(ctx, sizeof(struct dup_cluster), "duplicate cluster header"); db->num_bad = 0; db->inode_list = 0; dict_alloc_insert(&clstr_dict, INT_TO_VOIDPTR(cluster), db); } ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx, sizeof(struct inode_el), "inode element"); ino_el->inode = ino; ino_el->next = db->inode_list; db->inode_list = ino_el; db->num_bad++; n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino)); if (n) di = (struct dup_inode *) dnode_get(n); else { di = (struct dup_inode *) e2fsck_allocate_memory(ctx, sizeof(struct dup_inode), "duplicate inode header"); if (ino == EXT2_ROOT_INO) { di->dir = EXT2_ROOT_INO; dup_inode_founddir++; } else di->dir = 0; di->num_dupblocks = 0; di->cluster_list = 0; di->inode = *inode; dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di); } cluster_el = (struct cluster_el *) e2fsck_allocate_memory(ctx, sizeof(struct cluster_el), "cluster element"); cluster_el->cluster = cluster; cluster_el->next = di->cluster_list; di->cluster_list = cluster_el; di->num_dupblocks++; }
struct buffer_head *getblk(kdev_t kdev, blk64_t blocknr, int blocksize) { struct buffer_head *bh; int bufsize = sizeof(*bh) + kdev->k_ctx->fs->blocksize - sizeof(bh->b_data); bh = e2fsck_allocate_memory(kdev->k_ctx, bufsize, "block buffer"); if (!bh) return NULL; #ifdef CONFIG_JBD_DEBUG if (journal_enable_debug >= 3) bh_count++; #endif jfs_debug(4, "getblk for block %llu (%d bytes)(total %d)\n", (unsigned long long) blocknr, blocksize, bh_count); bh->b_ctx = kdev->k_ctx; if (kdev->k_dev == K_DEV_FS) bh->b_io = kdev->k_ctx->fs->io; else bh->b_io = kdev->k_ctx->journal_io; bh->b_size = blocksize; bh->b_blocknr = blocknr; return bh; }
struct dir_info_iter *e2fsck_dir_info_iter_begin(e2fsck_t ctx) { struct dir_info_iter *iter; struct dir_info_db *db = ctx->dir_info; iter = e2fsck_allocate_memory(ctx, sizeof(struct dir_info_iter), "dir_info iterator"); if (db->tdb) iter->tdb_iter = tdb_firstkey(db->tdb); return iter; }
struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize) { struct buffer_head *bh; bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer"); if (!bh) return NULL; jfs_debug(4, "getblk for block %lu (%d bytes)(total %d)\n", (unsigned long) blocknr, blocksize, ++bh_count); bh->b_ctx = kdev->k_ctx; if (kdev->k_dev == K_DEV_FS) bh->b_io = kdev->k_ctx->fs->io; else bh->b_io = kdev->k_ctx->journal_io; bh->b_size = blocksize; bh->b_blocknr = blocknr; return bh; }
static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) { struct ext2_super_block *sb = ctx->fs->super; struct ext2_super_block jsuper; struct problem_context pctx; struct buffer_head *bh; struct inode *j_inode = NULL; struct kdev_s *dev_fs = NULL, *dev_journal; const char *journal_name = 0; journal_t *journal = NULL; errcode_t retval = 0; io_manager io_ptr = 0; unsigned long start = 0; blk_t blk; int ext_journal = 0; int tried_backup_jnl = 0; int i; clear_problem_context(&pctx); journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); if (!journal) { return EXT2_ET_NO_MEMORY; } dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); if (!dev_fs) { retval = EXT2_ET_NO_MEMORY; goto errout; } dev_journal = dev_fs+1; dev_fs->k_ctx = dev_journal->k_ctx = ctx; dev_fs->k_dev = K_DEV_FS; dev_journal->k_dev = K_DEV_JOURNAL; journal->j_dev = dev_journal; journal->j_fs_dev = dev_fs; journal->j_inode = NULL; journal->j_blocksize = ctx->fs->blocksize; if (uuid_is_null(sb->s_journal_uuid)) { if (!sb->s_journal_inum) return EXT2_ET_BAD_INODE_NUM; j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), "journal inode"); if (!j_inode) { retval = EXT2_ET_NO_MEMORY; goto errout; } j_inode->i_ctx = ctx; j_inode->i_ino = sb->s_journal_inum; if ((retval = ext2fs_read_inode(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2))) { try_backup_journal: if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || tried_backup_jnl) goto errout; memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, EXT2_N_BLOCKS*4); j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; j_inode->i_ext2.i_links_count = 1; j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; tried_backup_jnl++; } if (!j_inode->i_ext2.i_links_count || !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { retval = EXT2_ET_NO_JOURNAL; goto try_backup_journal; } if (j_inode->i_ext2.i_size / journal->j_blocksize < JFS_MIN_JOURNAL_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } for (i=0; i < EXT2_N_BLOCKS; i++) { blk = j_inode->i_ext2.i_block[i]; if (!blk) { if (i < EXT2_NDIR_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } continue; } if (blk < sb->s_first_data_block || blk >= sb->s_blocks_count) { retval = EXT2_ET_BAD_BLOCK_NUM; goto try_backup_journal; } } journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize; #ifdef USE_INODE_IO retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2, &journal_name); if (retval) goto errout; io_ptr = inode_io_manager; #else journal->j_inode = j_inode; ctx->journal_io = ctx->fs->io; if ((retval = journal_bmap(journal, 0, &start)) != 0) goto errout; #endif } else { ext_journal = 1; if (!ctx->journal_name) { char uuid[37]; uuid_unparse(sb->s_journal_uuid, uuid); ctx->journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid); if (!ctx->journal_name) ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); } journal_name = ctx->journal_name; if (!journal_name) { fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); return EXT2_ET_LOAD_EXT_JOURNAL; } jfs_debug(1, "Using journal file %s\n", journal_name); io_ptr = unix_io_manager; } #if 0 test_io_backing_manager = io_ptr; io_ptr = test_io_manager; #endif #ifndef USE_INODE_IO if (ext_journal) #endif retval = io_ptr->open(journal_name, IO_FLAG_RW, &ctx->journal_io); if (retval) goto errout; io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); if (ext_journal) { if (ctx->fs->blocksize == 1024) start = 1; bh = getblk(dev_journal, start, ctx->fs->blocksize); if (!bh) { retval = EXT2_ET_NO_MEMORY; goto errout; } ll_rw_block(READ, 1, &bh); if ((retval = bh->b_err) != 0) { brelse(bh); goto errout; } memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024, sizeof(jsuper)); brelse(bh); #ifdef EXT2FS_ENABLE_SWAPFS if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(&jsuper); #endif if (jsuper.s_magic != EXT2_SUPER_MAGIC || !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } /* Make sure the journal UUID is correct */ if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, sizeof(jsuper.s_uuid))) { fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } journal->j_maxlen = jsuper.s_blocks_count; start++; } if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { retval = EXT2_ET_NO_MEMORY; goto errout; } journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data; #ifdef USE_INODE_IO if (j_inode) ext2fs_free_mem(&j_inode); #endif *ret_journal = journal; return 0; errout: if (dev_fs) ext2fs_free_mem(&dev_fs); if (j_inode) ext2fs_free_mem(&j_inode); if (journal) ext2fs_free_mem(&journal); return retval; }
static void swap_inodes(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; dgrp_t group; unsigned int i; ext2_ino_t ino = 1; char *buf = NULL, *block_buf = NULL; errcode_t retval; struct ext2_inode * inode; e2fsck_use_inode_shortcuts(ctx, 1); retval = ext2fs_get_array(fs->blocksize, fs->inode_blocks_per_group, &buf); if (retval) { com_err("swap_inodes", retval, _("while allocating inode buffer")); ctx->flags |= E2F_FLAG_ABORT; goto errout; } block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, "block interate buffer"); for (group = 0; group < fs->group_desc_count; group++) { retval = io_channel_read_blk(fs->io, fs->group_desc[group].bg_inode_table, fs->inode_blocks_per_group, buf); if (retval) { com_err("swap_inodes", retval, _("while reading inode table (group %d)"), group); ctx->flags |= E2F_FLAG_ABORT; goto errout; } inode = (struct ext2_inode *) buf; for (i=0; i < fs->super->s_inodes_per_group; i++, ino++, inode++) { ctx->stashed_ino = ino; ctx->stashed_inode = inode; if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ) ext2fs_swap_inode(fs, inode, inode, 0); /* * Skip deleted files. */ if (inode->i_links_count == 0) continue; if (LINUX_S_ISDIR(inode->i_mode) || ((inode->i_block[EXT2_IND_BLOCK] || inode->i_block[EXT2_DIND_BLOCK] || inode->i_block[EXT2_TIND_BLOCK]) && ext2fs_inode_has_valid_blocks(inode))) swap_inode_blocks(ctx, ino, block_buf, inode); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto errout; if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) ext2fs_swap_inode(fs, inode, inode, 1); } retval = io_channel_write_blk(fs->io, fs->group_desc[group].bg_inode_table, fs->inode_blocks_per_group, buf); if (retval) { com_err("swap_inodes", retval, _("while writing inode table (group %d)"), group); ctx->flags |= E2F_FLAG_ABORT; goto errout; } } errout: if (buf) ext2fs_free_mem(&buf); if (block_buf) ext2fs_free_mem(&block_buf); e2fsck_use_inode_shortcuts(ctx, 0); ext2fs_flush_icache(fs); }
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 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); }
void e2fsck_pass4(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t i; struct ext2_inode *inode; #ifdef RESOURCE_TRACK struct resource_track rtrack; #endif struct problem_context pctx; __u16 link_count, link_counted; char *buf = 0; dgrp_t group, maxgroup; init_resource_track(&rtrack, ctx->fs->io); #ifdef MTRACE mtrace_print("Pass 4"); #endif clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_4_PASS_HEADER, &pctx); group = 0; maxgroup = fs->group_desc_count; if (ctx->progress) if ((ctx->progress)(ctx, 4, 0, maxgroup)) return; inode = e2fsck_allocate_memory(ctx, EXT2_INODE_SIZE(fs->super), "scratch inode"); /* Protect loop from wrap-around if s_inodes_count maxed */ for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) { int isdir; if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto errout; if ((i % fs->super->s_inodes_per_group) == 0) { group++; if (ctx->progress) if ((ctx->progress)(ctx, 4, group, maxgroup)) goto errout; } if (i == EXT2_BAD_INO || (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) continue; if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, i)) || (ctx->inode_imagic_map && ext2fs_test_inode_bitmap2(ctx->inode_imagic_map, i)) || (ctx->inode_bb_map && ext2fs_test_inode_bitmap2(ctx->inode_bb_map, i))) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); if (link_counted == 0) { if (!buf) buf = e2fsck_allocate_memory(ctx, fs->blocksize, "bad_inode buffer"); if (e2fsck_process_bad_inode(ctx, 0, i, buf)) continue; if (disconnect_inode(ctx, i, inode)) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); } isdir = ext2fs_test_inode_bitmap2(ctx->inode_dir_map, i); if (isdir && (link_counted > EXT2_LINK_MAX)) link_counted = 1; if (link_counted != link_count) { e2fsck_read_inode(ctx, i, inode, "pass4"); pctx.ino = i; pctx.inode = inode; if ((link_count != inode->i_links_count) && !isdir && (inode->i_links_count <= EXT2_LINK_MAX)) { pctx.num = link_count; fix_problem(ctx, PR_4_INCONSISTENT_COUNT, &pctx); } pctx.num = link_counted; /* i_link_count was previously exceeded, but no longer * is, fix this but don't consider it an error */ if ((isdir && link_counted > 1 && (inode->i_flags & EXT2_INDEX_FL) && link_count == 1 && !(ctx->options & E2F_OPT_NO)) || fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) { inode->i_links_count = link_counted; e2fsck_write_inode(ctx, i, inode, "pass4"); } } } ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; ext2fs_free_inode_bitmap(ctx->inode_bb_map); ctx->inode_bb_map = 0; ext2fs_free_inode_bitmap(ctx->inode_imagic_map); ctx->inode_imagic_map = 0; errout: if (buf) ext2fs_free_mem(&buf); ext2fs_free_mem(&inode); print_resource_track(ctx, _("Pass 4"), &rtrack, ctx->fs->io); }
void e2fsck_pass2(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; ext2_filsys fs = ctx->fs; char *buf; #ifdef RESOURCE_TRACK struct resource_track rtrack; #endif struct check_dir_struct cd; struct dx_dir_info *dx_dir; struct dx_dirblock_info *dx_db, *dx_parent; unsigned int save_type; int b; int i, depth; problem_t code; int bad_dir; init_resource_track(&rtrack, ctx->fs->io); clear_problem_context(&cd.pctx); #ifdef MTRACE mtrace_print("Pass 2"); #endif if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); e2fsck_setup_tdb_icount(ctx, EXT2_ICOUNT_OPT_INCREMENT, &ctx->inode_count); if (ctx->inode_count) cd.pctx.errcode = 0; else { e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE, "inode_count", &save_type); cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT, 0, ctx->inode_link_info, &ctx->inode_count); fs->default_bitmap_type = save_type; } if (cd.pctx.errcode) { fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); ctx->flags |= E2F_FLAG_ABORT; return; } buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize, "directory scan buffer"); /* * Set up the parent pointer for the root directory, if * present. (If the root directory is not present, we will * create it in pass 3.) */ (void) e2fsck_dir_info_set_parent(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); cd.buf = buf; cd.ctx = ctx; cd.count = 1; cd.max = ext2fs_dblist_count2(fs->dblist); if (ctx->progress) (void) (ctx->progress)(ctx, 2, 0, cd.max); if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp); cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_block, &cd); if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) return; if (ctx->flags & E2F_FLAG_RESTART_LATER) { ctx->flags |= E2F_FLAG_RESTART; return; } if (cd.pctx.errcode) { fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); ctx->flags |= E2F_FLAG_ABORT; return; } #ifdef ENABLE_HTREE for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (dx_dir->numblocks == 0) continue; clear_problem_context(&pctx); bad_dir = 0; pctx.dir = dx_dir->ino; dx_db = dx_dir->dx_block; if (dx_db->flags & DX_FLAG_REFERENCED) dx_db->flags |= DX_FLAG_DUP_REF; else dx_db->flags |= DX_FLAG_REFERENCED; /* * Find all of the first and last leaf blocks, and * update their parent's min and max hash values */ for (b=0, dx_db = dx_dir->dx_block; b < dx_dir->numblocks; b++, dx_db++) { if ((dx_db->type != DX_DIRBLOCK_LEAF) || !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST))) continue; dx_parent = &dx_dir->dx_block[dx_db->parent]; /* * XXX Make sure dx_parent->min_hash > dx_db->min_hash */ if (dx_db->flags & DX_FLAG_FIRST) dx_parent->min_hash = dx_db->min_hash; /* * XXX Make sure dx_parent->max_hash < dx_db->max_hash */ if (dx_db->flags & DX_FLAG_LAST) dx_parent->max_hash = dx_db->max_hash; } for (b=0, dx_db = dx_dir->dx_block; b < dx_dir->numblocks; b++, dx_db++) { pctx.blkcount = b; pctx.group = dx_db->parent; code = 0; if (!(dx_db->flags & DX_FLAG_FIRST) && (dx_db->min_hash < dx_db->node_min_hash)) { pctx.blk = dx_db->min_hash; pctx.blk2 = dx_db->node_min_hash; code = PR_2_HTREE_MIN_HASH; fix_problem(ctx, code, &pctx); bad_dir++; } if (dx_db->type == DX_DIRBLOCK_LEAF) { depth = htree_depth(dx_dir, dx_db); if (depth != dx_dir->depth) { pctx.num = dx_dir->depth; code = PR_2_HTREE_BAD_DEPTH; fix_problem(ctx, code, &pctx); bad_dir++; } } /* * This test doesn't apply for the root block * at block #0 */ if (b && (dx_db->max_hash > dx_db->node_max_hash)) { pctx.blk = dx_db->max_hash; pctx.blk2 = dx_db->node_max_hash; code = PR_2_HTREE_MAX_HASH; fix_problem(ctx, code, &pctx); bad_dir++; } if (!(dx_db->flags & DX_FLAG_REFERENCED)) { code = PR_2_HTREE_NOTREF; fix_problem(ctx, code, &pctx); bad_dir++; } else if (dx_db->flags & DX_FLAG_DUP_REF) { code = PR_2_HTREE_DUPREF; fix_problem(ctx, code, &pctx); bad_dir++; } } if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) { clear_htree(ctx, dx_dir->ino); dx_dir->numblocks = 0; } } e2fsck_free_dx_dir_info(ctx); #endif ext2fs_free_mem(&buf); ext2fs_free_dblist(fs->dblist); if (ctx->inode_bad_map) { ext2fs_free_inode_bitmap(ctx->inode_bad_map); ctx->inode_bad_map = 0; } if (ctx->inode_reg_map) { ext2fs_free_inode_bitmap(ctx->inode_reg_map); ctx->inode_reg_map = 0; } clear_problem_context(&pctx); if (ctx->large_files) { if (!(sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) && fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; ext2fs_mark_super_dirty(fs); } if (sb->s_rev_level == EXT2_GOOD_OLD_REV && fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { ext2fs_update_dynamic_rev(fs); ext2fs_mark_super_dirty(fs); } } print_resource_track(ctx, _("Pass 2"), &rtrack, fs->io); }
static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) { struct process_block_struct pb; struct ext2_super_block *sb = ctx->fs->super; struct ext2_super_block jsuper; struct problem_context pctx; struct buffer_head *bh; struct inode *j_inode = NULL; struct kdev_s *dev_fs = NULL, *dev_journal; const char *journal_name = 0; journal_t *journal = NULL; errcode_t retval = 0; io_manager io_ptr = 0; unsigned long long start = 0; int ext_journal = 0; int tried_backup_jnl = 0; clear_problem_context(&pctx); journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); if (!journal) { return EXT2_ET_NO_MEMORY; } dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); if (!dev_fs) { retval = EXT2_ET_NO_MEMORY; goto errout; } dev_journal = dev_fs+1; dev_fs->k_ctx = dev_journal->k_ctx = ctx; dev_fs->k_dev = K_DEV_FS; dev_journal->k_dev = K_DEV_JOURNAL; journal->j_dev = dev_journal; journal->j_fs_dev = dev_fs; journal->j_inode = NULL; journal->j_blocksize = ctx->fs->blocksize; if (uuid_is_null(sb->s_journal_uuid)) { if (!sb->s_journal_inum) { retval = EXT2_ET_BAD_INODE_NUM; goto errout; } j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), "journal inode"); if (!j_inode) { retval = EXT2_ET_NO_MEMORY; goto errout; } j_inode->i_ctx = ctx; j_inode->i_ino = sb->s_journal_inum; if ((retval = ext2fs_read_inode(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2))) { try_backup_journal: if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || tried_backup_jnl) goto errout; memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, EXT2_N_BLOCKS*4); j_inode->i_ext2.i_size_high = sb->s_jnl_blocks[15]; j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; j_inode->i_ext2.i_links_count = 1; j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; e2fsck_use_inode_shortcuts(ctx, 1); ctx->stashed_ino = j_inode->i_ino; ctx->stashed_inode = &j_inode->i_ext2; tried_backup_jnl++; } if (!j_inode->i_ext2.i_links_count || !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { retval = EXT2_ET_NO_JOURNAL; goto try_backup_journal; } if (EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize < JFS_MIN_JOURNAL_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } pb.last_block = -1; retval = ext2fs_block_iterate3(ctx->fs, j_inode->i_ino, BLOCK_FLAG_HOLE, 0, process_journal_block, &pb); if ((pb.last_block + 1) * ctx->fs->blocksize < (int) EXT2_I_SIZE(&j_inode->i_ext2)) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } if (tried_backup_jnl && !(ctx->options & E2F_OPT_READONLY)) { retval = ext2fs_write_inode(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2); if (retval) goto errout; } journal->j_maxlen = EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize; #ifdef USE_INODE_IO retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2, &journal_name); if (retval) goto errout; io_ptr = inode_io_manager; #else journal->j_inode = j_inode; ctx->journal_io = ctx->fs->io; if ((retval = (errcode_t) journal_bmap(journal, 0, &start)) != 0) goto errout; #endif } else { ext_journal = 1; if (!ctx->journal_name) { char uuid[37]; uuid_unparse(sb->s_journal_uuid, uuid); ctx->journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid); if (!ctx->journal_name) ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); } journal_name = ctx->journal_name; if (!journal_name) { fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } jfs_debug(1, "Using journal file %s\n", journal_name); io_ptr = unix_io_manager; } #if 0 test_io_backing_manager = io_ptr; io_ptr = test_io_manager; #endif #ifndef USE_INODE_IO if (ext_journal) #endif { int flags = IO_FLAG_RW; if (!(ctx->mount_flags & EXT2_MF_ISROOT && ctx->mount_flags & EXT2_MF_READONLY)) flags |= IO_FLAG_EXCLUSIVE; if ((ctx->mount_flags & EXT2_MF_READONLY) && (ctx->options & E2F_OPT_FORCE)) flags &= ~IO_FLAG_EXCLUSIVE; retval = io_ptr->open(journal_name, flags, &ctx->journal_io); } if (retval) goto errout; io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); if (ext_journal) { blk64_t maxlen; start = ext2fs_journal_sb_start(ctx->fs->blocksize) - 1; bh = getblk(dev_journal, start, ctx->fs->blocksize); if (!bh) { retval = EXT2_ET_NO_MEMORY; goto errout; } ll_rw_block(READ, 1, &bh); if ((retval = bh->b_err) != 0) { brelse(bh); goto errout; } memcpy(&jsuper, start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET, sizeof(jsuper)); #ifdef WORDS_BIGENDIAN if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(&jsuper); #endif if (jsuper.s_magic != EXT2_SUPER_MAGIC || !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; brelse(bh); goto errout; } /* Make sure the journal UUID is correct */ if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, sizeof(jsuper.s_uuid))) { fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; brelse(bh); goto errout; } /* Check the superblock checksum */ if (jsuper.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { struct struct_ext2_filsys fsx; struct ext2_super_block superx; void *p; p = start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET; memcpy(&fsx, ctx->fs, sizeof(fsx)); memcpy(&superx, ctx->fs->super, sizeof(superx)); fsx.super = &superx; fsx.super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_METADATA_CSUM; if (!ext2fs_superblock_csum_verify(&fsx, p) && fix_problem(ctx, PR_0_EXT_JOURNAL_SUPER_CSUM_INVALID, &pctx)) { ext2fs_superblock_csum_set(&fsx, p); mark_buffer_dirty(bh); } } brelse(bh); maxlen = ext2fs_blocks_count(&jsuper); journal->j_maxlen = (maxlen < 1ULL << 32) ? maxlen : (1ULL << 32) - 1; start++; } if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { retval = EXT2_ET_NO_MEMORY; goto errout; } journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data; #ifdef USE_INODE_IO if (j_inode) ext2fs_free_mem(&j_inode); #endif *ret_journal = journal; e2fsck_use_inode_shortcuts(ctx, 0); return 0; errout: e2fsck_use_inode_shortcuts(ctx, 0); if (dev_fs) ext2fs_free_mem(&dev_fs); if (j_inode) ext2fs_free_mem(&j_inode); if (journal) ext2fs_free_mem(&journal); return retval; }
void e2fsck_pass2(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; ext2_filsys fs = ctx->fs; char *buf; #ifdef RESOURCE_TRACK struct resource_track rtrack; #endif struct dir_info *dir; struct check_dir_struct cd; #ifdef RESOURCE_TRACK init_resource_track(&rtrack); #endif clear_problem_context(&cd.pctx); #ifdef MTRACE mtrace_print("Pass 2"); #endif if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT, 0, ctx->inode_link_info, &ctx->inode_count); if (cd.pctx.errcode) { fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); ctx->flags |= E2F_FLAG_ABORT; return; } buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize, "directory scan buffer"); /* * Set up the parent pointer for the root directory, if * present. (If the root directory is not present, we will * create it in pass 3.) */ dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO); if (dir) dir->parent = EXT2_ROOT_INO; cd.buf = buf; cd.ctx = ctx; cd.count = 1; cd.max = ext2fs_dblist_count(fs->dblist); if (ctx->progress) (void) (ctx->progress)(ctx, 2, 0, cd.max); cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block, &cd); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (cd.pctx.errcode) { fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_free_mem((void **) &buf); ext2fs_free_dblist(fs->dblist); if (ctx->inode_bad_map) { ext2fs_free_inode_bitmap(ctx->inode_bad_map); ctx->inode_bad_map = 0; } if (ctx->inode_reg_map) { ext2fs_free_inode_bitmap(ctx->inode_reg_map); ctx->inode_reg_map = 0; } clear_problem_context(&pctx); if (ctx->large_files) { if (!(sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) && fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; ext2fs_mark_super_dirty(fs); } if (sb->s_rev_level == EXT2_GOOD_OLD_REV && fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { ext2fs_update_dynamic_rev(fs); ext2fs_mark_super_dirty(fs); } } else if (!ctx->large_files && (sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) { if (fs->flags & EXT2_FLAG_RW) { sb->s_feature_ro_compat &= ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE; ext2fs_mark_super_dirty(fs); } } #ifdef RESOURCE_TRACK if (ctx->options & E2F_OPT_TIME2) { e2fsck_clear_progbar(ctx); print_resource_track("Pass 2", &rtrack); } #endif }
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); }
void e2fsck_pass4(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t i; struct ext2_inode inode; #ifdef RESOURCE_TRACK struct resource_track rtrack; #endif struct problem_context pctx; __u16 link_count, link_counted; char *buf = 0; int group, maxgroup; #ifdef RESOURCE_TRACK init_resource_track(&rtrack); #endif #ifdef MTRACE mtrace_print("Pass 4"); #endif clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_4_PASS_HEADER, &pctx); group = 0; maxgroup = fs->group_desc_count; if (ctx->progress) if ((ctx->progress)(ctx, 4, 0, maxgroup)) return; /* Protect loop from wrap-around if s_inodes_count maxed */ for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto errout; if ((i % fs->super->s_inodes_per_group) == 0) { group++; if (ctx->progress) if ((ctx->progress)(ctx, 4, group, maxgroup)) goto errout; } if (i == EXT2_BAD_INO || (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) continue; if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) || (ctx->inode_imagic_map && ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)) || (ctx->inode_bb_map && ext2fs_test_inode_bitmap(ctx->inode_bb_map, i))) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); if (link_counted == 0) { if (!buf) buf = e2fsck_allocate_memory(ctx, fs->blocksize, "bad_inode buffer"); if (e2fsck_process_bad_inode(ctx, 0, i, buf)) continue; if (disconnect_inode(ctx, i)) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); } if (link_counted != link_count) { e2fsck_read_inode(ctx, i, &inode, "pass4"); pctx.ino = i; pctx.inode = &inode; if (link_count != inode.i_links_count) { pctx.num = link_count; fix_problem(ctx, PR_4_INCONSISTENT_COUNT, &pctx); } pctx.num = link_counted; if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) { inode.i_links_count = link_counted; e2fsck_write_inode(ctx, i, &inode, "pass4"); } } } ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; ext2fs_free_inode_bitmap(ctx->inode_bb_map); ctx->inode_bb_map = 0; ext2fs_free_inode_bitmap(ctx->inode_imagic_map); ctx->inode_imagic_map = 0; errout: if (buf) ext2fs_free_mem(&buf); #ifdef RESOURCE_TRACK if (ctx->options & E2F_OPT_TIME2) { e2fsck_clear_progbar(ctx); print_resource_track(_("Pass 4"), &rtrack); } #endif }