Whamcloud - gitweb
LU-16972 e2fsck: use rb-tree to track EA reference counts
[tools/e2fsprogs.git] / e2fsck / pass1.c
index 41eac08..037b99a 100644 (file)
  *     - A bitmap of which inodes are in use.          (inode_used_map)
  *     - A bitmap of which inodes are directories.     (inode_dir_map)
  *     - A bitmap of which inodes are regular files.   (inode_reg_map)
- *     - A bitmap of which inodes have bad fields.     (inode_bad_map)
+ *     - An icount mechanism is used to keep track of
+ *       inodes with bad fields and its badness        (ctx->inode_badness)
  *     - 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 inodes need to be expanded  (expand_eisize_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>
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
 
 #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,10 +87,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 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, ext2_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);
 static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
                                  dgrp_t group, void * priv_data);
@@ -115,30 +123,20 @@ struct process_inode_block {
 };
 
 struct scan_callback_struct {
-       e2fsck_t        ctx;
-       char            *block_buf;
+       e2fsck_t                         ctx;
+       char                            *block_buf;
+       struct process_inode_block      *inodes_to_process;
+       int                             *process_inode_count;
 };
 
-/*
- * For the inodes to process list.
- */
-static struct process_inode_block *inodes_to_process;
-static int process_inode_count;
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+                          struct process_inode_block *inodes_to_process,
+                          int *process_inode_count);
 
 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.
  *
@@ -181,11 +179,12 @@ int e2fsck_pass1_check_device_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
  * Check to make sure a symlink inode is real.  Returns 1 if the symlink
  * checks out, 0 if not.
  */
-int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
-                              struct ext2_inode *inode, char *buf)
+static int check_symlink(e2fsck_t ctx, struct problem_context *pctx,
+                        ext2_ino_t ino, struct ext2_inode *inode, char *buf)
 {
        unsigned int buflen;
        unsigned int len;
+       blk64_t blk;
 
        if ((inode->i_size_high || inode->i_size == 0) ||
            (inode->i_flags & EXT2_INDEX_FL))
@@ -196,7 +195,7 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
 
                if (inode->i_flags & EXT4_EXTENTS_FL)
                        return 0;
-               if (ext2fs_inline_data_size(fs, ino, &inline_size))
+               if (ext2fs_inline_data_size(ctx->fs, ino, &inline_size))
                        return 0;
                if (inode->i_size != inline_size)
                        return 0;
@@ -213,11 +212,10 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
                ext2_extent_handle_t    handle;
                struct ext2_extent_info info;
                struct ext2fs_extent    extent;
-               blk64_t blk;
                int i;
 
                if (inode->i_flags & EXT4_EXTENTS_FL) {
-                       if (ext2fs_extent_open2(fs, ino, inode, &handle))
+                       if (ext2fs_extent_open2(ctx->fs, ino, inode, &handle))
                                return 0;
                        if (ext2fs_extent_get_info(handle, &info) ||
                            (info.num_entries != 1) ||
@@ -242,29 +240,53 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
                                        return 0;
                }
 
-               if (blk < fs->super->s_first_data_block ||
-                   blk >= ext2fs_blocks_count(fs->super))
+               if (blk < ctx->fs->super->s_first_data_block ||
+                   blk >= ext2fs_blocks_count(ctx->fs->super))
                        return 0;
 
-               if (io_channel_read_blk64(fs->io, blk, 1, buf))
+               if (io_channel_read_blk64(ctx->fs->io, blk, 1, buf))
                        return 0;
 
-               buflen = fs->blocksize;
+               buflen = ctx->fs->blocksize;
        }
 
        if (inode->i_flags & EXT4_ENCRYPT_FL)
                len = ext2fs_le16_to_cpu(*(__u16 *)buf) + 2;
-       else
+       else {
                len = strnlen(buf, buflen);
 
+               /* Add missing NUL terminator at end of symlink (LU-1540),
+                * but only offer to fix this in pass1, not from pass2. */
+               if (len > inode->i_size && pctx != NULL &&
+                   fix_problem(ctx, PR_1_SYMLINK_NUL, pctx)) {
+                       buf[inode->i_size] = '\0';
+                       if (ext2fs_is_fast_symlink(inode)) {
+                               e2fsck_write_inode(ctx, ino,
+                                                  inode, "check_ext_attr");
+                       } else {
+                               if (io_channel_write_blk64(ctx->fs->io,
+                                                          blk, 1, buf))
+                                       return 0;
+                       }
+                       len = inode->i_size;
+               }
+       }
+
        if (len >= buflen)
                return 0;
 
        if (len != inode->i_size)
                return 0;
+
        return 1;
 }
 
+int e2fsck_pass1_check_symlink(e2fsck_t ctx, ext2_ino_t ino,
+                              struct ext2_inode *inode, char *buf)
+{
+       return check_symlink(ctx, NULL, ino, inode, buf);
+}
+
 /*
  * If the extents or inlinedata flags are set on the inode, offer to clear 'em.
  */
@@ -339,7 +361,7 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
                                      blk64_t *quota_blocks)
 {
        struct ext2_inode inode;
-       __u32 hash;
+       __u32 hash, signed_hash;
        errcode_t retval;
 
        /* Check if inode is within valid range */
@@ -351,7 +373,8 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
 
        e2fsck_read_inode(ctx, entry->e_value_inum, &inode, "pass1");
 
-       retval = ext2fs_ext_attr_hash_entry2(ctx->fs, entry, NULL, &hash);
+       retval = ext2fs_ext_attr_hash_entry3(ctx->fs, entry, NULL, &hash,
+                                            &signed_hash);
        if (retval) {
                com_err("check_large_ea_inode", retval,
                        _("while hashing entry with e_value_inum = %u"),
@@ -359,7 +382,7 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
                fatal_error(ctx, 0);
        }
 
-       if (hash == entry->e_hash) {
+       if ((hash == entry->e_hash) || (signed_hash == entry->e_hash)) {
                *quota_blocks = size_to_quota_blocks(ctx->fs,
                                                     entry->e_value_size);
        } else {
@@ -385,8 +408,10 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
                pctx->num = entry->e_value_inum;
                if (fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) {
                        inode.i_flags |= EXT4_EA_INODE_FL;
+                       e2fsck_pass1_fix_lock(ctx);
                        ext2fs_write_inode(ctx->fs, entry->e_value_inum,
                                           &inode);
+                       e2fsck_pass1_fix_unlock(ctx);
                } else {
                        return PR_1_ATTR_NO_EA_INODE_FL;
                }
@@ -397,16 +422,15 @@ 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);
+                       pctx->errcode = ea_refcount_create(&ctx->ea_inode_refs);
                        if (pctx->errcode) {
                                pctx->num = 4;
                                fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
@@ -416,6 +440,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);
        }
 }
 
@@ -434,13 +461,13 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
        ea_ibody_quota->inodes = 0;
 
        inode = (struct ext2_inode_large *) pctx->inode;
-       storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
-               inode->i_extra_isize;
+       storage_size = EXT2_INODE_SIZE(ctx->fs->super) -
+               EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize;
        header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
                 inode->i_extra_isize;
        end = header + storage_size;
-       start = header + sizeof(__u32);
-       entry = (struct ext2_ext_attr_entry *) start;
+       entry = &IHDR(inode)->h_first_entry[0];
+       start = (char *)entry;
 
        /* scan all entry's headers first */
 
@@ -500,7 +527,10 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
                        }
 
                        hash = ext2fs_ext_attr_hash_entry(entry,
-                                                         start + entry->e_value_offs);
+                                               start + entry->e_value_offs);
+                       if (entry->e_hash != 0 && entry->e_hash != hash)
+                               hash = ext2fs_ext_attr_hash_entry_signed(entry,
+                                               start + entry->e_value_offs);
 
                        /* e_hash may be 0 in older inode's ea */
                        if (entry->e_hash != 0 && entry->e_hash != hash) {
@@ -575,7 +605,7 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
        struct ext2_super_block *sb = ctx->fs->super;
        struct ext2_inode_large *inode;
        __u32 *eamagic;
-       int min, max;
+       int min, max, dirty = 0;
 
        ea_ibody_quota->blocks = 0;
        ea_ibody_quota->inodes = 0;
@@ -603,24 +633,40 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
                if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
                        return;
                if (inode->i_extra_isize < min || inode->i_extra_isize > max)
-                       inode->i_extra_isize = sb->s_want_extra_isize;
+                       inode->i_extra_isize = ctx->want_extra_isize;
                else
                        inode->i_extra_isize = (inode->i_extra_isize + 3) & ~3;
-               e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
-                                       EXT2_INODE_SIZE(sb), "pass1");
+               dirty = 1;
+
+               goto out;
        }
 
        /* check if there is no place for an EA header */
        if (inode->i_extra_isize >= max - sizeof(__u32))
                return;
 
-       eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
-                       inode->i_extra_isize);
-       if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
-               /* it seems inode has an extended attribute(s) in body */
-               check_ea_in_inode(ctx, pctx, ea_ibody_quota);
+       eamagic = &IHDR(inode)->h_magic;
+       if (*eamagic != EXT2_EXT_ATTR_MAGIC &&
+           (ctx->flags & E2F_FLAG_EXPAND_EISIZE) &&
+           (inode->i_extra_isize < ctx->want_extra_isize)) {
+               fix_problem(ctx, PR_1_EXPAND_EISIZE, pctx);
+               memset((char *)inode + EXT2_GOOD_OLD_INODE_SIZE, 0,
+                       EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE);
+               inode->i_extra_isize = ctx->want_extra_isize;
+               dirty = 1;
+               if (inode->i_extra_isize < ctx->min_extra_isize)
+                       ctx->min_extra_isize = inode->i_extra_isize;
        }
 
+       if (*eamagic == EXT2_EXT_ATTR_MAGIC)
+               check_ea_in_inode(ctx, pctx, ea_ibody_quota);
+
+       /* Since crtime cannot be set directly from userspace, consider
+        * very old/future values worse than a bad atime/mtime. */
+       if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_crtime, ctx->time_fudge))
+               e2fsck_mark_inode_badder(ctx, pctx, PR_1_CRTIME_BAD);
+       else if (EXT4_XTIME_ANCIENT(ctx, sb, inode->i_crtime, ctx->time_fudge))
+               e2fsck_mark_inode_badder(ctx, pctx, PR_1_CRTIME_BAD);
        /*
         * If the inode's extended atime (ctime, crtime, mtime) is stored in
         * the old, invalid format, repair it.
@@ -633,7 +679,7 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
             CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, crtime) ||
             CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, mtime))) {
 
-               if (!fix_problem(ctx, PR_1_EA_TIME_OUT_OF_RANGE, pctx))
+               if (!fix_problem_bad(ctx, PR_1_EA_TIME_OUT_OF_RANGE, pctx, 2))
                        return;
 
                if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, atime))
@@ -644,10 +690,38 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
                        inode->i_crtime_extra &= ~EXT4_EPOCH_MASK;
                if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, mtime))
                        inode->i_mtime_extra &= ~EXT4_EPOCH_MASK;
+               dirty = 1;
+       }
+
+out:
+       if (dirty)
                e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
                                        EXT2_INODE_SIZE(sb), "pass1");
+}
+
+static _INLINE_ int is_blocks_used(e2fsck_t ctx, blk64_t block,
+                                  unsigned int num)
+{
+       int retval;
+
+       /* used to avoid duplicate output from below */
+       retval = ext2fs_test_block_bitmap_range2_valid(ctx->block_found_map,
+                                                      block, num);
+       if (!retval)
+               return 0;
+
+       retval = ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num);
+       if (retval) {
+               e2fsck_pass1_block_map_r_lock(ctx);
+               if (ctx->global_ctx)
+                       retval = ext2fs_test_block_bitmap_range2(
+                                       ctx->global_ctx->block_found_map, block, num);
+               e2fsck_pass1_block_map_r_unlock(ctx);
+               if (retval)
+                       return 0;
        }
 
+       return 1;
 }
 
 /*
@@ -680,14 +754,14 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
            LINUX_S_ISLNK(inode->i_mode) || inode->i_block[0] == 0)
                return;
 
-       /* 
+       /*
         * Check the block numbers in the i_block array for validity:
         * zero blocks are skipped (but the first one cannot be zero -
         * see above), other blocks are checked against the first and
         * max data blocks (from the the superblock) and against the
         * block bitmap. Any invalid block found means this cannot be
         * a directory.
-        * 
+        *
         * If there are non-zero blocks past the fourth entry, then
         * this cannot be a device file: we remember that for the next
         * check.
@@ -722,7 +796,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
                 */
                memcpy(&dotdot, inode->i_block, sizeof(dotdot));
                memcpy(&de, ((char *)inode->i_block) + EXT4_INLINE_DATA_DOTDOT_SIZE,
-                      EXT2_DIR_REC_LEN(0));
+                      EXT2_DIR_NAME_LEN(0));
                dotdot = ext2fs_le32_to_cpu(dotdot);
                de.inode = ext2fs_le32_to_cpu(de.inode);
                de.rec_len = ext2fs_le16_to_cpu(de.rec_len);
@@ -755,8 +829,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
 
                        if (blk < ctx->fs->super->s_first_data_block ||
                            blk >= ext2fs_blocks_count(ctx->fs->super) ||
-                           ext2fs_fast_test_block_bitmap2(ctx->block_found_map,
-                                                          blk))
+                           is_blocks_used(ctx, blk, 1))
                                return; /* Invalid block, can't be dir */
                }
                blk = inode->i_block[0];
@@ -877,11 +950,158 @@ static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
        if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx))
                return 0;
 
+
+       e2fsck_pass1_fix_lock(ctx);
        retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
                                         sizeof(inode));
+       e2fsck_pass1_fix_unlock(ctx);
        return retval;
 }
 
