errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { __u32 curr_value; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; if (ext2fs_test_inode_bitmap2(icount->single, ino)) { /* * If the existing count is 1, then we know there is * no entry in the list. */ if (set_inode_count(icount, ino, 2)) return EXT2_ET_NO_MEMORY; curr_value = 2; ext2fs_unmark_inode_bitmap2(icount->single, ino); } else if (icount->multiple) { /* * The count is either zero or greater than 1; if the * inode is set in icount->multiple, then there should * be an entry in the list, so we need to fix it. */ if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) { get_inode_count(icount, ino, &curr_value); curr_value++; if (set_inode_count(icount, ino, curr_value)) return EXT2_ET_NO_MEMORY; } else { /* * The count was zero; mark the single bitmap * and return. */ ext2fs_mark_inode_bitmap2(icount->single, ino); if (ret) *ret = 1; return 0; } } else { /* * The count is either zero or greater than 1; try to * find an entry in the list to determine which. */ get_inode_count(icount, ino, &curr_value); curr_value++; if (set_inode_count(icount, ino, curr_value)) return EXT2_ET_NO_MEMORY; } if (icount->multiple) ext2fs_mark_inode_bitmap2(icount->multiple, ino); if (ret) *ret = icount_16_xlate(curr_value); return 0; }
void do_cleari(int argc, char *argv[]) { unsigned int inode; int err; int test_result, op_result; if (check_fs_open(argv[0])) return; if (argc != 2) { com_err(argv[0], 0, "Usage: clearb <inode>"); return; } inode = parse_ulong(argv[1], argv[0], "inode", &err); if (err) return; test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); op_result = ext2fs_unmark_inode_bitmap2(test_fs->inode_map, inode); printf("Clearing inode %u, was %s before\n", inode, op_result ? "set" : "clear"); if (!test_result != !op_result) { com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", test_result, op_result); exit_status++; } }
/* * Check the directory filetype (if present) */ static _INLINE_ int check_filetype(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t dir_ino EXT2FS_ATTR((unused)), struct problem_context *pctx) { int filetype = dirent->name_len >> 8; int should_be = EXT2_FT_UNKNOWN; struct ext2_inode inode; if (!(ctx->fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) { if (filetype == 0 || !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx)) return 0; dirent->name_len = dirent->name_len & 0xFF; return 1; } if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode)) { should_be = EXT2_FT_DIR; } else if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map, dirent->inode)) { should_be = EXT2_FT_REG_FILE; } else if (ctx->inode_bad_map && ext2fs_test_inode_bitmap2(ctx->inode_bad_map, dirent->inode)) should_be = 0; else { e2fsck_read_inode(ctx, dirent->inode, &inode, "check_filetype"); should_be = ext2_file_type(inode.i_mode); } if (filetype == should_be) return 0; pctx->num = should_be; if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, pctx) == 0) return 0; dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8; return 1; }
errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { __u32 curr_value; if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (ext2fs_test_inode_bitmap2(icount->single, ino)) { ext2fs_unmark_inode_bitmap2(icount->single, ino); if (icount->multiple) ext2fs_unmark_inode_bitmap2(icount->multiple, ino); else { set_inode_count(icount, ino, 0); } if (ret) *ret = 0; return 0; } if (icount->multiple && !ext2fs_test_inode_bitmap2(icount->multiple, ino)) return EXT2_ET_INVALID_ARGUMENT; get_inode_count(icount, ino, &curr_value); if (!curr_value) return EXT2_ET_INVALID_ARGUMENT; curr_value--; if (set_inode_count(icount, ino, curr_value)) return EXT2_ET_NO_MEMORY; if (curr_value == 1) ext2fs_mark_inode_bitmap2(icount->single, ino); if ((curr_value == 0) && icount->multiple) ext2fs_unmark_inode_bitmap2(icount->multiple, ino); if (ret) *ret = icount_16_xlate(curr_value); return 0; }
/* * Right now, just search forward from the parent directory's block * group to find the next free inode. * * Should have a special policy for directories. */ errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode EXT2FS_ATTR((unused)), ext2fs_inode_bitmap map, ext2_ino_t *ret) { ext2_ino_t start_inode = 0; ext2_ino_t i, ino_in_group, upto, first_zero; errcode_t retval; dgrp_t group; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->inode_map; if (!map) return EXT2_ET_NO_INODE_BITMAP; if (dir > 0) { group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); start_inode = (group * EXT2_INODES_PER_GROUP(fs->super)) + 1; } if (start_inode < EXT2_FIRST_INODE(fs->super)) start_inode = EXT2_FIRST_INODE(fs->super); if (start_inode > fs->super->s_inodes_count) return EXT2_ET_INODE_ALLOC_FAIL; i = start_inode; do { ino_in_group = (i - 1) % EXT2_INODES_PER_GROUP(fs->super); group = (i - 1) / EXT2_INODES_PER_GROUP(fs->super); check_inode_uninit(fs, map, group); upto = i + (EXT2_INODES_PER_GROUP(fs->super) - ino_in_group); if (i < start_inode && upto >= start_inode) upto = start_inode - 1; if (upto > fs->super->s_inodes_count) upto = fs->super->s_inodes_count; retval = ext2fs_find_first_zero_inode_bitmap2(map, i, upto, &first_zero); if (retval == 0) { i = first_zero; break; } if (retval != ENOENT) return EXT2_ET_INODE_ALLOC_FAIL; i = upto + 1; if (i > fs->super->s_inodes_count) i = EXT2_FIRST_INODE(fs->super); } while (i != start_inode); if (ext2fs_test_inode_bitmap2(map, i)) return EXT2_ET_INODE_ALLOC_FAIL; *ret = i; return 0; }
errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { __u32 val; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; if (ext2fs_test_inode_bitmap2(icount->single, ino)) { *ret = 1; return 0; } if (icount->multiple && !ext2fs_test_inode_bitmap2(icount->multiple, ino)) { *ret = 0; return 0; } get_inode_count(icount, ino, &val); *ret = icount_16_xlate(val); return 0; }
/* * Right now, just search forward from the parent directory's block * group to find the next free inode. * * Should have a special policy for directories. */ errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode EXT2FS_ATTR((unused)), ext2fs_inode_bitmap map, ext2_ino_t *ret) { ext2_ino_t dir_group = 0; ext2_ino_t i; ext2_ino_t start_inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->inode_map; if (!map) return EXT2_ET_NO_INODE_BITMAP; if (dir > 0) dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1; if (start_inode < EXT2_FIRST_INODE(fs->super)) start_inode = EXT2_FIRST_INODE(fs->super); if (start_inode > fs->super->s_inodes_count) return EXT2_ET_INODE_ALLOC_FAIL; i = start_inode; do { if (((i - 1) % EXT2_INODES_PER_GROUP(fs->super)) == 0) check_inode_uninit(fs, map, (i - 1) / EXT2_INODES_PER_GROUP(fs->super)); if (!ext2fs_fast_test_inode_bitmap2(map, i)) break; i++; if (i > fs->super->s_inodes_count) i = EXT2_FIRST_INODE(fs->super); } while (i != start_inode); if (ext2fs_test_inode_bitmap2(map, i)) return EXT2_ET_INODE_ALLOC_FAIL; *ret = i; return 0; }
void do_testi(int argc, char *argv[]) { unsigned int inode; int err; int test_result; if (check_fs_open(argv[0])) return; if (argc != 2) { com_err(argv[0], 0, "Usage: testb <inode>"); return; } inode = parse_ulong(argv[1], argv[0], "inode", &err); if (err) return; test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); printf("Inode %u is %s\n", inode, test_result ? "set" : "clear"); }
/* * Verify the touched map */ static void check_map(void) { int i, j, first=1; blk64_t blk; for (i=0; test_vec[i]; i++) { if (ext2fs_test_block_bitmap2(touched_map, test_vec[i])) { printf("Bad block was touched --- %llu\n", test_vec[i]); failed++; first_no_comma = 1; } ext2fs_mark_block_bitmap2(touched_map, test_vec[i]); } for (i = 0; i < test_fs->group_desc_count; i++) { for (j=0, blk = ext2fs_inode_table_loc(test_fs, i); j < test_fs->inode_blocks_per_group; j++, blk++) { if (!ext2fs_test_block_bitmap2(touched_map, blk) && !ext2fs_test_block_bitmap2(bad_block_map, blk)) { printf("Missing block --- %llu\n", blk); failed++; } } } printf("Bad inodes: "); for (i=1; i <= test_fs->super->s_inodes_count; i++) { if (ext2fs_test_inode_bitmap2(bad_inode_map, i)) { if (first) first = 0; else printf(", "); printf("%u", i); } } printf("\n"); }
/* Make a special files (block and character devices), fifo's, and sockets */ errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name, struct stat *st) { ext2_ino_t ino; errcode_t retval; struct ext2_inode inode; unsigned long devmajor, devminor, mode; int filetype; switch(st->st_mode & S_IFMT) { case S_IFCHR: mode = LINUX_S_IFCHR; filetype = EXT2_FT_CHRDEV; break; case S_IFBLK: mode = LINUX_S_IFBLK; filetype = EXT2_FT_BLKDEV; break; case S_IFIFO: mode = LINUX_S_IFIFO; filetype = EXT2_FT_FIFO; break; case S_IFSOCK: mode = LINUX_S_IFSOCK; filetype = EXT2_FT_SOCK; break; default: return EXT2_ET_INVALID_ARGUMENT; } if (!(fs->flags & EXT2_FLAG_RW)) { com_err(__func__, 0, "Filesystem opened read/only"); return EROFS; } retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino); if (retval) { com_err(__func__, retval, 0); return retval; } #ifdef DEBUGFS printf("Allocated inode: %u\n", ino); #endif retval = ext2fs_link(fs, cwd, name, ino, filetype); if (retval == EXT2_ET_DIR_NO_SPACE) { retval = ext2fs_expand_dir(fs, cwd); if (retval) { com_err(__func__, retval, "while expanding directory"); return retval; } retval = ext2fs_link(fs, cwd, name, ino, filetype); } if (retval) { com_err(name, retval, 0); return retval; } if (ext2fs_test_inode_bitmap2(fs->inode_map, ino)) com_err(__func__, 0, "Warning: inode already set"); ext2fs_inode_alloc_stats2(fs, ino, +1, 0); memset(&inode, 0, sizeof(inode)); inode.i_mode = mode; inode.i_atime = inode.i_ctime = inode.i_mtime = fs->now ? fs->now : time(0); if (filetype != S_IFIFO) { devmajor = major(st->st_rdev); devminor = minor(st->st_rdev); if ((devmajor < 256) && (devminor < 256)) { inode.i_block[0] = devmajor * 256 + devminor; inode.i_block[1] = 0; } else { inode.i_block[0] = 0; inode.i_block[1] = (devminor & 0xff) | (devmajor << 8) | ((devminor & ~0xff) << 12); } } inode.i_links_count = 1; retval = ext2fs_write_new_inode(fs, ino, &inode); if (retval) com_err(__func__, retval, "while creating inode %u", ino); return retval; }
void e2fsck_pass3(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; struct dir_info_iter *iter; #ifdef RESOURCE_TRACK struct resource_track rtrack; #endif struct problem_context pctx; struct dir_info *dir; unsigned long maxdirs, count; init_resource_track(&rtrack, ctx->fs->io); clear_problem_context(&pctx); #ifdef MTRACE mtrace_print("Pass 3"); #endif if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_3_PASS_HEADER, &pctx); /* * Allocate some bitmaps to do loop detection. */ pctx.errcode = e2fsck_allocate_inode_bitmap(fs, _("inode done bitmap"), EXT2FS_BMAP64_AUTODIR, "inode_done_map", &inode_done_map); if (pctx.errcode) { pctx.num = 2; fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; goto abort_exit; } print_resource_track(ctx, _("Peak memory"), &ctx->global_rtrack, NULL); check_root(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto abort_exit; ext2fs_mark_inode_bitmap2(inode_done_map, EXT2_ROOT_INO); maxdirs = e2fsck_get_num_dirinfo(ctx); count = 1; if (ctx->progress) if ((ctx->progress)(ctx, 3, 0, maxdirs)) goto abort_exit; iter = e2fsck_dir_info_iter_begin(ctx); while ((dir = e2fsck_dir_info_iter(ctx, iter)) != 0) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto abort_exit; if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs)) goto abort_exit; if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dir->ino)) if (check_directory(ctx, dir->ino, &pctx)) goto abort_exit; } e2fsck_dir_info_iter_end(ctx, iter); /* * Force the creation of /lost+found if not present */ if ((ctx->flags & E2F_OPT_READONLY) == 0) e2fsck_get_lost_and_found(ctx, 1); /* * If there are any directories that need to be indexed or * optimized, do it here. */ e2fsck_rehash_directories(ctx); abort_exit: e2fsck_free_dir_info(ctx); if (inode_loop_detect) { ext2fs_free_inode_bitmap(inode_loop_detect); inode_loop_detect = 0; } if (inode_done_map) { ext2fs_free_inode_bitmap(inode_done_map); inode_done_map = 0; } print_resource_track(ctx, _("Pass 3"), &rtrack, ctx->fs->io); }
/* * This subroutine is responsible for making sure that a particular * directory is connected to the root; if it isn't we trace it up as * far as we can go, and then offer to connect the resulting parent to * the lost+found. We have to do loop detection; if we ever discover * a loop, we treat that as a disconnected directory and offer to * reparent it to lost+found. * * However, loop detection is expensive, because for very large * filesystems, the inode_loop_detect bitmap is huge, and clearing it * is non-trivial. Loops in filesystems are also a rare error case, * and we shouldn't optimize for error cases. So we try two passes of * the algorithm. The first time, we ignore loop detection and merely * increment a counter; if the counter exceeds some extreme threshold, * then we try again with the loop detection bitmap enabled. */ static int check_directory(e2fsck_t ctx, ext2_ino_t dir, struct problem_context *pctx) { ext2_filsys fs = ctx->fs; ext2_ino_t ino = dir, parent; int loop_pass = 0, parent_count = 0; while (1) { /* * Mark this inode as being "done"; by the time we * return from this function, the inode we either be * verified as being connected to the directory tree, * or we will have offered to reconnect this to * lost+found. * * If it was marked done already, then we've reached a * parent we've already checked. */ if (ext2fs_mark_inode_bitmap2(inode_done_map, ino)) break; if (e2fsck_dir_info_get_parent(ctx, ino, &parent)) { fix_problem(ctx, PR_3_NO_DIRINFO, pctx); return 0; } /* * If this directory doesn't have a parent, or we've * seen the parent once already, then offer to * reparent it to lost+found */ if (!parent || (loop_pass && (ext2fs_test_inode_bitmap2(inode_loop_detect, parent)))) { pctx->ino = ino; if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) { if (e2fsck_reconnect_file(ctx, pctx->ino)) ext2fs_unmark_valid(fs); else { fix_dotdot(ctx, pctx->ino, ctx->lost_and_found); parent = ctx->lost_and_found; } } break; } ino = parent; if (loop_pass) { ext2fs_mark_inode_bitmap2(inode_loop_detect, ino); } else if (parent_count++ > 2048) { /* * If we've run into a path depth that's * greater than 2048, try again with the inode * loop bitmap turned on and start from the * top. */ loop_pass = 1; if (inode_loop_detect) ext2fs_clear_inode_bitmap(inode_loop_detect); else { pctx->errcode = e2fsck_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), EXT2FS_BMAP64_AUTODIR, "inode_loop_detect", &inode_loop_detect); if (pctx->errcode) { pctx->num = 1; fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, pctx); ctx->flags |= E2F_FLAG_ABORT; return -1; } } ino = dir; } } /* * Make sure that .. and the parent directory are the same; * offer to fix it if not. */ pctx->ino = dir; if (e2fsck_dir_info_get_dotdot(ctx, dir, &pctx->ino2) || e2fsck_dir_info_get_parent(ctx, dir, &pctx->dir)) { fix_problem(ctx, PR_3_NO_DIRINFO, pctx); return 0; } if (pctx->ino2 != pctx->dir) { if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx)) fix_dotdot(ctx, dir, pctx->dir); } return 0; }
/* * 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 int check_dir_block(ext2_filsys fs, struct ext2_db_entry2 *db, void *priv_data) { struct dx_dir_info *dx_dir; #ifdef ENABLE_HTREE struct dx_dirblock_info *dx_db = 0; #endif /* ENABLE_HTREE */ struct ext2_dir_entry *dirent, *prev; ext2_dirhash_t hash; unsigned int offset = 0; int dir_modified = 0; int dot_state; unsigned int rec_len; blk64_t block_nr = db->blk; ext2_ino_t ino = db->ino; ext2_ino_t subdir_parent; __u16 links; struct check_dir_struct *cd; char *buf; e2fsck_t ctx; int problem; struct ext2_dx_root_info *root; struct ext2_dx_countlimit *limit; static dict_t de_dict; struct problem_context pctx; int dups_found = 0; int ret; int dx_csum_size = 0, de_csum_size = 0; int failed_csum = 0; int is_leaf = 1; cd = (struct check_dir_struct *) priv_data; buf = cd->buf; ctx = cd->ctx; if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) return DIRENT_ABORT; if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) return DIRENT_ABORT; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { dx_csum_size = sizeof(struct ext2_dx_tail); de_csum_size = sizeof(struct ext2_dir_entry_tail); } /* * Make sure the inode is still in use (could have been * deleted in the duplicate/bad blocks pass. */ if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, ino))) return 0; cd->pctx.ino = ino; cd->pctx.blk = block_nr; cd->pctx.blkcount = db->blockcnt; cd->pctx.ino2 = 0; cd->pctx.dirent = 0; cd->pctx.num = 0; if (db->blk == 0) { if (allocate_dir_block(ctx, db, buf, &cd->pctx)) return 0; block_nr = db->blk; } if (db->blockcnt) dot_state = 2; else dot_state = 0; if (ctx->dirs_to_hash && ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) dups_found++; #if 0 printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, db->blockcnt, ino); #endif ehandler_operation(_("reading directory block")); cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino); ehandler_operation(0); if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) cd->pctx.errcode = 0; /* We'll handle this ourselves */ else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) { cd->pctx.errcode = 0; /* We'll handle this ourselves */ failed_csum = 1; } if (cd->pctx.errcode) { char *buf2; if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { ctx->flags |= E2F_FLAG_ABORT; return DIRENT_ABORT; } ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0, EXT2_ROOT_INO, &buf2); memcpy(buf, buf2, fs->blocksize); ext2fs_free_mem(&buf2); } #ifdef ENABLE_HTREE dx_dir = e2fsck_get_dx_dir_info(ctx, ino); if (dx_dir && dx_dir->numblocks) { if (db->blockcnt >= dx_dir->numblocks) { if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK, &pctx)) { clear_htree(ctx, ino); dx_dir->numblocks = 0; dx_db = 0; goto out_htree; } fatal_error(ctx, _("Can not continue.")); } dx_db = &dx_dir->dx_block[db->blockcnt]; dx_db->type = DX_DIRBLOCK_LEAF; dx_db->phys = block_nr; dx_db->min_hash = ~0; dx_db->max_hash = 0; dirent = (struct ext2_dir_entry *) buf; (void) ext2fs_get_rec_len(fs, dirent, &rec_len); limit = (struct ext2_dx_countlimit *) (buf+8); if (db->blockcnt == 0) { root = (struct ext2_dx_root_info *) (buf + 24); dx_db->type = DX_DIRBLOCK_ROOT; dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; if ((root->reserved_zero || root->info_length < 8 || root->indirect_levels > 1) && fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { clear_htree(ctx, ino); dx_dir->numblocks = 0; dx_db = 0; } dx_dir->hashversion = root->hash_version; if ((dx_dir->hashversion <= EXT2_HASH_TEA) && (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) dx_dir->hashversion += 3; dx_dir->depth = root->indirect_levels + 1; } else if ((dirent->inode == 0) && (rec_len == fs->blocksize) && (dirent->name_len == 0) && (ext2fs_le16_to_cpu(limit->limit) == ((fs->blocksize - (8 + dx_csum_size)) / sizeof(struct ext2_dx_entry)))) dx_db->type = DX_DIRBLOCK_NODE; is_leaf = 0; } out_htree: #endif /* ENABLE_HTREE */ /* Verify checksum. */ if (is_leaf && de_csum_size) { /* No space for csum? Rebuild dirs in pass 3A. */ if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) { de_csum_size = 0; if (e2fsck_dir_will_be_rehashed(ctx, ino)) goto skip_checksum; if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM, &cd->pctx)) goto skip_checksum; e2fsck_rehash_dir_later(ctx, ino); goto skip_checksum; } if (failed_csum) { char *buf2; if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID, &cd->pctx)) goto skip_checksum; ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0, EXT2_ROOT_INO, &buf2); memcpy(buf, buf2, fs->blocksize); ext2fs_free_mem(&buf2); dir_modified++; failed_csum = 0; } } /* htree nodes don't use fake dirents to store checksums */ if (!is_leaf) de_csum_size = 0; skip_checksum: dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); prev = 0; do { int group; ext2_ino_t first_unused_inode; problem = 0; dirent = (struct ext2_dir_entry *) (buf + offset); (void) ext2fs_get_rec_len(fs, dirent, &rec_len); cd->pctx.dirent = dirent; cd->pctx.num = offset; if (((offset + rec_len) > fs->blocksize) || (rec_len < 12) || ((rec_len % 4) != 0) || (((dirent->name_len & (unsigned) 0xFF)+8) > rec_len)) { if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { salvage_directory(fs, dirent, prev, &offset); dir_modified++; continue; } else goto abort_free_dict; } if (dot_state == 0) { if (check_dot(ctx, dirent, ino, &cd->pctx)) dir_modified++; } else if (dot_state == 1) { ret = check_dotdot(ctx, dirent, ino, &cd->pctx); if (ret < 0) goto abort_free_dict; if (ret) dir_modified++; } else if (dirent->inode == ino) { problem = PR_2_LINK_DOT; if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } } if (!dirent->inode) goto next; /* * Make sure the inode listed is a legal one. */ if (((dirent->inode != EXT2_ROOT_INO) && (dirent->inode < EXT2_FIRST_INODE(fs->super))) || (dirent->inode > fs->super->s_inodes_count)) { problem = PR_2_BAD_INO; } else if (ctx->inode_bb_map && (ext2fs_test_inode_bitmap2(ctx->inode_bb_map, dirent->inode))) { /* * If the inode is in a bad block, offer to * clear it. */ problem = PR_2_BB_INODE; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.')) { /* * If there's a '.' entry in anything other * than the first directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT; } else if ((dot_state > 1) && ((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { /* * If there's a '..' entry in anything other * than the second directory entry, it's a * duplicate entry that should be removed. */ problem = PR_2_DUP_DOT_DOT; } else if ((dot_state > 1) && (dirent->inode == EXT2_ROOT_INO)) { /* * Don't allow links to the root directory. * We check this specially to make sure we * catch this error case even if the root * directory hasn't been created yet. */ problem = PR_2_LINK_ROOT; } else if ((dot_state > 1) && (dirent->name_len & 0xFF) == 0) { /* * Don't allow zero-length directory names. */ problem = PR_2_NULL_NAME; } if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } /* * If the inode was marked as having bad fields in * pass1, process it and offer to fix/clear it. * (We wait until now so that we can display the * pathname to the user.) */ if (ctx->inode_bad_map && ext2fs_test_inode_bitmap2(ctx->inode_bad_map, dirent->inode)) { if (e2fsck_process_bad_inode(ctx, ino, dirent->inode, buf + fs->blocksize)) { dirent->inode = 0; dir_modified++; goto next; } if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return DIRENT_ABORT; } group = ext2fs_group_of_ino(fs, dirent->inode); first_unused_inode = group * fs->super->s_inodes_per_group + 1 + fs->super->s_inodes_per_group - ext2fs_bg_itable_unused(fs, group); cd->pctx.group = group; /* * Check if the inode was missed out because * _INODE_UNINIT flag was set or bg_itable_unused was * incorrect. If so, clear the _INODE_UNINIT flag and * restart e2fsck. In the future it would be nice if * we could call a function in pass1.c that checks the * newly visible inodes. */ if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)) { pctx.num = dirent->inode; if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT, &cd->pctx)){ ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); ext2fs_mark_super_dirty(fs); ctx->flags |= E2F_FLAG_RESTART_LATER; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } else if (dirent->inode >= first_unused_inode) { pctx.num = dirent->inode; if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){ ext2fs_bg_itable_unused_set(fs, group, 0); ext2fs_mark_super_dirty(fs); ctx->flags |= E2F_FLAG_RESTART_LATER; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } /* * Offer to clear unused inodes; if we are going to be * restarting the scan due to bg_itable_unused being * wrong, then don't clear any inodes to avoid zapping * inodes that were skipped during pass1 due to an * incorrect bg_itable_unused; we'll get any real * problems after we restart. */ if (!(ctx->flags & E2F_FLAG_RESTART_LATER) && !(ext2fs_test_inode_bitmap2(ctx->inode_used_map, dirent->inode))) problem = PR_2_UNUSED_INODE; if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) goto next; } } if (check_name(ctx, dirent, ino, &cd->pctx)) dir_modified++; if (check_filetype(ctx, dirent, ino, &cd->pctx)) dir_modified++; #ifdef ENABLE_HTREE if (dx_db) { ext2fs_dirhash(dx_dir->hashversion, dirent->name, (dirent->name_len & 0xFF), fs->super->s_hash_seed, &hash, 0); if (hash < dx_db->min_hash) dx_db->min_hash = hash; if (hash > dx_db->max_hash) dx_db->max_hash = hash; } #endif /* * If this is a directory, then mark its parent in its * dir_info structure. If the parent field is already * filled in, then this directory has more than one * hard link. We assume the first link is correct, * and ask the user if he/she wants to clear this one. */ if ((dot_state > 1) && (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode))) { if (e2fsck_dir_info_get_parent(ctx, dirent->inode, &subdir_parent)) { cd->pctx.ino = dirent->inode; fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); goto abort_free_dict; } if (subdir_parent) { cd->pctx.ino2 = subdir_parent; if (fix_problem(ctx, PR_2_LINK_DIR, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; } cd->pctx.ino2 = 0; } else { (void) e2fsck_dir_info_set_parent(ctx, dirent->inode, ino); } } if (dups_found) { ; } else if (dict_lookup(&de_dict, dirent)) { clear_problem_context(&pctx); pctx.ino = ino; pctx.dirent = dirent; fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); e2fsck_rehash_dir_later(ctx, ino); dups_found++; } else dict_alloc_insert(&de_dict, dirent, dirent); ext2fs_icount_increment(ctx->inode_count, dirent->inode, &links); if (links > 1) ctx->fs_links_count++; ctx->fs_total_count++; next: prev = dirent; if (dir_modified) (void) ext2fs_get_rec_len(fs, dirent, &rec_len); offset += rec_len; dot_state++; } while (offset < fs->blocksize - de_csum_size); #if 0 printf("\n"); #endif #ifdef ENABLE_HTREE if (dx_db) { #ifdef DX_DEBUG printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n", db->blockcnt, dx_db->type, dx_db->min_hash, dx_db->max_hash); #endif cd->pctx.dir = cd->pctx.ino; if ((dx_db->type == DX_DIRBLOCK_ROOT) || (dx_db->type == DX_DIRBLOCK_NODE)) parse_int_node(fs, db, cd, dx_dir, buf, failed_csum); } #endif /* ENABLE_HTREE */ if (offset != fs->blocksize - de_csum_size) { cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) + offset; if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { dirent->rec_len = cd->pctx.num; dir_modified++; } } if (dir_modified) { /* leaf block with no tail? Rehash dirs later. */ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && is_leaf && !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) e2fsck_rehash_dir_later(ctx, ino); write_and_fix: if (e2fsck_dir_will_be_rehashed(ctx, ino)) ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf, 0, ino); if (e2fsck_dir_will_be_rehashed(ctx, ino)) ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) goto abort_free_dict; } ext2fs_mark_changed(fs); } else if (is_leaf && failed_csum && !dir_modified) { /* * If a leaf node that fails csum makes it this far without * alteration, ask the user if the checksum should be fixed. */ if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID, &cd->pctx)) goto write_and_fix; } dict_free_nodes(&de_dict); return 0; abort_free_dict: ctx->flags |= E2F_FLAG_ABORT; dict_free_nodes(&de_dict); return DIRENT_ABORT; }
/* Copy the native file to the fs */ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src, const char *dest, ext2_ino_t root) { int fd; struct stat statbuf; ext2_ino_t newfile; errcode_t retval; struct ext2_inode inode; int bufsize = IO_BUFSIZE; int make_holes = 0; fd = ext2fs_open_file(src, O_RDONLY, 0); if (fd < 0) { com_err(src, errno, 0); return errno; } if (fstat(fd, &statbuf) < 0) { com_err(src, errno, 0); close(fd); return errno; } retval = ext2fs_namei(fs, root, cwd, dest, &newfile); if (retval == 0) { close(fd); return EXT2_ET_FILE_EXISTS; } retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile); if (retval) { com_err(__func__, retval, 0); close(fd); return retval; } #ifdef DEBUGFS printf("Allocated inode: %u\n", newfile); #endif retval = ext2fs_link(fs, cwd, dest, newfile, EXT2_FT_REG_FILE); if (retval == EXT2_ET_DIR_NO_SPACE) { retval = ext2fs_expand_dir(fs, cwd); if (retval) { com_err(__func__, retval, "while expanding directory"); close(fd); return retval; } retval = ext2fs_link(fs, cwd, dest, newfile, EXT2_FT_REG_FILE); } if (retval) { com_err(dest, retval, 0); close(fd); return errno; } if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile)) com_err(__func__, 0, "Warning: inode already set"); ext2fs_inode_alloc_stats2(fs, newfile, +1, 0); memset(&inode, 0, sizeof(inode)); inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG; inode.i_atime = inode.i_ctime = inode.i_mtime = fs->now ? fs->now : time(0); inode.i_links_count = 1; retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size); if (retval) { com_err(dest, retval, 0); close(fd); return retval; } if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_INLINE_DATA)) { inode.i_flags |= EXT4_INLINE_DATA_FL; } else if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { int i; struct ext3_extent_header *eh; eh = (struct ext3_extent_header *) &inode.i_block[0]; eh->eh_depth = 0; eh->eh_entries = 0; eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); i = (sizeof(inode.i_block) - sizeof(*eh)) / sizeof(struct ext3_extent); eh->eh_max = ext2fs_cpu_to_le16(i); inode.i_flags |= EXT4_EXTENTS_FL; } retval = ext2fs_write_new_inode(fs, newfile, &inode); if (retval) { com_err(__func__, retval, "while creating inode %u", newfile); close(fd); return retval; } if (inode.i_flags & EXT4_INLINE_DATA_FL) { retval = ext2fs_inline_data_init(fs, newfile); if (retval) { com_err("copy_file", retval, 0); close(fd); return retval; } } if (LINUX_S_ISREG(inode.i_mode)) { if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) { make_holes = 1; /* * Use I/O blocksize as buffer size when * copying sparse files. */ bufsize = statbuf.st_blksize; } retval = copy_file(fs, fd, newfile, bufsize, make_holes); if (retval) com_err("copy_file", retval, 0); } close(fd); return retval; }
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); }
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); }
/* * This routine gets the lost_and_found inode, making it a directory * if necessary */ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) { ext2_filsys fs = ctx->fs; ext2_ino_t ino; blk64_t blk; errcode_t retval; struct ext2_inode inode; char * block; static const char name[] = "lost+found"; struct problem_context pctx; if (ctx->lost_and_found) return ctx->lost_and_found; clear_problem_context(&pctx); retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, sizeof(name)-1, 0, &ino); if (retval && !fix) return 0; if (!retval) { if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, ino)) { ctx->lost_and_found = ino; return ino; } /* Lost+found isn't a directory! */ if (!fix) return 0; pctx.ino = ino; if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx)) return 0; /* OK, unlink the old /lost+found file. */ pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0); if (pctx.errcode) { pctx.str = "ext2fs_unlink"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } (void) e2fsck_dir_info_set_parent(ctx, ino, 0); e2fsck_adjust_inode_count(ctx, ino, -1); } else if (retval != EXT2_ET_FILE_NOT_FOUND) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx); } if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0)) return 0; /* * Read the inode and block bitmaps in; we'll be messing with * them. */ e2fsck_read_bitmaps(ctx); /* * First, find a free block */ retval = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx); return 0; } ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); ext2fs_block_alloc_stats2(fs, blk, +1); /* * Next find a free inode. */ retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700, ctx->inode_used_map, &ino); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx); return 0; } ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino); ext2fs_inode_alloc_stats2(fs, ino, +1, 1); /* * Now let's create the actual data block for the inode */ retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx); return 0; } retval = ext2fs_write_dir_block(fs, blk, block); ext2fs_free_mem(&block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx); return 0; } /* * Set up the inode structure */ memset(&inode, 0, sizeof(inode)); inode.i_mode = 040700; 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; /* * Next, write out the inode. */ pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode); if (pctx.errcode) { pctx.str = "ext2fs_write_inode"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Finally, create the directory link */ pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR); if (pctx.errcode) { pctx.str = "ext2fs_link"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Miscellaneous bookkeeping that needs to be kept straight. */ e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO); e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1); ext2fs_icount_store(ctx->inode_count, ino, 2); ext2fs_icount_store(ctx->inode_link_info, ino, 2); ctx->lost_and_found = ino; quota_data_add(ctx->qctx, &inode, ino, fs->blocksize); quota_data_inodes(ctx->qctx, &inode, ino, +1); #if 0 printf("/lost+found created; inode #%lu\n", ino); #endif return ino; }