static void check_inode_bitmap_checksum(e2fsck_t ctx) { struct problem_context pctx; char *buf; dgrp_t i; int nbytes; ext2_ino_t ino_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_ib_dirty(ctx->fs)) return; nbytes = (size_t)(EXT2_INODES_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_inode_bitmap_checksum: Memory allocation error")); fatal_error(ctx, 0); } clear_problem_context(&pctx); for (i = 0; i < ctx->fs->group_desc_count; i++) { if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_INODE_UNINIT)) continue; ino_itr = 1 + (i * (nbytes << 3)); retval = ext2fs_get_inode_bitmap_range2(ctx->fs->inode_map, ino_itr, nbytes << 3, buf); if (retval) break; if (ext2fs_inode_bitmap_csum_verify(ctx->fs, i, buf, nbytes)) continue; pctx.group = i; if (!fix_problem(ctx, PR_5_INODE_BITMAP_CSUM_INVALID, &pctx)) continue; /* * Fixing one checksum will rewrite all of them. The bitmap * will be checked against the one we made during pass1 for * discrepancies, and fixed if need be. */ ext2fs_mark_ib_dirty(ctx->fs); break; } ext2fs_free_mem(&buf); }
static void reserve_inodes(ext2_filsys fs) { ext2_ino_t i; int group; for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) { ext2fs_mark_inode_bitmap(fs->inode_map, i); group = ext2fs_group_of_ino(fs, i); fs->group_desc[group].bg_free_inodes_count--; fs->super->s_free_inodes_count--; } ext2fs_mark_ib_dirty(fs); }
void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, int inuse, int isdir) { int group = ext2fs_group_of_ino(fs, ino); if (inuse > 0) ext2fs_mark_inode_bitmap(fs->inode_map, ino); else ext2fs_unmark_inode_bitmap(fs->inode_map, ino); fs->group_desc[group].bg_free_inodes_count -= inuse; if (isdir) fs->group_desc[group].bg_used_dirs_count += inuse; fs->super->s_free_inodes_count -= inuse; ext2fs_mark_super_dirty(fs); ext2fs_mark_ib_dirty(fs); }
/* * 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 for uninit inode bitmaps and deal with them appropriately */ static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map, dgrp_t group) { ext2_ino_t i, ino; if (!ext2fs_has_group_desc_csum(fs) || !(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT))) return; ino = (group * fs->super->s_inodes_per_group) + 1; for (i=0; i < fs->super->s_inodes_per_group; i++, ino++) ext2fs_fast_unmark_inode_bitmap2(map, ino); ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); ext2fs_group_desc_csum_set(fs, group); ext2fs_mark_ib_dirty(fs); ext2fs_mark_super_dirty(fs); check_block_uninit(fs, fs->block_map, group); }
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; } }
void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, int inuse, int isdir) { int group = ext2fs_group_of_ino(fs, ino); #ifndef OMIT_COM_ERR if (ino > fs->super->s_inodes_count) { com_err("ext2fs_inode_alloc_stats2", 0, "Illegal inode number: %lu", (unsigned long) ino); return; } #endif if (inuse > 0) ext2fs_mark_inode_bitmap(fs->inode_map, ino); else ext2fs_unmark_inode_bitmap(fs->inode_map, ino); fs->group_desc[group].bg_free_inodes_count -= inuse; if (isdir) fs->group_desc[group].bg_used_dirs_count += inuse; /* We don't strictly need to be clearing the uninit flag if inuse < 0 * (i.e. freeing inodes) but it also means something is bad. */ fs->group_desc[group].bg_flags &= ~EXT2_BG_INODE_UNINIT; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { ext2_ino_t first_unused_inode = fs->super->s_inodes_per_group - fs->group_desc[group].bg_itable_unused + group * fs->super->s_inodes_per_group + 1; if (ino >= first_unused_inode) fs->group_desc[group].bg_itable_unused = group * fs->super->s_inodes_per_group + fs->super->s_inodes_per_group - ino; ext2fs_group_desc_csum_set(fs, group); } fs->super->s_free_inodes_count -= inuse; ext2fs_mark_super_dirty(fs); ext2fs_mark_ib_dirty(fs); }
errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype) { ext2_ino_t qf_ino; errcode_t retval; retval = ext2fs_read_bitmaps(fs); if (retval) { log_debug("Couldn't read bitmaps: %s", error_message(retval)); return retval; } qf_ino = *quota_sb_inump(fs->super, qtype); if (qf_ino == 0) return 0; retval = quota_inode_truncate(fs, qf_ino); if (retval) return retval; if (qf_ino >= EXT2_FIRST_INODE(fs->super)) { struct ext2_inode inode; retval = ext2fs_read_inode(fs, qf_ino, &inode); if (!retval) { memset(&inode, 0, sizeof(struct ext2_inode)); ext2fs_write_inode(fs, qf_ino, &inode); } ext2fs_inode_alloc_stats2(fs, qf_ino, -1, 0); ext2fs_mark_ib_dirty(fs); } quota_set_sb_inum(fs, 0, qtype); ext2fs_mark_super_dirty(fs); fs->flags &= ~EXT2_FLAG_SUPER_ONLY; retval = ext2fs_write_bitmaps(fs); if (retval) { log_debug("Couldn't write bitmaps: %s", error_message(retval)); return retval; } return 0; }
void e2fsck_move_ext3_journal(e2fsck_t ctx) { struct ext2_super_block *sb = ctx->fs->super; struct problem_context pctx; struct ext2_inode inode; ext2_filsys fs = ctx->fs; ext2_ino_t ino; errcode_t retval; const char * const * cpp; int group, mount_flags; clear_problem_context(&pctx); /* * If the filesystem is opened read-only, or there is no * journal, then do nothing. */ if ((ctx->options & E2F_OPT_READONLY) || (sb->s_journal_inum == 0) || !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) return; /* * Read in the journal inode */ if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0) return; /* * If it's necessary to backup the journal inode, do so. */ if ((sb->s_jnl_backup_type == 0) || ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) && memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) { if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) { memcpy(sb->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); sb->s_jnl_blocks[16] = inode.i_size; sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; ext2fs_mark_super_dirty(fs); fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; } } /* * If the journal is already the hidden inode, then do nothing */ if (sb->s_journal_inum == EXT2_JOURNAL_INO) return; /* * The journal inode had better have only one link and not be readable. */ if (inode.i_links_count != 1) return; /* * If the filesystem is mounted, or we can't tell whether * or not it's mounted, do nothing. */ retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags); if (retval || (mount_flags & EXT2_MF_MOUNTED)) return; /* * If we can't find the name of the journal inode, then do * nothing. */ for (cpp = journal_names; *cpp; cpp++) { retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp, strlen(*cpp), 0, &ino); if ((retval == 0) && (ino == sb->s_journal_inum)) break; } if (*cpp == 0) return; /* We need the inode bitmap to be loaded */ retval = ext2fs_read_bitmaps(fs); if (retval) return; pctx.str = *cpp; if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx)) return; /* * OK, we've done all the checks, let's actually move the * journal inode. Errors at this point mean we need to force * an ext2 filesystem check. */ if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0) goto err_out; if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0) goto err_out; sb->s_journal_inum = EXT2_JOURNAL_INO; ext2fs_mark_super_dirty(fs); fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; inode.i_links_count = 0; inode.i_dtime = ctx->now; if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0) goto err_out; group = ext2fs_group_of_ino(fs, ino); ext2fs_unmark_inode_bitmap(fs->inode_map, ino); ext2fs_mark_ib_dirty(fs); fs->group_desc[group].bg_free_inodes_count++; fs->super->s_free_inodes_count++; return; err_out: pctx.errcode = retval; fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx); fs->super->s_state &= ~EXT2_VALID_FS; ext2fs_mark_super_dirty(fs); return; }
/* * This makes sure the root inode is present; if not, we ask if the * user wants us to create it. Not creating it is a fatal error. */ static void check_root(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk64_t blk; struct ext2_inode inode; char * block; struct problem_context pctx; clear_problem_context(&pctx); if (ext2fs_test_inode_bitmap2(ctx->inode_used_map, EXT2_ROOT_INO)) { /* * If the root inode is not a directory, die here. The * user must have answered 'no' in pass1 when we * offered to clear it. */ if (!(ext2fs_test_inode_bitmap2(ctx->inode_dir_map, EXT2_ROOT_INO))) { fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx); ctx->flags |= E2F_FLAG_ABORT; } return; } if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) { fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } e2fsck_read_bitmaps(ctx); /* * First, find a free block */ pctx.errcode = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk); if (pctx.errcode) { pctx.str = "ext2fs_new_block"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); ext2fs_mark_block_bitmap2(fs->block_map, blk); ext2fs_mark_bb_dirty(fs); /* * Now let's create the actual data block for the inode */ pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, &block); if (pctx.errcode) { pctx.str = "ext2fs_new_dir_block"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } pctx.errcode = ext2fs_write_dir_block4(fs, blk, block, 0, EXT2_ROOT_INO); if (pctx.errcode) { pctx.str = "ext2fs_write_dir_block"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } ext2fs_free_mem(&block); /* * Set up the inode structure */ memset(&inode, 0, sizeof(inode)); inode.i_mode = 040755; inode.i_size = fs->blocksize; inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now; inode.i_links_count = 2; ext2fs_iblk_set(fs, &inode, 1); inode.i_block[0] = blk; /* * Write out the inode. */ pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode); if (pctx.errcode) { pctx.str = "ext2fs_write_inode"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } /* * Miscellaneous bookkeeping... */ e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2); ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2); ext2fs_mark_inode_bitmap2(ctx->inode_used_map, EXT2_ROOT_INO); ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, EXT2_ROOT_INO); ext2fs_mark_inode_bitmap2(fs->inode_map, EXT2_ROOT_INO); ext2fs_mark_ib_dirty(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; 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); }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; int frags_per_block; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk_t numblocks; int rsv_gdt; int io_flags; char *buf; char c; if (!param || !param->s_blocks_count) return EXT2_ET_INVALID_ARGUMENT; retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags | EXT2_FLAG_RW; fs->umask = 022; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif io_flags = IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; retval = manager->open(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_first_meta_bg, 0); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); } set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->blocksize = EXT2_BLOCK_SIZE(super); fs->fragsize = EXT2_FRAG_SIZE(super); frags_per_block = fs->blocksize / fs->fragsize; /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; super->s_blocks_count = param->s_blocks_count; super->s_r_blocks_count = param->s_r_blocks_count; if (super->s_r_blocks_count >= param->s_blocks_count) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = ext2fs_div_ceil(super->s_blocks_count - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; set_field(s_inodes_count, super->s_blocks_count / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; super->s_blocks_count = param->s_blocks_count; super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; goto retry; } else return EXT2_ET_TOO_MANY_INODES; } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Overhead is the number of bookkeeping blocks per group. It * includes the superblock backup, the group descriptor * backups, the inode bitmap, the block bitmap, and the inode * table. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) return EXT2_ET_TOO_MANY_INODES; /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. */ rem = ((super->s_blocks_count - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) return EXT2_ET_TOOSMALL; if (rem && (rem < overhead+50)) { super->s_blocks_count -= rem; goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; sprintf(buf, "block bitmap for %s", fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; sprintf(buf, "inode bitmap for %s", fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. */ super->s_free_blocks_count = 0; for (i = 0; i < fs->group_desc_count; i++) { numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); super->s_free_blocks_count += numblocks; fs->group_desc[i].bg_free_blocks_count = numblocks; fs->group_desc[i].bg_free_inodes_count = fs->super->s_inodes_per_group; fs->group_desc[i].bg_used_dirs_count = 0; } c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); return retval; }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk64_t free_blocks; blk_t numblocks; int rsv_gdt; int csum_flag; int bigalloc_flag; int io_flags; unsigned reserved_inos; char *buf = 0; char c; double reserved_ratio; if (!param || !ext2fs_blocks_count(param)) return EXT2_ET_INVALID_ARGUMENT; retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags | EXT2_FLAG_RW; fs->umask = 022; fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif io_flags = IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; if (flags & EXT2_FLAG_DIRECT_IO) io_flags |= IO_FLAG_DIRECT_IO; retval = manager->open(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) #define assign_field(field) (super->field = param->field) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; bigalloc_flag = EXT2_HAS_RO_COMPAT_FEATURE(param, EXT4_FEATURE_RO_COMPAT_BIGALLOC); assign_field(s_log_block_size); if (bigalloc_flag) { set_field(s_log_cluster_size, super->s_log_block_size+4); if (super->s_log_block_size > super->s_log_cluster_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } } else super->s_log_cluster_size = super->s_log_block_size; set_field(s_first_data_block, super->s_log_cluster_size ? 0 : 1); set_field(s_max_mnt_count, 0); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_default_mount_opts, 0); set_field(s_first_meta_bg, 0); set_field(s_raid_stride, 0); /* default stride size: 0 */ set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */ set_field(s_log_groups_per_flex, 0); set_field(s_flags, 0); assign_field(s_backup_bgs[0]); assign_field(s_backup_bgs[1]); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); if (super->s_inode_size >= sizeof(struct ext2_inode_large)) { int extra_isize = sizeof(struct ext2_inode_large) - EXT2_GOOD_OLD_INODE_SIZE; set_field(s_min_extra_isize, extra_isize); set_field(s_want_extra_isize, extra_isize); } } else { super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; super->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; } set_field(s_checkinterval, 0); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(super); fs->cluster_ratio_bits = super->s_log_cluster_size - super->s_log_block_size; if (bigalloc_flag) { unsigned long long bpg; if (param->s_blocks_per_group && param->s_clusters_per_group && ((param->s_clusters_per_group * EXT2FS_CLUSTER_RATIO(fs)) != param->s_blocks_per_group)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } if (param->s_clusters_per_group) assign_field(s_clusters_per_group); else if (param->s_blocks_per_group) super->s_clusters_per_group = param->s_blocks_per_group / EXT2FS_CLUSTER_RATIO(fs); else if (super->s_log_cluster_size + 15 < 32) super->s_clusters_per_group = fs->blocksize * 8; else super->s_clusters_per_group = (fs->blocksize - 1) * 8; if (super->s_clusters_per_group > EXT2_MAX_CLUSTERS_PER_GROUP(super)) super->s_clusters_per_group = EXT2_MAX_CLUSTERS_PER_GROUP(super); bpg = EXT2FS_C2B(fs, (unsigned long long) super->s_clusters_per_group); if (bpg >= (((unsigned long long) 1) << 32)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } super->s_blocks_per_group = bpg; } else { set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_clusters_per_group = super->s_blocks_per_group; } ext2fs_blocks_count_set(super, ext2fs_blocks_count(param) & ~((blk64_t) EXT2FS_CLUSTER_MASK(fs))); ext2fs_r_blocks_count_set(super, ext2fs_r_blocks_count(param)); if (ext2fs_r_blocks_count(super) >= ext2fs_blocks_count(param)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } set_field(s_mmp_update_interval, 0); /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = (dgrp_t) ext2fs_div64_ceil( ext2fs_blocks_count(super) - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } set_field(s_desc_size, super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? EXT2_MIN_DESC_SIZE_64BIT : 0); fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT && (ext2fs_blocks_count(super) / i) > (1ULL << 32)) set_field(s_inodes_count, ~0U); else set_field(s_inodes_count, ext2fs_blocks_count(super) / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (!bigalloc_flag && super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; ext2fs_blocks_count_set(super, ext2fs_blocks_count(param)); super->s_clusters_per_group = super->s_blocks_per_group; goto retry; } else { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ if (super->s_inodes_per_group < 8) super->s_inodes_per_group = 8; super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Calculate the maximum number of bookkeeping blocks per * group. It includes the superblock, the block group * descriptors, the block bitmap, the inode bitmap, the inode * table, and the reserved gdt blocks. */ overhead = (int) (3 + fs->inode_blocks_per_group + fs->desc_blocks + super->s_reserved_gdt_blocks); /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. We need to recalculate the overhead for the last block * group, since it might or might not have a superblock * backup. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; rem = ((ext2fs_blocks_count(super) - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { retval = EXT2_ET_TOOSMALL; goto cleanup; } if (rem && (rem < overhead+50)) { ext2fs_blocks_count_set(super, ext2fs_blocks_count(super) - rem); /* * If blocks count is changed, we need to recalculate * reserved blocks count not to exceed 50%. */ reserved_ratio = 100.0 * ext2fs_r_blocks_count(param) / ext2fs_blocks_count(param); ext2fs_r_blocks_count_set(super, reserved_ratio * ext2fs_blocks_count(super) / 100.0); goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ /* Set up the locations of the backup superblocks */ if (super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) { if (super->s_backup_bgs[0] >= fs->group_desc_count) super->s_backup_bgs[0] = fs->group_desc_count - 1; if (super->s_backup_bgs[1] >= fs->group_desc_count) super->s_backup_bgs[1] = fs->group_desc_count - 1; if (super->s_backup_bgs[0] == super->s_backup_bgs[1]) super->s_backup_bgs[1] = 0; if (super->s_backup_bgs[0] > super->s_backup_bgs[1]) { __u32 t = super->s_backup_bgs[0]; super->s_backup_bgs[0] = super->s_backup_bgs[1]; super->s_backup_bgs[1] = t; } } retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_subcluster_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. * * If FLEX_BG meta-data grouping is used, only account for the * superblock and group descriptors (the inode tables and * bitmaps will be accounted for when allocated). */ free_blocks = 0; csum_flag = ext2fs_has_group_desc_csum(fs); reserved_inos = super->s_first_ino; for (i = 0; i < fs->group_desc_count; i++) { /* * Don't set the BLOCK_UNINIT group for the last group * because the block bitmap needs to be padded. */ if (csum_flag) { if (i != fs->group_desc_count - 1) ext2fs_bg_flags_set(fs, i, EXT2_BG_BLOCK_UNINIT); ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); numblocks = super->s_inodes_per_group; if (reserved_inos) { if (numblocks > reserved_inos) { numblocks -= reserved_inos; reserved_inos = 0; } else { reserved_inos -= numblocks; numblocks = 0; } } ext2fs_bg_itable_unused_set(fs, i, numblocks); } numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); if (fs->super->s_log_groups_per_flex) numblocks += 2 + fs->inode_blocks_per_group; free_blocks += numblocks; ext2fs_bg_free_blocks_count_set(fs, i, numblocks); ext2fs_bg_free_inodes_count_set(fs, i, fs->super->s_inodes_per_group); ext2fs_bg_used_dirs_count_set(fs, i, 0); ext2fs_group_desc_csum_set(fs, i); } free_blocks &= ~EXT2FS_CLUSTER_MASK(fs); ext2fs_free_blocks_count_set(super, free_blocks); c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: free(buf); ext2fs_free(fs); return retval; }
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); }