+int e2fsck_pass1_delete_attr(e2fsck_t ctx, struct ext2_inode_large *inode,
+                            struct problem_context *pctx, int needed_size)
+{
+       struct ext2_ext_attr_header *header;
+       struct ext2_ext_attr_entry *entry_ino, *entry_blk = NULL, *entry;
+       char *start, name[4096], block_buf[4096];
+       int len, index = EXT2_ATTR_INDEX_USER, entry_size, ea_size;
+       int in_inode = 1, error;
+       unsigned int freed_bytes = inode->i_extra_isize;
+
+       entry_ino = &IHDR(inode)->h_first_entry[0];
+       start = (char *)entry_ino;
+
+       if (inode->i_file_acl) {
+               error = ext2fs_read_ext_attr(ctx->fs, inode->i_file_acl,
+                                            block_buf);
+               /* We have already checked this block, shouldn't happen */
+               if (error) {
+                       fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, pctx);
+                       return 0;
+               }
+               header = BHDR(block_buf);
+               if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+                       fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, pctx);
+                       return 0;
+               }
+
+               entry_blk = (struct ext2_ext_attr_entry *)(header+1);
+       }
+       entry = entry_ino;
+       len = sizeof(entry->e_name);
+       entry_size = ext2fs_attr_get_next_attr(entry, index, name, len, 1);
+
+       while (freed_bytes < needed_size) {
+               if (entry_size && name[0] != '\0') {
+                       pctx->str = name;
+                       if (fix_problem(ctx, PR_1_EISIZE_DELETE_EA, pctx)) {
+                               ea_size = EXT2_EXT_ATTR_LEN(entry->e_name_len) +
+                                         EXT2_EXT_ATTR_SIZE(entry->e_value_size);
+                               error = ext2fs_attr_set(ctx->fs, pctx->ino,
+                                                       (struct ext2_inode *)inode,
+                                                       index, name, 0, 0, 0);
+                               if (!error)
+                                       freed_bytes += ea_size;
+                       }
+               }
+               len = sizeof(entry->e_name);
+               entry_size = ext2fs_attr_get_next_attr(entry, index,name,len,0);
+               entry = EXT2_EXT_ATTR_NEXT(entry);
+               if (EXT2_EXT_IS_LAST_ENTRY(entry)) {
+                       if (in_inode) {
+                               entry = entry_blk;
+                               len = sizeof(entry->e_name);
+                               entry_size = ext2fs_attr_get_next_attr(entry,
+                                                       index, name, len, 1);
+                               in_inode = 0;
+                       } else {
+                               index += 1;
+                               in_inode = 1;
+                               if (!entry && index < EXT2_ATTR_INDEX_MAX)
+                                       entry = (struct ext2_ext_attr_entry *)start;
+                               else
+                                       return freed_bytes;
+                       }
+               }
+       }
+
+       return freed_bytes;
+}
+
+int e2fsck_pass1_expand_eisize(e2fsck_t ctx, struct ext2_inode_large *inode,
+                              struct problem_context *pctx)
+{
+       int needed_size = 0, retval, ret = EXT2_EXPAND_EISIZE_UNSAFE;
+       static int message;
+
+retry:
+       retval = ext2fs_expand_extra_isize(ctx->fs, pctx->ino, inode,
+                                          ctx->want_extra_isize, &ret,
+                                          &needed_size);
+       if (ret & EXT2_EXPAND_EISIZE_NEW_BLOCK)
+               goto mark_expand_eisize_map;
+       if (!retval) {
+               e2fsck_write_inode_full(ctx, pctx->ino,
+                                       (struct ext2_inode *)inode,
+                                       EXT2_INODE_SIZE(ctx->fs->super),
+                                       "pass1");
+               return 0;
+       }
+
+       if (ret & EXT2_EXPAND_EISIZE_NOSPC) {
+               if (ctx->options & (E2F_OPT_PREEN | E2F_OPT_YES)) {
+                       fix_problem(ctx, PR_1_EA_BLK_NOSPC, pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return -1;
+               }
+
+               if (!message) {
+                       pctx->num = ctx->fs->super->s_min_extra_isize;
+                       fix_problem(ctx, PR_1_EXPAND_EISIZE_WARNING, pctx);
+                       message = 1;
+               }
+delete_EA:
+               retval = e2fsck_pass1_delete_attr(ctx, inode, pctx,
+                                                 needed_size);
+               if (retval >= ctx->want_extra_isize)
+                       goto retry;
+
+               needed_size -= retval;
+
+               /*
+                * We loop here until either the user deletes EA(s) or
+                * EXTRA_ISIZE feature is disabled.
+                */
+               if (fix_problem(ctx, PR_1_CLEAR_EXTRA_ISIZE, pctx)) {
+                       ctx->fs->super->s_feature_ro_compat &=
+                                       ~EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE;
+                       ext2fs_mark_super_dirty(ctx->fs);
+               } else {
+                       goto delete_EA;
+               }
+               ctx->fs_unexpanded_inodes++;
+
+               /* No EA was deleted, inode cannot be expanded */
+               return -1;
+       }
+
+mark_expand_eisize_map:
+       if (!ctx->expand_eisize_map) {
+               pctx->errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
+                                        _("expand extrz isize map"),
+                                        &ctx->expand_eisize_map);
+               if (pctx->errcode) {
+                       fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR,
+                                   pctx);
+                       exit(1);
+               }
+       }
+
+       /* Add this inode to the expand_eisize_map */
+       ext2fs_mark_inode_bitmap2(ctx->expand_eisize_map, pctx->ino);
+       return 0;
+}
+
 static void reserve_block_for_root_repair(e2fsck_t ctx)
 {
        blk64_t         blk = 0;
@@ -916,9 +1136,11 @@ static void reserve_block_for_lnf_repair(e2fsck_t ctx)
                return;
        ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
        ctx->lnf_repair_block = blk;
+       return;
 }
 
 static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino,
+                                        struct ext2_inode *inode,
                                         size_t *sz)
 {
        void *p;
@@ -929,7 +1151,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;
 
@@ -942,6 +1165,28 @@ err:
        return retval;
 }
 
+int e2fsck_fix_bad_inode(e2fsck_t ctx, struct problem_context *pctx)
+{
+       __u16 badness;
+       int rc = 0;
+
+       if (!ctx->inode_badness)
+               return 0;
+
+       if (ext2fs_icount_fetch(ctx->inode_badness, pctx->ino, &badness))
+               return 0;
+
+       if (badness > ctx->inode_badness_threshold) {
+               __u64 pctx_num_sav = pctx->num;
+
+               pctx->num = badness;
+               rc = fix_problem_notbad(ctx, PR_1B_INODE_TOOBAD, pctx);
+               pctx->num = pctx_num_sav;
+       }
+
+       return rc;
+}
+
 static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino,
                                    struct problem_context *pctx,
                                    int failed_csum)
@@ -961,8 +1206,10 @@ static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino,
 #define FINISH_INODE_LOOP(ctx, ino, pctx, failed_csum) \
        do { \
                finish_processing_inode((ctx), (ino), (pctx), (failed_csum)); \
-               if ((ctx)->flags & E2F_FLAG_ABORT) \
+               if (e2fsck_should_abort(ctx)) { \
+                       e2fsck_pass1_check_unlock(ctx); \
                        return; \
+               } \
        } while (0)
 
 static int could_be_block_map(ext2_filsys fs, struct ext2_inode *inode)
