Whamcloud - gitweb
e2fsck: copy fs when using multi-thread fsck
[tools/e2fsprogs.git] / e2fsck / pass1.c
index 524577a..d35abbe 100644 (file)
  *     - A bitmap of which inodes have bad fields.     (inode_bad_map)
  *     - A bitmap of which inodes are in bad blocks.   (inode_bb_map)
  *     - A bitmap of which inodes are imagic inodes.   (inode_imagic_map)
+ *     - A bitmap of which inodes are casefolded.      (inode_casefold_map)
  *     - A bitmap of which blocks are in use.          (block_found_map)
  *     - A bitmap of which blocks are in use by two inodes     (block_dup_map)
  *     - The data blocks of the directory inodes.      (dir_map)
  *     - Ref counts for ea_inodes.                     (ea_inode_refs)
+ *     - The encryption policy ID of each encrypted inode. (encrypted_files)
  *
  * Pass 1 is designed to stash away enough information so that the
  * other passes should not need to read in the inode information
@@ -48,6 +50,8 @@
 
 #include "e2fsck.h"
 #include <ext2fs/ext2_ext_attr.h>
+/* todo remove this finally */
+#include <ext2fs/ext2fsP.h>
 #include <e2p/e2p.h>
 
 #include "problem.h"
@@ -78,7 +82,7 @@ static void mark_table_blocks(e2fsck_t ctx);
 static void alloc_bb_map(e2fsck_t ctx);
 static void alloc_imagic_map(e2fsck_t ctx);
 static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
-static void add_encrypted_dir(e2fsck_t ctx, ino_t ino);
+static void add_casefolded_dir(e2fsck_t ctx, ino_t ino);
 static void handle_fs_bad_blocks(e2fsck_t ctx);
 static void process_inodes(e2fsck_t ctx, char *block_buf);
 static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
