Whamcloud - gitweb
e2fsck: add assert when copying context
[tools/e2fsprogs.git] / e2fsck / pass1.c
index 5015d93..b48b893 100644 (file)
  *     - A bitmap of which inodes have bad fields.     (inode_bad_map)
  *     - A bitmap of which inodes are in bad blocks.   (inode_bb_map)
  *     - A bitmap of which inodes are imagic inodes.   (inode_imagic_map)
+ *     - A bitmap of which inodes are casefolded.      (inode_casefold_map)
  *     - A bitmap of which blocks are in use.          (block_found_map)
  *     - A bitmap of which blocks are in use by two inodes     (block_dup_map)
  *     - The data blocks of the directory inodes.      (dir_map)
  *     - Ref counts for ea_inodes.                     (ea_inode_refs)
+ *     - The encryption policy ID of each encrypted inode. (encrypted_files)
  *
  * Pass 1 is designed to stash away enough information so that the
  * other passes should not need to read in the inode information
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
+#include <assert.h>
 
 #include "e2fsck.h"
 #include <ext2fs/ext2_ext_attr.h>
+/* todo remove this finally */
+#include <ext2fs/ext2fsP.h>
+#include <e2p/e2p.h>
 
 #include "problem.h"
 
@@ -77,7 +83,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_encrypted_dir(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);
@@ -128,16 +134,6 @@ static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
                            EXT2_MIN_BLOCK_LOG_SIZE + 1];
 
 /*
- * Free all memory allocated by pass1 in preparation for restarting
- * things.
- */
-static void unwind_pass1(ext2_filsys fs EXT2FS_ATTR((unused)))
-{
-       ext2fs_free_mem(&inodes_to_process);
-       inodes_to_process = 0;
-}
-
-/*
  * Check to make sure a device inode is real.  Returns 1 if the device
  * checks out, 0 if not.
  *
@@ -151,10 +147,10 @@ int e2fsck_pass1_check_device_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
        int     i;
 
        /*
-        * If the index flag is set, then this is a bogus
+        * If the index or extents flag is set, then this is a bogus
         * device/fifo/socket
         */