@@ -1068,16 +1315,20 @@ out:
 static void pass1_readahead(e2fsck_t ctx, dgrp_t *group, ext2_ino_t *next_ino)
 {
        ext2_ino_t inodes_in_group = 0, inodes_per_block, inodes_per_buffer;
-       dgrp_t start = *group, grp;
+       dgrp_t start = *group, grp, grp_end = ctx->fs->group_desc_count;
        blk64_t blocks_to_read = 0;
        errcode_t err = EXT2_ET_INVALID_ARGUMENT;
 
+#ifdef HAVE_PTHREAD
+       if (ctx->fs->fs_num_threads > 1)
+               grp_end = ctx->thread_info.et_group_end;
+#endif
        if (ctx->readahead_kb == 0)
                goto out;
 
        /* Keep iterating groups until we have enough to readahead */
        inodes_per_block = EXT2_INODES_PER_BLOCK(ctx->fs->super);
-       for (grp = start; grp < ctx->fs->group_desc_count; grp++) {
+       for (grp = start; grp < grp_end; grp++) {
                if (ext2fs_bg_flags_test(ctx->fs, grp, EXT2_BG_INODE_UNINIT))
                        continue;
                inodes_in_group = ctx->fs->super->s_inodes_per_group -
@@ -1154,55 +1405,30 @@ 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)
 {
-       int     i;
-       __u64   max_sizes;
-       ext2_filsys fs = ctx->fs;
-       ext2_ino_t      ino = 0;
-       struct ext2_inode *inode = NULL;
-       ext2_inode_scan scan = NULL;
-       char            *block_buf = NULL;
-#ifdef RESOURCE_TRACK
-       struct resource_track   rtrack;
-#endif
-       unsigned char   frag, fsize;
-       struct          problem_context pctx;
-       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;
-       int             low_dtime_check = 1;
-       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;
-       struct ea_quota ea_ibody_quota;
-
-       init_resource_track(&rtrack, ctx->fs->io);
-       clear_problem_context(&pctx);
-
-       /* If we can do readahead, figure out how many groups to pull in. */
-       if (!e2fsck_can_readahead(ctx->fs))
-               ctx->readahead_kb = 0;
-       else if (ctx->readahead_kb == ~0ULL)
-               ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
-       pass1_readahead(ctx, &ra_group, &ino_threshold);
+       e2fsck_t global_ctx;
 
-       if (!(ctx->options & E2F_OPT_PREEN))
-               fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               return 1;
 
-       if (ext2fs_has_feature_dir_index(fs->super) &&
-           !(ctx->options & E2F_OPT_NO)) {
-               if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
-                       ctx->dirs_to_hash = 0;
+       if (ctx->global_ctx) {
+               global_ctx = ctx->global_ctx;
+               if (global_ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       return 1;
        }
+       return 0;
+}
 
-#ifdef MTRACE
-       mtrace_print("Pass 1");
-#endif
+static void init_ext2_max_sizes()
+{
+       int     i;
+       __u64   max_sizes;
 
+       /*
+        * Init ext2_max_sizes which will be immutable and shared between
+        * threads
+        */
 #define EXT2_BPP(bits) (1ULL << ((bits) - 2))
 
        for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
@@ -1213,61 +1439,373 @@ void e2fsck_pass1(e2fsck_t ctx)
                ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
        }
 #undef EXT2_BPP
+}
 
-       imagic_fs = ext2fs_has_feature_imagic_inodes(sb);
-       extent_fs = ext2fs_has_feature_extents(sb);
-       inlinedata_fs = ext2fs_has_feature_inline_data(sb);
+#ifdef HAVE_PTHREAD
+/* TODO: tdb needs to be handled properly for multiple threads*/
+static int multiple_threads_supported(e2fsck_t ctx)
+{
+#ifdef CONFIG_TDB
+       unsigned int            threshold;
+       ext2_ino_t              num_dirs;
+       errcode_t               retval;
+       char                    *tdb_dir;
+       int                     enable;
 
-       /*
-        * Allocate bitmaps structures
-        */
-       pctx.errcode = e2fsck_allocate_inode_bitmap(fs, _("in-use inode map"),
-                                                   EXT2FS_BMAP64_RBTREE,
-                                                   "inode_used_map",
-                                                   &ctx->inode_used_map);
-       if (pctx.errcode) {
-               pctx.num = 1;
-               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT;
-               return;
+       profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
+                          &tdb_dir);
+       profile_get_uint(ctx->profile, "scratch_files",
+                        "numdirs_threshold", 0, 0, &threshold);
+       profile_get_boolean(ctx->profile, "scratch_files",
+                           "icount", 0, 1, &enable);
+
+       retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
+       if (retval)
+               num_dirs = 1024;        /* Guess */
+
+       /* tdb is unsupported now */
+       if (enable && tdb_dir && !access(tdb_dir, W_OK) &&
+           (!threshold || num_dirs > threshold))
+               return 0;
+#endif
+       return 1;
+}
+
+/**
+ * Even though we could specify number of threads,
+ * but it might be more than the whole filesystem
+ * block groups, correct it here.
+ */
+static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
+{
+       unsigned flexbg_size = 1;
+       ext2_filsys fs = ctx->fs;
+       int num_threads = ctx->pfs_num_threads;
+       int max_threads;
+
+       if (num_threads < 1) {
+               num_threads = 1;
+               goto out;
        }
-       pctx.errcode = e2fsck_allocate_inode_bitmap(fs,
-                       _("directory inode map"),
-                       EXT2FS_BMAP64_AUTODIR,
-                       "inode_dir_map", &ctx->inode_dir_map);
-       if (pctx.errcode) {
-               pctx.num = 2;
-               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT;
-               return;
+
+       if (!multiple_threads_supported(ctx)) {
+               num_threads = 1;
+               fprintf(stderr, "Fall through single thread for pass1 "
+                       "because tdb could not handle properly\n");
+               goto out;
        }
-       pctx.errcode = e2fsck_allocate_inode_bitmap(fs,
-                       _("regular file inode map"), EXT2FS_BMAP64_RBTREE,
-                       "inode_reg_map", &ctx->inode_reg_map);
-       if (pctx.errcode) {
-               pctx.num = 6;
-               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT;
-               return;
+
+       if (ext2fs_has_feature_flex_bg(fs->super))
+               flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+       max_threads = fs->group_desc_count / flexbg_size;
+       if (max_threads == 0)
+               max_threads = 1;
+       if (max_threads > E2FSCK_MAX_THREADS)
+               max_threads = E2FSCK_MAX_THREADS;
+
+       if (num_threads > max_threads) {
+               fprintf(stderr, "Use max possible thread num: %d instead\n",
+                               max_threads);
+               num_threads = max_threads;
        }
-       pctx.errcode = e2fsck_allocate_subcluster_bitmap(fs,
+out:
+       ctx->pfs_num_threads = num_threads;
+       ctx->fs->fs_num_threads = num_threads;
+}
+#endif
+
+/*
+ * We need call mark_table_blocks() before multiple
+ * thread start, since all known system blocks should be
+ * marked and checked later.
+ */
+static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
+{
+       struct problem_context pctx;
+       ext2_filsys fs = ctx->fs;
+       unsigned long long readahead_kb;
+
+       init_ext2_max_sizes();
+#ifdef HAVE_PTHREAD
+       e2fsck_pass1_set_thread_num(ctx);
+#endif
+       /* If we can do readahead, figure out how many groups to pull in. */
+       if (!e2fsck_can_readahead(ctx->fs))
+               ctx->readahead_kb = 0;
+       else if (ctx->readahead_kb == ~0ULL)
+               ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
+
+#ifdef HAVE_PTHREAD
+       /* don't use more than 1/10 of memory for threads checking */
+       readahead_kb = get_memory_size() / (10 * ctx->pfs_num_threads);
+       /* maybe better disable RA if this is too small? */
+       if (ctx->readahead_kb > readahead_kb)
+               ctx->readahead_kb = readahead_kb;
+#endif
+       clear_problem_context(&pctx);
+       if (!(ctx->options & E2F_OPT_PREEN))
+               fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+
+       pctx.errcode = e2fsck_allocate_subcluster_bitmap(ctx->fs,
                        _("in-use block map"), EXT2FS_BMAP64_RBTREE,
                        "block_found_map", &ctx->block_found_map);
        if (pctx.errcode) {
                pctx.num = 1;
                fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
                ctx->flags |= E2F_FLAG_ABORT;
-               return;
+               return pctx.errcode;
        }
-       pctx.errcode = e2fsck_allocate_block_bitmap(fs,
+       pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
                        _("metadata block map"), EXT2FS_BMAP64_RBTREE,
                        "block_metadata_map", &ctx->block_metadata_map);
        if (pctx.errcode) {
                pctx.num = 1;
                fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
                ctx->flags |= E2F_FLAG_ABORT;
+               return pctx.errcode;
+       }
+
+       mark_table_blocks(ctx);
+       pctx.errcode = ext2fs_convert_subcluster_bitmap(ctx->fs,
+                                               &ctx->block_found_map);
+       if (pctx.errcode) {
+               fix_problem(ctx, PR_1_CONVERT_SUBCLUSTER, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return pctx.errcode;
+       }
+
+       pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
+                       _("multiply claimed block map"),
+                       EXT2FS_BMAP64_RBTREE, "block_dup_map",
+                       &ctx->block_dup_map);
+       if (pctx.errcode) {
+               pctx.num = 3;
+               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
+                           &pctx);
+               /* Should never get here */
+               ctx->flags |= E2F_FLAG_ABORT;
+               return pctx.errcode;
+       }
+
+       if (ext2fs_has_feature_mmp(fs->super) &&
+           fs->super->s_mmp_block > fs->super->s_first_data_block &&
+           fs->super->s_mmp_block < ext2fs_blocks_count(fs->super))
+               ext2fs_mark_block_bitmap2(ctx->block_found_map,
+                                         fs->super->s_mmp_block);
+#ifdef HAVE_PTHREAD
+       pthread_rwlock_init(&ctx->fs_fix_rwlock, NULL);
+       pthread_rwlock_init(&ctx->fs_block_map_rwlock, NULL);
+       if (ctx->pfs_num_threads > 1)
+               ctx->fs_need_locking = 1;
+#endif
+
+       return 0;
+}
+
+static void e2fsck_pass1_post(e2fsck_t ctx)
+{
+       struct problem_context pctx;
+       ext2_filsys fs = ctx->fs;
+       char *block_buf;
+
+       if (e2fsck_should_abort(ctx))
+               return;
+
+       block_buf = (char *)e2fsck_allocate_memory(ctx, ctx->fs->blocksize * 3,
+                                             "block interate buffer");
+       reserve_block_for_root_repair(ctx);
+       reserve_block_for_lnf_repair(ctx);
+
+       /*
+        * If any extended attribute blocks' reference counts need to
+        * be adjusted, either up (ctx->refcount_extra), or down
+        * (ctx->refcount), then fix them.
+        */
+       if (ctx->refcount) {
+               adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
+               ea_refcount_free(ctx->refcount);
+               ctx->refcount = 0;
+       }
+       if (ctx->refcount_extra) {
+               adjust_extattr_refcount(ctx, ctx->refcount_extra,
+                                       block_buf, +1);
+               ea_refcount_free(ctx->refcount_extra);
+               ctx->refcount_extra = 0;
+       }
+
+       if (ctx->invalid_bitmaps)
+               handle_fs_bad_blocks(ctx);
+
+       /* We don't need the block_ea_map any more */
+       if (ctx->block_ea_map) {
+               ext2fs_free_block_bitmap(ctx->block_ea_map);
+               ctx->block_ea_map = 0;
+       }
+
+       if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
+               struct ext2_inode *inode;
+               int inode_size = EXT2_INODE_SIZE(fs->super);
+               inode = e2fsck_allocate_memory(ctx, inode_size,
+                                              "scratch inode");
+
+               clear_problem_context(&pctx);
+               pctx.errcode = ext2fs_create_resize_inode(fs);
+               if (pctx.errcode) {
+                       if (!fix_problem(ctx, PR_1_RESIZE_INODE_CREATE,
+                                        &pctx)) {
+                               ctx->flags |= E2F_FLAG_ABORT;
+                               ext2fs_free_mem(&inode);
+                               ext2fs_free_mem(&block_buf);
+                               return;
+                       }
+                       pctx.errcode = 0;
+               }
+               if (!pctx.errcode) {
+                       e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
+                                         "recreate inode");
+                       inode->i_mtime = ctx->now;
+                       e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
+                                          "recreate inode");
+               }
+               ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
+               ext2fs_free_mem(&inode);
+       }
+
+       if (ctx->flags & E2F_FLAG_RESTART) {
+               ext2fs_free_mem(&block_buf);
+               return;
+       }
+
+       if (ctx->block_dup_map) {
+               if (!(ctx->flags & E2F_FLAG_DUP_BLOCK)) {
+                       ext2fs_free_mem(&block_buf);
+                       return;
+               }
+               if (ctx->options & E2F_OPT_PREEN) {
+                       clear_problem_context(&pctx);
+                       fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
+               }
+               e2fsck_pass1_dupblocks(ctx, block_buf);
+               ext2fs_free_mem(&block_buf);
+               ctx->flags &= ~E2F_FLAG_DUP_BLOCK;
+       }
+
+       ctx->flags |= E2F_FLAG_ALLOC_OK;
+}
+
+
+/*
+ * Lustre FS creates special inodes - precreated objects.
+ * They are zero-sized and have special attributes:
+ * mode |= S_ISUID | S_ISGID;
+ * valid |= LA_ATIME | LA_MTIME | LA_CTIME;
+ * atime = 0;
+ * mtime = 0;
+ * ctime = 0;
+ */
+static int precreated_object(struct ext2_inode *inode)
+{
+       if (((inode->i_mode & (S_ISUID | S_ISGID)) == (S_ISUID | S_ISGID)) &&
+            inode->i_ctime == 0)
+               return 1;
+       return 0;
+}
+
+void e2fsck_pass1_run(e2fsck_t ctx)
+{
+       ext2_filsys fs = ctx->fs;
+       ext2_ino_t      ino = 0;
+       struct ext2_inode *inode = NULL;
+       ext2_inode_scan scan = NULL;
+       char            *block_buf = NULL;
+#ifdef RESOURCE_TRACK
+       struct resource_track   rtrack;
+#endif
+       unsigned char   frag, fsize;
+       struct          problem_context pctx;
+       struct          scan_callback_struct scan_struct;
+       struct ext2_super_block *sb = ctx->fs->super;
+       const char      *old_op;
+       const char      *eop_next_inode = _("getting next inode from scan");
+       int             imagic_fs, extent_fs, inlinedata_fs, casefold_fs;
+       int             low_dtime_check = 1;
+       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;
+       struct ea_quota ea_ibody_quota;
+       struct process_inode_block *inodes_to_process;
+       int             process_inode_count, check_mmp = 0;
+       e2fsck_t        global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
+       int             inode_exp = 0;
+
+       init_resource_track(&rtrack, ctx->fs->io);
+       clear_problem_context(&pctx);
+
+       pass1_readahead(ctx, &ra_group, &ino_threshold);
+       if (ext2fs_has_feature_dir_index(fs->super) &&
+           !(ctx->options & E2F_OPT_NO)) {
+               if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
+                       ctx->dirs_to_hash = 0;
+       }
+
+#ifdef MTRACE
+       mtrace_print("Pass 1");
+#endif
+
+       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
+        */
+       pctx.errcode = e2fsck_allocate_inode_bitmap(fs, _("in-use inode map"),
+                                                   EXT2FS_BMAP64_RBTREE,
+                                                   "inode_used_map",
+                                                   &ctx->inode_used_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_allocate_inode_bitmap(fs,
+                       _("directory inode map"),
+                       ctx->global_ctx ? EXT2FS_BMAP64_RBTREE :
+                       EXT2FS_BMAP64_AUTODIR,
+                       "inode_dir_map", &ctx->inode_dir_map);
+       if (pctx.errcode) {
+               pctx.num = 2;
+               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
+       pctx.errcode = e2fsck_allocate_inode_bitmap(fs,
+                       _("regular file inode map"), EXT2FS_BMAP64_RBTREE,
+                       "inode_reg_map", &ctx->inode_reg_map);
+       if (pctx.errcode) {
+               pctx.num = 6;
+               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+               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) {
@@ -1309,16 +1847,8 @@ void e2fsck_pass1(e2fsck_t ctx)
                }
        }
 
-       mark_table_blocks(ctx);
-       pctx.errcode = ext2fs_convert_subcluster_bitmap(fs,
-                                               &ctx->block_found_map);
-       if (pctx.errcode) {
-               fix_problem(ctx, PR_1_CONVERT_SUBCLUSTER, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT;
-               goto endit;
-       }
        block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
-                                                   "block interate buffer");
+                                                   "block iterate buffer");
        if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
                e2fsck_use_inode_shortcuts(ctx, 1);
        e2fsck_intercept_block_allocations(ctx);
@@ -1336,38 +1866,77 @@ void e2fsck_pass1(e2fsck_t ctx)
        ctx->stashed_inode = inode;
        scan_struct.ctx = ctx;
        scan_struct.block_buf = block_buf;
+       scan_struct.inodes_to_process = inodes_to_process;
+       scan_struct.process_inode_count = &process_inode_count;
        ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
        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;
 
-       if (ext2fs_has_feature_mmp(fs->super) &&
-           fs->super->s_mmp_block > fs->super->s_first_data_block &&
-           fs->super->s_mmp_block < ext2fs_blocks_count(fs->super))
-               ext2fs_mark_block_bitmap2(ctx->block_found_map,
-                                         fs->super->s_mmp_block);
-
        /* Set up ctx->lost_and_found if possible */
        (void) e2fsck_get_lost_and_found(ctx, 0);
 
+#ifdef HAVE_PTHREAD
+       if (ctx->global_ctx) {
+               if (ctx->options & E2F_OPT_DEBUG &&
+                   ctx->options & E2F_OPT_MULTITHREAD)
+                       log_out(ctx, "jumping to group %u\n",
+                               ctx->thread_info.et_group_start);
+               pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan,
+                                       ctx->thread_info.et_group_start);
+               if (pctx.errcode) {
+                       fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       goto endit;
+               }
+       }
+#endif
+
        while (1) {
-               if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
+               check_mmp = 0;
+               e2fsck_pass1_check_lock(ctx);
+#ifdef HAVE_PTHREAD
+               if (!global_ctx->mmp_update_thread) {
+                       e2fsck_pass1_block_map_w_lock(ctx);
+                       if (!global_ctx->mmp_update_thread) {
+                               global_ctx->mmp_update_thread =
+                                       ctx->thread_info.et_thread_index + 1;
+                               check_mmp = 1;
+                       }
+                       e2fsck_pass1_block_map_w_unlock(ctx);
+               }
+
+               /* only one active thread could update mmp block. */
+               e2fsck_pass1_block_map_r_lock(ctx);
+               if (global_ctx->mmp_update_thread ==
+                   ctx->thread_info.et_thread_index + 1)
+                       check_mmp = 1;
+               e2fsck_pass1_block_map_r_unlock(ctx);
+#else
+               check_mmp = 1;
+#endif
+
+               if (check_mmp && (ino % (fs->super->s_inodes_per_group * 4) == 1)) {
                        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)) {
+                       e2fsck_pass1_check_unlock(ctx);
                        goto endit;
+               }
                if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
                        /*
                         * If badblocks says badblocks is bad, offer to clear
@@ -1388,33 +1957,49 @@ void e2fsck_pass1(e2fsck_t ctx)
                                        fix_problem(ctx, PR_1_ISCAN_ERROR,
                                                    &pctx);
                                        ctx->flags |= E2F_FLAG_ABORT;
+                                       e2fsck_pass1_check_unlock(ctx);
                                        goto endit;
-                               }
+                               } else
+                                       ctx->flags |= E2F_FLAG_RESTART;
                                err = ext2fs_inode_scan_goto_blockgroup(scan,
                                                                        0);
                                if (err) {
                                        fix_problem(ctx, PR_1_ISCAN_ERROR,
                                                    &pctx);
                                        ctx->flags |= E2F_FLAG_ABORT;
+                                       e2fsck_pass1_check_unlock(ctx);
                                        goto endit;
                                }
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                        if (!ctx->inode_bb_map)
                                alloc_bb_map(ctx);
                        ext2fs_mark_inode_bitmap2(ctx->inode_bb_map, ino);
                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                }
+               if (pctx.errcode == EXT2_ET_SCAN_FINISHED) {
+                       e2fsck_pass1_check_unlock(ctx);
+                       break;
+               }
                if (pctx.errcode &&
                    pctx.errcode != EXT2_ET_INODE_CSUM_INVALID &&
                    pctx.errcode != EXT2_ET_INODE_IS_GARBAGE) {
                        fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
                        ctx->flags |= E2F_FLAG_ABORT;
+                       e2fsck_pass1_check_unlock(ctx);
                        goto endit;
                }
-               if (!ino)
+               if (!ino) {
+                       e2fsck_pass1_check_unlock(ctx);
                        break;
+               }
+#ifdef HAVE_PTHREAD
+               if (ctx->global_ctx)
+                       ctx->thread_info.et_inode_number++;
+#endif
                pctx.ino = ino;
                pctx.inode = inode;
                ctx->stashed_ino = ino;
@@ -1463,6 +2048,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                pctx.num = inode->i_links_count;
                                fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                } else if ((ino >= EXT2_FIRST_INODE(fs->super)) &&
@@ -1477,9 +2063,19 @@ void e2fsck_pass1(e2fsck_t ctx)
                                }
                        }
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(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)) {
@@ -1488,6 +2084,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                                               &pctx);
                        if (res < 0) {
                                /* skip FINISH_INODE_LOOP */
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -1497,15 +2094,19 @@ 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)) {
+                               e2fsck_pass1_fix_lock(ctx);
                                ext2fs_set_feature_inline_data(sb);
                                ext2fs_mark_super_dirty(fs);
+                               e2fsck_pass1_fix_unlock(ctx);
                                inlinedata_fs = 1;
                        } else if (fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) {
                                e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
                                /* skip FINISH_INODE_LOOP */
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -1520,7 +2121,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);
 
