diff options
Diffstat (limited to 'e2fsck')
-rw-r--r-- | e2fsck/e2fsck.h | 1 | ||||
-rw-r--r-- | e2fsck/journal.c | 8 | ||||
-rw-r--r-- | e2fsck/pass1.c | 36 | ||||
-rw-r--r-- | e2fsck/pass2.c | 3 | ||||
-rw-r--r-- | e2fsck/pass4.c | 49 | ||||
-rw-r--r-- | e2fsck/problem.c | 79 | ||||
-rw-r--r-- | e2fsck/problem.h | 46 | ||||
-rw-r--r-- | e2fsck/rehash.c | 6 | ||||
-rw-r--r-- | e2fsck/super.c | 365 | ||||
-rw-r--r-- | e2fsck/unix.c | 72 |
10 files changed, 590 insertions, 75 deletions
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index b8caa43b..3f2dc308 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -656,6 +656,7 @@ void sigcatcher_setup(void); void check_super_block(e2fsck_t ctx); int check_backup_super_block(e2fsck_t ctx); void check_resize_inode(e2fsck_t ctx); +int check_init_orphan_file(e2fsck_t ctx); /* util.c */ extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size, diff --git a/e2fsck/journal.c b/e2fsck/journal.c index 8ae89bf7..c7868d89 100644 --- a/e2fsck/journal.c +++ b/e2fsck/journal.c @@ -610,10 +610,10 @@ static void ext4_fc_flush_extents(e2fsck_t ctx, ext2_ino_t ino) /* Helper struct for dentry replay routines */ struct dentry_info_args { - ext2_ino_t parent_ino; - int dname_len; - ext2_ino_t ino; - char *dname; + ext2_ino_t parent_ino; + ext2_ino_t ino; + int dname_len; + char *dname; }; static inline int tl_to_darg(struct dentry_info_args *darg, diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 78540119..a341c72a 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -918,6 +918,7 @@ static void reserve_block_for_lnf_repair(e2fsck_t ctx) } static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, size_t *sz) { void *p; @@ -928,7 +929,8 @@ static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino, if (retval) return retval; - retval = ext2fs_xattrs_read(handle); + retval = ext2fs_xattrs_read_inode(handle, + (struct ext2_inode_large *)inode); if (retval) goto err; @@ -1515,7 +1517,8 @@ void e2fsck_pass1(e2fsck_t ctx) (ino >= EXT2_FIRST_INODE(fs->super))) { size_t size = 0; - pctx.errcode = get_inline_data_ea_size(fs, ino, &size); + pctx.errcode = get_inline_data_ea_size(fs, ino, inode, + &size); if (!pctx.errcode && fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) { ext2fs_set_feature_inline_data(sb); @@ -1538,7 +1541,7 @@ void e2fsck_pass1(e2fsck_t ctx) flags = fs->flags; if (failed_csum) fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; - err = get_inline_data_ea_size(fs, ino, &size); + err = get_inline_data_ea_size(fs, ino, inode, &size); fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); @@ -1785,6 +1788,32 @@ void e2fsck_pass1(e2fsck_t ctx) inode_size, "pass1"); failed_csum = 0; } + } else if (ino == fs->super->s_orphan_file_inum) { + ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); + if (ext2fs_has_feature_orphan_file(fs->super)) { + if (!LINUX_S_ISREG(inode->i_mode) && + fix_problem(ctx, PR_1_ORPHAN_FILE_BAD_MODE, + &pctx)) { + inode->i_mode = LINUX_S_IFREG; + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + failed_csum = 0; + } + check_blocks(ctx, &pctx, block_buf, NULL); + FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); + continue; + } + if ((inode->i_links_count || + inode->i_blocks || inode->i_block[0]) && + fix_problem(ctx, PR_1_ORPHAN_FILE_NOT_CLEAR, + &pctx)) { + memset(inode, 0, inode_size); + ext2fs_icount_store(ctx->inode_link_info, ino, + 0); + e2fsck_write_inode_full(ctx, ino, inode, + inode_size, "pass1"); + failed_csum = 0; + } } else if (ino < EXT2_FIRST_INODE(fs->super)) { problem_t problem = 0; @@ -3494,6 +3523,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, } if (ino != quota_type2inum(PRJQUOTA, fs->super) && + ino != fs->super->s_orphan_file_inum && (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) && !(inode->i_flags & EXT4_EA_INODE_FL)) { quota_data_add(ctx->qctx, (struct ext2_inode_large *) inode, diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 28736094..410edd11 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -1446,7 +1446,8 @@ skip_checksum: (dirent->inode > fs->super->s_inodes_count) || (dirent->inode == fs->super->s_usr_quota_inum) || (dirent->inode == fs->super->s_grp_quota_inum) || - (dirent->inode == fs->super->s_prj_quota_inum)) { + (dirent->inode == fs->super->s_prj_quota_inum) || + (dirent->inode == fs->super->s_orphan_file_inum)) { problem = PR_2_BAD_INO; } else if (ctx->inode_bb_map && (ext2fs_test_inode_bitmap2(ctx->inode_bb_map, diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c index 8c2d2f1f..d2dda02a 100644 --- a/e2fsck/pass4.c +++ b/e2fsck/pass4.c @@ -26,7 +26,7 @@ * 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, +static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, ext2_ino_t *last_ino, struct ext2_inode_large *inode) { ext2_filsys fs = ctx->fs; @@ -34,9 +34,12 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, __u32 eamagic = 0; int extra_size = 0; - e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), - EXT2_INODE_SIZE(fs->super), - "pass4: disconnect_inode"); + if (*last_ino != i) { + e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), + EXT2_INODE_SIZE(fs->super), + "pass4: disconnect_inode"); + *last_ino = i; + } if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) extra_size = inode->i_extra_isize; @@ -75,6 +78,7 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) { if (e2fsck_reconnect_file(ctx, i)) ext2fs_unmark_valid(fs); + *last_ino = 0; } else { /* * If we don't attach the inode, then skip the @@ -87,20 +91,22 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, return 0; } -static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i, +/* + * This function is called when link_counted is zero. So this may not be + * an xattr inode at all. Return immediately if EA_INODE flag is not set. + */ +static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i, ext2_ino_t *last_ino, struct ext2_inode_large *inode, __u16 *link_counted) { __u64 actual_refs = 0; __u64 ref_count; - /* - * This function is called when link_counted is zero. So this may not - * be an xattr inode at all. Return immediately if EA_INODE flag is not - * set. - */ - e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), - EXT2_INODE_SIZE(ctx->fs->super), - "pass4: check_ea_inode"); + if (*last_ino != i) { + e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), + EXT2_INODE_SIZE(ctx->fs->super), + "pass4: check_ea_inode"); + *last_ino = i; + } if (!(inode->i_flags & EXT4_EA_INODE_FL)) return; @@ -180,7 +186,8 @@ void e2fsck_pass4(e2fsck_t ctx) inode = e2fsck_allocate_memory(ctx, inode_size, "scratch inode"); /* Protect loop from wrap-around if s_inodes_count maxed */ - for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) { + for (i = 1; i <= fs->super->s_inodes_count && i > 0; i++) { + ext2_ino_t last_ino = 0; int isdir; if (ctx->flags & E2F_FLAG_SIGNAL_MASK) @@ -192,7 +199,7 @@ void e2fsck_pass4(e2fsck_t ctx) goto errout; } if (i == quota_type2inum(PRJQUOTA, ctx->fs->super) || - i == EXT2_BAD_INO || + i == fs->super->s_orphan_file_inum || 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)) || @@ -210,7 +217,7 @@ void e2fsck_pass4(e2fsck_t ctx) * check_ea_inode() will update link_counted if * necessary. */ - check_ea_inode(ctx, i, inode, &link_counted); + check_ea_inode(ctx, i, &last_ino, inode, &link_counted); } if (link_counted == 0) { @@ -219,7 +226,7 @@ void e2fsck_pass4(e2fsck_t ctx) fs->blocksize, "bad_inode buffer"); if (e2fsck_process_bad_inode(ctx, 0, i, buf)) continue; - if (disconnect_inode(ctx, i, inode)) + if (disconnect_inode(ctx, i, &last_ino, inode)) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); @@ -239,8 +246,12 @@ void e2fsck_pass4(e2fsck_t ctx) if (link_counted != link_count) { int fix_nlink = 0; - e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), - inode_size, "pass4"); + if (last_ino != i) { + e2fsck_read_inode_full(ctx, i, + EXT2_INODE(inode), + inode_size, "pass4"); + last_ino = i; + } pctx.ino = i; pctx.inode = EXT2_INODE(inode); if ((link_count != inode->i_links_count) && !isdir && diff --git a/e2fsck/problem.c b/e2fsck/problem.c index e2572f59..6ad6fb84 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -526,6 +526,26 @@ static struct e2fsck_problem problem_table[] = { "not compatible. Resize @i should be disabled. "), PROMPT_FIX, 0, 0, 0, 0 }, + /* Orphan file contains holes */ + { PR_0_ORPHAN_FILE_HOLE, + N_("Orphan file (@i %i) contains hole at @b %b. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file block has wrong magic */ + { PR_0_ORPHAN_FILE_BAD_MAGIC, + N_("Orphan file (@i %i) @b %b contains wrong magic. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file block has wrong checksum */ + { PR_0_ORPHAN_FILE_BAD_CHECKSUM, + N_("Orphan file (@i %i) @b %b contains wrong checksum. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file size isn't multiple of blocks size */ + { PR_0_ORPHAN_FILE_WRONG_SIZE, + N_("Orphan file (@i %i) size is not multiple of block size. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + /* Pass 1 errors */ /* Pass 1: Checking inodes, blocks, and sizes */ @@ -1279,6 +1299,15 @@ static struct e2fsck_problem problem_table[] = { N_("@h %i uses SipHash, but should not. "), PROMPT_CLEAR_HTREE, PR_PREEN_OK, 0, 0, 0 }, + /* Orphan file has bad mode */ + { PR_1_ORPHAN_FILE_BAD_MODE, + N_("Orphan file @i %i is not regular file. "), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Orphan file inode is not in use, but contains data */ + { PR_1_ORPHAN_FILE_NOT_CLEAR, + N_("Orphan file @i %i is not in use, but contains data. "), + PROMPT_CLEAR, PR_PREEN_OK }, /* Pass 1b errors */ @@ -2265,6 +2294,56 @@ static struct e2fsck_problem problem_table[] = { N_("Error writing quota info for quota type %N: %m\n"), PROMPT_NULL, 0, 0, 0, 0 }, + /* Orphan file without a journal */ + { PR_6_ORPHAN_FILE_WITHOUT_JOURNAL, + N_("@S has orphan file without @j.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Orphan file truncation failed */ + { PR_6_ORPHAN_FILE_TRUNC_FAILED, + N_("Failed to truncate orphan file.\n"), + PROMPT_NONE, 0 }, + + /* Failed to initialize orphan file */ + { PR_6_ORPHAN_FILE_CORRUPTED, + N_("Failed to initialize orphan file.\n"), + PROMPT_RECREATE, PR_PREEN_OK }, + + /* Cannot fix corrupted orphan file with invalid bitmaps */ + { PR_6_ORPHAN_FILE_BITMAP_INVALID, + N_("Cannot fix corrupted orphan file with invalid bitmaps.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file creation failed */ + { PR_6_ORPHAN_FILE_CREATE_FAILED, + N_("Failed to truncate orphan file (@i %i).\n"), + PROMPT_NONE, 0 }, + + /* Orphan file block contains data */ + { PR_6_ORPHAN_BLOCK_DIRTY, + N_("Orphan file (@i %i) @b %b is not clean.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* orphan_present set but orphan file is empty */ + { PR_6_ORPHAN_PRESENT_CLEAN_FILE, + N_("Feature orphan_present is set but orphan file is clean.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* orphan_present set but orphan_file is not */ + { PR_6_ORPHAN_PRESENT_NO_FILE, + N_("Feature orphan_present is set but feature orphan_file is not.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Orphan file size isn't multiple of blocks size */ + { PR_6_ORPHAN_FILE_WRONG_SIZE, + N_("Orphan file (@i %i) size is not multiple of block size.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file contains holes */ + { PR_6_ORPHAN_FILE_HOLE, + N_("Orphan file (@i %i) contains hole at @b %b.\n"), + PROMPT_NONE, 0 }, + { 0 } }; diff --git a/e2fsck/problem.h b/e2fsck/problem.h index e86bc889..b47b0c63 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -288,6 +288,18 @@ struct problem_context { /* Meta_bg and resize_inode are not compatible, remove resize_inode*/ #define PR_0_DISABLE_RESIZE_INODE 0x000051 +/* Orphan file contains holes */ +#define PR_0_ORPHAN_FILE_HOLE 0x000052 + +/* Orphan file block has wrong magic */ +#define PR_0_ORPHAN_FILE_BAD_MAGIC 0x000053 + +/* Orphan file block has wrong checksum */ +#define PR_0_ORPHAN_FILE_BAD_CHECKSUM 0x000054 + +/* Orphan file size isn't multiple of blocks size */ +#define PR_0_ORPHAN_FILE_WRONG_SIZE 0x000055 + /* * Pass 1 errors */ @@ -716,6 +728,11 @@ struct problem_context { /* Htree directory uses SipHash but should not */ #define PR_1_HTREE_CANNOT_SIPHASH 0x01008E +/* Orphan file inode is not a regular file */ +#define PR_1_ORPHAN_FILE_BAD_MODE 0x01008F + +/* Orphan file inode is not in use, but contains data */ +#define PR_1_ORPHAN_FILE_NOT_CLEAR 0x010090 /* * Pass 1b errors @@ -1296,6 +1313,35 @@ struct problem_context { /* Error updating quota information */ #define PR_6_WRITE_QUOTAS 0x060006 +/* Orphan file without a journal */ +#define PR_6_ORPHAN_FILE_WITHOUT_JOURNAL 0x060007 + +/* Orphan file truncation failed */ +#define PR_6_ORPHAN_FILE_TRUNC_FAILED 0x060008 + +/* Failed to initialize orphan file */ +#define PR_6_ORPHAN_FILE_CORRUPTED 0x060009 + +/* Cannot fix corrupted orphan file with invalid bitmaps */ +#define PR_6_ORPHAN_FILE_BITMAP_INVALID 0x06000A + +/* Orphan file creation failed */ +#define PR_6_ORPHAN_FILE_CREATE_FAILED 0x06000B + +/* Orphan file block contains data */ +#define PR_6_ORPHAN_BLOCK_DIRTY 0x06000C + +/* orphan_present set but orphan file is empty */ +#define PR_6_ORPHAN_PRESENT_CLEAN_FILE 0x06000D + +/* orphan_present set but orphan_file is not */ +#define PR_6_ORPHAN_PRESENT_NO_FILE 0x06000E + +/* Orphan file size isn't multiple of blocks size */ +#define PR_6_ORPHAN_FILE_WRONG_SIZE 0x06000F + +/* Orphan file contains holes */ +#define PR_6_ORPHAN_FILE_HOLE 0x060010 /* * Function declarations diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index 8235e468..c1da7d52 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -89,9 +89,9 @@ struct fill_dir_struct { }; struct hash_entry { - ext2_dirhash_t hash; - ext2_dirhash_t minor_hash; - ext2_ino_t ino; + ext2_dirhash_t hash; + ext2_dirhash_t minor_hash; + ext2_ino_t ino; struct ext2_dir_entry *dir; }; diff --git a/e2fsck/super.c b/e2fsck/super.c index 123813be..9495e029 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -314,6 +314,180 @@ static errcode_t e2fsck_write_all_quotas(e2fsck_t ctx) return pctx.errcode; } +static int release_orphan_inode(e2fsck_t ctx, ext2_ino_t *ino, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + struct problem_context pctx; + struct ext2_inode_large inode; + ext2_ino_t next_ino; + + e2fsck_read_inode_full(ctx, *ino, EXT2_INODE(&inode), + sizeof(inode), "release_orphan_inode"); + clear_problem_context(&pctx); + pctx.ino = *ino; + pctx.inode = EXT2_INODE(&inode); + pctx.str = inode.i_links_count ? _("Truncating") : _("Clearing"); + + fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx); + + next_ino = inode.i_dtime; + if (next_ino && + ((next_ino < EXT2_FIRST_INODE(fs->super)) || + (next_ino > fs->super->s_inodes_count))) { + pctx.ino = next_ino; + fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx); + return 1; + } + + if (release_inode_blocks(ctx, *ino, &inode, block_buf, &pctx)) + return 1; + + if (!inode.i_links_count) { + if (ctx->qctx) + quota_data_inodes(ctx->qctx, &inode, *ino, -1); + ext2fs_inode_alloc_stats2(fs, *ino, -1, + LINUX_S_ISDIR(inode.i_mode)); + ctx->free_inodes++; + inode.i_dtime = ctx->now; + } else { + inode.i_dtime = 0; + } + e2fsck_write_inode_full(ctx, *ino, EXT2_INODE(&inode), + sizeof(inode), "delete_file"); + *ino = next_ino; + return 0; +} + +struct process_orphan_block_data { + e2fsck_t ctx; + char *buf; + char *block_buf; + e2_blkcnt_t blocks; + int abort; + int clear; + errcode_t errcode; + ext2_ino_t ino; + __u32 generation; +}; + +static int process_orphan_block(ext2_filsys fs, + blk64_t *block_nr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct process_orphan_block_data *pd; + e2fsck_t ctx; + struct problem_context pctx; + blk64_t blk = *block_nr; + struct ext4_orphan_block_tail *tail; + int j; + int inodes_per_ob; + __u32 *bdata; + ext2_ino_t ino; + + pd = priv_data; + ctx = pd->ctx; + clear_problem_context(&pctx); + pctx.ino = fs->super->s_orphan_file_inum; + pctx.blk = blockcnt; + + /* Orphan file must not have holes */ + if (!blk) { + if (blockcnt == pd->blocks) + return BLOCK_ABORT; + fix_problem(ctx, PR_0_ORPHAN_FILE_HOLE, &pctx); +return_abort: + pd->abort = 1; + return BLOCK_ABORT; + } + inodes_per_ob = ext2fs_inodes_per_orphan_block(fs); + pd->errcode = io_channel_read_blk64(fs->io, blk, 1, pd->buf); + if (pd->errcode) + goto return_abort; + tail = ext2fs_orphan_block_tail(fs, pd->buf); + if (ext2fs_le32_to_cpu(tail->ob_magic) != + EXT4_ORPHAN_BLOCK_MAGIC) { + fix_problem(ctx, PR_0_ORPHAN_FILE_BAD_MAGIC, &pctx); + goto return_abort; + } + if (!ext2fs_orphan_file_block_csum_verify(fs, + fs->super->s_orphan_file_inum, blk, pd->buf)) { + fix_problem(ctx, PR_0_ORPHAN_FILE_BAD_CHECKSUM, &pctx); + goto return_abort; + } + bdata = (__u32 *)pd->buf; + for (j = 0; j < inodes_per_ob; j++) { + if (!bdata[j]) + continue; + ino = ext2fs_le32_to_cpu(bdata[j]); + if (release_orphan_inode(ctx, &ino, pd->block_buf)) + goto return_abort; + } + return 0; +} + +static int process_orphan_file(e2fsck_t ctx, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + char *orphan_buf; + struct process_orphan_block_data pd; + int ret = 0; + ext2_ino_t orphan_inum = fs->super->s_orphan_file_inum; + struct ext2_inode orphan_inode; + struct problem_context pctx; + errcode_t retval; + + if (!ext2fs_has_feature_orphan_file(fs->super)) + return 0; + + clear_problem_context(&pctx); + pctx.ino = orphan_inum; + + orphan_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, + "orphan block buffer"); + retval = ext2fs_read_inode(fs, orphan_inum, &orphan_inode); + if (retval < 0) { + com_err("process_orphan_file", retval, + _("while reading inode %d"), orphan_inum); + ret = 1; + goto out; + } + if (EXT2_I_SIZE(&orphan_inode) & (fs->blocksize - 1)) { + fix_problem(ctx, PR_0_ORPHAN_FILE_WRONG_SIZE, &pctx); + ret = 1; + goto out; + } + pd.buf = orphan_buf + 3 * fs->blocksize; + pd.block_buf = block_buf; + pd.blocks = EXT2_I_SIZE(&orphan_inode) / fs->blocksize; + pd.ctx = ctx; + pd.abort = 0; + pd.errcode = 0; + retval = ext2fs_block_iterate3(fs, orphan_inum, + BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE, + orphan_buf, process_orphan_block, &pd); + if (retval) { + com_err("process_orphan_block", retval, + _("while calling ext2fs_block_iterate for inode %d"), + orphan_inum); + ret = 1; + goto out; + } + if (pd.abort) { + if (pd.errcode) { + com_err("process_orphan_block", pd.errcode, + _("while reading blocks of inode %d"), + orphan_inum); + } + ret = 1; + } +out: + ext2fs_free_mem(&orphan_buf); + return ret; +} + /* * This function releases all of the orphan inodes. It returns 1 if * it hit some error, and 0 on success. @@ -321,15 +495,17 @@ static errcode_t e2fsck_write_all_quotas(e2fsck_t ctx) static int release_orphan_inodes(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; - ext2_ino_t ino, next_ino; - struct ext2_inode_large inode; + ext2_ino_t ino; struct problem_context pctx; char *block_buf; - if ((ino = fs->super->s_last_orphan) == 0) + if (fs->super->s_last_orphan == 0 && + !ext2fs_has_feature_orphan_present(fs->super)) return 0; clear_problem_context(&pctx); + ino = fs->super->s_last_orphan; + pctx.ino = ino; pctx.errcode = e2fsck_read_all_quotas(ctx); if (pctx.errcode) { fix_problem(ctx, PR_0_QUOTA_INIT_CTX, &pctx); @@ -344,9 +520,10 @@ static int release_orphan_inodes(e2fsck_t ctx) ext2fs_mark_super_dirty(fs); /* - * If the filesystem contains errors, don't run the orphan - * list, since the orphan list can't be trusted; and we're - * going to be running a full e2fsck run anyway... + * If the filesystem contains errors, don't process the orphan list + * or orphan file, since neither can be trusted; and we're going to + * be running a full e2fsck run anyway... We clear orphan file contents + * after filesystem is checked to avoid clearing someone else's data. */ if (fs->super->s_state & EXT2_ERROR_FS) { if (ctx->qctx) @@ -354,10 +531,8 @@ static int release_orphan_inodes(e2fsck_t ctx) return 0; } - if ((ino < EXT2_FIRST_INODE(fs->super)) || - (ino > fs->super->s_inodes_count)) { - clear_problem_context(&pctx); - pctx.ino = ino; + if (ino && ((ino < EXT2_FIRST_INODE(fs->super)) || + (ino > fs->super->s_inodes_count))) { fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx); goto err_qctx; } @@ -366,43 +541,19 @@ static int release_orphan_inodes(e2fsck_t ctx) "block iterate buffer"); e2fsck_read_bitmaps(ctx); + /* First process orphan list */ while (ino) { - e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode), - sizeof(inode), "release_orphan_inodes"); - clear_problem_context(&pctx); - pctx.ino = ino; - pctx.inode = EXT2_INODE(&inode); - pctx.str = inode.i_links_count ? _("Truncating") : - _("Clearing"); - - fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx); - - next_ino = inode.i_dtime; - if (next_ino && - ((next_ino < EXT2_FIRST_INODE(fs->super)) || - (next_ino > fs->super->s_inodes_count))) { - pctx.ino = next_ino; - fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx); + if (release_orphan_inode(ctx, &ino, block_buf)) goto err_buf; - } + } - if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx)) - goto err_buf; + /* Next process orphan file */ + if (ext2fs_has_feature_orphan_present(fs->super) && + !ext2fs_has_feature_orphan_file(fs->super)) + goto err_buf; + if (process_orphan_file(ctx, block_buf)) + goto err_buf; - if (!inode.i_links_count) { - if (ctx->qctx) - quota_data_inodes(ctx->qctx, &inode, ino, -1); - ext2fs_inode_alloc_stats2(fs, ino, -1, - LINUX_S_ISDIR(inode.i_mode)); - ctx->free_inodes++; - inode.i_dtime = ctx->now; - } else { - inode.i_dtime = 0; - } - e2fsck_write_inode_full(ctx, ino, EXT2_INODE(&inode), - sizeof(inode), "delete_file"); - ino = next_ino; - } ext2fs_free_mem(&block_buf); pctx.errcode = e2fsck_write_all_quotas(ctx); if (pctx.errcode) @@ -417,6 +568,134 @@ err: return 1; } +static int reinit_orphan_block(ext2_filsys fs, + blk64_t *block_nr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct process_orphan_block_data *pd; + e2fsck_t ctx; + blk64_t blk = *block_nr; + struct problem_context pctx; + + pd = priv_data; + ctx = pd->ctx; + + /* Orphan file must not have holes */ + if (!blk) { + if (blockcnt == pd->blocks) + return BLOCK_ABORT; + + clear_problem_context(&pctx); + pctx.ino = fs->super->s_orphan_file_inum; + pctx.blk = blockcnt; + fix_problem(ctx, PR_6_ORPHAN_FILE_HOLE, &pctx); +return_abort: + pd->abort = 1; + return BLOCK_ABORT; + } + + if (ext2fs_has_feature_metadata_csum(fs->super)) { + struct ext4_orphan_block_tail *tail; + + tail = ext2fs_orphan_block_tail(fs, pd->buf); + /* + * Update checksum to match expected buffer contents with + * appropriate block number. + */ + tail->ob_checksum = ext2fs_do_orphan_file_block_csum(fs, + pd->ino, pd->generation, blk, pd->buf); + } + if (!pd->clear) { + pd->errcode = io_channel_read_blk64(fs->io, blk, 1, + pd->block_buf); + /* Block is already cleanly initialized? */ + if (!memcmp(pd->block_buf, pd->buf, fs->blocksize)) + return 0; + + clear_problem_context(&pctx); + pctx.ino = fs->super->s_orphan_file_inum; + pctx.blk = blockcnt; + if (!fix_problem(ctx, PR_6_ORPHAN_BLOCK_DIRTY, &pctx)) + goto return_abort; + pd->clear = 1; + } + pd->errcode = io_channel_write_blk64(fs->io, blk, 1, pd->buf); + if (pd->errcode) + goto return_abort; + return 0; +} + +/* + * Check and clear orphan file. We just return non-zero if we hit some + * inconsistency. Caller will truncate & recreate new orphan file. + */ +int check_init_orphan_file(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + char *orphan_buf; + struct process_orphan_block_data pd; + struct ext4_orphan_block_tail *tail; + ext2_ino_t orphan_inum = fs->super->s_orphan_file_inum; + struct ext2_inode orphan_inode; + int ret = 0; + errcode_t retval; + + orphan_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 5, + "orphan block buffer"); + e2fsck_read_inode(ctx, orphan_inum, &orphan_inode, "orphan inode"); + if (EXT2_I_SIZE(&orphan_inode) & (fs->blocksize - 1)) { + struct problem_context pctx; + + clear_problem_context(&pctx); + pctx.ino = orphan_inum; + fix_problem(ctx, PR_6_ORPHAN_FILE_WRONG_SIZE, &pctx); + ret = 1; + goto out; + } + pd.buf = orphan_buf + 3 * fs->blocksize; + pd.block_buf = orphan_buf + 4 * fs->blocksize; + pd.blocks = EXT2_I_SIZE(&orphan_inode) / fs->blocksize; + pd.ctx = ctx; + pd.abort = 0; + pd.clear = 0; + pd.errcode = 0; + pd.ino = orphan_inum; + pd.generation = orphan_inode.i_generation; + /* Initialize buffer to write */ + memset(pd.buf, 0, fs->blocksize); + tail = ext2fs_orphan_block_tail(fs, pd.buf); + tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC); + + retval = ext2fs_block_iterate3(fs, orphan_inum, + BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE, + orphan_buf, reinit_orphan_block, &pd); + if (retval) { + com_err("reinit_orphan_block", retval, + _("while calling ext2fs_block_iterate for inode %d"), + orphan_inum); + ret = 1; + goto out; + } + if (pd.abort) { + if (pd.errcode) { + com_err("process_orphan_block", pd.errcode, + _("while reading blocks of inode %d"), + orphan_inum); + } + ret = 1; + } + + /* We had to clear some blocks. Report it up. */ + if (ret == 0 && pd.clear) + ret = 2; +out: + ext2fs_free_mem(&orphan_buf); + return ret; +} + /* * Check the resize inode to make sure it is sane. We check both for * the case where on-line resizing is not enabled (in which case the diff --git a/e2fsck/unix.c b/e2fsck/unix.c index 853eb296..e5b672a2 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -1954,15 +1954,82 @@ print_unsupp_features: _("\n*** journal has been regenerated ***\n")); } } -no_journal: +no_journal: if (run_result & E2F_FLAG_ABORT) { fatal_error(ctx, _("aborted")); } else if (run_result & E2F_FLAG_CANCEL) { log_out(ctx, _("%s: e2fsck canceled.\n"), ctx->device_name ? ctx->device_name : ctx->filesystem_name); exit_value |= FSCK_CANCELED; - } else if (ctx->qctx && !ctx->invalid_bitmaps) { + goto cleanup; + } + + if (ext2fs_has_feature_orphan_file(fs->super)) { + int ret; + + /* No point in orphan file without a journal... */ + if (!ext2fs_has_feature_journal(fs->super) && + fix_problem(ctx, PR_6_ORPHAN_FILE_WITHOUT_JOURNAL, &pctx)) { + retval = ext2fs_truncate_orphan_file(fs); + if (retval) { + /* Huh, failed to delete file */ + fix_problem(ctx, PR_6_ORPHAN_FILE_TRUNC_FAILED, + &pctx); + goto check_quotas; + } + ext2fs_clear_feature_orphan_file(fs->super); + ext2fs_mark_super_dirty(fs); + goto check_quotas; + } + ret = check_init_orphan_file(ctx); + if (ret == 2 || + (ret == 0 && ext2fs_has_feature_orphan_present(fs->super) && + fix_problem(ctx, PR_6_ORPHAN_PRESENT_CLEAN_FILE, &pctx))) { + ext2fs_clear_feature_orphan_present(fs->super); + ext2fs_mark_super_dirty(fs); + } else if (ret == 1 && + fix_problem(ctx, PR_6_ORPHAN_FILE_CORRUPTED, &pctx)) { + int orphan_file_blocks; + + if (ctx->invalid_bitmaps) { + fix_problem(ctx, + PR_6_ORPHAN_FILE_BITMAP_INVALID, + &pctx); + goto check_quotas; + } + + retval = ext2fs_truncate_orphan_file(fs); + if (retval) { + /* Huh, failed to truncate file */ + fix_problem(ctx, PR_6_ORPHAN_FILE_TRUNC_FAILED, + &pctx); + goto check_quotas; + } + + orphan_file_blocks = + ext2fs_default_orphan_file_blocks(fs); + log_out(ctx, _("Creating orphan file (%d blocks): "), + orphan_file_blocks); + fflush(stdout); + retval = ext2fs_create_orphan_file(fs, + orphan_file_blocks); + if (retval) { + log_out(ctx, "%s: while trying to create " + "orphan file\n", error_message(retval)); + fix_problem(ctx, PR_6_ORPHAN_FILE_CREATE_FAILED, + &pctx); + goto check_quotas; + } + log_out(ctx, "%s", _(" Done.\n")); + } + } else if (ext2fs_has_feature_orphan_present(fs->super) && + fix_problem(ctx, PR_6_ORPHAN_PRESENT_NO_FILE, &pctx)) { + ext2fs_clear_feature_orphan_present(fs->super); + ext2fs_mark_super_dirty(fs); + } +check_quotas: + if (ctx->qctx && !ctx->invalid_bitmaps) { int needs_writeout; for (qtype = 0; qtype < MAXQUOTAS; qtype++) { @@ -1997,6 +2064,7 @@ no_journal: goto restart; } +cleanup: #ifdef MTRACE mtrace_print("Cleanup"); #endif |