-       if (inode->i_flags & EXT2_INDEX_FL)
+       if (inode->i_flags & (EXT2_INDEX_FL | EXT4_EXTENTS_FL))
                return 0;
 
        /*
@@ -183,43 +179,18 @@ int e2fsck_pass1_check_device_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
 int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
                               struct ext2_inode *inode, char *buf)
 {
+       unsigned int buflen;
        unsigned int len;
-       int i;
-       ext2_extent_handle_t    handle;
-       struct ext2_extent_info info;
-       struct ext2fs_extent    extent;
 
        if ((inode->i_size_high || inode->i_size == 0) ||
            (inode->i_flags & EXT2_INDEX_FL))
                return 0;
 
-       if (inode->i_flags & EXT4_EXTENTS_FL) {
-               if (inode->i_flags & EXT4_INLINE_DATA_FL)
-                       return 0;
-               if (inode->i_size > fs->blocksize)
-                       return 0;
-               if (ext2fs_extent_open2(fs, ino, inode, &handle))
-                       return 0;
-               i = 0;
-               if (ext2fs_extent_get_info(handle, &info) ||
-                   (info.num_entries != 1) ||
-                   (info.max_depth != 0))
-                       goto exit_extent;
-               if (ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent) ||
-                   (extent.e_lblk != 0) ||
-                   (extent.e_len != 1) ||
-                   (extent.e_pblk < fs->super->s_first_data_block) ||
-                   (extent.e_pblk >= ext2fs_blocks_count(fs->super)))
-                       goto exit_extent;
-               i = 1;
-       exit_extent:
-               ext2fs_extent_free(handle);
-               return i;
-       }
-
        if (inode->i_flags & EXT4_INLINE_DATA_FL) {
                size_t inline_size;
 
+               if (inode->i_flags & EXT4_EXTENTS_FL)
+                       return 0;
                if (ext2fs_inline_data_size(fs, ino, &inline_size))
                        return 0;
                if (inode->i_size != inline_size)
@@ -229,36 +200,63 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
        }
 
        if (ext2fs_is_fast_symlink(inode)) {
-               if (inode->i_size >= sizeof(inode->i_block))
-                       return 0;
-
-               len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
-               if (len == sizeof(inode->i_block))
+               if (inode->i_flags & EXT4_EXTENTS_FL)
                        return 0;
+               buf = (char *)inode->i_block;
+               buflen = sizeof(inode->i_block);
        } else {
-               if ((inode->i_size >= fs->blocksize) ||
-                   (inode->i_block[0] < fs->super->s_first_data_block) ||
-                   (inode->i_block[0] >= ext2fs_blocks_count(fs->super)))
-                       return 0;
+               ext2_extent_handle_t    handle;
+               struct ext2_extent_info info;
+               struct ext2fs_extent    extent;
+               blk64_t blk;
+               int i;
 
-               for (i = 1; i < EXT2_N_BLOCKS; i++)
-                       if (inode->i_block[i])
+               if (inode->i_flags & EXT4_EXTENTS_FL) {
+                       if (ext2fs_extent_open2(fs, ino, inode, &handle))
                                return 0;
+                       if (ext2fs_extent_get_info(handle, &info) ||
+                           (info.num_entries != 1) ||
+                           (info.max_depth != 0)) {
+                               ext2fs_extent_free(handle);
+                               return 0;
+                       }
+                       if (ext2fs_extent_get(handle, EXT2_EXTENT_ROOT,
+                                             &extent) ||
+                           (extent.e_lblk != 0) ||
+                           (extent.e_len != 1)) {
+                               ext2fs_extent_free(handle);
+                               return 0;
+                       }
+                       blk = extent.e_pblk;
+                       ext2fs_extent_free(handle);
+               } else {
+                       blk = inode->i_block[0];
+
+                       for (i = 1; i < EXT2_N_BLOCKS; i++)
+                               if (inode->i_block[i])
+                                       return 0;
+               }
 
-               if (io_channel_read_blk64(fs->io, inode->i_block[0], 1, buf))
+               if (blk < fs->super->s_first_data_block ||
+                   blk >= ext2fs_blocks_count(fs->super))
                        return 0;
 
-               if (inode->i_flags & EXT4_ENCRYPT_FL) {
-                       len = ext2fs_le32_to_cpu(*((__u32 *)buf)) + 4;
-               } else {
-                       len = strnlen(buf, fs->blocksize);
-               }
-               if (len == fs->blocksize)
+               if (io_channel_read_blk64(fs->io, blk, 1, buf))
                        return 0;
+
+               buflen = fs->blocksize;
        }
+
+       if (inode->i_flags & EXT4_ENCRYPT_FL)
+               len = ext2fs_le16_to_cpu(*(__u16 *)buf) + 2;
+       else
+               len = strnlen(buf, buflen);
+
+       if (len >= buflen)
+               return 0;
+
        if (len != inode->i_size)
-               if ((inode->i_flags & EXT4_ENCRYPT_FL) == 0)
-                       return 0;
+               return 0;
        return 1;
 }
 
@@ -394,13 +392,13 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
 static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
                              struct ext2_ext_attr_entry *first, void *end)
 {
-       struct ext2_ext_attr_entry *entry;
+       struct ext2_ext_attr_entry *entry = first;
+       struct ext2_ext_attr_entry *np = EXT2_EXT_ATTR_NEXT(entry);
 
-       for (entry = first;
-            (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry);
-            entry = EXT2_EXT_ATTR_NEXT(entry)) {
+       while ((void *) entry < end && (void *) np < end &&
+              !EXT2_EXT_IS_LAST_ENTRY(entry)) {
                if (!entry->e_value_inum)
-                       continue;
+                       goto next;
                if (!ctx->ea_inode_refs) {
                        pctx->errcode = ea_refcount_create(0,
                                                           &ctx->ea_inode_refs);
@@ -413,6 +411,9 @@ static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
                }
                ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum,
                                      0);
+       next:
+               entry = np;
+               np = EXT2_EXT_ATTR_NEXT(entry);
        }
 }
 
@@ -817,7 +818,6 @@ extern errcode_t e2fsck_setup_icount(e2fsck_t ctx, const char *icount_name,
        errcode_t               retval;
        char                    *tdb_dir;
        int                     enable;
-       int                     full_map;
 
        *ret = 0;
 
@@ -1152,7 +1152,22 @@ static int quota_inum_is_reserved(ext2_filsys fs, ext2_ino_t ino)
        return 0;
 }
 
-void e2fsck_pass1(e2fsck_t ctx)
+static int e2fsck_should_abort(e2fsck_t ctx)
+{
+       e2fsck_t global_ctx;
+
+       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               return 1;
+
+       if (ctx->global_ctx) {
+               global_ctx = ctx->global_ctx;
+               if (global_ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       return 1;
+       }
+       return 0;
+}
+
+void e2fsck_pass1_thread(e2fsck_t ctx)
 {
        int     i;
        __u64   max_sizes;
@@ -1169,10 +1184,11 @@ void e2fsck_pass1(e2fsck_t ctx)
        struct          scan_callback_struct scan_struct;
        struct ext2_super_block *sb = ctx->fs->super;
        const char      *old_op;
-       int             imagic_fs, extent_fs, inlinedata_fs;
+       const char      *eop_next_inode = _("getting next inode from scan");
+       int             imagic_fs, extent_fs, inlinedata_fs, casefold_fs;
        int             low_dtime_check = 1;
-       int             inode_size = EXT2_INODE_SIZE(fs->super);
-       int             bufsize;
+       unsigned int    inode_size = EXT2_INODE_SIZE(fs->super);
+       unsigned int    bufsize;
        int             failed_csum = 0;
        ext2_ino_t      ino_threshold = 0;
        dgrp_t          ra_group = 0;
@@ -1215,6 +1231,7 @@ void e2fsck_pass1(e2fsck_t ctx)
        imagic_fs = ext2fs_has_feature_imagic_inodes(sb);
        extent_fs = ext2fs_has_feature_extents(sb);
        inlinedata_fs = ext2fs_has_feature_inline_data(sb);
+       casefold_fs = ext2fs_has_feature_casefold(sb);
 
        /*
         * Allocate bitmaps structures
@@ -1266,6 +1283,20 @@ void e2fsck_pass1(e2fsck_t ctx)
                ctx->flags |= E2F_FLAG_ABORT;
                return;
        }
+       if (casefold_fs) {
+               pctx.errcode =
+                       e2fsck_allocate_inode_bitmap(fs,
+                                                    _("inode casefold map"),
+                                                    EXT2FS_BMAP64_RBTREE,
+                                                    "inode_casefold_map",
+                                                    &ctx->inode_casefold_map);
+               if (pctx.errcode) {
+                       pctx.num = 1;
+                       fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return;
+               }
+       }
        pctx.errcode = e2fsck_setup_icount(ctx, "inode_link_info", 0, NULL,
                                           &ctx->inode_link_info);
        if (pctx.errcode) {
@@ -1338,8 +1369,10 @@ void e2fsck_pass1(e2fsck_t ctx)
        if (ctx->progress && ((ctx->progress)(ctx, 1, 0,
                                              ctx->fs->group_desc_count)))
                goto endit;
-       if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
-           (fs->super->s_mtime < fs->super->s_inodes_count) ||
+       if ((fs->super->s_wtime &&
+            fs->super->s_wtime < fs->super->s_inodes_count) ||
+           (fs->super->s_mtime &&
+            fs->super->s_mtime < fs->super->s_inodes_count) ||
            (fs->super->s_mkfs_time &&
             fs->super->s_mkfs_time < fs->super->s_inodes_count))
                low_dtime_check = 0;
@@ -1358,13 +1391,13 @@ void e2fsck_pass1(e2fsck_t ctx)
                        if (e2fsck_mmp_update(fs))
                                fatal_error(ctx, 0);
                }
-               old_op = ehandler_operation(_("getting next inode from scan"));
+               old_op = ehandler_operation(eop_next_inode);
                pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
                                                          inode, inode_size);
                if (ino > ino_threshold)
                        pass1_readahead(ctx, &ra_group, &ino_threshold);
                ehandler_operation(old_op);
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               if (e2fsck_should_abort(ctx))
                        goto endit;
                if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
                        /*
@@ -1386,17 +1419,9 @@ void e2fsck_pass1(e2fsck_t ctx)
                                        fix_problem(ctx, PR_1_ISCAN_ERROR,
                                                    &pctx);
                                        ctx->flags |= E2F_FLAG_ABORT;
-                                       goto endit;
-                               }
-                               err = ext2fs_inode_scan_goto_blockgroup(scan,
-                                                                       0);
-                               if (err) {
-                                       fix_problem(ctx, PR_1_ISCAN_ERROR,
-                                                   &pctx);
-                                       ctx->flags |= E2F_FLAG_ABORT;
-                                       goto endit;
-                               }
-                               continue;
+                               } else
+                                       ctx->flags |= E2F_FLAG_RESTART;
+                               goto endit;
                        }
                        if (!ctx->inode_bb_map)
                                alloc_bb_map(ctx);
@@ -1478,6 +1503,15 @@ void e2fsck_pass1(e2fsck_t ctx)
                        continue;
                }
 
+               if ((inode->i_flags & EXT4_CASEFOLD_FL) &&
+                   ((!LINUX_S_ISDIR(inode->i_mode) &&
+                     fix_problem(ctx, PR_1_CASEFOLD_NONDIR, &pctx)) ||
+                    (!casefold_fs &&
+                     fix_problem(ctx, PR_1_CASEFOLD_FEATURE, &pctx)))) {
+                       inode->i_flags &= ~EXT4_CASEFOLD_FL;
+                       e2fsck_write_inode(ctx, ino, inode, "pass1");
+               }
+
                /* Conflicting inlinedata/extents inode flags? */
                if ((inode->i_flags & EXT4_INLINE_DATA_FL) &&
                    (inode->i_flags & EXT4_EXTENTS_FL)) {
@@ -1495,8 +1529,8 @@ void e2fsck_pass1(e2fsck_t ctx)
                    (ino >= EXT2_FIRST_INODE(fs->super))) {
                        size_t size = 0;
 
-                       pctx.errcode = ext2fs_inline_data_size(fs, ino, &size);
-                       if (!pctx.errcode && size &&
+                       pctx.errcode = get_inline_data_ea_size(fs, ino, &size);
+                       if (!pctx.errcode &&
                            fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) {
                                ext2fs_set_feature_inline_data(sb);
                                ext2fs_mark_super_dirty(fs);
@@ -1540,6 +1574,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                        case EXT2_ET_NO_INLINE_DATA:
                        case EXT2_ET_EXT_ATTR_CSUM_INVALID:
                        case EXT2_ET_EA_BAD_VALUE_OFFSET:
+                       case EXT2_ET_EA_INODE_CORRUPTED:
                                /* broken EA or no system.data EA; truncate */
                                if (fix_problem(ctx, PR_1_INLINE_DATA_NO_ATTR,
                                                &pctx)) {
@@ -1868,12 +1903,19 @@ void e2fsck_pass1(e2fsck_t ctx)
                        failed_csum = 0;
                }
 
+               if ((inode->i_flags & EXT4_ENCRYPT_FL) &&
+                   add_encrypted_file(ctx, &pctx) < 0)
+                       goto clear_inode;
+
+               if (casefold_fs && inode->i_flags & EXT4_CASEFOLD_FL)
+                       ext2fs_mark_inode_bitmap2(ctx->inode_casefold_map, ino);
+
                if (LINUX_S_ISDIR(inode->i_mode)) {
                        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_ENCRYPT_FL)
-                               add_encrypted_dir(ctx, ino);
+                       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++;
@@ -1955,7 +1997,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                if (process_inode_count >= ctx->process_inode_size) {
                        process_inodes(ctx, block_buf);
 
-                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       if (e2fsck_should_abort(ctx))
                                goto endit;
                }
        }
@@ -2002,6 +2044,9 @@ void e2fsck_pass1(e2fsck_t ctx)
                ctx->block_ea_map = 0;
        }
 
+       /* We don't need the encryption policy => ID map any more */
+       destroy_encryption_policy_map(ctx);
+
        if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
                clear_problem_context(&pctx);
                pctx.errcode = ext2fs_create_resize_inode(fs);
@@ -2031,10 +2076,22 @@ void e2fsck_pass1(e2fsck_t ctx)
                 * master superblock.
                 */
                ctx->use_superblock = 0;
-               unwind_pass1(fs);
                goto endit;
        }
 