@@ -1550,6 +2151,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                        if (err) {
                                                pctx.errcode = err;
                                                ctx->flags |= E2F_FLAG_ABORT;
+                                               e2fsck_pass1_check_unlock(ctx);
                                                goto endit;
                                        }
                                        inode->i_flags &= ~EXT4_INLINE_DATA_FL;
@@ -1564,6 +2166,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                /* Some other kind of non-xattr error? */
                                pctx.errcode = err;
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                }
@@ -1589,9 +2192,11 @@ void e2fsck_pass1(e2fsck_t ctx)
                        if ((ext2fs_extent_header_verify(inode->i_block,
                                                 sizeof(inode->i_block)) == 0) &&
                            fix_problem(ctx, PR_1_EXTENT_FEATURE, &pctx)) {
+                               e2fsck_pass1_fix_lock(ctx);
                                ext2fs_set_feature_extents(sb);
                                ext2fs_mark_super_dirty(fs);
                                extent_fs = 1;
+                               e2fsck_pass1_fix_unlock(ctx);
                        } else if (fix_problem(ctx, PR_1_EXTENTS_SET, &pctx)) {
                        clear_inode:
                                e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
@@ -1599,6 +2204,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map,
                                                                 ino);
                                /* skip FINISH_INODE_LOOP */
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -1619,6 +2225,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                        void *ehp;
 #ifdef WORDS_BIGENDIAN
                        __u32 tmp_block[EXT2_N_BLOCKS];
+                       int i;
 
                        for (i = 0; i < EXT2_N_BLOCKS; i++)
                                tmp_block[i] = ext2fs_swab32(inode->i_block[i]);
@@ -1636,6 +2243,12 @@ void e2fsck_pass1(e2fsck_t ctx)
 #endif
                                e2fsck_write_inode(ctx, ino, inode, "pass1");
                                failed_csum = 0;
+                       } else {
+                               /* Consider an inode in extent fs w/o extents
+                                * at least a bit suspect. It only matters if
+                                * the inode has several other problems. */
+                               e2fsck_mark_inode_bad(ctx, &pctx,
+                                                     PR_1_UNSET_EXTENT_FL);
                        }
                }
 
@@ -1653,12 +2266,16 @@ void e2fsck_pass1(e2fsck_t ctx)
                                failed_csum = 0;
                        }
 
-                       pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
-                                                         &pb.fs_meta_blocks);
+                       e2fsck_pass1_block_map_r_lock(ctx);
+                       pctx.errcode = ext2fs_copy_bitmap(ctx->global_ctx ?
+                                       ctx->global_ctx->block_found_map :
+                                       ctx->block_found_map, &pb.fs_meta_blocks);
+                       e2fsck_pass1_block_map_r_unlock(ctx);
                        if (pctx.errcode) {
                                pctx.num = 4;
                                fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                        pb.ino = EXT2_BAD_INO;
@@ -1676,16 +2293,19 @@ void e2fsck_pass1(e2fsck_t ctx)
                        if (pctx.errcode) {
                                fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                        if (pb.bbcheck)
                                if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
                        clear_problem_context(&pctx);
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                } else if (ino == EXT2_ROOT_INO) {
                        /*
@@ -1727,6 +2347,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                }
                                check_blocks(ctx, &pctx, block_buf, NULL);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                        if ((inode->i_links_count ||
@@ -1754,6 +2375,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                }
                                check_blocks(ctx, &pctx, block_buf, NULL);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                        if ((inode->i_links_count ||
@@ -1767,6 +2389,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;
 
@@ -1792,11 +2440,13 @@ void e2fsck_pass1(e2fsck_t ctx)
                        }
                        check_blocks(ctx, &pctx, block_buf, NULL);
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                }
 
                if (!inode->i_links_count) {
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                }
                /*
@@ -1827,18 +2477,21 @@ void e2fsck_pass1(e2fsck_t ctx)
                        frag = fsize = 0;
                }
 
+               /* Fixed in pass2, e2fsck_process_bad_inode(). */
                if (inode->i_faddr || frag || fsize ||
                    (!ext2fs_has_feature_largedir(fs->super) &&
-                   (LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high)))
-                       mark_inode_bad(ctx, ino);
+                    LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high))
+                       e2fsck_mark_inode_bad(ctx, &pctx,
+                                             PR_2_DIR_SIZE_HIGH_ZERO);
                if ((fs->super->s_creator_os != EXT2_OS_HURD) &&
                    !ext2fs_has_feature_64bit(fs->super) &&
                    inode->osd2.linux2.l_i_file_acl_high != 0)
-                       mark_inode_bad(ctx, ino);
+                       e2fsck_mark_inode_bad(ctx, &pctx,
+                                             PR_2_I_FILE_ACL_HI_ZERO);
                if ((fs->super->s_creator_os != EXT2_OS_HURD) &&
                    !ext2fs_has_feature_huge_file(fs->super) &&
                    (inode->osd2.linux2.l_i_blocks_hi != 0))