@@ -129,16 +133,6 @@ static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
                            EXT2_MIN_BLOCK_LOG_SIZE + 1];
 
 /*
- * Free all memory allocated by pass1 in preparation for restarting
- * things.
- */
-static void unwind_pass1(ext2_filsys fs EXT2FS_ATTR((unused)))
-{
-       ext2fs_free_mem(&inodes_to_process);
-       inodes_to_process = 0;
-}
-
-/*
  * Check to make sure a device inode is real.  Returns 1 if the device
  * checks out, 0 if not.
  *
@@ -397,13 +391,13 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
 static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
                              struct ext2_ext_attr_entry *first, void *end)
 {
-       struct ext2_ext_attr_entry *entry;
+       struct ext2_ext_attr_entry *entry = first;
+       struct ext2_ext_attr_entry *np = EXT2_EXT_ATTR_NEXT(entry);
 
-       for (entry = first;
-            (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry);
-            entry = EXT2_EXT_ATTR_NEXT(entry)) {
+       while ((void *) entry < end && (void *) np < end &&
+              !EXT2_EXT_IS_LAST_ENTRY(entry)) {
                if (!entry->e_value_inum)
-                       continue;
+                       goto next;
                if (!ctx->ea_inode_refs) {
                        pctx->errcode = ea_refcount_create(0,
                                                           &ctx->ea_inode_refs);
@@ -416,6 +410,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);
        }
 }
 
@@ -1154,7 +1151,22 @@ static int quota_inum_is_reserved(ext2_filsys fs, ext2_ino_t ino)
        return 0;
 }
 
-void e2fsck_pass1(e2fsck_t ctx)
+static int e2fsck_should_abort(e2fsck_t ctx)
+{
+       e2fsck_t global_ctx;
+
+       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               return 1;
+
+       if (ctx->global_ctx) {
+               global_ctx = ctx->global_ctx;
+               if (global_ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       return 1;
+       }
+       return 0;
+}
+
+void e2fsck_pass1_thread(e2fsck_t ctx)
 {
        int     i;
        __u64   max_sizes;
@@ -1171,7 +1183,8 @@ void e2fsck_pass1(e2fsck_t ctx)
        struct          scan_callback_struct scan_struct;
        struct ext2_super_block *sb = ctx->fs->super;
        const char      *old_op;
-       int             imagic_fs, extent_fs, inlinedata_fs;
+       const char      *eop_next_inode = _("getting next inode from scan");
+       int             imagic_fs, extent_fs, inlinedata_fs, casefold_fs;
        int             low_dtime_check = 1;
        unsigned int    inode_size = EXT2_INODE_SIZE(fs->super);
        unsigned int    bufsize;
@@ -1217,6 +1230,7 @@ void e2fsck_pass1(e2fsck_t ctx)
        imagic_fs = ext2fs_has_feature_imagic_inodes(sb);
        extent_fs = ext2fs_has_feature_extents(sb);
        inlinedata_fs = ext2fs_has_feature_inline_data(sb);
+       casefold_fs = ext2fs_has_feature_casefold(sb);
 
        /*
         * Allocate bitmaps structures
@@ -1268,6 +1282,20 @@ void e2fsck_pass1(e2fsck_t ctx)
                ctx->flags |= E2F_FLAG_ABORT;
                return;
        }
+       if (casefold_fs) {
+               pctx.errcode =
+                       e2fsck_allocate_inode_bitmap(fs,
+                                                    _("inode casefold map"),
+                                                    EXT2FS_BMAP64_RBTREE,
+                                                    "inode_casefold_map",
+                                                    &ctx->inode_casefold_map);
+               if (pctx.errcode) {
+                       pctx.num = 1;
+                       fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return;
+               }
+       }
        pctx.errcode = e2fsck_setup_icount(ctx, "inode_link_info", 0, NULL,
                                           &ctx->inode_link_info);
        if (pctx.errcode) {
@@ -1340,8 +1368,10 @@ void e2fsck_pass1(e2fsck_t ctx)
        if (ctx->progress && ((ctx->progress)(ctx, 1, 0,
                                              ctx->fs->group_desc_count)))
                goto endit;
-       if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
-           (fs->super->s_mtime < fs->super->s_inodes_count) ||
+       if ((fs->super->s_wtime &&
+            fs->super->s_wtime < fs->super->s_inodes_count) ||
+           (fs->super->s_mtime &&
+            fs->super->s_mtime < fs->super->s_inodes_count) ||
            (fs->super->s_mkfs_time &&
             fs->super->s_mkfs_time < fs->super->s_inodes_count))
                low_dtime_check = 0;
@@ -1360,13 +1390,13 @@ void e2fsck_pass1(e2fsck_t ctx)
                        if (e2fsck_mmp_update(fs))
                                fatal_error(ctx, 0);
                }
-               old_op = ehandler_operation(_("getting next inode from scan"));
+               old_op = ehandler_operation(eop_next_inode);
                pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
                                                          inode, inode_size);
                if (ino > ino_threshold)
                        pass1_readahead(ctx, &ra_group, &ino_threshold);
                ehandler_operation(old_op);
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               if (e2fsck_should_abort(ctx))
                        goto endit;
                if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
                        /*
@@ -1388,17 +1418,9 @@ void e2fsck_pass1(e2fsck_t ctx)
                                        fix_problem(ctx, PR_1_ISCAN_ERROR,
                                                    &pctx);
                                        ctx->flags |= E2F_FLAG_ABORT;
-                                       goto endit;
-                               }
-                               err = ext2fs_inode_scan_goto_blockgroup(scan,
-                                                                       0);
-                               if (err) {
-                                       fix_problem(ctx, PR_1_ISCAN_ERROR,
-                                                   &pctx);
-                                       ctx->flags |= E2F_FLAG_ABORT;
-                                       goto endit;
-                               }
-                               continue;
+                               } else
+                                       ctx->flags |= E2F_FLAG_RESTART;
+                               goto endit;
                        }
                        if (!ctx->inode_bb_map)
                                alloc_bb_map(ctx);
@@ -1480,6 +1502,15 @@ void e2fsck_pass1(e2fsck_t ctx)
                        continue;
                }
 
+               if ((inode->i_flags & EXT4_CASEFOLD_FL) &&
+                   ((!LINUX_S_ISDIR(inode->i_mode) &&
+                     fix_problem(ctx, PR_1_CASEFOLD_NONDIR, &pctx)) ||
+                    (!casefold_fs &&
+                     fix_problem(ctx, PR_1_CASEFOLD_FEATURE, &pctx)))) {
+                       inode->i_flags &= ~EXT4_CASEFOLD_FL;
+                       e2fsck_write_inode(ctx, ino, inode, "pass1");
+               }
+
                /* Conflicting inlinedata/extents inode flags? */
                if ((inode->i_flags & EXT4_INLINE_DATA_FL) &&
                    (inode->i_flags & EXT4_EXTENTS_FL)) {
@@ -1871,12 +1902,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++;
@@ -1958,7 +1996,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                if (process_inode_count >= ctx->process_inode_size) {
                        process_inodes(ctx, block_buf);
 
-                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       if (e2fsck_should_abort(ctx))
                                goto endit;
                }
        }
@@ -2005,6 +2043,9 @@ void e2fsck_pass1(e2fsck_t ctx)
                ctx->block_ea_map = 0;
        }
 