+       if (ctx->large_dirs && !ext2fs_has_feature_largedir(fs->super)) {
+               if (fix_problem(ctx, PR_2_FEATURE_LARGE_DIRS, &pctx)) {
+                       ext2fs_set_feature_largedir(fs->super);
+                       fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+                       ext2fs_mark_super_dirty(fs);
+               }
+               if (fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
+                   fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
+                       ext2fs_update_dynamic_rev(fs);
+                       ext2fs_mark_super_dirty(fs);
+               }
+       }
+
        if (ctx->block_dup_map) {
                if (ctx->options & E2F_OPT_PREEN) {
                        clear_problem_context(&pctx);
@@ -2043,9 +2100,10 @@ void e2fsck_pass1(e2fsck_t ctx)
                e2fsck_pass1_dupblocks(ctx, block_buf);
        }
        ctx->flags |= E2F_FLAG_ALLOC_OK;
-       ext2fs_free_mem(&inodes_to_process);
 endit:
        e2fsck_use_inode_shortcuts(ctx, 0);
+       ext2fs_free_mem(&inodes_to_process);
+       inodes_to_process = 0;
 
        if (scan)
                ext2fs_close_inode_scan(scan);
@@ -2065,6 +2123,164 @@ endit:
        else
                ctx->invalid_bitmaps++;
 }
