Whamcloud - gitweb
LU-16972 e2fsck: use rb-tree to track EA reference counts
[tools/e2fsprogs.git] / e2fsck / pass1.c
index f0e0a36..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)
@@ -85,7 +87,6 @@ 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, ext2_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 EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
@@ -178,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))
@@ -193,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;
@@ -210,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) ||
@@ -239,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.
  */
@@ -405,8 +430,7 @@ static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
                if (!entry->e_value_inum)
                        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);
@@ -437,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 */
 
@@ -581,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;
@@ -609,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.
@@ -639,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))
@@ -650,10 +690,13 @@ 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;
-               e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
-                                       EXT2_INODE_SIZE(sb), "pass1");
+               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,
@@ -753,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);
@@ -915,6 +958,150 @@ static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
        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;
@@ -978,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)
@@ -997,7 +1206,7 @@ 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; \
                } \
@@ -1271,7 +1480,7 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
 {
        unsigned flexbg_size = 1;
        ext2_filsys fs = ctx->fs;
-       int num_threads = ctx->fs_num_threads;
+       int num_threads = ctx->pfs_num_threads;
        int max_threads;
 
        if (num_threads < 1) {
@@ -1291,6 +1500,8 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
        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",
@@ -1298,7 +1509,7 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
                num_threads = max_threads;
        }
 out:
-       ctx->fs_num_threads = num_threads;
+       ctx->pfs_num_threads = num_threads;
        ctx->fs->fs_num_threads = num_threads;
 }
 #endif
@@ -1326,7 +1537,7 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
 
 #ifdef HAVE_PTHREAD
        /* don't use more than 1/10 of memory for threads checking */
-       readahead_kb = get_memory_size() / (10 * ctx->fs_num_threads);
+       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;
@@ -1384,7 +1595,7 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
 #ifdef HAVE_PTHREAD
        pthread_rwlock_init(&ctx->fs_fix_rwlock, NULL);
        pthread_rwlock_init(&ctx->fs_block_map_rwlock, NULL);
-       if (ctx->fs_num_threads > 1)
+       if (ctx->pfs_num_threads > 1)
                ctx->fs_need_locking = 1;
 #endif
 
@@ -1483,9 +1694,25 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
 }
 
 