+       /* We don't need the encryption policy => ID map any more */
+       destroy_encryption_policy_map(ctx);
+
        if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
                clear_problem_context(&pctx);
                pctx.errcode = ext2fs_create_resize_inode(fs);
@@ -2034,10 +2075,22 @@ void e2fsck_pass1(e2fsck_t ctx)
                 * master superblock.
                 */
                ctx->use_superblock = 0;
-               unwind_pass1(fs);
                goto endit;
        }
 
+       if (ctx->large_dirs && !ext2fs_has_feature_largedir(fs->super)) {
+               if (fix_problem(ctx, PR_2_FEATURE_LARGE_DIRS, &pctx)) {
+                       ext2fs_set_feature_largedir(fs->super);
+                       fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+                       ext2fs_mark_super_dirty(fs);
+               }
+               if (fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
+                   fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
+                       ext2fs_update_dynamic_rev(fs);
+                       ext2fs_mark_super_dirty(fs);
+               }
+       }
+
        if (ctx->block_dup_map) {
                if (ctx->options & E2F_OPT_PREEN) {
                        clear_problem_context(&pctx);
@@ -2046,9 +2099,10 @@ void e2fsck_pass1(e2fsck_t ctx)
                e2fsck_pass1_dupblocks(ctx, block_buf);
        }
        ctx->flags |= E2F_FLAG_ALLOC_OK;
-       ext2fs_free_mem(&inodes_to_process);
 endit:
        e2fsck_use_inode_shortcuts(ctx, 0);
+       ext2fs_free_mem(&inodes_to_process);
+       inodes_to_process = 0;
 
        if (scan)
                ext2fs_close_inode_scan(scan);
@@ -2068,6 +2122,120 @@ endit:
        else
                ctx->invalid_bitmaps++;
 }
+
+static void e2fsck_pass1_copy_fs(ext2_filsys dest, ext2_filsys src)
+{
+       memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+       if (dest->dblist)
+               dest->dblist->fs = dest;
+       if (dest->inode_map)
+               dest->inode_map->fs = dest;
+       if (dest->block_map)
+               dest->block_map->fs = dest;
+}
+
+static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx)
+{
+       errcode_t       retval;
+       e2fsck_t        thread_context;
+       ext2_filsys     thread_fs;
+       ext2_filsys     global_fs = global_ctx->fs;
+
+       retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context);
+       if (retval) {
+               com_err(global_ctx->program_name, retval, "while allocating memory");
+               return retval;
+       }
+       memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
+       thread_context->global_ctx = global_ctx;
+
+       retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &thread_fs);
+       if (retval) {
+               com_err(global_ctx->program_name, retval, "while allocating memory");
+               goto out_context;
+       }
+
+       e2fsck_pass1_copy_fs(thread_fs, global_fs);
+       thread_fs->priv_data = thread_context;
+
+       thread_context->fs = thread_fs;
+       *thread_ctx = thread_context;
+       return 0;
+out_context:
+       ext2fs_free_mem(&thread_context);
+       return retval;
+}
+
+static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       int             flags = global_ctx->flags;
+       ext2_filsys     thread_fs = thread_ctx->fs;
+       ext2_filsys     global_fs = global_ctx->fs;
+#ifdef HAVE_SETJMP_H
+       jmp_buf         old_jmp;
+
+       memcpy(old_jmp, global_ctx->abort_loc, sizeof(jmp_buf));
+#endif
+       memcpy(global_ctx, thread_ctx, sizeof(struct e2fsck_struct));
+#ifdef HAVE_SETJMP_H
+       memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf));
+#endif
+       /* Keep the global singal flags*/
+       global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
+                            (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
+
+       e2fsck_pass1_copy_fs(global_fs, thread_fs);
+       global_fs->priv_data = global_ctx;
+       global_ctx->fs = global_fs;
+
+       ext2fs_free_mem(&thread_ctx->fs);
+       ext2fs_free_mem(&thread_ctx);
+       return 0;
+}
+
+void e2fsck_pass1_multithread(e2fsck_t ctx)
+{
+       errcode_t       retval;
+       e2fsck_t        thread_ctx;
+
+       retval = e2fsck_pass1_thread_prepare(ctx, &thread_ctx);
+       if (retval) {
+               com_err(ctx->program_name, 0,
+                       _("while preparing pass1 thread\n"));
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
+
+#ifdef HAVE_SETJMP_H
+       /*
+        * When fatal_error() happens, jump to here. The thread
+        * context's flags will be saved, but its abort_loc will
+        * be overwritten by original jump buffer for the later
+        * tests.
+        */
+       if (setjmp(thread_ctx->abort_loc)) {
+               thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+               e2fsck_pass1_thread_join(ctx, thread_ctx);
+               return;
+       }
+       thread_ctx->flags |= E2F_FLAG_SETJMP_OK;
+#endif
+
+       e2fsck_pass1_thread(thread_ctx);
+       retval = e2fsck_pass1_thread_join(ctx, thread_ctx);
+       if (retval) {
+               com_err(ctx->program_name, 0,
+                       _("while joining pass1 thread\n"));
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
+}
+
+void e2fsck_pass1(e2fsck_t ctx)
+{
+       e2fsck_pass1_multithread(ctx);
+}
+
 #undef FINISH_INODE_LOOP
 
 /*
@@ -2130,7 +2298,7 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
                ehandler_operation(buf);
                check_blocks(ctx, &pctx, block_buf,
                             &inodes_to_process[i].ea_ibody_quota);
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               if (e2fsck_should_abort(ctx))
                        break;
        }
        ctx->stashed_inode = old_stashed_inode;
@@ -2189,20 +2357,20 @@ static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
        ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino);
 }
 
-static void add_encrypted_dir(e2fsck_t ctx, ino_t ino)
+static void add_casefolded_dir(e2fsck_t ctx, ino_t ino)
 {
        struct          problem_context pctx;
 
-       if (!ctx->encrypted_dirs) {
-               pctx.errcode = ext2fs_u32_list_create(&ctx->encrypted_dirs, 0);
+       if (!ctx->casefolded_dirs) {
+               pctx.errcode = ext2fs_u32_list_create(&ctx->casefolded_dirs, 0);
                if (pctx.errcode)
                        goto error;
        }
-       pctx.errcode = ext2fs_u32_list_add(ctx->encrypted_dirs, ino);
+       pctx.errcode = ext2fs_u32_list_add(ctx->casefolded_dirs, ino);
        if (pctx.errcode == 0)
                return;
 error:
-       fix_problem(ctx, PR_1_ALLOCATE_ENCRYPTED_DIRLIST, &pctx);
+       fix_problem(ctx, PR_1_ALLOCATE_CASEFOLDED_DIRLIST, &pctx);
        /* Should never get here */
        ctx->flags |= E2F_FLAG_ABORT;
 }
@@ -2519,8 +2687,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;
@@ -2664,18 +2833,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;
 }
 