+
+static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, ext2_filsys src)
+{
+       errcode_t       retval;
+
+       memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+       if (dest->dblist)
+               dest->dblist->fs = dest;
+       if (dest->inode_map)
+               dest->inode_map->fs = dest;
+       if (dest->block_map)
+               dest->block_map->fs = dest;
+
+       /* icache will be rebuilt if needed, so do not copy from @src */
+       src->icache = NULL;
+       return 0;
+}
+
+static void e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
+{
+       struct ext2_inode_cache *icache = dest->icache;
+
+       memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+       if (dest->dblist)
+               dest->dblist->fs = dest;
+       if (dest->inode_map)
+               dest->inode_map->fs = dest;
+       if (dest->block_map)
+               dest->block_map->fs = dest;
+       dest->icache = icache;
+
+       if (src->icache) {
+               ext2fs_free_inode_cache(src->icache);
+               src->icache = NULL;
+       }
+}
+
+static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx)
+{
+       errcode_t       retval;
+       e2fsck_t        thread_context;
+       ext2_filsys     thread_fs;
+       ext2_filsys     global_fs = global_ctx->fs;
+
+       assert(global_ctx->inode_used_map == NULL);
+       assert(global_ctx->inode_dir_map == NULL);
+       assert(global_ctx->inode_bb_map == NULL);
+       assert(global_ctx->inode_imagic_map == NULL);
+       assert(global_ctx->inode_reg_map == NULL);
+       assert(global_ctx->inodes_to_rebuild == NULL);
+
+       assert(global_ctx->block_found_map == NULL);
+       assert(global_ctx->block_dup_map == NULL);
+       assert(global_ctx->block_ea_map == NULL);
+       assert(global_ctx->block_metadata_map == NULL);
+       assert(global_ctx->fs->dblist == NULL);
+
+       retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context);
+       if (retval) {
+               com_err(global_ctx->program_name, retval, "while allocating memory");
+               return retval;
+       }
+       memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
+       thread_context->global_ctx = global_ctx;
+
+       retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &thread_fs);
+       if (retval) {
+               com_err(global_ctx->program_name, retval, "while allocating memory");
+               goto out_context;
+       }
+
+       retval = e2fsck_pass1_copy_fs(thread_fs, global_fs);
+       if (retval) {
+               com_err(global_ctx->program_name, retval, "while copying fs");
+               goto out_fs;
+       }
+       thread_fs->priv_data = thread_context;
+
+       thread_context->fs = thread_fs;
+       *thread_ctx = thread_context;
+       return 0;
+out_fs:
+       ext2fs_free_mem(&thread_fs);
+out_context:
+       ext2fs_free_mem(&thread_context);
+       return retval;
+}
+
+static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       int             flags = global_ctx->flags;
+       ext2_filsys     thread_fs = thread_ctx->fs;
+       ext2_filsys     global_fs = global_ctx->fs;
+#ifdef HAVE_SETJMP_H
+       jmp_buf         old_jmp;
+
+       memcpy(old_jmp, global_ctx->abort_loc, sizeof(jmp_buf));
+#endif
+       memcpy(global_ctx, thread_ctx, sizeof(struct e2fsck_struct));
+#ifdef HAVE_SETJMP_H
+       memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf));
+#endif
+       /* Keep the global singal flags*/
+       global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
+                            (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
+
+       e2fsck_pass1_merge_fs(global_fs, thread_fs);
+       global_fs->priv_data = global_ctx;
+       global_ctx->fs = global_fs;
+
+       ext2fs_free_mem(&thread_ctx->fs);
+       ext2fs_free_mem(&thread_ctx);
+       return 0;
+}
+
+void e2fsck_pass1_multithread(e2fsck_t ctx)
+{
+       errcode_t       retval;
+       e2fsck_t        thread_ctx;
+
+       retval = e2fsck_pass1_thread_prepare(ctx, &thread_ctx);
+       if (retval) {
+               com_err(ctx->program_name, 0,
+                       _("while preparing pass1 thread\n"));
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
+
+#ifdef HAVE_SETJMP_H
+       /*
+        * When fatal_error() happens, jump to here. The thread
+        * context's flags will be saved, but its abort_loc will
+        * be overwritten by original jump buffer for the later
+        * tests.
+        */
+       if (setjmp(thread_ctx->abort_loc)) {
+               thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+               e2fsck_pass1_thread_join(ctx, thread_ctx);
+               return;
+       }
+       thread_ctx->flags |= E2F_FLAG_SETJMP_OK;
+#endif
+
+       e2fsck_pass1_thread(thread_ctx);
+       retval = e2fsck_pass1_thread_join(ctx, thread_ctx);
+       if (retval) {
+               com_err(ctx->program_name, 0,
+                       _("while joining pass1 thread\n"));
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
+}
+
+void e2fsck_pass1(e2fsck_t ctx)
+{
+       e2fsck_pass1_multithread(ctx);
+}
+
 #undef FINISH_INODE_LOOP
 
 /*
@@ -2127,7 +2343,7 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
                ehandler_operation(buf);
                check_blocks(ctx, &pctx, block_buf,
                             &inodes_to_process[i].ea_ibody_quota);
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               if (e2fsck_should_abort(ctx))
                        break;
        }
        ctx->stashed_inode = old_stashed_inode;
@@ -2186,20 +2402,20 @@ static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
        ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino);
 }
 
-static void add_encrypted_dir(e2fsck_t ctx, ino_t ino)
+static void add_casefolded_dir(e2fsck_t ctx, ino_t ino)
 {
        struct          problem_context pctx;
 
-       if (!ctx->encrypted_dirs) {
-               pctx.errcode = ext2fs_u32_list_create(&ctx->encrypted_dirs, 0);
+       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->encrypted_dirs, ino);
+       pctx.errcode = ext2fs_u32_list_add(ctx->casefolded_dirs, ino);
        if (pctx.errcode == 0)
                return;
 error:
-       fix_problem(ctx, PR_1_ALLOCATE_ENCRYPTED_DIRLIST, &pctx);
+       fix_problem(ctx, PR_1_ALLOCATE_CASEFOLDED_DIRLIST, &pctx);
        /* Should never get here */
        ctx->flags |= E2F_FLAG_ABORT;
 }
@@ -2258,6 +2474,10 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
        clear_problem_context(&pctx);
 
        if (ext2fs_fast_test_block_bitmap2(ctx->block_found_map, block)) {
+               if (ext2fs_has_feature_shared_blocks(ctx->fs->super) &&
+                   !(ctx->options & E2F_OPT_UNSHARE_BLOCKS)) {
+                       return;
+               }
                if (!ctx->block_dup_map) {
                        pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
                                        _("multiply claimed block map"),
@@ -2288,7 +2508,8 @@ static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
        if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num))
                ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
        else {
-               int i;
+               unsigned int i;
+
                for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
                        mark_block_used(ctx, block + i);
        }
@@ -2511,8 +2732,9 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
                        break;
                }
                if (entry->e_value_inum == 0) {
-                       if (entry->e_value_offs + entry->e_value_size >
-                           fs->blocksize) {
+                       if (entry->e_value_size > EXT2_XATTR_SIZE_MAX ||
+                           (entry->e_value_offs + entry->e_value_size >
+                            fs->blocksize)) {
                                if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
                                        goto clear_extattr;
                                break;
@@ -2567,7 +2789,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
                        return 0;
        }
 
-       if (quota_blocks != EXT2FS_C2B(fs, 1)) {
+       if (quota_blocks != EXT2FS_C2B(fs, 1U)) {
                if (!ctx->ea_block_quota_blocks) {
                        pctx->errcode = ea_refcount_create(0,
                                                &ctx->ea_block_quota_blocks);
@@ -2656,18 +2878,49 @@ 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;
 
        pctx->num = root->indirect_levels;
-       if ((root->indirect_levels > ext2_dir_htree_level(fs)) &&
+       /* if htree level is clearly too high, consider it to be broken */
+       if (root->indirect_levels > EXT4_HTREE_LEVEL &&
            fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
                return 1;
 
+       /* if level is only maybe too high, LARGE_DIR feature could be unset */
+       if (root->indirect_levels > ext2_dir_htree_level(fs) &&
+           !ext2fs_has_feature_largedir(fs->super)) {
+               int blockbits = EXT2_BLOCK_SIZE_BITS(fs->super) + 10;
+               unsigned idx_pb = 1 << (blockbits - 3);
+
+               /* compare inode size/blocks vs. max-sized 2-level htree */
+               if (EXT2_I_SIZE(pctx->inode) <
+                   (idx_pb - 1) * (idx_pb - 2) << blockbits &&
+                   pctx->inode->i_blocks <
+                   (idx_pb - 1) * (idx_pb - 2) << (blockbits - 9) &&
+                   fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
+                       return 1;
+       }
+
+       if (root->indirect_levels > EXT4_HTREE_LEVEL_COMPAT ||
+           ext2fs_needs_large_file_feature(EXT2_I_SIZE(inode)))
+               ctx->large_dirs++;
+
        return 0;
 }
 
@@ -2765,7 +3018,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
        if (pctx->errcode)
                return;
        if (!(ctx->options & E2F_OPT_FIXES_ONLY) &&
-           !pb->eti.force_rebuild) {
+           !pb->eti.force_rebuild &&
+           info.curr_level < MAX_EXTENT_DEPTH_COUNT) {
                struct extent_tree_level *etl;
 
                etl = pb->eti.ext_info + info.curr_level;
@@ -2804,8 +3058,9 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                else if (extent.e_lblk < start_block)
                        problem = PR_1_OUT_OF_ORDER_EXTENTS;
                else if ((end_block && last_lblk > end_block) &&
-                        (!(extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT &&
-                               last_lblk > eof_block)))
+                        !(last_lblk > eof_block &&
+                          ((extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) ||
+                           (pctx->inode->i_flags & EXT4_VERITY_FL))))
                        problem = PR_1_EXTENT_END_OUT_OF_BOUNDS;
                else if (is_leaf && extent.e_len == 0)
                        problem = PR_1_EXTENT_LENGTH_ZERO;
@@ -2813,7 +3068,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                         (extent.e_pblk + extent.e_len) >
                         ext2fs_blocks_count(ctx->fs->super))
                        problem = PR_1_EXTENT_ENDS_BEYOND;
-               else if (is_leaf && is_dir &&
+               else if (is_leaf && is_dir && !pctx->inode->i_size_high &&
+                        !ext2fs_has_feature_largedir(ctx->fs->super) &&
                         ((extent.e_lblk + extent.e_len) >
                          (1U << (21 - ctx->fs->super->s_log_block_size))))
                        problem = PR_1_TOOBIG_DIR;
@@ -2821,9 +3077,10 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                if (is_leaf && problem == 0 && extent.e_len > 0) {
 #if 0
                        printf("extent_region(ino=%u, expect=%llu, "
-                              "lblk=%llu, len=%u)\n",
-                              pb->ino, pb->next_lblock,
-                              extent.e_lblk, extent.e_len);
+                              "lblk=%llu, len=%u)\n", pb->ino,
+                              (unsigned long long) pb->next_lblock,
+                              (unsigned long long) extent.e_lblk,
+                              extent.e_len);
 #endif
                        if (extent.e_lblk < pb->next_lblock)
                                problem = PR_1_EXTENT_COLLISION;
@@ -2846,7 +3103,20 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                                return;
                        failed_csum = 0;
                }
-
+#ifdef CONFIG_DEVELOPER_FEATURES
+               if (try_repairs && !is_dir && problem == 0 &&
+                   (ctx->options & E2F_OPT_CLEAR_UNINIT) &&
+                   (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+                   fix_problem(ctx, PR_1_CLEAR_UNINIT_EXTENT, pctx)) {
+                       extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT;
+                       pb->inode_modified = 1;
+                       pctx->errcode = ext2fs_extent_replace(ehandle, 0,
+                                                             &extent);
+                       if (pctx->errcode)
+                               return;
+                       failed_csum = 0;
+               }
+#endif
                if (try_repairs && problem) {
 report_problem:
                        if (fix_problem(ctx, problem, pctx)) {
@@ -2936,7 +3206,12 @@ report_problem:
                        if (extent.e_lblk != lblk) {
                                struct ext2_extent_info e_info;
 
-                               ext2fs_extent_get_info(ehandle, &e_info);
+                               pctx->errcode = ext2fs_extent_get_info(ehandle,
+                                                                      &e_info);
+                               if (pctx->errcode) {
+                                       pctx->str = "ext2fs_extent_get_info";
+                                       return;
+                               }
                                pctx->blk = lblk;
                                pctx->blk2 = extent.e_lblk;
                                pctx->num = e_info.curr_level - 1;
@@ -3296,7 +3571,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super);
 
        if (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) {
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               if (e2fsck_should_abort(ctx))
                        goto out;
                pb.num_blocks += EXT2FS_B2C(ctx->fs, ea_block_quota.blocks);
        }
@@ -3351,7 +3626,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        }
        end_problem_latch(ctx, PR_LATCH_BLOCK);
        end_problem_latch(ctx, PR_LATCH_TOOBIG);
-       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+       if (e2fsck_should_abort(ctx))
                goto out;
        if (pctx->errcode)
                fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
@@ -3374,7 +3649,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                        inode->i_flags &= ~EXT2_INDEX_FL;
                        dirty_inode++;
                } else {
-                       e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
+                       e2fsck_add_dx_dir(ctx, ino, inode, pb.last_block+1);
                }
        }
 
@@ -3405,11 +3680,13 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        pb.num_blocks *= EXT2FS_CLUSTER_RATIO(fs);
 #if 0
        printf("inode %u, i_size = %u, last_block = %llu, i_blocks=%llu, num_blocks = %llu\n",
-              ino, inode->i_size, pb.last_block, ext2fs_inode_i_blocks(fs, inode),
-              pb.num_blocks);
+              ino, inode->i_size, (unsigned long long) pb.last_block,
+              (unsigned long long) ext2fs_inode_i_blocks(fs, inode),
+              (unsigned long long) pb.num_blocks);
 #endif
+       size = EXT2_I_SIZE(inode);
        if (pb.is_dir) {
-               unsigned nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
+               unsigned nblock = size >> EXT2_BLOCK_SIZE_BITS(fs->super);
                if (inode->i_flags & EXT4_INLINE_DATA_FL) {
                        int flags;
                        size_t sz = 0;
@@ -3423,11 +3700,11 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                                          EXT2_FLAG_IGNORE_CSUM_ERRORS) |
                                         (ctx->fs->flags &
                                          ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
-                       if (err || sz != inode->i_size) {
+                       if (err || sz != size) {
                                bad_size = 7;
                                pctx->num = sz;
                        }
-               } else if (inode->i_size & (fs->blocksize - 1))
+               } else if (size & (fs->blocksize - 1))
                        bad_size = 5;
                else if (nblock > (pb.last_block + 1))
                        bad_size = 1;
@@ -3437,15 +3714,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                                bad_size = 2;
                }
        } else {
-               e2_blkcnt_t blkpg = ctx->blocks_per_page;
-
-               size = EXT2_I_SIZE(inode);
                if ((pb.last_init_lblock >= 0) &&
-                   /* allow allocated blocks to end of PAGE_SIZE */
+                   /* Do not allow initialized allocated blocks past i_size*/
                    (size < (__u64)pb.last_init_lblock * fs->blocksize) &&
-                   (pb.last_init_lblock / blkpg * blkpg != pb.last_init_lblock ||
-                    size < (__u64)(pb.last_init_lblock & ~(blkpg-1)) *
-                    fs->blocksize))
+                   !(inode->i_flags & EXT4_VERITY_FL))
                        bad_size = 3;
                else if (!(extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) &&
                         size > ext2_max_sizes[fs->super->s_log_block_size])
@@ -3464,8 +3736,6 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                        pctx->num = (pb.last_block + 1) * fs->blocksize;
                pctx->group = bad_size;
                if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
-                       if (LINUX_S_ISDIR(inode->i_mode))
-                               pctx->num &= 0xFFFFFFFFULL;
                        ext2fs_inode_size_set(fs, inode, pctx->num);
                        if (EXT2_I_SIZE(inode) == 0 &&
                            (inode->i_flags & EXT4_INLINE_DATA_FL)) {
@@ -3637,15 +3907,19 @@ static int process_block(ext2_filsys fs,
                                       (unsigned long) pctx->ino, type,
                                       (unsigned long) p->previous_block+1,
                                       (unsigned long) blk,
-                                      blockcnt);
+                                      (long long) blockcnt);
                        }
                        p->fragmented = 1;
                }
        }
 
-       if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
+       if (p->is_dir && !ext2fs_has_feature_largedir(fs->super) &&
+           !pctx->inode->i_size_high &&
+           blockcnt > (1 << (21 - fs->super->s_log_block_size)))
                problem = PR_1_TOOBIG_DIR;
-       if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
+       if (p->is_dir && p->num_blocks + 1 >= p->max_blocks)
+               problem = PR_1_TOOBIG_DIR;
+       if (p->is_reg && p->num_blocks + 1 >= p->max_blocks)
                problem = PR_1_TOOBIG_REG;
        if (!p->is_dir && !p->is_reg && blockcnt > 0)
                problem = PR_1_TOOBIG_SYMLINK;
@@ -3833,7 +4107,7 @@ static int process_bad_block(ext2_filsys fs,
                                *block_nr = 0;
                                return BLOCK_CHANGED;
                        }
-                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       if (e2fsck_should_abort(ctx))
                                return BLOCK_ABORT;
                } else
                        mark_block_used(ctx, blk);
@@ -3930,7 +4204,7 @@ static int process_bad_block(ext2_filsys fs,
                        *block_nr = 0;
                        return BLOCK_CHANGED;
                }
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               if (e2fsck_should_abort(ctx))
                        return BLOCK_ABORT;
                return 0;
        }
@@ -3969,7 +4243,7 @@ static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group,
         */
        is_flexbg = ext2fs_has_feature_flex_bg(fs->super);
        if (is_flexbg) {
-               flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+               flexbg_size = 1U << fs->super->s_log_groups_per_flex;
                flexbg = group / flexbg_size;
                first_block = ext2fs_group_first_block2(fs,
                                                        flexbg_size * flexbg);