Checks that directory leaf blocks have the necessary fake dir_entry at the end of the block to hold a checksum and that the checksum is valid. It will resize the block and/or rebuild the directory if necessary. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- e2fsck/pass2.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++------- e2fsck/pass3.c | 4 +++ e2fsck/problem.c | 15 ++++++++++ e2fsck/problem.h | 9 ++++++ 4 files changed, 97 insertions(+), 10 deletions(-) diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 3a0d050..108bdb5 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -748,8 +748,9 @@ static int check_dir_block(ext2_filsys fs, struct problem_context pctx; int dups_found = 0; int ret; - int dx_csum_size = 0; + 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; @@ -762,8 +763,10 @@ static int check_dir_block(ext2_filsys fs, return DIRENT_ABORT; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + 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 @@ -809,11 +812,15 @@ static int check_dir_block(ext2_filsys fs, 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; } - memset(buf, 0, fs->blocksize); + 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); @@ -861,10 +868,43 @@ static int check_dir_block(ext2_filsys fs, ((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 { @@ -1112,10 +1152,7 @@ out_htree: pctx.ino = ino; pctx.dirent = dirent; fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); - if (!ctx->dirs_to_hash) - ext2fs_u32_list_create(&ctx->dirs_to_hash, 50); - if (ctx->dirs_to_hash) - ext2fs_u32_list_add(ctx->dirs_to_hash, ino); + e2fsck_rehash_dir_later(ctx, ino); dups_found++; } else dict_alloc_insert(&de_dict, dirent, dirent); @@ -1131,7 +1168,7 @@ out_htree: (void) ext2fs_get_rec_len(fs, dirent, &rec_len); offset += rec_len; dot_state++; - } while (offset < fs->blocksize); + } while (offset < fs->blocksize - de_csum_size); #if 0 printf("\n"); #endif @@ -1148,22 +1185,44 @@ out_htree: parse_int_node(fs, db, cd, dx_dir, buf, failed_csum); } #endif /* ENABLE_HTREE */ - if (offset != fs->blocksize) { - cd->pctx.num = rec_len - fs->blocksize + offset; + + 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; diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c index 86a509c..7ebbadc 100644 --- a/e2fsck/pass3.c +++ b/e2fsck/pass3.c @@ -658,8 +658,12 @@ static void fix_dotdot(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent) clear_problem_context(&pctx); pctx.ino = ino; + if (e2fsck_dir_will_be_rehashed(ctx, ino)) + ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; retval = ext2fs_dir_iterate(fs, ino, DIRENT_FLAG_INCLUDE_EMPTY, 0, fix_dotdot_proc, &fp); + if (e2fsck_dir_will_be_rehashed(ctx, ino)) + ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (retval || !fp.done) { pctx.errcode = retval; fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR : diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 894b2f8..6dbd01c 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -1398,6 +1398,21 @@ static struct e2fsck_problem problem_table[] = { N_("@p @h %d: internal node fails checksum\n"), PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + /* leaf node fails checksum */ + { PR_2_LEAF_NODE_CSUM_INVALID, + N_("@d @i %i, %B, offset %N: @d fails checksum\n"), + PROMPT_SALVAGE, 0 }, + + /* leaf node has no checksum */ + { PR_2_LEAF_NODE_MISSING_CSUM, + N_("@d @i %i, %B, offset %N: @d has no checksum\n"), + PROMPT_FIX, PR_PREEN_OK }, + + /* leaf node passes checks, but fails checksum */ + { PR_2_LEAF_NODE_CSUM_INVALID, + N_("@d @i %i, %B, offset %N: @d passes checks, but fails checksum\n"), + PROMPT_FIX, 0 }, + /* Pass 3 errors */ /* Pass 3: Checking directory connectivity */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 3185da9..3f2b2e9 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -833,6 +833,15 @@ struct problem_context { /* htree node fails checksum */ #define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A +/* dir leaf node fails checksum */ +#define PR_2_LEAF_NODE_CSUM_INVALID 0x02004B + +/* no space in leaf for checksum */ +#define PR_2_LEAF_NODE_MISSING_CSUM 0x02004C + +/* dir leaf node passes checks, but fails checksum */ +#define PR_2_LEAF_NODE_ONLY_CSUM_INVALID 0x02004D + /* * Pass 3 errors */ -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html