@@ -2773,7 +2973,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 +3023,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 +3032,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;
@@ -2855,7 +3058,20 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                                return;
                        failed_csum = 0;
                }
-
+#ifdef CONFIG_DEVELOPER_FEATURES
+               if (try_repairs && !is_dir && problem == 0 &&
+                   (ctx->options & E2F_OPT_CLEAR_UNINIT) &&
+                   (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+                   fix_problem(ctx, PR_1_CLEAR_UNINIT_EXTENT, pctx)) {
+                       extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT;
+                       pb->inode_modified = 1;
+                       pctx->errcode = ext2fs_extent_replace(ehandle, 0,
+                                                             &extent);
+                       if (pctx->errcode)
+                               return;
+                       failed_csum = 0;
+               }
+#endif
                if (try_repairs && problem) {
 report_problem:
                        if (fix_problem(ctx, problem, pctx)) {
@@ -2945,7 +3161,12 @@ report_problem:
                        if (extent.e_lblk != lblk) {
                                struct ext2_extent_info e_info;
 
-                               ext2fs_extent_get_info(ehandle, &e_info);
+                               pctx->errcode = ext2fs_extent_get_info(ehandle,
+                                                                      &e_info);
+                               if (pctx->errcode) {
+                                       pctx->str = "ext2fs_extent_get_info";
+                                       return;
+                               }
                                pctx->blk = lblk;
                                pctx->blk2 = extent.e_lblk;
                                pctx->num = e_info.curr_level - 1;
@@ -3305,7 +3526,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);
        }
@@ -3360,7 +3581,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);
@@ -3414,11 +3635,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;
@@ -3432,11 +3655,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;
@@ -3446,7 +3669,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) &&
@@ -3469,8 +3691,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)) {
@@ -3642,13 +3862,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)
@@ -3841,7 +4062,7 @@ static int process_bad_block(ext2_filsys fs,
                                *block_nr = 0;
                                return BLOCK_CHANGED;
                        }
-                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       if (e2fsck_should_abort(ctx))
                                return BLOCK_ABORT;
                } else
                        mark_block_used(ctx, blk);
@@ -3938,7 +4159,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;
        }
@@ -3977,7 +4198,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);