-                       mark_inode_bad(ctx, ino);
+                       e2fsck_mark_inode_bad(ctx, &pctx, PR_2_BLOCKS_HI_ZERO);
                if (inode->i_flags & EXT2_IMAGIC_FL) {
                        if (imagic_fs) {
                                if (!ctx->inode_imagic_map)
@@ -1871,12 +2524,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++;
@@ -1893,18 +2553,19 @@ void e2fsck_pass1(e2fsck_t ctx)
                        check_size(ctx, &pctx);
                        ctx->fs_blockdev_count++;
                } else if (LINUX_S_ISLNK (inode->i_mode) &&
-                          e2fsck_pass1_check_symlink(fs, ino, inode,
-                                                     block_buf)) {
+                          check_symlink(ctx, &pctx, ino, inode, block_buf)) {
                        check_immutable(ctx, &pctx);
                        ctx->fs_symlinks_count++;
                        if (inode->i_flags & EXT4_INLINE_DATA_FL) {
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        } else if (ext2fs_is_fast_symlink(inode)) {
                                ctx->fs_fast_symlinks_count++;
                                check_blocks(ctx, &pctx, block_buf,
                                             &ea_ibody_quota);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -1920,8 +2581,37 @@ void e2fsck_pass1(e2fsck_t ctx)
                        check_immutable(ctx, &pctx);
                        check_size(ctx, &pctx);
                        ctx->fs_sockets_count++;
-               } else
-                       mark_inode_bad(ctx, ino);
+               } else {
+                       e2fsck_mark_inode_bad(ctx, &pctx, PR_2_BAD_MODE);
+               }
+
+               /* Future atime/mtime may be valid in rare cases, but are more
+                * likely to indicate corruption.  Don't try to fix timestamps,
+                * but take into consideration whether inode is corrupted.  If
+                * no other problems with the inode, probably it is OK. */
+               if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_atime, ctx->time_fudge))
+                       e2fsck_mark_inode_bad(ctx, &pctx, PR_1_INODE_BAD_TIME);
+               if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_mtime, ctx->time_fudge))
+                       e2fsck_mark_inode_bad(ctx, &pctx, PR_1_INODE_BAD_TIME);
+
+               /* Since ctime cannot be set directly from userspace, consider
+                * very old/future values worse than a bad atime/mtime. Same for
+                * crtime, but it is checked in check_inode_extra_space(). */
+               if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_ctime, ctx->time_fudge))
+                       e2fsck_mark_inode_badder(ctx, &pctx,
+                                                PR_1_INODE_BAD_TIME);
+               else if (!precreated_object(inode) &&
+                        EXT4_XTIME_ANCIENT(ctx, sb, inode->i_ctime,
+                                           ctx->time_fudge))
+                       e2fsck_mark_inode_badder(ctx, &pctx,
+                                                PR_1_INODE_BAD_TIME);
+
+               /* no restart if clearing bad inode before block processing */
+               if (e2fsck_fix_bad_inode(ctx, &pctx)) {
+                       e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
+                       goto next_unlock;
+               }
+
                if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
                    !(inode->i_flags & EXT4_INLINE_DATA_FL)) {
                        if (inode->i_block[EXT2_IND_BLOCK])
@@ -1939,135 +2629,1148 @@ void e2fsck_pass1(e2fsck_t ctx)
                     ext2fs_file_acl_block(fs, inode))) {
                        struct process_inode_block *itp;
 
-                       itp = &inodes_to_process[process_inode_count];
-                       itp->ino = ino;
-                       itp->ea_ibody_quota = ea_ibody_quota;
-                       if (inode_size < sizeof(struct ext2_inode_large))
-                               memcpy(&itp->inode, inode, inode_size);
-                       else
-                               memcpy(&itp->inode, inode, sizeof(itp->inode));
-                       process_inode_count++;
-               } else
-                       check_blocks(ctx, &pctx, block_buf, &ea_ibody_quota);
+                       itp = &inodes_to_process[process_inode_count];
+                       itp->ino = ino;
+                       itp->ea_ibody_quota = ea_ibody_quota;
+                       if (inode_size < sizeof(struct ext2_inode_large))
+                               memcpy(&itp->inode, inode, inode_size);
+                       else
+                               memcpy(&itp->inode, inode, sizeof(itp->inode));
+                       process_inode_count++;
+               } else
+                       check_blocks(ctx, &pctx, block_buf, &ea_ibody_quota);
+
+               FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+
+               if (ctx->flags & E2F_FLAG_EXPAND_EISIZE) {
+                       struct ext2_inode_large *inode_l;
+
+                       inode_l = (struct ext2_inode_large *)inode;
+
+                       if (inode_l->i_extra_isize < ctx->want_extra_isize) {
+                               fix_problem(ctx, PR_1_EXPAND_EISIZE, &pctx);
+                               inode_exp = e2fsck_pass1_expand_eisize(ctx,
+                                                                      inode_l,
+                                                                      &pctx);
+                       }
+                       if ((inode_l->i_extra_isize < ctx->min_extra_isize) &&
+                           inode_exp == 0)
+                               ctx->min_extra_isize = inode_l->i_extra_isize;
+               }
+
+               if (e2fsck_should_abort(ctx)) {
+                       e2fsck_pass1_check_unlock(ctx);
+                       goto endit;
+               }
+
+               if (process_inode_count >= ctx->process_inode_size) {
+                       process_inodes(ctx, block_buf, inodes_to_process,
+                                      &process_inode_count);
+
+                       if (e2fsck_should_abort(ctx)) {
+                               e2fsck_pass1_check_unlock(ctx);
+                               goto endit;
+                       }
+               }
+       next_unlock:
+               e2fsck_pass1_check_unlock(ctx);
+       }
+       process_inodes(ctx, block_buf, inodes_to_process,
+                      &process_inode_count);
+       ext2fs_close_inode_scan(scan);
+       scan = NULL;
+
+       if (ctx->ea_block_quota_blocks) {
+               ea_refcount_free(ctx->ea_block_quota_blocks);
+               ctx->ea_block_quota_blocks = 0;
+       }
+
+       if (ctx->ea_block_quota_inodes) {
+               ea_refcount_free(ctx->ea_block_quota_inodes);
+               ctx->ea_block_quota_inodes = 0;
+       }
+
+       if (ctx->flags & E2F_FLAG_RESTART) {
+               /*
+                * Only the master copy of the superblock and block
+                * group descriptors are going to be written during a
+                * restart, so set the superblock to be used to be the
+                * master superblock.
+                */
+               ctx->use_superblock = 0;
+               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);
+               }
+       }
+
+       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);
+       if (block_buf)
+               ext2fs_free_mem(&block_buf);
+       if (inode)
+               ext2fs_free_mem(&inode);
+
+       /*
+        * The l+f inode may have been cleared, so zap it now and
+        * later passes will recalculate it if necessary
+        */
+       ctx->lost_and_found = 0;
+
+       if ((ctx->flags & E2F_FLAG_SIGNAL_MASK) == 0)
+               print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
+       else
+               ctx->invalid_bitmaps++;
+#ifdef HAVE_PTHREAD
+       /* reset update_thread after this thread exit */
+       e2fsck_pass1_block_map_w_lock(ctx);
+       if (check_mmp)
+               global_ctx->mmp_update_thread = 0;
+       e2fsck_pass1_block_map_w_unlock(ctx);
+#endif
+}
+
+#ifdef HAVE_PTHREAD
+static errcode_t e2fsck_pass1_copy_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
+                                         ext2fs_generic_bitmap *dest)
+{
+       errcode_t ret;
+
+       ret = ext2fs_copy_bitmap(*src, dest);
+       if (ret)
+               return ret;
+
+       (*dest)->fs = fs;
+
+       return 0;
+}
+
+static void e2fsck_pass1_free_bitmap(ext2fs_generic_bitmap *bitmap)
+{
+       if (*bitmap) {
+               ext2fs_free_generic_bmap(*bitmap);
+               *bitmap = NULL;
+       }
+
+}
+
+static errcode_t e2fsck_pass1_merge_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
+                                         ext2fs_generic_bitmap *dest)
+{
+       errcode_t ret = 0;
+
+       if (*src) {
+               if (*dest == NULL) {
+                       *dest = *src;
+                       *src = NULL;
+               } else {
+                       ret = ext2fs_merge_bitmap(*src, *dest, NULL, NULL);
+                       if (ret)
+                               return ret;
+               }
+               (*dest)->fs = fs;
+       }
+
+       return 0;
+}
+
+static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, e2fsck_t src_context,
+                                     ext2_filsys src)
+{
+       errcode_t       retval;
+
+       memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+       dest->inode_map = NULL;
+       dest->block_map = NULL;
+       dest->badblocks = NULL;
+       if (dest->dblist)
+               dest->dblist->fs = dest;
+       if (src->block_map) {
+               retval = e2fsck_pass1_copy_bitmap(dest, &src->block_map,
+                                                 &dest->block_map);
+               if (retval)
+                       return retval;
+       }
+       if (src->inode_map) {
+               retval = e2fsck_pass1_copy_bitmap(dest, &src->inode_map,
+                                                 &dest->inode_map);
+               if (retval)
+                       return retval;
+       }
+
+       if (src->badblocks) {
+               retval = ext2fs_badblocks_copy(src->badblocks,
+                                              &dest->badblocks);
+               if (retval)
+                       return retval;
+       }
+
+       /* disable it for now */
+       src_context->openfs_flags &= ~EXT2_FLAG_EXCLUSIVE;
+       retval = ext2fs_open_channel(dest, src_context->io_options,
+                                    src_context->io_manager,
+                                    src_context->openfs_flags,
+                                    src->io->block_size);
+       if (retval)
+               return retval;
+
+       /* Block size might not be default */
+       io_channel_set_blksize(dest->io, src->io->block_size);
+       ehandler_init(dest->io);
+
+       assert(dest->io->magic == src->io->magic);
+       assert(dest->io->manager == src->io->manager);
+       assert(strcmp(dest->io->name, src->io->name) == 0);
+       assert(dest->io->block_size == src->io->block_size);
+       assert(dest->io->read_error == src->io->read_error);
+       assert(dest->io->write_error == src->io->write_error);
+       assert(dest->io->refcount == src->io->refcount);
+       assert(dest->io->flags == src->io->flags);
+       assert(dest->io->app_data == dest);
+       assert(src->io->app_data == src);
+       assert(dest->io->align == src->io->align);
+
+       /* The data should be written to disk immediately */
+       dest->io->flags |= CHANNEL_FLAGS_WRITETHROUGH;
+       /* icache will be rebuilt if needed, so do not copy from @src */
+       src->icache = NULL;
+       return 0;
+}
+
+static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
+{
+       struct ext2_inode_cache *icache = dest->icache;
+       errcode_t retval = 0;
+       io_channel dest_io;
+       io_channel dest_image_io;
+       ext2fs_inode_bitmap inode_map;
+       ext2fs_block_bitmap block_map;
+       ext2_badblocks_list badblocks;
+       ext2_dblist dblist;
+       int flags;
+       e2fsck_t dest_ctx = dest->priv_data;
+
+       dest_io = dest->io;
+       dest_image_io = dest->image_io;
+       inode_map = dest->inode_map;
+       block_map = dest->block_map;
+       badblocks = dest->badblocks;
+       dblist = dest->dblist;
+       flags = dest->flags;
+
+       memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+       dest->io = dest_io;
+       dest->image_io = dest_image_io;
+       dest->icache = icache;
+       dest->inode_map = inode_map;
+       dest->block_map = block_map;
+       dest->badblocks = badblocks;
+       dest->dblist = dblist;
+       dest->priv_data = dest_ctx;
+       if (dest->dblist)
+               dest->dblist->fs = dest;
+       dest->flags = src->flags | flags;
+       if (!(src->flags & EXT2_FLAG_VALID) || !(flags & EXT2_FLAG_VALID))
+               ext2fs_unmark_valid(dest);
+
+       if (src->icache) {
+               ext2fs_free_inode_cache(src->icache);
+               src->icache = NULL;
+       }
+
+       retval = e2fsck_pass1_merge_bitmap(dest, &src->inode_map,
+                                          &dest->inode_map);
+       if (retval)
+               goto out;
+
+       retval = e2fsck_pass1_merge_bitmap(dest, &src->block_map,
+                                         &dest->block_map);
+       if (retval)
+               goto out;
+
+       if (src->dblist) {
+               if (dest->dblist) {
+                       retval = ext2fs_merge_dblist(src->dblist,
+                                                    dest->dblist);
+                       if (retval)
+                               goto out;
+               } else {
+                       dest->dblist = src->dblist;
+                       dest->dblist->fs = dest;
+                       src->dblist = NULL;
+               }
+       }
+
+       if (src->badblocks) {
+               if (dest->badblocks == NULL)
+                       retval = ext2fs_badblocks_copy(src->badblocks,
+                                                      &dest->badblocks);
+               else
+                       retval = ext2fs_badblocks_merge(src->badblocks,
+                                                       dest->badblocks);
+       }
+out:
+       io_channel_close(src->io);
+       if (src->inode_map)
+               ext2fs_free_generic_bmap(src->inode_map);
+       if (src->block_map)
+               ext2fs_free_generic_bmap(src->block_map);
+       if (src->badblocks)
+               ext2fs_badblocks_list_free(src->badblocks);
+       if (src->dblist)
+               ext2fs_free_dblist(src->dblist);
+
+       return retval;
+}
+
+static void e2fsck_pass1_copy_invalid_bitmaps(e2fsck_t global_ctx,
+                                             e2fsck_t thread_ctx)
+{
+       dgrp_t i, j;
+       dgrp_t grp_start = thread_ctx->thread_info.et_group_start;
+       dgrp_t grp_end = thread_ctx->thread_info.et_group_end;
+       dgrp_t total = grp_end - grp_start;
+
+       thread_ctx->invalid_inode_bitmap_flag =
+                       e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+                                               "invalid_inode_bitmap");
+       thread_ctx->invalid_block_bitmap_flag =
+                       e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+                                              "invalid_block_bitmap");
+       thread_ctx->invalid_inode_table_flag =
+                       e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+                                              "invalid_inode_table");
+
+       memcpy(thread_ctx->invalid_block_bitmap_flag,
+              &global_ctx->invalid_block_bitmap_flag[grp_start],
+              total * sizeof(int));
+       memcpy(thread_ctx->invalid_inode_bitmap_flag,
+              &global_ctx->invalid_inode_bitmap_flag[grp_start],
+              total * sizeof(int));
+       memcpy(thread_ctx->invalid_inode_table_flag,
+              &global_ctx->invalid_inode_table_flag[grp_start],
+              total * sizeof(int));
+
+       thread_ctx->invalid_bitmaps = 0;
+       for (i = grp_start, j = 0; i < grp_end; i++, j++) {
+               if (thread_ctx->invalid_block_bitmap_flag[j])
+                       thread_ctx->invalid_bitmaps++;
+               if (thread_ctx->invalid_inode_bitmap_flag[j])
+                       thread_ctx->invalid_bitmaps++;
+               if (thread_ctx->invalid_inode_table_flag[j])
+                       thread_ctx->invalid_bitmaps++;
+       }
+}
+
+static void e2fsck_pass1_merge_invalid_bitmaps(e2fsck_t global_ctx,
+                                              e2fsck_t thread_ctx)
+{
+       dgrp_t grp_start = thread_ctx->thread_info.et_group_start;
+       dgrp_t grp_end = thread_ctx->thread_info.et_group_end;
+       dgrp_t total = grp_end - grp_start;
+
+       memcpy(&global_ctx->invalid_block_bitmap_flag[grp_start],
+              thread_ctx->invalid_block_bitmap_flag, total * sizeof(int));
+       memcpy(&global_ctx->invalid_inode_bitmap_flag[grp_start],
+              thread_ctx->invalid_inode_bitmap_flag, total * sizeof(int));
+       memcpy(&global_ctx->invalid_inode_table_flag[grp_start],
+              thread_ctx->invalid_inode_table_flag, total * sizeof(int));
+       global_ctx->invalid_bitmaps += thread_ctx->invalid_bitmaps;
+}
+
+static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
+                                            int thread_index, int num_threads,
+                                            dgrp_t average_group)
+{
+       errcode_t               retval;
+       e2fsck_t                thread_context;
+       ext2_filsys             thread_fs;
+       ext2_filsys             global_fs = global_ctx->fs;
+       struct e2fsck_thread    *tinfo;
+
+       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_metadata_map != NULL);
+       assert(global_ctx->block_dup_map != NULL);
+       assert(global_ctx->block_ea_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->block_dup_map = NULL;
+       thread_context->casefolded_dirs = NULL;
+       thread_context->expand_eisize_map = NULL;
+       thread_context->inode_badness = NULL;
+
+       retval = e2fsck_allocate_block_bitmap(global_ctx->fs,
+                               _("in-use block map"), EXT2FS_BMAP64_RBTREE,
+                               "block_found_map", &thread_context->block_found_map);
+       if (retval)
+               goto out_context;
+
+       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;
+       }
+
+       io_channel_flush_cleanup(global_fs->io);
+       retval = e2fsck_pass1_copy_fs(thread_fs, global_ctx, 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->thread_info.et_thread_index = thread_index;
+       set_up_logging(thread_context);
+
+       tinfo = &thread_context->thread_info;
+       tinfo->et_group_start = average_group * thread_index;
+       if (thread_index == global_fs->fs_num_threads - 1)
+               tinfo->et_group_end = thread_fs->group_desc_count;
+       else
+               tinfo->et_group_end = average_group * (thread_index + 1);
+       tinfo->et_group_next = tinfo->et_group_start;
+       tinfo->et_inode_number = 0;
+       tinfo->et_log_buf[0] = '\0';
+       tinfo->et_log_length = 0;
+       if (thread_context->options & E2F_OPT_MULTITHREAD)
+               log_out(thread_context, _("Scan group range [%d, %d)\n"),
+                       tinfo->et_group_start, tinfo->et_group_end);
+       thread_context->fs = thread_fs;
+       retval = quota_init_context(&thread_context->qctx, thread_fs, 0);
+       if (retval) {
+               com_err(global_ctx->program_name, retval,
+                       "while init quota context");
+               goto out_fs;
+       }
+       *thread_ctx = thread_context;
+       e2fsck_pass1_copy_invalid_bitmaps(global_ctx, thread_context);
+       return 0;
+out_fs:
+       ext2fs_free_mem(&thread_fs);
+out_context:
+       if (thread_context->block_found_map)
+               ext2fs_free_mem(&thread_context->block_found_map);
+       ext2fs_free_mem(&thread_context);
+       return retval;
+}
+
+static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       if (thread_ctx->dir_info == NULL)
+               return;
+
+       if (global_ctx->dir_info == NULL) {
+               global_ctx->dir_info = thread_ctx->dir_info;
+               thread_ctx->dir_info = NULL;
+               return;
+       }
+
+       e2fsck_merge_dir_info(global_ctx, thread_ctx->dir_info,
+                             global_ctx->dir_info);
+}
+
+static void e2fsck_pass1_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       if (thread_ctx->dx_dir_info == NULL)
+               return;
+
+       if (global_ctx->dx_dir_info == NULL) {
+               global_ctx->dx_dir_info = thread_ctx->dx_dir_info;
+               global_ctx->dx_dir_info_size = thread_ctx->dx_dir_info_size;
+               global_ctx->dx_dir_info_count = thread_ctx->dx_dir_info_count;
+               thread_ctx->dx_dir_info = NULL;
+               return;
+       }
+
+       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)
+{
+       if (*src_icount) {
+               if (*dest_icount == NULL) {
+                       *dest_icount = *src_icount;
+                       *src_icount = NULL;
+               } else {
+                       errcode_t ret;
+
+                       ret = ext2fs_icount_merge(*src_icount,
+                                                 *dest_icount);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       errcode_t ret;
+
+       ret = e2fsck_pass1_merge_icount(&global_ctx->inode_count,
+                                       &thread_ctx->inode_count);
+       if (ret)
+               return ret;
+       ret = e2fsck_pass1_merge_icount(&global_ctx->inode_link_info,
+                                       &thread_ctx->inode_link_info);
+       if (ret)
+               return ret;
+
+       ret = e2fsck_pass1_merge_icount(&global_ctx->inode_badness,
+                                       &thread_ctx->inode_badness);
+
+       return ret;
+}
+
+static errcode_t e2fsck_pass1_merge_dirs_to_hash(e2fsck_t global_ctx,
+                                                e2fsck_t thread_ctx)
+{
+       errcode_t retval = 0;
+
+       if (!thread_ctx->dirs_to_hash)
+               return 0;
+
+       if (!global_ctx->dirs_to_hash)
+               retval = ext2fs_badblocks_copy(thread_ctx->dirs_to_hash,
+                                              &global_ctx->dirs_to_hash);
+       else
+               retval = ext2fs_badblocks_merge(thread_ctx->dirs_to_hash,
+                                               global_ctx->dirs_to_hash);
+
+       return retval;
+}
+
+static errcode_t e2fsck_pass1_merge_ea_inode_refs(e2fsck_t global_ctx,
+                                                 e2fsck_t thread_ctx)
+{
+       ea_value_t thread_count, global_count;
+       ea_key_t ino;
+       errcode_t retval;
+
+       if (!thread_ctx->ea_inode_refs)
+               return 0;
+
+       if (!global_ctx->ea_inode_refs) {
+               global_ctx->ea_inode_refs = thread_ctx->ea_inode_refs;
+               thread_ctx->ea_inode_refs = NULL;
+               return 0;
+       }
+
+       ea_refcount_intr_begin(thread_ctx->ea_inode_refs);
+       while (1) {
+               if ((ino = ea_refcount_intr_next(thread_ctx->ea_inode_refs,
+                                                &thread_count)) == 0)
+                       break;
+               ea_refcount_fetch(global_ctx->ea_inode_refs,
+                                 ino, &global_count);
+               retval = ea_refcount_store(global_ctx->ea_inode_refs,
+                                          ino, thread_count + global_count);
+               if (retval)
+                       return retval;
+       }
+
+       return retval;
+}
+
+static ea_value_t ea_refcount_usage(e2fsck_t ctx, blk64_t blk,
+                                   ea_value_t *orig)
+{
+       ea_value_t count_cur;
+       ea_value_t count_extra = 0;
+       ea_value_t count_orig;
+
+       ea_refcount_fetch(ctx->refcount_orig, blk, &count_orig);
+       ea_refcount_fetch(ctx->refcount, blk, &count_cur);
+       /* most of time this is not needed */
+       if (ctx->refcount_extra && count_cur == 0)
+               ea_refcount_fetch(ctx->refcount_extra, blk, &count_extra);
+
+       if (!count_orig)
+               count_orig = *orig;
+       else if (orig)
+               *orig = count_orig;
+
+       return count_orig + count_extra - count_cur;
+}
+
+static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
+                                               e2fsck_t thread_ctx)
+{
+       ea_value_t count;
+       blk64_t blk;
+       errcode_t retval = 0;
+
+       if (!thread_ctx->refcount)
+               return 0;
+
+       if (!global_ctx->refcount) {
+               global_ctx->refcount = thread_ctx->refcount;
+               thread_ctx->refcount = NULL;
+               global_ctx->refcount_extra = thread_ctx->refcount;
+               thread_ctx->refcount_extra = NULL;
+               return 0;
+       }
+
+       ea_refcount_intr_begin(thread_ctx->refcount);
+       while (1) {
+               if ((blk = ea_refcount_intr_next(thread_ctx->refcount,
+                                                &count)) == 0)
+                       break;
+               /**
+                * this EA has never seen before, so just store its
+                * refcount and refcount_extra into global_ctx if needed.
+                */
+               if (!global_ctx->block_ea_map ||
+                   !ext2fs_fast_test_block_bitmap2(global_ctx->block_ea_map,
+                                                   blk)) {
+                       ea_value_t extra;
+
+                       retval = ea_refcount_store(global_ctx->refcount,
+                                                  blk, count);
+                       if (retval)
+                               return retval;
+
+                       if (count > 0 || !thread_ctx->refcount_extra)
+                               continue;
+                       ea_refcount_fetch(thread_ctx->refcount_extra, blk,
+                                         &extra);
+                       if (extra == 0)
+                               continue;
+
+                       if (!global_ctx->refcount_extra) {
+                               retval = ea_refcount_create(&global_ctx->refcount_extra);
+                               if (retval)
+                                       return retval;
+                       }
+                       retval = ea_refcount_store(global_ctx->refcount_extra,
+                                                  blk, extra);
+                       if (retval)
+                               return retval;
+               } else {
+                       ea_value_t orig;
+                       ea_value_t thread_usage;
+                       ea_value_t global_usage;
+                       ea_value_t new;
+
+                       thread_usage = ea_refcount_usage(thread_ctx,
+                                                        blk, &orig);
+                       global_usage = ea_refcount_usage(global_ctx,
+                                                        blk, &orig);
+                       if (thread_usage + global_usage <= orig) {
+                               new = orig - thread_usage - global_usage;
+                               retval = ea_refcount_store(global_ctx->refcount,
+                                                          blk, new);
+                               if (retval)
+                                       return retval;
+                               continue;
+                       }
+                       /* update it is as zero */
+                       retval = ea_refcount_store(global_ctx->refcount,
+                                                  blk, 0);
+                       if (retval)
+                               return retval;
+                       /* Ooops, this EA was referenced more than it stated */
+                       if (!global_ctx->refcount_extra) {
+                               retval = ea_refcount_create(&global_ctx->refcount_extra);
+                               if (retval)
+                                       return retval;
+                       }
+                       new = global_usage + thread_usage - orig;
+                       retval = ea_refcount_store(global_ctx->refcount_extra,
+                                                  blk, new);
+                       if (retval)
+                               return retval;
+               }
+       }
+
+       return retval;
+}
+
+static errcode_t e2fsck_pass1_merge_casefolded_dirs(e2fsck_t global_ctx,
+                                                  e2fsck_t thread_ctx)
+{
+       errcode_t retval = 0;
+
+       if (!thread_ctx->casefolded_dirs)
+               return 0;
+
+       if (!global_ctx->casefolded_dirs)
+               retval = ext2fs_badblocks_copy(thread_ctx->casefolded_dirs,
+                                              &global_ctx->casefolded_dirs);
+       else
+               retval = ext2fs_badblocks_merge(thread_ctx->casefolded_dirs,
+                                               global_ctx->casefolded_dirs);
+
+       return retval;
+}
+
+static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
+                                           e2fsck_t thread_ctx)
+{
+       ext2_filsys global_fs = global_ctx->fs;
+       errcode_t retval;
+       int i;
+
+       global_ctx->fs_directory_count += thread_ctx->fs_directory_count;
+       global_ctx->fs_regular_count += thread_ctx->fs_regular_count;
+       global_ctx->fs_blockdev_count += thread_ctx->fs_blockdev_count;
+       global_ctx->fs_chardev_count += thread_ctx->fs_chardev_count;
+       global_ctx->fs_links_count += thread_ctx->fs_links_count;
+       global_ctx->fs_symlinks_count += thread_ctx->fs_symlinks_count;
+       global_ctx->fs_fast_symlinks_count += thread_ctx->fs_fast_symlinks_count;
+       global_ctx->fs_fifo_count += thread_ctx->fs_fifo_count;
+       global_ctx->fs_total_count += thread_ctx->fs_total_count;
+       global_ctx->fs_badblocks_count += thread_ctx->fs_badblocks_count;
+       global_ctx->fs_sockets_count += thread_ctx->fs_sockets_count;
+       global_ctx->fs_ind_count += thread_ctx->fs_ind_count;
+       global_ctx->fs_dind_count += thread_ctx->fs_dind_count;
+       global_ctx->fs_tind_count += thread_ctx->fs_tind_count;
+       global_ctx->fs_fragmented += thread_ctx->fs_fragmented;
+       global_ctx->fs_fragmented_dir += thread_ctx->fs_fragmented_dir;
+       global_ctx->large_files += thread_ctx->large_files;
+       /* threads might enable E2F_OPT_YES */
+       global_ctx->options |= thread_ctx->options;
+       global_ctx->flags |= thread_ctx->flags;
+       /*
+        * The l+f inode may have been cleared, so zap it now and
+        * later passes will recalculate it if necessary
+        */
+       global_ctx->lost_and_found = 0;
+       /* merge extent depth count */
+       for (i = 0; i < MAX_EXTENT_DEPTH_COUNT; i++)
+               global_ctx->extent_depth_count[i] +=
+                       thread_ctx->extent_depth_count[i];
+
+       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 = e2fsck_pass1_merge_fs(global_ctx->fs, thread_ctx->fs);
+       if (retval) {
+               com_err(global_ctx->program_name, 0, _("while merging fs\n"));
+               return retval;
+       }
+       retval = e2fsck_pass1_merge_icounts(global_ctx, thread_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, 0,
+                       _("while merging icounts\n"));
+               return retval;
+       }
+
+       retval = e2fsck_pass1_merge_dirs_to_hash(global_ctx, thread_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, 0,
+                       _("while merging dirs to hash\n"));
+               return retval;
+       }
+
+       e2fsck_pass1_merge_ea_inode_refs(global_ctx, thread_ctx);
+       e2fsck_pass1_merge_ea_refcount(global_ctx, thread_ctx);
+       retval = quota_merge_and_update_usage(global_ctx->qctx,
+                                             thread_ctx->qctx);
+       if (retval)
+               return retval;
+
+       retval = e2fsck_pass1_merge_casefolded_dirs(global_ctx, thread_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, 0,
+                       _("while merging casefolded dirs\n"));
+               return retval;
+       }
+
+       e2fsck_pass1_merge_invalid_bitmaps(global_ctx, thread_ctx);
+
+       if (thread_ctx->min_extra_isize < global_ctx->min_extra_isize)
+               global_ctx->min_extra_isize = thread_ctx->min_extra_isize;
+
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_used_map,
+                               &global_ctx->inode_used_map);
+       if (retval)
+               return retval;
+
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                                       &thread_ctx->inode_dir_map,
+                                       &global_ctx->inode_dir_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_bb_map,
+                               &global_ctx->inode_bb_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_imagic_map,
+                               &global_ctx->inode_imagic_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_reg_map,
+                               &global_ctx->inode_reg_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inodes_to_rebuild,
+                               &global_ctx->inodes_to_rebuild);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->block_ea_map,
+                               &global_ctx->block_ea_map);
+       if (retval)
+               return retval;
+
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->expand_eisize_map,
+                               &global_ctx->expand_eisize_map);
+       if (retval)
+               return retval;
+
+       if (ext2fs_has_feature_shared_blocks(global_fs->super) &&
+           !(global_ctx->options & E2F_OPT_UNSHARE_BLOCKS))
+               return 0;
+       /*
+        * This need be done after merging block_ea_map
+        * because ea block might be shared, we need exclude
+        * them from dup blocks.
+        */
+       e2fsck_pass1_block_map_w_lock(thread_ctx);
+       retval = ext2fs_merge_bitmap(thread_ctx->block_found_map,
+                                    global_ctx->block_found_map,
+                                    global_ctx->block_dup_map,
+                                    global_ctx->block_ea_map);
+       e2fsck_pass1_block_map_w_unlock(thread_ctx);
+       if (retval == EEXIST)
+               global_ctx->flags |= E2F_FLAG_DUP_BLOCK;
 
