diff options
author | Theodore Ts'o <tytso@mit.edu> | 2021-01-28 00:52:22 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2021-01-28 00:52:22 -0500 |
commit | 6be3ce7a545d3b49199e65cdde709ad51d1be502 (patch) | |
tree | 7970537931cffcebae9380b9daced36df34baecb /e2fsck | |
parent | 3304da1696f85225779b11aef420fdba9a20175a (diff) | |
parent | e1af9546e6a1572cbd7d8b70cf5344b5f7e2f51b (diff) | |
download | e2fsprogs-6be3ce7a545d3b49199e65cdde709ad51d1be502.tar.gz |
Merge branch 'maint' into next
Diffstat (limited to 'e2fsck')
-rw-r--r-- | e2fsck/Android.bp | 2 | ||||
-rw-r--r-- | e2fsck/e2fsck.c | 4 | ||||
-rw-r--r-- | e2fsck/e2fsck.h | 1 | ||||
-rw-r--r-- | e2fsck/jfs_user.h | 2 | ||||
-rw-r--r-- | e2fsck/pass1.c | 32 | ||||
-rw-r--r-- | e2fsck/pass2.c | 46 | ||||
-rw-r--r-- | e2fsck/problem.c | 17 | ||||
-rw-r--r-- | e2fsck/problem.h | 13 | ||||
-rw-r--r-- | e2fsck/rehash.c | 76 |
9 files changed, 166 insertions, 27 deletions
diff --git a/e2fsck/Android.bp b/e2fsck/Android.bp index d410a5e39..603e2ed51 100644 --- a/e2fsck/Android.bp +++ b/e2fsck/Android.bp @@ -54,10 +54,12 @@ e2fsck_libs = [ cc_binary { name: "e2fsck", host_supported: true, + vendor_ramdisk_available: true, defaults: ["e2fsck-defaults"], shared_libs: e2fsck_libs, system_shared_libs: ["libc", "libdl"], + required: ["badblocks"], } cc_binary { diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c index dc4b45e25..51fc2a99a 100644 --- a/e2fsck/e2fsck.c +++ b/e2fsck/e2fsck.c @@ -159,6 +159,10 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx) ext2fs_free_mem(&ctx->invalid_inode_table_flag); ctx->invalid_inode_table_flag = 0; } + if (ctx->casefolded_dirs) { + ext2fs_u32_list_free(ctx->casefolded_dirs); + ctx->casefolded_dirs = 0; + } if (ctx->inode_count) { ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index 0e910a92a..b5b16f049 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -425,6 +425,7 @@ struct e2fsck_struct { int ext_attr_ver; profile_t profile; int blocks_per_page; + ext2_u32_list casefolded_dirs; /* Reserve blocks for root and l+f re-creation */ blk64_t root_repair_block, lnf_repair_block; diff --git a/e2fsck/jfs_user.h b/e2fsck/jfs_user.h index a97fcc18a..1babf4172 100644 --- a/e2fsck/jfs_user.h +++ b/e2fsck/jfs_user.h @@ -51,7 +51,7 @@ struct buffer_head { unsigned int b_dirty:1; unsigned int b_uptodate:1; unsigned long long b_blocknr; - char b_data[1024]; + char b_data[4096]; }; struct inode { diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 6909fed58..b866cc83f 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -80,6 +80,7 @@ static void mark_table_blocks(e2fsck_t ctx); static void alloc_bb_map(e2fsck_t ctx); static void alloc_imagic_map(e2fsck_t ctx); static void mark_inode_bad(e2fsck_t ctx, ino_t ino); +static void add_casefolded_dir(e2fsck_t ctx, ino_t ino); static void handle_fs_bad_blocks(e2fsck_t ctx); static void process_inodes(e2fsck_t ctx, char *block_buf); static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b); @@ -1892,6 +1893,8 @@ void e2fsck_pass1(e2fsck_t ctx) ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino); e2fsck_add_dir_info(ctx, ino, 0); ctx->fs_directory_count++; + if (inode->i_flags & EXT4_CASEFOLD_FL) + add_casefolded_dir(ctx, ino); } else if (LINUX_S_ISREG (inode->i_mode)) { ext2fs_mark_inode_bitmap2(ctx->inode_reg_map, ino); ctx->fs_regular_count++; @@ -2207,6 +2210,24 @@ static void mark_inode_bad(e2fsck_t ctx, ino_t ino) ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino); } +static void add_casefolded_dir(e2fsck_t ctx, ino_t ino) +{ + struct problem_context pctx; + + if (!ctx->casefolded_dirs) { + pctx.errcode = ext2fs_u32_list_create(&ctx->casefolded_dirs, 0); + if (pctx.errcode) + goto error; + } + pctx.errcode = ext2fs_u32_list_add(ctx->casefolded_dirs, ino); + if (pctx.errcode == 0) + return; +error: + fix_problem(ctx, PR_1_ALLOCATE_CASEFOLDED_DIRLIST, &pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; +} + /* * This procedure will allocate the inode "bb" (badblock) map table */ @@ -2664,9 +2685,20 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, if ((root->hash_version != EXT2_HASH_LEGACY) && (root->hash_version != EXT2_HASH_HALF_MD4) && (root->hash_version != EXT2_HASH_TEA) && + (root->hash_version != EXT2_HASH_SIPHASH) && fix_problem(ctx, PR_1_HTREE_HASHV, pctx)) return 1; + if (ext4_hash_in_dirent(inode)) { + if (root->hash_version != EXT2_HASH_SIPHASH && + fix_problem(ctx, PR_1_HTREE_NEEDS_SIPHASH, pctx)) + return 1; + } else { + if (root->hash_version == EXT2_HASH_SIPHASH && + fix_problem(ctx, PR_1_HTREE_CANNOT_SIPHASH, pctx)) + return 1; + } + if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) && fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx)) return 1; diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 7e961e82f..8b7f84a7d 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -294,6 +294,10 @@ void e2fsck_pass2(e2fsck_t ctx) ctx->inode_casefold_map = 0; } destroy_encrypted_file_info(ctx); + if (ctx->casefolded_dirs) { + ext2fs_u32_list_free(ctx->casefolded_dirs); + ctx->casefolded_dirs = 0; + } clear_problem_context(&pctx); if (ctx->large_files) { @@ -747,7 +751,8 @@ static void salvage_directory(ext2_filsys fs, struct ext2_dir_entry *dirent, struct ext2_dir_entry *prev, unsigned int *offset, - unsigned int block_len) + unsigned int block_len, + int hash_in_dirent) { char *cp = (char *) dirent; int left; @@ -772,7 +777,8 @@ static void salvage_directory(ext2_filsys fs, * Special case of directory entry of size 8: copy what's left * of the directory block up to cover up the invalid hole. */ - if ((left >= 12) && (rec_len == EXT2_DIR_ENTRY_HEADER_LEN)) { + if ((left >= ext2fs_dir_rec_len(1, hash_in_dirent)) && + (rec_len == EXT2_DIR_ENTRY_HEADER_LEN)) { memmove(cp, cp+EXT2_DIR_ENTRY_HEADER_LEN, left); memset(cp + left, 0, EXT2_DIR_ENTRY_HEADER_LEN); return; @@ -784,7 +790,7 @@ static void salvage_directory(ext2_filsys fs, */ if ((left < 0) && ((int) rec_len + left > EXT2_DIR_ENTRY_HEADER_LEN) && - ((int) name_len + EXT2_DIR_ENTRY_HEADER_LEN <= (int) rec_len + left) && + ((int) ext2fs_dir_rec_len(name_len, hash_in_dirent) <= (int) rec_len + left) && dirent->inode <= fs->super->s_inodes_count && strnlen(dirent->name, name_len) == name_len) { (void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent); @@ -1039,6 +1045,8 @@ static int check_dir_block(ext2_filsys fs, size_t inline_data_size = 0; int filetype = 0; __u32 dir_encpolicy_id = NO_ENCRYPTION_POLICY; + int hash_in_dirent = 0; + int casefolded = 0; size_t max_block_size; int hash_flags = 0; static char *eop_read_dirblock = NULL; @@ -1276,12 +1284,19 @@ skip_checksum: } else { dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); } + if (ctx->casefolded_dirs) + casefolded = ext2fs_u32_list_test(ctx->casefolded_dirs, ino); + hash_in_dirent = (casefolded && + (dir_encpolicy_id != NO_ENCRYPTION_POLICY)); prev = 0; do { dgrp_t group; ext2_ino_t first_unused_inode; unsigned int name_len; + /* csum entry is not checked here, so don't worry about it */ + int extended = (dot_state > 1) && hash_in_dirent; + int min_dir_len = ext2fs_dir_rec_len(1, extended); problem = 0; if (!inline_data_size || dot_state > 1) { @@ -1291,15 +1306,16 @@ skip_checksum: * force salvaging this dir. */ if (max_block_size - offset < EXT2_DIR_ENTRY_HEADER_LEN) - rec_len = EXT2_DIR_REC_LEN(1); + rec_len = ext2fs_dir_rec_len(1, extended); else (void) ext2fs_get_rec_len(fs, dirent, &rec_len); cd->pctx.dirent = dirent; cd->pctx.num = offset; if ((offset + rec_len > max_block_size) || - (rec_len < 12) || + (rec_len < min_dir_len) || ((rec_len % 4) != 0) || - (((unsigned) ext2fs_dirent_name_len(dirent) + EXT2_DIR_ENTRY_HEADER_LEN) > rec_len)) { + ((ext2fs_dir_rec_len(ext2fs_dirent_name_len(dirent), + extended)) > rec_len)) { if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { #ifdef WORDS_BIGENDIAN @@ -1332,7 +1348,8 @@ skip_checksum: #endif salvage_directory(fs, dirent, prev, &offset, - max_block_size); + max_block_size, + hash_in_dirent); #ifdef WORDS_BIGENDIAN if (need_reswab) { (void) ext2fs_get_rec_len(fs, @@ -1565,10 +1582,17 @@ skip_checksum: if (dx_dir->casefolded_hash) hash_flags = EXT4_CASEFOLD_FL; - ext2fs_dirhash2(dx_dir->hashversion, dirent->name, - ext2fs_dirent_name_len(dirent), - fs->encoding, hash_flags, - fs->super->s_hash_seed, &hash, 0); + if (dx_dir->hashversion == EXT2_HASH_SIPHASH) { + if (dot_state > 1) + hash = EXT2_DIRENT_HASH(dirent); + } else { + ext2fs_dirhash2(dx_dir->hashversion, + dirent->name, + ext2fs_dirent_name_len(dirent), + fs->encoding, hash_flags, + fs->super->s_hash_seed, + &hash, 0); + } if (hash < dx_db->min_hash) dx_db->min_hash = hash; if (hash > dx_db->max_hash) diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 995223429..fe3b06a6d 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -1269,6 +1269,17 @@ static struct e2fsck_problem problem_table[] = { N_("Encrypted @i %i has corrupt encryption @a.\n"), PROMPT_CLEAR_INODE, 0, 0, 0, 0 }, + /* Htree directory should use SipHash but does not */ + { PR_1_HTREE_NEEDS_SIPHASH, + N_("@h %i uses hash version (%N), but should use SipHash (6) \n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK, 0, 0, 0 }, + + /* Htree directory uses SipHash but should not */ + { PR_1_HTREE_CANNOT_SIPHASH, + N_("@h %i uses SipHash, but should not. "), + PROMPT_CLEAR_HTREE, PR_PREEN_OK, 0, 0, 0 }, + + /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ @@ -1810,6 +1821,12 @@ static struct e2fsck_problem problem_table[] = { N_("@E has illegal UTF-8 characters in its name.\n"), PROMPT_FIX, 0, 0, 0, 0 }, + /* Non-unique filename found, but can't rename */ + { PR_2_NON_UNIQUE_FILE_NO_RENAME, + N_("Duplicate filename @E found. "), + PROMPT_CLEAR, 0, 0, 0, 0 }, + + /* Pass 3 errors */ /* Pass 3: Checking directory connectivity */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index e2fbb597a..922c99df3 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -707,6 +707,16 @@ struct problem_context { /* Encrypted inode has corrupt encryption extended attribute */ #define PR_1_CORRUPT_ENCRYPTION_XATTR 0x01008B +/* Error allocating memory for casefolded directory list */ +#define PR_1_ALLOCATE_CASEFOLDED_DIRLIST 0x01008C + +/* Htree directory should use SipHash but does not */ +#define PR_1_HTREE_NEEDS_SIPHASH 0x01008D + +/* Htree directory uses SipHash but should not */ +#define PR_1_HTREE_CANNOT_SIPHASH 0x01008E + + /* * Pass 1b errors */ @@ -1031,6 +1041,9 @@ struct problem_context { /* Encoded directory entry has illegal characters in its name */ #define PR_2_BAD_ENCODED_NAME 0x020053 +/* Non-unique filename found, but can't rename */ +#define PR_2_NON_UNIQUE_FILE_NO_RENAME 0x020054 + /* * Pass 3 errors */ diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index 0fbf4cbdd..1ff4aa1ee 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -101,6 +101,21 @@ struct out_dir { ext2_dirhash_t *hashes; }; +#define DOTDOT_OFFSET 12 + +static int is_fake_entry(ext2_filsys fs, int lblk, unsigned int offset) +{ + /* Entries in the first block before this value refer to . or .. */ + if (lblk == 0 && offset <= DOTDOT_OFFSET) + return 1; + /* Check if this is likely the csum entry */ + if (ext2fs_has_feature_metadata_csum(fs->super) && + (offset & (fs->blocksize - 1)) == + fs->blocksize - sizeof(struct ext2_dir_entry_tail)) + return 1; + return 0; +} + static int fill_dir_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, @@ -113,7 +128,7 @@ static int fill_dir_block(ext2_filsys fs, struct ext2_dir_entry *dirent; char *dir; unsigned int offset, dir_offset, rec_len, name_len; - int hash_alg, hash_flags; + int hash_alg, hash_flags, hash_in_entry; if (blockcnt < 0) return 0; @@ -140,6 +155,7 @@ static int fill_dir_block(ext2_filsys fs, return BLOCK_ABORT; } hash_flags = fd->inode->i_flags & EXT4_CASEFOLD_FL; + hash_in_entry = ext4_hash_in_dirent(fd->inode); hash_alg = fs->super->s_def_hash_version; if ((hash_alg <= EXT2_HASH_TEA) && (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) @@ -147,13 +163,18 @@ static int fill_dir_block(ext2_filsys fs, /* While the directory block is "hot", index it. */ dir_offset = 0; while (dir_offset < fs->blocksize) { + int min_rec = EXT2_DIR_ENTRY_HEADER_LEN; + int extended = hash_in_entry && !is_fake_entry(fs, blockcnt, dir_offset); + + if (extended) + min_rec += EXT2_DIR_ENTRY_HASH_LEN; dirent = (struct ext2_dir_entry *) (dir + dir_offset); (void) ext2fs_get_rec_len(fs, dirent, &rec_len); name_len = ext2fs_dirent_name_len(dirent); if (((dir_offset + rec_len) > fs->blocksize) || - (rec_len < 8) || + (rec_len < min_rec) || ((rec_len % 4) != 0) || - (name_len + 8 > rec_len)) { + (name_len + min_rec > rec_len)) { fd->err = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } @@ -187,11 +208,14 @@ static int fill_dir_block(ext2_filsys fs, } ent = fd->harray + fd->num_array++; ent->dir = dirent; - fd->dir_size += EXT2_DIR_REC_LEN(name_len); + fd->dir_size += ext2fs_dir_rec_len(name_len, extended); ent->ino = dirent->inode; - if (fd->compress) + if (extended) { + ent->hash = EXT2_DIRENT_HASH(dirent); + ent->minor_hash = EXT2_DIRENT_MINOR_HASH(dirent); + } else if (fd->compress) { ent->hash = ent->minor_hash = 0; - else { + } else { fd->err = ext2fs_dirhash2(hash_alg, dirent->name, name_len, fs->encoding, hash_flags, @@ -462,6 +486,15 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, fixed++; continue; } + /* Can't alter encrypted name without key, so just drop it */ + if (fd->inode->i_flags & EXT4_ENCRYPT_FL) { + if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE_NO_RENAME, &pctx)) { + e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1); + ent->dir->inode = 0; + fixed++; + continue; + } + } new_len = ext2fs_dirent_name_len(ent->dir); if (new_len == 0) { /* should never happen */ @@ -511,6 +544,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, ext2_dirhash_t prev_hash; int csum_size = 0; struct ext2_dir_entry_tail *t; + int hash_in_entry = ext4_hash_in_dirent(fd->inode); + int min_rec_len = ext2fs_dir_rec_len(1, hash_in_entry); if (ctx->htree_slack_percentage == 255) { profile_get_uint(ctx->profile, "options", @@ -539,15 +574,16 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, prev_rec_len = 0; rec_len = 0; left = fs->blocksize - csum_size; - slack = fd->compress ? 12 : + slack = fd->compress ? min_rec_len : ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100; - if (slack < 12) - slack = 12; + if (slack < min_rec_len) + slack = min_rec_len; for (i = 0; i < fd->num_array; i++) { ent = fd->harray + i; if (ent->dir->inode == 0) continue; - rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir)); + rec_len = ext2fs_dir_rec_len(ext2fs_dirent_name_len(ent->dir), + hash_in_entry); if (rec_len > left) { if (left) { left += prev_rec_len; @@ -584,6 +620,11 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, prev_rec_len = rec_len; memcpy(dirent->name, ent->dir->name, ext2fs_dirent_name_len(dirent)); + if (hash_in_entry) { + EXT2_DIRENT_HASHES(dirent)->hash = ext2fs_cpu_to_le32(ent->hash); + EXT2_DIRENT_HASHES(dirent)->minor_hash = + ext2fs_cpu_to_le32(ent->minor_hash); + } offset += rec_len; left -= rec_len; if (left < slack) { @@ -608,7 +649,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, - ext2_ino_t ino, ext2_ino_t parent) + ext2_ino_t ino, ext2_ino_t parent, + struct ext2_inode *inode) { struct ext2_dir_entry *dir; struct ext2_dx_root_info *root; @@ -636,7 +678,10 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, root = (struct ext2_dx_root_info *) (buf+24); root->reserved_zero = 0; - root->hash_version = fs->super->s_def_hash_version; + if (ext4_hash_in_dirent(inode)) + root->hash_version = EXT2_HASH_SIPHASH; + else + root->hash_version = fs->super->s_def_hash_version; root->info_length = 8; root->indirect_levels = 0; root->unused_flags = 0; @@ -722,7 +767,8 @@ static int alloc_blocks(ext2_filsys fs, static errcode_t calculate_tree(ext2_filsys fs, struct out_dir *outdir, ext2_ino_t ino, - ext2_ino_t parent) + ext2_ino_t parent, + struct ext2_inode *inode) { struct ext2_dx_root_info *root_info; struct ext2_dx_entry *root, *int_ent, *dx_ent = 0; @@ -731,7 +777,7 @@ static errcode_t calculate_tree(ext2_filsys fs, int i, c1, c2, c3, nblks; int limit_offset, int_offset, root_offset; - root_info = set_root_node(fs, outdir->buf, ino, parent); + root_info = set_root_node(fs, outdir->buf, ino, parent, inode); root_offset = limit_offset = ((char *) root_info - outdir->buf) + root_info->info_length; root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); @@ -1036,7 +1082,7 @@ resort: if (!fd.compress) { /* Calculate the interior nodes */ - retval = calculate_tree(fs, &outdir, ino, fd.parent); + retval = calculate_tree(fs, &outdir, ino, fd.parent, fd.inode); if (retval) goto errout; } |