static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, struct out_dir *outdir, ext2_ino_t ino, int compress) { struct write_dir_struct wd; errcode_t retval; struct ext2_inode inode; retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num); if (retval) return retval; wd.outdir = outdir; wd.err = 0; wd.ctx = ctx; wd.cleared = 0; retval = ext2fs_block_iterate2(fs, ino, 0, 0, write_dir_block, &wd); if (retval) return retval; if (wd.err) return wd.err; e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); if (compress) inode.i_flags &= ~EXT2_INDEX_FL; else inode.i_flags |= EXT2_INDEX_FL; inode.i_size = outdir->num * fs->blocksize; ext2fs_iblk_sub_blocks(fs, &inode, wd.cleared); e2fsck_write_inode(ctx, ino, &inode, "rehash_dir"); return 0; }
/* * This fuction clears the htree flag on an inode */ static void clear_htree(e2fsck_t ctx, ext2_ino_t ino) { struct ext2_inode inode; e2fsck_read_inode(ctx, ino, &inode, "clear_htree"); inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL; e2fsck_write_inode(ctx, ino, &inode, "clear_htree"); if (ctx->dirs_to_hash) ext2fs_u32_list_add(ctx->dirs_to_hash, ino); }
/* * This routine is called when an inode is not connected to the * directory tree. * * This subroutine returns 1 then the caller shouldn't bother with the * rest of the pass 4 tests. */ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode"); clear_problem_context(&pctx); pctx.ino = i; pctx.inode = &inode; /* * Offer to delete any zero-length files that does not have * blocks. If there is an EA block, it might have useful * information, so we won't prompt to delete it, but let it be * reconnected to lost+found. */ if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) || LINUX_S_ISDIR(inode.i_mode))) { if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) { ext2fs_icount_store(ctx->inode_link_info, i, 0); inode.i_links_count = 0; inode.i_dtime = ctx->now; e2fsck_write_inode(ctx, i, &inode, "disconnect_inode"); /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i); ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i); ext2fs_inode_alloc_stats2(fs, i, -1, LINUX_S_ISDIR(inode.i_mode)); return 0; } } /* * Prompt to reconnect. */ if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) { if (e2fsck_reconnect_file(ctx, i)) ext2fs_unmark_valid(fs); } else { /* * If we don't attach the inode, then skip the * i_links_test since there's no point in trying to * force i_links_count to zero. */ ext2fs_unmark_valid(fs); return 1; } return 0; }
/* * 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; } }
extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, ext2_ino_t ino, char *buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; int inode_modified = 0; int not_fixed = 0; unsigned char *frag, *fsize; struct problem_context pctx; int problem = 0; e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); clear_problem_context(&pctx); pctx.ino = ino; pctx.dir = dir; pctx.inode = &inode; if (ext2fs_file_acl_block(fs, &inode) && !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { if (fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { ext2fs_file_acl_block_set(fs, &inode, 0); inode_modified++; } else not_fixed++; } if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && !(LINUX_S_ISSOCK(inode.i_mode))) problem = PR_2_BAD_MODE; else if (LINUX_S_ISCHR(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_CHAR_DEV; else if (LINUX_S_ISBLK(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_BLOCK_DEV; else if (LINUX_S_ISFIFO(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_FIFO; else if (LINUX_S_ISSOCK(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_SOCKET; else if (LINUX_S_ISLNK(inode.i_mode) && !e2fsck_pass1_check_symlink(fs, ino, &inode, buf)) { problem = PR_2_INVALID_SYMLINK; } if (problem) { if (fix_problem(ctx, problem, &pctx)) { deallocate_inode(ctx, ino, 0); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return 0; return 1; } else not_fixed++; problem = 0; } if (inode.i_faddr) { if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { inode.i_faddr = 0; inode_modified++; } else not_fixed++; } switch (fs->super->s_creator_os) { case EXT2_OS_HURD: frag = &inode.osd2.hurd2.h_i_frag; fsize = &inode.osd2.hurd2.h_i_fsize; break; default: frag = fsize = 0; } if (frag && *frag) { pctx.num = *frag; if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { *frag = 0; inode_modified++; } else not_fixed++; pctx.num = 0; } if (fsize && *fsize) { pctx.num = *fsize; if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { *fsize = 0; inode_modified++; } else not_fixed++; pctx.num = 0; } if ((fs->super->s_creator_os == EXT2_OS_LINUX) && !(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && (inode.osd2.linux2.l_i_blocks_hi != 0)) { pctx.num = inode.osd2.linux2.l_i_blocks_hi; if (fix_problem(ctx, PR_2_BLOCKS_HI_ZERO, &pctx)) { inode.osd2.linux2.l_i_blocks_hi = 0; inode_modified++; } } if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) && inode.osd2.linux2.l_i_file_acl_high != 0) { pctx.num = inode.osd2.linux2.l_i_file_acl_high; if (fix_problem(ctx, PR_2_I_FILE_ACL_HI_ZERO, &pctx)) { inode.osd2.linux2.l_i_file_acl_high = 0; inode_modified++; } else not_fixed++; } if (ext2fs_file_acl_block(fs, &inode) && ((ext2fs_file_acl_block(fs, &inode) < fs->super->s_first_data_block) || (ext2fs_file_acl_block(fs, &inode) >= ext2fs_blocks_count(fs->super)))) { if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { ext2fs_file_acl_block_set(fs, &inode, 0); inode_modified++; } else not_fixed++; } if (inode.i_dir_acl && LINUX_S_ISDIR(inode.i_mode)) { if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { inode.i_dir_acl = 0; inode_modified++; } else not_fixed++; } if (inode_modified) e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); if (!not_fixed && ctx->inode_bad_map) ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino); return 0; }
void e2fsck_pass4(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t i; struct ext2_inode *inode; #ifdef RESOURCE_TRACK struct resource_track rtrack; #endif struct problem_context pctx; __u16 link_count, link_counted; char *buf = 0; dgrp_t group, maxgroup; init_resource_track(&rtrack, ctx->fs->io); #ifdef MTRACE mtrace_print("Pass 4"); #endif clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_4_PASS_HEADER, &pctx); group = 0; maxgroup = fs->group_desc_count; if (ctx->progress) if ((ctx->progress)(ctx, 4, 0, maxgroup)) return; inode = e2fsck_allocate_memory(ctx, EXT2_INODE_SIZE(fs->super), "scratch inode"); /* Protect loop from wrap-around if s_inodes_count maxed */ for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) { int isdir; if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto errout; if ((i % fs->super->s_inodes_per_group) == 0) { group++; if (ctx->progress) if ((ctx->progress)(ctx, 4, group, maxgroup)) goto errout; } if (i == EXT2_BAD_INO || (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) continue; if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, i)) || (ctx->inode_imagic_map && ext2fs_test_inode_bitmap2(ctx->inode_imagic_map, i)) || (ctx->inode_bb_map && ext2fs_test_inode_bitmap2(ctx->inode_bb_map, i))) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); if (link_counted == 0) { if (!buf) buf = e2fsck_allocate_memory(ctx, fs->blocksize, "bad_inode buffer"); if (e2fsck_process_bad_inode(ctx, 0, i, buf)) continue; if (disconnect_inode(ctx, i, inode)) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); } isdir = ext2fs_test_inode_bitmap2(ctx->inode_dir_map, i); if (isdir && (link_counted > EXT2_LINK_MAX)) link_counted = 1; if (link_counted != link_count) { e2fsck_read_inode(ctx, i, inode, "pass4"); pctx.ino = i; pctx.inode = inode; if ((link_count != inode->i_links_count) && !isdir && (inode->i_links_count <= EXT2_LINK_MAX)) { pctx.num = link_count; fix_problem(ctx, PR_4_INCONSISTENT_COUNT, &pctx); } pctx.num = link_counted; /* i_link_count was previously exceeded, but no longer * is, fix this but don't consider it an error */ if ((isdir && link_counted > 1 && (inode->i_flags & EXT2_INDEX_FL) && link_count == 1 && !(ctx->options & E2F_OPT_NO)) || fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) { inode->i_links_count = link_counted; e2fsck_write_inode(ctx, i, inode, "pass4"); } } } ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; ext2fs_free_inode_bitmap(ctx->inode_bb_map); ctx->inode_bb_map = 0; ext2fs_free_inode_bitmap(ctx->inode_imagic_map); ctx->inode_imagic_map = 0; errout: if (buf) ext2fs_free_mem(&buf); ext2fs_free_mem(&inode); print_resource_track(ctx, _("Pass 4"), &rtrack, ctx->fs->io); }
void e2fsck_pass4(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t i; struct ext2_inode inode; #ifdef RESOURCE_TRACK struct resource_track rtrack; #endif struct problem_context pctx; __u16 link_count, link_counted; char *buf = 0; int group, maxgroup; #ifdef RESOURCE_TRACK init_resource_track(&rtrack); #endif #ifdef MTRACE mtrace_print("Pass 4"); #endif clear_problem_context(&pctx); if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_4_PASS_HEADER, &pctx); group = 0; maxgroup = fs->group_desc_count; if (ctx->progress) if ((ctx->progress)(ctx, 4, 0, maxgroup)) return; /* Protect loop from wrap-around if s_inodes_count maxed */ for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto errout; if ((i % fs->super->s_inodes_per_group) == 0) { group++; if (ctx->progress) if ((ctx->progress)(ctx, 4, group, maxgroup)) goto errout; } if (i == EXT2_BAD_INO || (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) continue; if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) || (ctx->inode_imagic_map && ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)) || (ctx->inode_bb_map && ext2fs_test_inode_bitmap(ctx->inode_bb_map, i))) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); if (link_counted == 0) { if (!buf) buf = e2fsck_allocate_memory(ctx, fs->blocksize, "bad_inode buffer"); if (e2fsck_process_bad_inode(ctx, 0, i, buf)) continue; if (disconnect_inode(ctx, i)) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); } if (link_counted != link_count) { e2fsck_read_inode(ctx, i, &inode, "pass4"); pctx.ino = i; pctx.inode = &inode; if (link_count != inode.i_links_count) { pctx.num = link_count; fix_problem(ctx, PR_4_INCONSISTENT_COUNT, &pctx); } pctx.num = link_counted; if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) { inode.i_links_count = link_counted; e2fsck_write_inode(ctx, i, &inode, "pass4"); } } } ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; ext2fs_free_inode_bitmap(ctx->inode_bb_map); ctx->inode_bb_map = 0; ext2fs_free_inode_bitmap(ctx->inode_imagic_map); ctx->inode_imagic_map = 0; errout: if (buf) ext2fs_free_mem(&buf); #ifdef RESOURCE_TRACK if (ctx->options & E2F_OPT_TIME2) { e2fsck_clear_progbar(ctx); print_resource_track(_("Pass 4"), &rtrack); } #endif }
/* * allocate_dir_block --- this function allocates a new directory * block for a particular inode; this is done if a directory has * a "hole" in it, or if a directory has a illegal block number * that was zeroed out and now needs to be replaced. */ static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db, char *buf, struct problem_context *pctx) { ext2_filsys fs = ctx->fs; blk_t blk; char *block; struct ext2_inode inode; if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) return 1; /* * Read the inode and block bitmaps in; we'll be messing with * them. */ e2fsck_read_bitmaps(ctx); /* * First, find a free block */ pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); if (pctx->errcode) { pctx->str = "ext2fs_new_block"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } ext2fs_mark_block_bitmap(ctx->block_found_map, blk); ext2fs_mark_block_bitmap(fs->block_map, blk); ext2fs_mark_bb_dirty(fs); /* * Now let's create the actual data block for the inode */ if (db->blockcnt) pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block); else pctx->errcode = ext2fs_new_dir_block(fs, db->ino, EXT2_ROOT_INO, &block); if (pctx->errcode) { pctx->str = "ext2fs_new_dir_block"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } pctx->errcode = ext2fs_write_dir_block(fs, blk, block); ext2fs_free_mem((void **) &block); if (pctx->errcode) { pctx->str = "ext2fs_write_dir_block"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } /* * Update the inode block count */ e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block"); inode.i_blocks += fs->blocksize / 512; if (inode.i_size < (db->blockcnt+1) * fs->blocksize) inode.i_size = (db->blockcnt+1) * fs->blocksize; e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block"); /* * Finally, update the block pointers for the inode */ db->blk = blk; pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE, 0, update_dir_block, db); if (pctx->errcode) { pctx->str = "ext2fs_block_iterate"; fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); return 1; } return 0; }
extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, ext2_ino_t ino) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; int inode_modified = 0; unsigned char *frag, *fsize; struct problem_context pctx; int problem = 0; e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); clear_problem_context(&pctx); pctx.ino = ino; pctx.dir = dir; pctx.inode = &inode; if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && !(LINUX_S_ISSOCK(inode.i_mode))) problem = PR_2_BAD_MODE; else if (LINUX_S_ISCHR(inode.i_mode) && !e2fsck_pass1_check_device_inode(&inode)) problem = PR_2_BAD_CHAR_DEV; else if (LINUX_S_ISBLK(inode.i_mode) && !e2fsck_pass1_check_device_inode(&inode)) problem = PR_2_BAD_BLOCK_DEV; else if (LINUX_S_ISFIFO(inode.i_mode) && !e2fsck_pass1_check_device_inode(&inode)) problem = PR_2_BAD_FIFO; else if (LINUX_S_ISSOCK(inode.i_mode) && !e2fsck_pass1_check_device_inode(&inode)) problem = PR_2_BAD_SOCKET; else if (LINUX_S_ISLNK(inode.i_mode) && !e2fsck_pass1_check_symlink(fs, &inode)) { problem = PR_2_SYMLINK_SIZE; } if (problem) { if (fix_problem(ctx, problem, &pctx)) { deallocate_inode(ctx, ino, 0); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return 0; return 1; } problem = 0; } if (inode.i_faddr && fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { inode.i_faddr = 0; inode_modified++; } switch (fs->super->s_creator_os) { case EXT2_OS_LINUX: frag = &inode.osd2.linux2.l_i_frag; fsize = &inode.osd2.linux2.l_i_fsize; break; case EXT2_OS_HURD: frag = &inode.osd2.hurd2.h_i_frag; fsize = &inode.osd2.hurd2.h_i_fsize; break; case EXT2_OS_MASIX: frag = &inode.osd2.masix2.m_i_frag; fsize = &inode.osd2.masix2.m_i_fsize; break; default: frag = fsize = 0; } if (frag && *frag) { pctx.num = *frag; if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { *frag = 0; inode_modified++; } pctx.num = 0; } if (fsize && *fsize) { pctx.num = *fsize; if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { *fsize = 0; inode_modified++; } pctx.num = 0; } if (inode.i_file_acl && !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) && fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { inode.i_file_acl = 0; inode_modified++; } if (inode.i_file_acl && ((inode.i_file_acl < fs->super->s_first_data_block) || (inode.i_file_acl >= fs->super->s_blocks_count)) && fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { inode.i_file_acl = 0; inode_modified++; } if (inode.i_dir_acl && LINUX_S_ISDIR(inode.i_mode) && fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { inode.i_dir_acl = 0; inode_modified++; } if (inode_modified) e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); return 0; }