From: Sebastien Buisson <sbuisson@xxxxxxx> e2fsck needs to be improved in order to support encrypted files handling in parallel fsck mode. The e2fsck_merge_encrypted_info() function is added to merge encrypted inodes info collected from different threads in pass1, so that it can be used in pass2. Signed-off-by: Sebastien Buisson <sbuisson@xxxxxxx> Tested-by: Maloo <maloo@xxxxxxxxxxxxx> Reviewed-by: Li Dongyang <dongyangli@xxxxxxx> Reviewed-by: Andreas Dilger <adilger@xxxxxxxxxxxxx> Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@xxxxxxxxx> --- e2fsck/e2fsck.h | 2 + e2fsck/encrypted_files.c | 139 +++++++++++++++++++++++++++++++++++++++ e2fsck/pass1.c | 26 +++++++- 3 files changed, 164 insertions(+), 3 deletions(-) diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index 1e82b048..e4fb782a 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -642,6 +642,8 @@ __u32 find_encryption_policy(e2fsck_t ctx, ext2_ino_t ino); void destroy_encryption_policy_map(e2fsck_t ctx); void destroy_encrypted_file_info(e2fsck_t ctx); +int e2fsck_merge_encrypted_info(e2fsck_t ctx, struct encrypted_file_info *src, + struct encrypted_file_info *dest); /* extents.c */ errcode_t e2fsck_rebuild_extents_later(e2fsck_t ctx, ext2_ino_t ino); diff --git a/e2fsck/encrypted_files.c b/e2fsck/encrypted_files.c index 16be2d6d..53e03a62 100644 --- a/e2fsck/encrypted_files.c +++ b/e2fsck/encrypted_files.c @@ -456,3 +456,142 @@ void destroy_encrypted_file_info(e2fsck_t ctx) ctx->encrypted_files = NULL; } } + +/** + * Search policy matching @policy in @info->policies + * @ctx: e2fsck context + * @info: encrypted_file_info to look into + * @policy: the policy we are looking for + * @parent: (out) last known parent, useful to insert a new leaf + * in @info->policies + * + * Return: id of found policy on success, -1 if no matching policy found. + */ +static inline int search_policy(e2fsck_t ctx, struct encrypted_file_info *info, + union fscrypt_policy policy, + struct rb_node **parent) +{ + struct rb_node *n = info->policies.rb_node; + struct policy_map_entry *entry; + + while (n) { + int res; + + *parent = n; + entry = ext2fs_rb_entry(n, struct policy_map_entry, node); + res = cmp_fscrypt_policies(ctx, &policy, &entry->policy); + if (res < 0) + n = n->rb_left; + else if (res > 0) + n = n->rb_right; + else + return entry->policy_id; + } + return -1; +} + +/* + * Merge @src encrypted info into @dest + */ +int e2fsck_merge_encrypted_info(e2fsck_t ctx, struct encrypted_file_info *src, + struct encrypted_file_info *dest) +{ + struct rb_root *src_policies = &src->policies; + __u32 *policy_trans; + int i, rc = 0; + + if (dest->file_ranges[src->file_ranges_count - 1].last_ino > + src->file_ranges[0].first_ino) { + /* Should never get here */ + fatal_error(ctx, "Encrypted inodes processed out of order"); + } + + rc = ext2fs_get_array(src->next_policy_id, sizeof(__u32), + &policy_trans); + if (rc) + return rc; + + /* First, deal with the encryption policy => ID map. + * Compare encryption policies in src with policies already recorded + * in dest. It can be similar policies, but recorded with a different + * id, so policy_trans array converts policy ids in src to ids in dest. + * This loop examines each policy in src->policies rb tree, updates + * policy_trans, and removes the entry from src, so that src->policies + * rb tree is cleaned up at the end of the loop. + */ + while (!ext2fs_rb_empty_root(src_policies)) { + struct policy_map_entry *entry, *newentry; + struct rb_node *new, *parent = NULL; + int existing_polid; + + entry = ext2fs_rb_entry(src_policies->rb_node, + struct policy_map_entry, node); + existing_polid = search_policy(ctx, dest, + entry->policy, &parent); + if (existing_polid >= 0) { + /* The policy in src is already recorded in dest, + * so just update its id. + */ + policy_trans[entry->policy_id] = existing_polid; + } else { + /* The policy in src is new to dest, so insert it + * with the next available id (its original id could + * be already used in dest). + */ + rc = ext2fs_get_mem(sizeof(*newentry), &newentry); + if (rc) + goto out_merge; + newentry->policy_id = dest->next_policy_id++; + newentry->policy = entry->policy; + ext2fs_rb_link_node(&newentry->node, parent, &new); + ext2fs_rb_insert_color(&newentry->node, + &dest->policies); + policy_trans[entry->policy_id] = newentry->policy_id; + } + ext2fs_rb_erase(&entry->node, src_policies); + ext2fs_free_mem(&entry); + } + + /* Second, deal with the inode number => encryption policy ID map. */ + if (dest->file_ranges_capacity < + dest->file_ranges_count + src->file_ranges_count) { + /* dest->file_ranges is too short, increase its capacity. */ + size_t new_capacity = dest->file_ranges_count + + src->file_ranges_count; + + /* Make sure we at least double the capacity. */ + if (new_capacity < (dest->file_ranges_capacity * 2)) + new_capacity = dest->file_ranges_capacity * 2; + + /* We won't need more than the filesystem's inode count. */ + if (new_capacity > ctx->fs->super->s_inodes_count) + new_capacity = ctx->fs->super->s_inodes_count; + + rc = ext2fs_resize_mem(dest->file_ranges_capacity * + sizeof(struct encrypted_file_range), + new_capacity * + sizeof(struct encrypted_file_range), + &dest->file_ranges); + if (rc) { + fix_problem(ctx, PR_1_ALLOCATE_ENCRYPTED_INODE_LIST, + NULL); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + goto out_merge; + } + + dest->file_ranges_capacity = new_capacity; + } + /* Copy file ranges from src to dest. */ + for (i = 0; i < src->file_ranges_count; i++) { + /* Make sure to convert policy ids in src. */ + src->file_ranges[i].policy_id = + policy_trans[src->file_ranges[i].policy_id]; + dest->file_ranges[dest->file_ranges_count++] = + src->file_ranges[i]; + } + +out_merge: + ext2fs_free_mem(&policy_trans); + return rc; +} diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 7345c96d..e7dc017c 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -2411,9 +2411,6 @@ void e2fsck_pass1_run(e2fsck_t ctx) ctx->ea_block_quota_inodes = 0; } - /* We don't need the encryption policy => ID map any more */ - destroy_encryption_policy_map(ctx); - if (ctx->flags & E2F_FLAG_RESTART) { /* * Only the master copy of the superblock and block @@ -2703,6 +2700,23 @@ static void e2fsck_pass1_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx) e2fsck_merge_dx_dir(global_ctx, thread_ctx); } +static int e2fsck_pass1_merge_encrypted_info(e2fsck_t global_ctx, + e2fsck_t thread_ctx) +{ + if (thread_ctx->encrypted_files == NULL) + return 0; + + if (global_ctx->encrypted_files == NULL) { + global_ctx->encrypted_files = thread_ctx->encrypted_files; + thread_ctx->encrypted_files = NULL; + return 0; + } + + return e2fsck_merge_encrypted_info(global_ctx, + thread_ctx->encrypted_files, + global_ctx->encrypted_files); +} + static inline errcode_t e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount, ext2_icount_t *src_icount) @@ -2963,6 +2977,12 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx, e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx); e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx); + retval = e2fsck_pass1_merge_encrypted_info(global_ctx, thread_ctx); + if (retval) { + com_err(global_ctx->program_name, 0, + _("while merging encrypted info\n")); + return retval; + } retval = ext2fs_merge_fs(&(thread_ctx->fs)); if (retval) { -- 2.37.3