-               FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+       return 0;
+}
 
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
-                       goto endit;
+static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       errcode_t       retval;
 
-               if (process_inode_count >= ctx->process_inode_size) {
-                       process_inodes(ctx, block_buf);
+       retval = e2fsck_pass1_merge_context(global_ctx, thread_ctx);
+       ext2fs_free_mem(&thread_ctx->fs);
+       if (thread_ctx->logf)
+               fclose(thread_ctx->logf);
+       if (thread_ctx->problem_logf) {
+               fputs("</problem_log>\n", thread_ctx->problem_logf);
+               fclose(thread_ctx->problem_logf);
+       }
 
-                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
-                               goto endit;
+       quota_release_context(&thread_ctx->qctx);
+       /*
+        * @block_metadata_map and @block_dup_map are
+        * shared, so we don't free them.
+        */
+       thread_ctx->block_metadata_map = NULL;
+       thread_ctx->block_dup_map = NULL;
+       e2fsck_reset_context(thread_ctx);
+       ext2fs_free_mem(&thread_ctx);
+
+       return retval;
+}
+
+static int e2fsck_pass1_threads_join(e2fsck_t global_ctx)
+{
+       errcode_t rc;
+       errcode_t ret = 0;
+       struct e2fsck_thread_info *infos = global_ctx->infos;
+       struct e2fsck_thread_info *pinfo;
+       int num_threads = global_ctx->pfs_num_threads;
+       int i;
+
+       /* merge invalid bitmaps will recalculate it */
+       global_ctx->invalid_bitmaps = 0;
+       for (i = 0; i < num_threads; i++) {
+               pinfo = &infos[i];
+
+               if (!pinfo->eti_started)
+                       continue;
+
+               rc = pthread_join(pinfo->eti_thread_id, NULL);
+               if (rc) {
+                       com_err(global_ctx->program_name, rc,
+                               _("while joining thread\n"));
+                       if (ret == 0)
+                               ret = rc;
+               }
+               rc = e2fsck_pass1_thread_join(global_ctx, infos[i].eti_thread_ctx);
+               if (rc) {
+                       com_err(global_ctx->program_name, rc,
+                               _("while joining pass1 thread\n"));
+                       if (ret == 0)
+                               ret = rc;
                }
        }
-       process_inodes(ctx, block_buf);
-       ext2fs_close_inode_scan(scan);
-       scan = NULL;
+       free(infos);
+       global_ctx->infos = NULL;
 
-       reserve_block_for_root_repair(ctx);
-       reserve_block_for_lnf_repair(ctx);
+       return ret;
+}
+
+static void *e2fsck_pass1_thread(void *arg)
+{
+       struct e2fsck_thread_info       *info = arg;
+       e2fsck_t                         thread_ctx = info->eti_thread_ctx;
+#ifdef DEBUG_THREADS
+       struct e2fsck_thread_debug      *thread_debug = info->eti_debug;
+#endif
+
+#ifdef DEBUG_THREADS
+       pthread_mutex_lock(&thread_debug->etd_mutex);
+       while (info->eti_thread_index > thread_debug->etd_finished_threads) {
+               pthread_cond_wait(&thread_debug->etd_cond,
+                                 &thread_debug->etd_mutex);
+       }
+       pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
 
+#ifdef HAVE_SETJMP_H
        /*
-        * If any extended attribute blocks' reference counts need to
-        * be adjusted, either up (ctx->refcount_extra), or down
-        * (ctx->refcount), then fix them.
+        * 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 (ctx->refcount) {
-               adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
-               ea_refcount_free(ctx->refcount);
-               ctx->refcount = 0;
-       }
-       if (ctx->refcount_extra) {
-               adjust_extattr_refcount(ctx, ctx->refcount_extra,
-                                       block_buf, +1);
-               ea_refcount_free(ctx->refcount_extra);
-               ctx->refcount_extra = 0;
+       if (setjmp(thread_ctx->abort_loc)) {
+               thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+               goto out;
        }
+       thread_ctx->flags |= E2F_FLAG_SETJMP_OK;
+#endif
 
-       if (ctx->ea_block_quota_blocks) {
-               ea_refcount_free(ctx->ea_block_quota_blocks);
-               ctx->ea_block_quota_blocks = 0;
-       }
+       e2fsck_pass1_run(thread_ctx);
 
-       if (ctx->ea_block_quota_inodes) {
-               ea_refcount_free(ctx->ea_block_quota_inodes);
-               ctx->ea_block_quota_inodes = 0;
+out:
+       if (thread_ctx->options & E2F_OPT_MULTITHREAD)
+               log_out(thread_ctx,
+                       _("Scanned group range [%u, %u), inodes %u\n"),
+                       thread_ctx->thread_info.et_group_start,
+                       thread_ctx->thread_info.et_group_end,
+                       thread_ctx->thread_info.et_inode_number);
+
+#ifdef DEBUG_THREADS
+       pthread_mutex_lock(&thread_debug->etd_mutex);
+       thread_debug->etd_finished_threads++;
+       pthread_cond_broadcast(&thread_debug->etd_cond);
+       pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
+
+       return NULL;
+}
+
+static dgrp_t ext2fs_get_avg_group(ext2_filsys fs)
+{
+#ifdef HAVE_PTHREAD
+       dgrp_t average_group;
+       unsigned flexbg_size;
+
+       if (fs->fs_num_threads <= 1)
+               return fs->group_desc_count;
+
+       average_group = fs->group_desc_count / fs->fs_num_threads;
+       if (average_group <= 1)
+               return 1;
+
+       if (ext2fs_has_feature_flex_bg(fs->super)) {
+               int times = 1;
+
+               flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+               if (average_group % flexbg_size) {
+                       times = average_group / flexbg_size;
+                       average_group = times * flexbg_size;
+               }
        }
 
-       if (ctx->invalid_bitmaps)
-               handle_fs_bad_blocks(ctx);
+       return average_group;
+#else
+       return fs->group_desc_count;
+#endif
+}
 
-       /* We don't need the block_ea_map any more */
-       if (ctx->block_ea_map) {
-               ext2fs_free_block_bitmap(ctx->block_ea_map);
-               ctx->block_ea_map = 0;
+static int e2fsck_pass1_threads_start(e2fsck_t global_ctx)
+{
+       struct e2fsck_thread_info       *infos;
+       pthread_attr_t                   attr;
+       errcode_t                        retval;
+       errcode_t                        ret;
+       struct e2fsck_thread_info       *tmp_pinfo;
+       int                              i;
+       e2fsck_t                         thread_ctx;
+       dgrp_t                           average_group;
+       int num_threads = global_ctx->pfs_num_threads;
+#ifdef DEBUG_THREADS
+       struct e2fsck_thread_debug       thread_debug =
+               {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
+
+       thread_debug.etd_finished_threads = 0;
+#endif
+
+       retval = pthread_attr_init(&attr);
+       if (retval) {
+               com_err(global_ctx->program_name, retval,
+                       _("while setting pthread attribute\n"));
+               return retval;
        }
 
-       if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
-               clear_problem_context(&pctx);
-               pctx.errcode = ext2fs_create_resize_inode(fs);
-               if (pctx.errcode) {
-                       if (!fix_problem(ctx, PR_1_RESIZE_INODE_CREATE,
-                                        &pctx)) {
-                               ctx->flags |= E2F_FLAG_ABORT;
-                               goto endit;
-                       }
-                       pctx.errcode = 0;
+       infos = calloc(num_threads, sizeof(struct e2fsck_thread_info));
+       if (infos == NULL) {
+               retval = -ENOMEM;
+               com_err(global_ctx->program_name, retval,
+                       _("while allocating memory for threads\n"));
+               pthread_attr_destroy(&attr);
+               return retval;
+       }
+       global_ctx->infos = infos;
+
+       average_group = ext2fs_get_avg_group(global_ctx->fs);
+       for (i = 0; i < num_threads; i++) {
+               tmp_pinfo = &infos[i];
+               tmp_pinfo->eti_thread_index = i;
+#ifdef DEBUG_THREADS
+               tmp_pinfo->eti_debug = &thread_debug;
+#endif
+               retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx,
+                                                    i, num_threads,
+                                                    average_group);
+               if (retval) {
+                       com_err(global_ctx->program_name, retval,
+                               _("while preparing pass1 thread\n"));
+                       break;
                }
-               if (!pctx.errcode) {
-                       e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
-                                         "recreate inode");
-                       inode->i_mtime = ctx->now;
-                       e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
-                                          "recreate inode");
+               tmp_pinfo->eti_thread_ctx = thread_ctx;
+
+               retval = pthread_create(&tmp_pinfo->eti_thread_id, &attr,
+                                       &e2fsck_pass1_thread, tmp_pinfo);
+               if (retval) {
+                       com_err(global_ctx->program_name, retval,
+                               _("while creating thread\n"));
+                       e2fsck_pass1_thread_join(global_ctx, thread_ctx);
+                       break;
                }
-               ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
+
+               tmp_pinfo->eti_started = 1;
        }
 
-       if (ctx->flags & E2F_FLAG_RESTART) {
-               /*
-                * Only the master copy of the superblock and block
-                * group descriptors are going to be written during a
-                * restart, so set the superblock to be used to be the
-                * master superblock.
-                */
-               ctx->use_superblock = 0;
-               unwind_pass1(fs);
-               goto endit;
+       /* destroy the thread attribute object, since it is no longer needed */
+       ret = pthread_attr_destroy(&attr);
+       if (ret) {
+               com_err(global_ctx->program_name, ret,
+                       _("while destroying thread attribute\n"));
+               if (retval == 0)
+                       retval = ret;
        }
 
-       if (ctx->block_dup_map) {
-               if (ctx->options & E2F_OPT_PREEN) {
-                       clear_problem_context(&pctx);
-                       fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
-               }
-               e2fsck_pass1_dupblocks(ctx, block_buf);
+       if (retval) {
+               e2fsck_pass1_threads_join(global_ctx);
+               return retval;
        }
-       ctx->flags |= E2F_FLAG_ALLOC_OK;
-       ext2fs_free_mem(&inodes_to_process);
-endit:
-       e2fsck_use_inode_shortcuts(ctx, 0);
+       return 0;
+}
 
-       if (scan)
-               ext2fs_close_inode_scan(scan);
-       if (block_buf)
-               ext2fs_free_mem(&block_buf);
-       if (inode)
-               ext2fs_free_mem(&inode);
+static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
+{
+       errcode_t retval;
 
-       /*
-        * The l+f inode may have been cleared, so zap it now and
-        * later passes will recalculate it if necessary
-        */
-       ctx->lost_and_found = 0;
+       retval = e2fsck_pass1_threads_start(global_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, retval,
+                       _("while starting pass1 threads\n"));
+               goto out_abort;
+       }
 
-       if ((ctx->flags & E2F_FLAG_SIGNAL_MASK) == 0)
-               print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
-       else
-               ctx->invalid_bitmaps++;
+       retval = e2fsck_pass1_threads_join(global_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, retval,
+                       _("while joining pass1 threads\n"));
+               goto out_abort;
+       }
+       return;
+out_abort:
+       global_ctx->flags |= E2F_FLAG_ABORT;
+       return;
+}
+#endif
+
+void e2fsck_pass1(e2fsck_t ctx)
+{
+       errcode_t retval;
+       int need_single = 1;
+
+       retval = e2fsck_pass1_prepare(ctx);
+       if (retval)
+               return;
+#ifdef HAVE_PTHREAD
+       if (ctx->pfs_num_threads > 1 || ctx->options & E2F_OPT_MULTITHREAD) {
+               need_single = 0;
+               e2fsck_pass1_multithread(ctx);
+       }
+       /* No lock is needed at this time */
+       ctx->fs_need_locking = 0;
+#endif
+       if (need_single)
+               e2fsck_pass1_run(ctx);
+       e2fsck_pass1_post(ctx);
 }
+
 #undef FINISH_INODE_LOOP
 
 /*
@@ -2080,24 +3783,65 @@ static errcode_t scan_callback(ext2_filsys fs,
 {
        struct scan_callback_struct *scan_struct;
        e2fsck_t ctx;
+       dgrp_t cur = group + 1;
+       struct e2fsck_thread *tinfo;
+       struct e2fsck_thread_info *pinfo, *infos;
+       int i;
 
        scan_struct = (struct scan_callback_struct *) priv_data;
        ctx = scan_struct->ctx;
 
-       process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
+       process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf,
+                      scan_struct->inodes_to_process,
+                      scan_struct->process_inode_count);
+
+#ifdef HAVE_PTHREAD
+       if (ctx->global_ctx) {
+               cur = 0;
+               infos = ctx->global_ctx->infos;
+               for (i = 0; i < ctx->global_ctx->pfs_num_threads; i++) {
+                       pinfo = &infos[i];
+
+                       if (!pinfo->eti_started)
+                               continue;
+
+                       tinfo = &pinfo->eti_thread_ctx->thread_info;
+                       if (ctx == pinfo->eti_thread_ctx)
+                               cur += group + 1 - tinfo->et_group_start;
+                       else
+                               cur += tinfo->et_group_next -
+                                       tinfo->et_group_start;
+               }
+       }
+#endif
 
        if (ctx->progress)
-               if ((ctx->progress)(ctx, 1, group+1,
+               if ((ctx->progress)(ctx, 1, cur,
                                    ctx->fs->group_desc_count))
                        return EXT2_ET_CANCEL_REQUESTED;
 
+#ifdef HAVE_PTHREAD
+       if (ctx->global_ctx) {
+               tinfo = &ctx->thread_info;
+               tinfo->et_group_next++;
+               if (ctx->options & E2F_OPT_DEBUG &&
+                   ctx->options & E2F_OPT_MULTITHREAD)
+                       log_out(ctx, _("group %d finished\n"),
+                               tinfo->et_group_next);
+               if (tinfo->et_group_next >= tinfo->et_group_end)
+                       return EXT2_ET_SCAN_FINISHED;
+       }
+#endif
+
        return 0;
 }
 
 /*
  * Process the inodes in the "inodes to process" list.
  */
-static void process_inodes(e2fsck_t ctx, char *block_buf)
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+                          struct process_inode_block *inodes_to_process,
+                          int *process_inode_count)
 {
        int                     i;
        struct ext2_inode       *old_stashed_inode;
@@ -2109,15 +3853,15 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
 #if 0
        printf("begin process_inodes: ");
 #endif
-       if (process_inode_count == 0)
+       if (*process_inode_count == 0)
                return;
        old_operation = ehandler_operation(0);
        old_stashed_inode = ctx->stashed_inode;
        old_stashed_ino = ctx->stashed_ino;
-       qsort(inodes_to_process, process_inode_count,
+       qsort(inodes_to_process, *process_inode_count,
                      sizeof(struct process_inode_block), process_inode_cmp);
        clear_problem_context(&pctx);
-       for (i=0; i < process_inode_count; i++) {
+       for (i=0; i < *process_inode_count; i++) {
                pctx.inode = ctx->stashed_inode =
                        (struct ext2_inode *) &inodes_to_process[i].inode;
                pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
@@ -2130,12 +3874,12 @@ 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;
        ctx->stashed_ino = old_stashed_ino;
-       process_inode_count = 0;
+       *process_inode_count = 0;
 #if 0
        printf("end process inodes\n");
 #endif
@@ -2166,43 +3910,59 @@ static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b)
 }
 
 /*
- * Mark an inode as being bad in some what
+ * Mark an inode as being bad and increment its badness counter.
  */
-static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
+void e2fsck_mark_inode_bad_loc(e2fsck_t ctx, struct problem_context *pctx,
+                              __u32 code, int badness, const char *func,
+                              const int line)
 {
-       struct          problem_context pctx;
+       __u16 badness_before, badness_after;
 
-       if (!ctx->inode_bad_map) {
-               clear_problem_context(&pctx);
+       if (!ctx->inode_badness_threshold)      /* badness is disabled */
+               return;
 
-               pctx.errcode = e2fsck_allocate_inode_bitmap(ctx->fs,
-                               _("bad inode map"), EXT2FS_BMAP64_RBTREE,
-                               "inode_bad_map", &ctx->inode_bad_map);
-               if (pctx.errcode) {
-                       pctx.num = 3;
-                       fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
-                       /* Should never get here */
+       if (!ctx->inode_badness) {
+               errcode_t retval;
+
+               retval = ext2fs_create_icount2(ctx->fs, 0, 0, NULL,
+                                              &ctx->inode_badness);
+               if (retval) {
+                       pctx->errcode = retval;
+                       fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, pctx);
                        ctx->flags |= E2F_FLAG_ABORT;
                        return;
                }
        }
-       ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino);
+       ext2fs_icount_fetch(ctx->inode_badness, pctx->ino, &badness_before);
+       if (badness + badness_before > BADNESS_MAX)
+               badness_after = BADNESS_MAX;
+       else if (badness < 0 && badness_before < -badness)
+               badness_after = 0;
+       else
+               badness_after = badness_before + badness;
+       ext2fs_icount_store(ctx->inode_badness, pctx->ino, badness_after);
+
+       if (ctx->options & E2F_OPT_DEBUG)
+               log_out(ctx,
+                       "%s:%d: increase inode %lu badness %u to %u for %x\n",
+                       func, line, (unsigned long)pctx->ino, badness_before,
+                       badness_after, code);
 }
 
-static void add_encrypted_dir(e2fsck_t ctx, ino_t ino)
+static void add_casefolded_dir(e2fsck_t ctx, ext2_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;
 }
@@ -2256,30 +4016,20 @@ static void alloc_imagic_map(e2fsck_t ctx)
  */
 static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
 {
-       struct          problem_context pctx;
+       struct problem_context pctx;
+       e2fsck_t global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
 
        clear_problem_context(&pctx);
 
-       if (ext2fs_fast_test_block_bitmap2(ctx->block_found_map, block)) {
+       if (is_blocks_used(ctx, block, 1)) {
                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"),
-                                       EXT2FS_BMAP64_RBTREE, "block_dup_map",
-                                       &ctx->block_dup_map);
-                       if (pctx.errcode) {
-                               pctx.num = 3;
-                               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
-                                           &pctx);
-                               /* Should never get here */
-                               ctx->flags |= E2F_FLAG_ABORT;
-                               return;
-                       }
-               }
-               ext2fs_fast_mark_block_bitmap2(ctx->block_dup_map, block);
+               ctx->flags |= E2F_FLAG_DUP_BLOCK;
+               e2fsck_pass1_block_map_w_lock(ctx);
+               ext2fs_fast_mark_block_bitmap2(global_ctx->block_dup_map, block);
+               e2fsck_pass1_block_map_w_unlock(ctx);
        } else {
                ext2fs_fast_mark_block_bitmap2(ctx->block_found_map, block);
        }
@@ -2292,9 +4042,9 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
 static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
                                      unsigned int num)
 {
-       if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num))
+       if (!is_blocks_used(ctx, block, num)) {
                ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
-       else {
+       else {
                unsigned int i;
 
                for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
@@ -2302,6 +4052,18 @@ static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
        }
 }
 
+static errcode_t _INLINE_ e2fsck_write_ext_attr3(e2fsck_t ctx, blk64_t block,
+                                                void *inbuf, ext2_ino_t inum)
+{
+       errcode_t retval;
+       ext2_filsys fs = ctx->fs;
+
+       e2fsck_pass1_fix_lock(ctx);
+       retval = ext2fs_write_ext_attr3(fs, block, inbuf, inum);
+       e2fsck_pass1_fix_unlock(ctx);
+
+       return retval;
+}
 /*
  * Adjust the extended attribute block's reference counts at the end
  * of pass 1, either by subtracting out references for EA blocks that
@@ -2328,17 +4090,23 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
                pctx.blk = blk;
                pctx.errcode = ext2fs_read_ext_attr3(fs, blk, block_buf,
                                                     pctx.ino);
+               /* We already checked this block, shouldn't happen */
                if (pctx.errcode) {
                        fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
                        return;
                }
-               header = (struct ext2_ext_attr_header *) block_buf;
+               header = BHDR(block_buf);
+               if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+                       fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
+                       return;
+               }
+
                pctx.blkcount = header->h_refcount;
                should_be = header->h_refcount + adjust_sign * (int)count;
                pctx.num = should_be;
                if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
                        header->h_refcount = should_be;