+/*
+ * 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)
 {
-       int     i;
        ext2_filsys fs = ctx->fs;
        ext2_ino_t      ino = 0;
        struct ext2_inode *inode = NULL;
@@ -1509,8 +1736,9 @@ void e2fsck_pass1_run(e2fsck_t ctx)
        dgrp_t          ra_group = 0;
        struct ea_quota ea_ibody_quota;
        struct process_inode_block *inodes_to_process;
-       int             process_inode_count, check_mmp;
+       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);
@@ -1659,9 +1887,8 @@ void e2fsck_pass1_run(e2fsck_t ctx)
        if (ctx->global_ctx) {
                if (ctx->options & E2F_OPT_DEBUG &&
                    ctx->options & E2F_OPT_MULTITHREAD)
-                       fprintf(stderr, "thread %d jumping to group %d\n",
-                                       ctx->thread_info.et_thread_index,
-                                       ctx->thread_info.et_group_start);
+                       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) {
@@ -1998,6 +2225,7 @@ void e2fsck_pass1_run(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]);
@@ -2015,6 +2243,12 @@ void e2fsck_pass1_run(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);
                        }
                }
 
@@ -2243,18 +2477,21 @@ void e2fsck_pass1_run(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)
@@ -2316,8 +2553,7 @@ void e2fsck_pass1_run(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) {
@@ -2345,8 +2581,37 @@ void e2fsck_pass1_run(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])
@@ -2377,6 +2642,22 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 
                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;
@@ -2391,6 +2672,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                goto endit;
                        }
                }
+       next_unlock:
                e2fsck_pass1_check_unlock(ctx);
        }
        process_inodes(ctx, block_buf, inodes_to_process,
@@ -2408,9 +2690,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                ctx->ea_block_quota_inodes = 0;
        }
 
-       /* We don't need the encryption policy => ID map any more */
-       destroy_encryption_policy_map(ctx);
-
        if (ctx->flags & E2F_FLAG_RESTART) {
                /*
                 * Only the master copy of the superblock and block
@@ -2747,6 +3026,8 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
        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,
@@ -2835,6 +3116,23 @@ static void e2fsck_pass1_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx)
        e2fsck_merge_dx_dir(global_ctx, thread_ctx);
 }
 
+static int e2fsck_pass1_merge_encrypted_info(e2fsck_t global_ctx,
+                                             e2fsck_t thread_ctx)
+{
+       if (thread_ctx->encrypted_files == NULL)
+               return 0;
+
+       if (global_ctx->encrypted_files == NULL) {
+               global_ctx->encrypted_files = thread_ctx->encrypted_files;
+               thread_ctx->encrypted_files = NULL;
+               return 0;
+       }
+
+       return e2fsck_merge_encrypted_info(global_ctx,
+                                          thread_ctx->encrypted_files,
+                                          global_ctx->encrypted_files);
+}
+
 static inline errcode_t
 e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount,
                          ext2_icount_t *src_icount)
@@ -2866,6 +3164,11 @@ static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread
                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;
 }
@@ -2891,8 +3194,8 @@ static errcode_t e2fsck_pass1_merge_dirs_to_hash(e2fsck_t global_ctx,
 static errcode_t e2fsck_pass1_merge_ea_inode_refs(e2fsck_t global_ctx,
                                                  e2fsck_t thread_ctx)
 {
-       ea_value_t count;
-       blk64_t blk;
+       ea_value_t thread_count, global_count;
+       ea_key_t ino;
        errcode_t retval;
 
        if (!thread_ctx->ea_inode_refs)
@@ -2906,17 +3209,15 @@ static errcode_t e2fsck_pass1_merge_ea_inode_refs(e2fsck_t global_ctx,
 
        ea_refcount_intr_begin(thread_ctx->ea_inode_refs);
        while (1) {
-               if ((blk = ea_refcount_intr_next(thread_ctx->ea_inode_refs,
-                                                &count)) == 0)
+               if ((ino = ea_refcount_intr_next(thread_ctx->ea_inode_refs,
+                                                &thread_count)) == 0)
                        break;
-               if (!global_ctx->block_ea_map ||
-                   !ext2fs_fast_test_block_bitmap2(global_ctx->block_ea_map,
-                                                   blk)) {
-                       retval = ea_refcount_store(global_ctx->ea_inode_refs,
-                                                  blk, count);
-                       if (retval)
-                               return retval;
-               }
+               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;
@@ -2988,8 +3289,7 @@ static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
                                continue;
 
                        if (!global_ctx->refcount_extra) {
-                               retval = ea_refcount_create(0,
-                                               &global_ctx->refcount_extra);
+                               retval = ea_refcount_create(&global_ctx->refcount_extra);
                                if (retval)
                                        return retval;
                        }
@@ -3022,8 +3322,7 @@ static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
                                return retval;
                        /* Ooops, this EA was referenced more than it stated */
                        if (!global_ctx->refcount_extra) {
-                               retval = ea_refcount_create(0,
-                                               &global_ctx->refcount_extra);
+                               retval = ea_refcount_create(&global_ctx->refcount_extra);
                                if (retval)
                                        return retval;
                        }
@@ -3095,6 +3394,12 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
 
        e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
        e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx);
+       retval = e2fsck_pass1_merge_encrypted_info(global_ctx, thread_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, 0,
+                       _("while merging encrypted info\n"));
+               return retval;
+       }
 
        retval = e2fsck_pass1_merge_fs(global_ctx->fs, thread_ctx->fs);
        if (retval) {
@@ -3131,6 +3436,9 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
 
        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);
@@ -3138,11 +3446,6 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
                return retval;
 
        retval = e2fsck_pass1_merge_bitmap(global_fs,
-                               &thread_ctx->inode_bad_map,
-                               &global_ctx->inode_bad_map);
-       if (retval)
-               return retval;
-       retval = e2fsck_pass1_merge_bitmap(global_fs,
                                        &thread_ctx->inode_dir_map,
                                        &global_ctx->inode_dir_map);
        if (retval)
@@ -3173,6 +3476,12 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
        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;
@@ -3219,14 +3528,14 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
        return retval;
 }
 
-static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
-                                    e2fsck_t global_ctx)
+static int e2fsck_pass1_threads_join(e2fsck_t global_ctx)
 {
-       errcode_t                        rc;
-       errcode_t                        ret = 0;
-       int                              i;
-       struct e2fsck_thread_info       *pinfo;
-       int                              num_threads = global_ctx->fs_num_threads;
+       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;
@@ -3252,6 +3561,7 @@ static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
                }
        }
        free(infos);
+       global_ctx->infos = NULL;
 
        return ret;
 }
@@ -3292,7 +3602,7 @@ static void *e2fsck_pass1_thread(void *arg)
 out:
        if (thread_ctx->options & E2F_OPT_MULTITHREAD)
                log_out(thread_ctx,
-                       _("Scanned group range [%lu, %lu), inodes %lu\n"),
+                       _("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);
@@ -3336,8 +3646,7 @@ static dgrp_t ext2fs_get_avg_group(ext2_filsys fs)
 #endif
 }
 
-static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
-                                     e2fsck_t global_ctx)
+static int e2fsck_pass1_threads_start(e2fsck_t global_ctx)
 {
        struct e2fsck_thread_info       *infos;
        pthread_attr_t                   attr;
@@ -3347,7 +3656,7 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
        int                              i;
        e2fsck_t                         thread_ctx;
        dgrp_t                           average_group;
-       int                              num_threads = global_ctx->fs_num_threads;
+       int num_threads = global_ctx->pfs_num_threads;
 #ifdef DEBUG_THREADS
        struct e2fsck_thread_debug       thread_debug =
                {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
@@ -3370,6 +3679,7 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
                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++) {
@@ -3410,26 +3720,24 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
        }
 
        if (retval) {
-               e2fsck_pass1_threads_join(infos, global_ctx);
+               e2fsck_pass1_threads_join(global_ctx);
                return retval;
        }
-       *pinfo = infos;
        return 0;
 }
 
 static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
 {
-       struct e2fsck_thread_info *infos = NULL;
        errcode_t retval;
 
-       retval = e2fsck_pass1_threads_start(&infos, global_ctx);
+       retval = e2fsck_pass1_threads_start(global_ctx);
        if (retval) {
                com_err(global_ctx->program_name, retval,
                        _("while starting pass1 threads\n"));
                goto out_abort;
        }
 
-       retval = e2fsck_pass1_threads_join(infos, global_ctx);
+       retval = e2fsck_pass1_threads_join(global_ctx);
        if (retval) {
                com_err(global_ctx->program_name, retval,
                        _("while joining pass1 threads\n"));
@@ -3451,8 +3759,7 @@ void e2fsck_pass1(e2fsck_t ctx)
        if (retval)
                return;
 #ifdef HAVE_PTHREAD
-       if (ctx->fs_num_threads > 1 ||
-           ctx->options & E2F_OPT_MULTITHREAD) {
+       if (ctx->pfs_num_threads > 1 || ctx->options & E2F_OPT_MULTITHREAD) {
                need_single = 0;
                e2fsck_pass1_multithread(ctx);
        }
@@ -3476,7 +3783,10 @@ 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;
@@ -3485,8 +3795,28 @@ static errcode_t scan_callback(ext2_filsys fs,
                       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;
 
@@ -3580,27 +3910,43 @@ 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, ext2_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_casefolded_dir(e2fsck_t ctx, ext2_ino_t ino)
@@ -3744,11 +4090,17 @@ 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;
@@ -3801,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;
        }
 
@@ -3821,8 +4174,7 @@ 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_orig);
+               pctx->errcode = ea_refcount_create(&ctx->refcount_orig);
                if (pctx->errcode) {
                        pctx->num = 1;
                        fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
@@ -3830,7 +4182,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
                        return 0;
                }
 
-               pctx->errcode = ea_refcount_create(0, &ctx->refcount);
+               pctx->errcode = ea_refcount_create(&ctx->refcount);
                if (pctx->errcode) {
                        pctx->num = 1;
                        fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
@@ -3864,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);
@@ -3894,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)) ||
@@ -4006,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;
@@ -4019,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:
@@ -4085,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))
@@ -4168,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
@@ -4342,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,
@@ -4411,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;
                        }
@@ -4890,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--;
@@ -5023,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");
 }
 
@@ -5175,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;
        }