-                       pctx.errcode = ext2fs_write_ext_attr3(fs, blk,
+                       pctx.errcode = e2fsck_write_ext_attr3(ctx, blk,
                                                             block_buf,
                                                             pctx.ino);
                        if (pctx.errcode) {
@@ -2385,7 +4153,8 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
        if (!ext2fs_has_feature_xattr(fs->super) ||
            (blk < fs->super->s_first_data_block) ||
            (blk >= ext2fs_blocks_count(fs->super))) {
-               mark_inode_bad(ctx, ino);
+               /* Fixed in pass2, e2fsck_process_bad_inode(). */
+               e2fsck_mark_inode_bad(ctx, pctx, PR_2_FILE_ACL_ZERO);
                return 0;
        }
 
@@ -2405,7 +4174,15 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 
        /* Create the EA refcount structure if necessary */
        if (!ctx->refcount) {
-               pctx->errcode = ea_refcount_create(0, &ctx->refcount);
+               pctx->errcode = ea_refcount_create(&ctx->refcount_orig);
+               if (pctx->errcode) {
+                       pctx->num = 1;
+                       fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return 0;
+               }
+
+               pctx->errcode = ea_refcount_create(&ctx->refcount);
                if (pctx->errcode) {
                        pctx->num = 1;
                        fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
@@ -2439,8 +4216,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
                        return 1;
                /* Ooops, this EA was referenced more than it stated */
                if (!ctx->refcount_extra) {
-                       pctx->errcode = ea_refcount_create(0,
-                                          &ctx->refcount_extra);
+                       pctx->errcode = ea_refcount_create(&ctx->refcount_extra);
                        if (pctx->errcode) {
                                pctx->num = 2;
                                fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
@@ -2469,7 +4245,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
                pctx->errcode = 0;
                goto clear_extattr;
        }
-       header = (struct ext2_ext_attr_header *) block_buf;
+       header = BHDR(block_buf);
        pctx->blk = ext2fs_file_acl_block(fs, inode);
        if (((ctx->ext_attr_ver == 1) &&
             (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
@@ -2519,8 +4295,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;
@@ -2535,6 +4312,9 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 
                        hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
                                                          entry->e_value_offs);
+                       if (entry->e_hash != hash)
+                               hash = ext2fs_ext_attr_hash_entry_signed(entry,
+                                       block_buf + entry->e_value_offs);
 
                        if (entry->e_hash != hash) {
                                pctx->num = entry->e_hash;
@@ -2569,7 +4349,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
         */
        if (failed_csum &&
            fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) {
-               pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf,
+               pctx->errcode = e2fsck_write_ext_attr3(ctx, blk, block_buf,
                                                       pctx->ino);
                if (pctx->errcode)
                        return 0;
@@ -2577,8 +4357,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 
        if (quota_blocks != EXT2FS_C2B(fs, 1U)) {
                if (!ctx->ea_block_quota_blocks) {
-                       pctx->errcode = ea_refcount_create(0,
-                                               &ctx->ea_block_quota_blocks);
+                       pctx->errcode = ea_refcount_create(&ctx->ea_block_quota_blocks);
                        if (pctx->errcode) {
                                pctx->num = 3;
                                goto refcount_fail;
@@ -2590,8 +4369,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 
        if (quota_inodes) {
                if (!ctx->ea_block_quota_inodes) {
-                       pctx->errcode = ea_refcount_create(0,
-                                               &ctx->ea_block_quota_inodes);
+                       pctx->errcode = ea_refcount_create(&ctx->ea_block_quota_inodes);
                        if (pctx->errcode) {
                                pctx->num = 4;
 refcount_fail:
@@ -2609,7 +4387,13 @@ refcount_fail:
 
        inc_ea_inode_refs(ctx, pctx, first, end);
        ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
-       mark_block_used(ctx, blk);
+       ea_refcount_store(ctx->refcount_orig, blk, header->h_refcount);
+       /**
+        * It might be racy that this block has been merged in the
+        * global found map.
+        */
+       if (!is_blocks_used(ctx, blk, 1))
+               ext2fs_fast_mark_block_bitmap2(ctx->block_found_map, blk);
        ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
        return 1;
 
@@ -2650,11 +4434,13 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
        }
 
        retval = io_channel_read_blk64(fs->io, blk, 1, block_buf);
-       if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
-               return 1;
+       if (retval) {
+               if (fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+                       return 1;
+       }
 
        /* XXX should check that beginning matches a directory */
-       root = (struct ext2_dx_root_info *) (block_buf + 24);
+       root = get_ext2_dx_root_info(fs, block_buf);
 
        if ((root->reserved_zero || root->info_length < 8) &&
            fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
@@ -2664,18 +4450,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;
 }
 
@@ -2702,8 +4519,8 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino,
        ext2fs_unmark_inode_bitmap2(ctx->inode_used_map, ino);
        if (ctx->inode_reg_map)
                ext2fs_unmark_inode_bitmap2(ctx->inode_reg_map, ino);
-       if (ctx->inode_bad_map)
-               ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino);
+       if (ctx->inode_badness)
+               ext2fs_icount_store(ctx->inode_badness, ino, 0);
 
        /*
         * If the inode was partially accounted for before processing
@@ -2773,7 +4590,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;
@@ -2822,7 +4640,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;
@@ -2830,9 +4649,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;
@@ -2847,10 +4667,12 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                if (try_repairs && is_dir && problem == 0 &&
                    (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
                    fix_problem(ctx, PR_1_UNINIT_DBLOCK, pctx)) {
+                       e2fsck_pass1_fix_lock(ctx);
                        extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT;
                        pb->inode_modified = 1;
                        pctx->errcode = ext2fs_extent_replace(ehandle, 0,
                                                              &extent);
+                       e2fsck_pass1_fix_unlock(ctx);
                        if (pctx->errcode)
                                return;
                        failed_csum = 0;
@@ -2871,7 +4693,9 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
 #endif
                if (try_repairs && problem) {
 report_problem:
-                       if (fix_problem(ctx, problem, pctx)) {
+                       /* Record badness only if extent is within inode */
+                       if (fix_problem_bad(ctx, problem, pctx,
+                                           info.curr_level == 0)) {
                                if (ctx->invalid_bitmaps) {
                                        /*
                                         * If fsck knows the bitmaps are bad,
@@ -2892,15 +4716,19 @@ report_problem:
                                        }
                                        continue;
                                }
+                               e2fsck_pass1_fix_lock(ctx);
                                e2fsck_read_bitmaps(ctx);
                                pb->inode_modified = 1;
                                pctx->errcode =
                                        ext2fs_extent_delete(ehandle, 0);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode) {
                                        pctx->str = "ext2fs_extent_delete";
                                        return;
                                }
+                               e2fsck_pass1_fix_lock(ctx);
                                pctx->errcode = ext2fs_extent_fix_parents(ehandle);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode &&
                                    pctx->errcode != EXT2_ET_NO_CURRENT_NODE) {
                                        pctx->str = "ext2fs_extent_fix_parents";
@@ -2936,9 +4764,9 @@ report_problem:
                                                      extent.e_pblk)) {
                                next_try_repairs = 0;
                                pctx->blk = blk;
-                               fix_problem(ctx,
-                                           PR_1_CRITICAL_METADATA_COLLISION,
-                                           pctx);
+                               fix_problem_bad(ctx,
+                                              PR_1_CRITICAL_METADATA_COLLISION,
+                                              pctx, 2);
                                if ((ctx->options & E2F_OPT_NO) == 0)
                                        ctx->flags |= E2F_FLAG_RESTART_LATER;
                        }
@@ -2958,15 +4786,22 @@ 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;
                                problem = PR_1_EXTENT_INDEX_START_INVALID;
                                if (fix_problem(ctx, problem, pctx)) {
+                                       e2fsck_pass1_fix_lock(ctx);
                                        pb->inode_modified = 1;
                                        pctx->errcode =
                                                ext2fs_extent_fix_parents(ehandle);
+                                       e2fsck_pass1_fix_unlock(ctx);
                                        if (pctx->errcode) {
                                                pctx->str = "ext2fs_extent_fix_parents";
                                                return;
@@ -3030,15 +4865,19 @@ report_problem:
                        pctx->blk = extent.e_lblk;
                        pctx->blk2 = new_lblk;
                        if (fix_problem(ctx, PR_1_COLLAPSE_DBLOCK, pctx)) {
+                               e2fsck_pass1_fix_lock(ctx);
                                extent.e_lblk = new_lblk;
                                pb->inode_modified = 1;
                                pctx->errcode = ext2fs_extent_replace(ehandle,
                                                                0, &extent);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode) {
                                        pctx->errcode = 0;
                                        goto alloc_later;
                                }
+                               e2fsck_pass1_fix_lock(ctx);
                                pctx->errcode = ext2fs_extent_fix_parents(ehandle);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode)
                                        goto failed_add_dir_block;
                                pctx->errcode = ext2fs_extent_goto(ehandle,
@@ -3134,8 +4973,10 @@ alloc_later:
        /* Failed csum but passes checks?  Ask to fix checksum. */
        if (failed_csum &&
            fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID, pctx)) {
+               e2fsck_pass1_fix_lock(ctx);
                pb->inode_modified = 1;
                pctx->errcode = ext2fs_extent_replace(ehandle, 0, &extent);
+               e2fsck_pass1_fix_unlock(ctx);
                if (pctx->errcode)
                        return;
        }
@@ -3318,7 +5159,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);
        }
@@ -3373,7 +5214,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);
@@ -3402,6 +5243,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 
        if (!pb.num_blocks && pb.is_dir &&
            !(inode->i_flags & EXT4_INLINE_DATA_FL)) {
+               /*
+                * The mode might be in-correct. Increasing the badness by
+                * small amount won't hurt much.
+                */
                if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
                        e2fsck_clear_inode(ctx, ino, inode, 0, "check_blocks");
                        ctx->fs_directory_count--;
@@ -3410,6 +5255,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,
@@ -3427,11 +5273,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;
@@ -3445,11 +5293,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;
@@ -3459,7 +5307,6 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                                bad_size = 2;
                }
        } else {
-               size = EXT2_I_SIZE(inode);
                if ((pb.last_init_lblock >= 0) &&
                    /* Do not allow initialized allocated blocks past i_size*/
                    (size < (__u64)pb.last_init_lblock * fs->blocksize) &&
@@ -3482,8 +5329,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)) {
@@ -3535,7 +5380,11 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                e2fsck_rehash_dir_later(ctx, ino);
 
 out:
-       if (dirty_inode)
+       /* need restart if clearing bad inode after block processing */
+       if (e2fsck_fix_bad_inode(ctx, pctx))
+               e2fsck_clear_inode(ctx, ino, inode, E2F_FLAG_RESTART,
+                                  "check_blocks_bad");
+       else if (dirty_inode)
                e2fsck_write_inode(ctx, ino, inode, "check_blocks");
 }
 
@@ -3655,13 +5504,14 @@ 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 && !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_dir && p->num_blocks + 1 >= p->max_blocks)
@@ -3686,7 +5536,7 @@ static int process_block(ext2_filsys fs,
            blk < ctx->fs->super->s_blocks_count &&
            ext2fs_test_block_bitmap2(ctx->block_metadata_map, blk)) {
                pctx->blk = blk;
-               fix_problem(ctx, PR_1_CRITICAL_METADATA_COLLISION, pctx);
+               fix_problem_bad(ctx, PR_1_CRITICAL_METADATA_COLLISION, pctx, 2);
                if ((ctx->options & E2F_OPT_NO) == 0)
                        ctx->flags |= E2F_FLAG_RESTART_LATER;
        }
@@ -3846,18 +5696,18 @@ static int process_bad_block(ext2_filsys fs,
                                *block_nr = 0;
                                return BLOCK_CHANGED;
                        }
-               } else if (ext2fs_test_block_bitmap2(ctx->block_found_map,
-                                                   blk)) {
+               } else if (is_blocks_used(ctx, blk, 1)) {
                        p->bbcheck = 1;
                        if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
                                        pctx)) {
                                *block_nr = 0;
                                return BLOCK_CHANGED;
                        }
-                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       if (e2fsck_should_abort(ctx))
                                return BLOCK_ABORT;
-               } else
+               } else {
                        mark_block_used(ctx, blk);
+               }
                return 0;
        }
 #if 0
@@ -3870,7 +5720,7 @@ static int process_bad_block(ext2_filsys fs,
         * there's an overlap between the filesystem table blocks
         * (bitmaps and inode table) and the bad block list.
         */
-       if (!ext2fs_test_block_bitmap2(ctx->block_found_map, blk)) {
+       if (!is_blocks_used(ctx, blk, 1)) {
                ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
                return 0;
        }
@@ -3951,7 +5801,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;
        }
@@ -3990,7 +5840,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);