]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
Merge branch 'maint' into next
authorTheodore Ts'o <tytso@mit.edu>
Thu, 12 Dec 2013 20:39:14 +0000 (15:39 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 12 Dec 2013 20:39:14 +0000 (15:39 -0500)
Conflicts:
lib/ext2fs/newdir.c

16 files changed:
1  2 
debugfs/debugfs.c
debugfs/dump.c
debugfs/set_fields.c
e2fsck/journal.c
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/pass3.c
e2fsck/problem.c
e2fsck/unix.c
lib/ext2fs/ext2_err.et.in
lib/ext2fs/ext2fs.h
lib/ext2fs/ext2fsP.h
lib/ext2fs/gen_bitmap64.c
lib/ext2fs/newdir.c
misc/dumpe2fs.c
misc/mke2fs.c

diff --combined debugfs/debugfs.c
index 4ecf4749c62673e2b11af5117aa024937d5e7d65,6982ff6ab589bd1cfcd2a67570bcac17f99ec310..578d5773371a25e2840cbec349c9bd49b68fafec
@@@ -181,8 -181,7 +181,7 @@@ void do_open_filesys(int argc, char **a
                                return;
                        break;
                case 's':
-                       superblock = parse_ulong(optarg, argv[0],
-                                                "superblock number", &err);
+                       err = strtoblk(argv[0], optarg, &superblock);
                        if (err)
                                return;
                        break;
@@@ -278,14 -277,17 +277,17 @@@ void do_init_filesys(int argc, char **a
        struct ext2_super_block param;
        errcode_t       retval;
        int             err;
+       blk64_t         blocks;
  
        if (common_args_process(argc, argv, 3, 3, "initialize",
-                               "<device> <blocksize>", CHECK_FS_NOTOPEN))
+                               "<device> <blocks>", CHECK_FS_NOTOPEN))
                return;
  
        memset(&param, 0, sizeof(struct ext2_super_block));
-       ext2fs_blocks_count_set(&param, parse_ulong(argv[2], argv[0],
-                                                   "blocks count", &err));
+       err = strtoblk(argv[0], argv[2], &blocks);
+       if (err)
+               return;
+       ext2fs_blocks_count_set(&param, blocks);
        if (err)
                return;
        retval = ext2fs_initialize(argv[1], 0, &param,
@@@ -371,7 -373,8 +373,7 @@@ void do_show_super_stats(int argc, cha
                return;
        }
  
 -      gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(current_fs->super,
 -                                            EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
 +      gdt_csum = ext2fs_has_group_desc_csum(current_fs);
        for (i = 0; i < current_fs->group_desc_count; i++) {
                fprintf(out, " Group %2d: block bitmap at %llu, "
                        "inode bitmap at %llu, "
@@@ -816,19 -819,6 +818,19 @@@ void internal_dump_inode(FILE *out, con
        if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
                internal_dump_inode_extra(out, prefix, inode_num,
                                          (struct ext2_inode_large *) inode);
 +      if (current_fs->super->s_creator_os == EXT2_OS_LINUX &&
 +          current_fs->super->s_feature_ro_compat &
 +              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
 +              __u32 crc = inode->i_checksum_lo;
 +              if (is_large_inode &&
 +                  large_inode->i_extra_isize >=
 +                              (offsetof(struct ext2_inode_large,
 +                                        i_checksum_hi) -
 +                               EXT2_GOOD_OLD_INODE_SIZE))
 +                      crc |= ((__u32)large_inode->i_checksum_hi) << 16;
 +              fprintf(out, "Inode checksum: 0x%08x\n", crc);
 +      }
 +
        if (LINUX_S_ISLNK(inode->i_mode) && ext2fs_inode_data_blocks(current_fs,inode) == 0)
                fprintf(out, "%sFast_link_dest: %.*s\n", prefix,
                        (int) inode->i_size, (char *)inode->i_block);
@@@ -1602,16 -1592,17 +1604,17 @@@ static errcode_t copy_file(int fd, ext2
        if (retval)
                return retval;
  
-       if (!(buf = (char *) malloc(bufsize))){
-               com_err("copy_file", errno, "can't allocate buffer\n");
-               return;
+       retval = ext2fs_get_mem(bufsize, &buf);
+       if (retval) {
+               com_err("copy_file", retval, "can't allocate buffer\n");
+               return retval;
        }
  
        /* This is used for checking whether the whole block is zero */
        retval = ext2fs_get_memzero(bufsize, &zero_buf);
        if (retval) {
                com_err("copy_file", retval, "can't allocate buffer\n");
-               free(buf);
+               ext2fs_free_mem(&buf);
                return retval;
        }
  
                        ptr += written;
                }
        }
-       free(buf);
+       ext2fs_free_mem(&buf);
        ext2fs_free_mem(&zero_buf);
        retval = ext2fs_file_close(e2_file);
        return retval;
  
  fail:
-       free(buf);
+       ext2fs_free_mem(&buf);
        ext2fs_free_mem(&zero_buf);
        (void) ext2fs_file_close(e2_file);
        return retval;
@@@ -1981,9 -1972,9 +1984,9 @@@ static int rmdir_proc(ext2_ino_t dir EX
  
        if (dirent->inode == 0)
                return 0;
 -      if (((dirent->name_len&0xFF) == 1) && (dirent->name[0] == '.'))
 +      if ((ext2fs_dirent_name_len(dirent) == 1) && (dirent->name[0] == '.'))
                return 0;
 -      if (((dirent->name_len&0xFF) == 2) && (dirent->name[0] == '.') &&
 +      if ((ext2fs_dirent_name_len(dirent) == 2) && (dirent->name[0] == '.') &&
            (dirent->name[1] == '.')) {
                rds->parent = dirent->inode;
                return 0;
@@@ -2109,11 -2100,13 +2112,13 @@@ void do_bmap(int argc, char *argv[]
        ino = string_to_inode(argv[1]);
        if (!ino)
                return;
-       blk = parse_ulong(argv[2], argv[0], "logical_block", &err);
+       err = strtoblk(argv[0], argv[2], &blk);
+       if (err)
+               return;
  
        errcode = ext2fs_bmap2(current_fs, ino, 0, 0, 0, blk, 0, &pblk);
        if (errcode) {
-               com_err("argv[0]", errcode,
+               com_err(argv[0], errcode,
                        "while mapping logical block %llu\n", blk);
                return;
        }
@@@ -2254,10 -2247,14 +2259,14 @@@ void do_punch(int argc, char *argv[]
        ino = string_to_inode(argv[1]);
        if (!ino)
                return;
-       start = parse_ulong(argv[2], argv[0], "logical_block", &err);
-       if (argc == 4)
-               end = parse_ulong(argv[3], argv[0], "logical_block", &err);
-       else
+       err = strtoblk(argv[0], argv[2], &start);
+       if (err)
+               return;
+       if (argc == 4) {
+               err = strtoblk(argv[0], argv[3], &end);
+               if (err)
+                       return;
+       } else
                end = ~0;
  
        errcode = ext2fs_punch(current_fs, ino, 0, 0, start, end);
@@@ -2316,7 -2313,6 +2325,7 @@@ try_again
  
  void do_dump_mmp(int argc EXT2FS_ATTR((unused)), char *argv[])
  {
 +#if CONFIG_MMP
        struct ext2_super_block *sb;
        struct mmp_struct *mmp_s;
        time_t t;
        fprintf(stdout, "node_name: %s\n", mmp_s->mmp_nodename);
        fprintf(stdout, "device_name: %s\n", mmp_s->mmp_bdevname);
        fprintf(stdout, "magic: 0x%x\n", mmp_s->mmp_magic);
 +      fprintf(stdout, "checksum: 0x%08x\n", mmp_s->mmp_checksum);
 +#else
 +      fprintf(stdout, "MMP is unsupported, please recompile with "
 +                      "--enable-mmp\n");
 +#endif
  }
  
  static int source_file(const char *cmd_file, int ss_idx)
@@@ -2424,9 -2415,9 +2433,9 @@@ int main(int argc, char **argv
        int             catastrophic = 0;
        char            *data_filename = 0;
  #ifdef READ_ONLY
 -      const char      *opt_string = "icR:f:b:s:Vd:D";
 +      const char      *opt_string = "nicR:f:b:s:Vd:D";
  #else
 -      const char      *opt_string = "iwcR:f:b:s:Vd:D";
 +      const char      *opt_string = "niwcR:f:b:s:Vd:D";
  #endif
  
        if (debug_prog_name == 0)
                case 'i':
                        open_flags |= EXT2_FLAG_IMAGE_FILE;
                        break;
 +              case 'n':
 +                      open_flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
 +                      break;
  #ifndef READ_ONLY
                case 'w':
                        open_flags |= EXT2_FLAG_RW;
                                                "block size", 0);
                        break;
                case 's':
-                       superblock = parse_ulong(optarg, argv[0],
-                                                "superblock number", 0);
+                       retval = strtoblk(argv[0], optarg, &superblock);
+                       if (retval) {
+                               com_err(argv[0], retval, 0, debug_prog_name);
+                               return 1;
+                       }
                        break;
                case 'c':
                        catastrophic = 1;
diff --combined debugfs/dump.c
index c75b9f15480c79664e43118505be66c1da228924,51bc734aae938f55e21595f4ae34ca304c7fe99a..952a752d5ab870f2d9863d5ee2dcaa55d7ac47ed
@@@ -143,8 -143,6 +143,6 @@@ static void dump_file(const char *cmdna
  
        if (preserve)
                fix_perms("dump_file", &inode, fd, outname);
-       else if (fd != 1)
-               close(fd);
  
        return;
  }
@@@ -191,6 -189,11 +189,11 @@@ void do_dump(int argc, char **argv
        }
  
        dump_file(argv[0], inode, fd, preserve, out_fn);
+       if (close(fd) != 0) {
+               com_err(argv[0], errno, "while closing %s for dump_inode",
+                       out_fn);
+               return;
+       }
  
        return;
  }
@@@ -273,6 -276,10 +276,10 @@@ static void rdump_inode(ext2_ino_t ino
                        goto errout;
                }
                dump_file("rdump", ino, fd, 1, fullname);
+               if (close(fd) != 0) {
+                       com_err("rdump", errno, "while dumping %s", fullname);
+                       goto errout;
+               }
        }
        else if (LINUX_S_ISDIR(inode->i_mode) && strcmp(name, ".") && strcmp(name, "..")) {
                errcode_t retval;
@@@ -308,7 -315,7 +315,7 @@@ static int rdump_dirent(struct ext2_dir
        const char *dumproot = private;
        struct ext2_inode inode;
  
 -      thislen = dirent->name_len & 0xFF;
 +      thislen = ext2fs_dirent_name_len(dirent);
        strncpy(name, dirent->name, thislen);
        name[thislen] = 0;
  
diff --combined debugfs/set_fields.c
index aad1cd84c3c47a1b3f7d9efc9f30999496dd2bf4,064de395d4f60ba38c690f3d1a29e6ca71d7cb95..1e5727754dbff2c57cfa21ae430a2b85c03c71f4
@@@ -151,7 -151,6 +151,7 @@@ static struct field_set_info super_fiel
        { "grp_quota_inum", &set_sb.s_grp_quota_inum, NULL, 4, parse_uint },
        { "overhead_blocks", &set_sb.s_overhead_blocks, NULL, 4, parse_uint },
        { "checksum", &set_sb.s_checksum, NULL, 4, parse_uint },
 +      { "checksum_type", &set_sb.s_checksum_type, NULL, 1, parse_uint },
        { 0, 0, 0, 0 }
  };
  
@@@ -256,7 -255,6 +256,7 @@@ static struct field_set_info mmp_fields
        { "bdevname", &set_mmp.mmp_bdevname, NULL, sizeof(set_mmp.mmp_bdevname),
                parse_string },
        { "check_interval", &set_mmp.mmp_check_interval, NULL, 2, parse_uint },
 +      { "checksum", &set_mmp.mmp_checksum, NULL, 4, parse_uint },
  };
  
  static int check_suffix(const char *field)
@@@ -768,7 -766,6 +768,7 @@@ static errcode_t parse_mmp_clear(struc
  
  void do_set_mmp_value(int argc, char *argv[])
  {
 +#ifdef CONFIG_MMP
        const char *usage = "<field> <value>\n"
                "\t\"set_mmp_value -l\" will list the names of "
                "MMP fields\n\twhich can be set.";
                if (retval) {
                        com_err(argv[0], retval, "reading MMP block %llu.\n",
                                (long long)current_fs->super->s_mmp_block);
-                       ext2fs_free_mem(mmp_s);
+                       ext2fs_free_mem(&mmp_s);
                        return;
                }
                current_fs->mmp_buf = mmp_s;
                                 &set_mmp);
                *mmp_s = set_mmp;
        }
 +#else
 +      fprintf(stdout, "MMP is unsupported, please recompile with "
 +                      "--enable-mmp\n");
 +#endif
  }
  
diff --combined e2fsck/journal.c
index e3f80bc5fa926fd32cf9acff931d62d8039342c7,6483481f4d651acb4c924f02de84346e7371b2a5..22f06e79d89e9fc5b45183e645c1e15994eeab0e
@@@ -40,56 -40,6 +40,56 @@@ static int bh_count = 0
   */
  #undef USE_INODE_IO
  
 +/* Checksumming functions */
 +static int e2fsck_journal_verify_csum_type(journal_t *j,
 +                                         journal_superblock_t *jsb)
 +{
 +      if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
 +              return 1;
 +
 +      return jsb->s_checksum_type == JBD2_CRC32C_CHKSUM;
 +}
 +
 +static __u32 e2fsck_journal_sb_csum(journal_superblock_t *jsb)
 +{
 +      __u32 crc, old_crc;
 +
 +      old_crc = jsb->s_checksum;
 +      jsb->s_checksum = 0;
 +      crc = ext2fs_crc32c_le(~0, (unsigned char *)jsb,
 +                             sizeof(journal_superblock_t));
 +      jsb->s_checksum = old_crc;
 +
 +      return crc;
 +}
 +
 +static int e2fsck_journal_sb_csum_verify(journal_t *j,
 +                                       journal_superblock_t *jsb)
 +{
 +      __u32 provided, calculated;
 +
 +      if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
 +              return 1;
 +
 +      provided = ext2fs_be32_to_cpu(jsb->s_checksum);
 +      calculated = e2fsck_journal_sb_csum(jsb);
 +
 +      return provided == calculated;
 +}
 +
 +static errcode_t e2fsck_journal_sb_csum_set(journal_t *j,
 +                                          journal_superblock_t *jsb)
 +{
 +      __u32 crc;
 +
 +      if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
 +              return 0;
 +
 +      crc = e2fsck_journal_sb_csum(jsb);
 +      jsb->s_checksum = ext2fs_cpu_to_be32(crc);
 +      return 0;
 +}
 +
  /* Kernel compatibility functions for handling the journal.  These allow us
   * to use the recovery.c file virtually unchanged from the kernel, so we
   * don't have to do much to keep kernel and user recovery in sync.
@@@ -624,15 -574,6 +624,15 @@@ static errcode_t e2fsck_journal_load(jo
        if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
                return EXT2_ET_RO_UNSUPP_FEATURE;
  
 +      /* Checksum v1 and v2 are mutually exclusive features. */
 +      if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2) &&
 +          JFS_HAS_COMPAT_FEATURE(journal, JFS_FEATURE_COMPAT_CHECKSUM))
 +              return EXT2_ET_CORRUPT_SUPERBLOCK;
 +
 +      if (!e2fsck_journal_verify_csum_type(journal, jsb) ||
 +          !e2fsck_journal_sb_csum_verify(journal, jsb))
 +              return EXT2_ET_CORRUPT_SUPERBLOCK;
 +
        /* We have now checked whether we know enough about the journal
         * format to be able to proceed safely, so any other checks that
         * fail we should attempt to recover from. */
@@@ -700,7 -641,6 +700,7 @@@ static void e2fsck_journal_reset_super(
        for (i = 0; i < 4; i ++)
                new_seq ^= u.val[i];
        jsb->s_sequence = htonl(new_seq);
 +      e2fsck_journal_sb_csum_set(journal, jsb);
  
        mark_buffer_dirty(journal->j_sb_buffer);
        ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
@@@ -741,7 -681,6 +741,7 @@@ static void e2fsck_journal_release(e2fs
                jsb->s_sequence = htonl(journal->j_transaction_sequence);
                if (reset)
                        jsb->s_start = 0; /* this marks the journal as empty */
 +              e2fsck_journal_sb_csum_set(journal, jsb);
                mark_buffer_dirty(journal->j_sb_buffer);
        }
        brelse(journal->j_sb_buffer);
@@@ -887,7 -826,6 +887,7 @@@ no_has_journal
                ctx->fs->super->s_state |= EXT2_ERROR_FS;
                ext2fs_mark_super_dirty(ctx->fs);
                journal->j_superblock->s_errno = 0;
 +              e2fsck_journal_sb_csum_set(journal, journal->j_superblock);
                mark_buffer_dirty(journal->j_sb_buffer);
        }
  
@@@ -1139,8 -1077,10 +1139,10 @@@ int e2fsck_fix_ext3_journal_hint(e2fsck
        if (!journal_name)
                return 0;
  
-       if (stat(journal_name, &st) < 0)
+       if (stat(journal_name, &st) < 0) {
+               free(journal_name);
                return 0;
+       }
  
        if (st.st_rdev != sb->s_journal_dev) {
                clear_problem_context(&pctx);
diff --combined e2fsck/pass1.c
index 9a5dac7a5508029fef7b1f19130a0f1661cc0c6a,a8237af9edbeb9b32b18f9a4c104b353ea1e4cfe..821239e59c6aa724ce4504e659a06b326e6a8602
@@@ -473,7 -473,7 +473,7 @@@ static void check_is_really_dir(e2fsck_
  
        /* read the first block */
        ehandler_operation(_("reading directory block"));
 -      retval = ext2fs_read_dir_block3(ctx->fs, blk, buf, 0);
 +      retval = ext2fs_read_dir_block4(ctx->fs, blk, buf, 0, pctx->ino);
        ehandler_operation(0);
        if (retval)
                return;
        retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
        if (retval)
                return;
 -      if (((dirent->name_len & 0xFF) != 1) ||
 +      if ((ext2fs_dirent_name_len(dirent) != 1) ||
            (dirent->name[0] != '.') ||
            (dirent->inode != pctx->ino) ||
            (rec_len < 12) ||
        retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
        if (retval)
                return;
 -      if (((dirent->name_len & 0xFF) != 2) ||
 +      if ((ext2fs_dirent_name_len(dirent) != 2) ||
            (dirent->name[0] != '.') ||
            (dirent->name[1] != '.') ||
            (rec_len < 12) ||
@@@ -540,40 -540,6 +540,40 @@@ extern void e2fsck_setup_tdb_icount(e2f
                *ret = 0;
  }
  
 +static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
 +                                          e2fsck_t ctx,
 +                                          struct problem_context *pctx)
 +{
 +      errcode_t retval;
 +      struct ext2_inode_large inode;
 +
 +      /*
 +       * Reread inode.  If we don't see checksum error, then this inode
 +       * has been fixed elsewhere.
 +       */
 +      retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
 +                                      sizeof(inode));
 +      if (retval && retval != EXT2_ET_INODE_CSUM_INVALID)
 +              return retval;
 +      if (!retval)
 +              return 0;
 +
 +      /*
 +       * Checksum still doesn't match.  That implies that the inode passes
 +       * all the sanity checks, so maybe the checksum is simply corrupt.
 +       * See if the user will go for fixing that.
 +       */
 +      if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx))
 +              return 0;
 +
 +      retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
 +                                       sizeof(inode));
 +      if (retval)
 +              return retval;
 +
 +      return 0;
 +}
 +
  void e2fsck_pass1(e2fsck_t ctx)
  {
        int     i;
        int             imagic_fs, extent_fs;
        int             busted_fs_time = 0;
        int             inode_size;
 +      int             failed_csum = 0;
  
        init_resource_track(&rtrack, ctx->fs->io);
        clear_problem_context(&pctx);
        }
        block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
                                                    "block interate buffer");
 -      e2fsck_use_inode_shortcuts(ctx, 1);
 +      if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
 +              e2fsck_use_inode_shortcuts(ctx, 1);
        old_op = ehandler_operation(_("opening inode scan"));
        pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
                                              &scan);
                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);
 +
        while (1) {
                if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
                        if (e2fsck_mmp_update(fs))
                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
                        continue;
                }
 -              if (pctx.errcode) {
 +              if (pctx.errcode &&
 +                  pctx.errcode != EXT2_ET_INODE_CSUM_INVALID) {
                        fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
                        ctx->flags |= E2F_FLAG_ABORT;
                        return;
                pctx.ino = ino;
                pctx.inode = inode;
                ctx->stashed_ino = ino;
 +
 +              /* Clear corrupt inode? */
 +              if (pctx.errcode == EXT2_ET_INODE_CSUM_INVALID) {
 +                      if (fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx))
 +                              goto clear_inode;
 +                      failed_csum = 1;
 +              }
 +
                if (inode->i_links_count) {
                        pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
                                           ino, inode->i_links_count);
                } else
                        check_blocks(ctx, &pctx, block_buf);
  
 +              /*
 +               * If the inode failed the checksum and the user didn't
 +               * clear the inode, test the checksum again -- if it still
 +               * fails, ask the user if the checksum should be corrected.
 +               */
 +              if (failed_csum) {
 +                      pctx.errcode = recheck_bad_inode_checksum(fs, ino, ctx,
 +                                                                &pctx);
 +                      if (pctx.errcode) {
 +                              ctx->flags |= E2F_FLAG_ABORT;
 +                              return;
 +                      }
 +              }
 +
                if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                        return;
  
@@@ -1298,12 -1236,6 +1298,12 @@@ endit
        ext2fs_free_mem(&block_buf);
        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;
 +
        print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
  }
  
@@@ -1533,8 -1465,7 +1533,8 @@@ static void adjust_extattr_refcount(e2f
                if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
                        break;
                pctx.blk = blk;
 -              pctx.errcode = ext2fs_read_ext_attr2(fs, blk, block_buf);
 +              pctx.errcode = ext2fs_read_ext_attr3(fs, blk, block_buf,
 +                                                   pctx.ino);
                if (pctx.errcode) {
                        fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
                        return;
                pctx.num = should_be;
                if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
                        header->h_refcount = should_be;
 -                      pctx.errcode = ext2fs_write_ext_attr2(fs, blk,
 -                                                           block_buf);
 +                      pctx.errcode = ext2fs_write_ext_attr3(fs, blk,
 +                                                           block_buf,
 +                                                           pctx.ino);
                        if (pctx.errcode) {
                                fix_problem(ctx, PR_1_EXTATTR_WRITE_ABORT,
                                            &pctx);
@@@ -1572,7 -1502,6 +1572,7 @@@ static int check_ext_attr(e2fsck_t ctx
        struct ext2_ext_attr_entry *entry;
        int             count;
        region_t        region = 0;
 +      int             failed_csum = 0;
  
        blk = ext2fs_file_acl_block(fs, inode);
        if (blk == 0)
         * validate it
         */
        pctx->blk = blk;
 -      pctx->errcode = ext2fs_read_ext_attr2(fs, blk, block_buf);
 +      pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino);
 +      if (pctx->errcode == EXT2_ET_EXT_ATTR_CSUM_INVALID) {
 +              if (fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx))
 +                      goto clear_extattr;
 +              failed_csum = 1;
 +      }
        if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
                goto clear_extattr;
        header = (struct ext2_ext_attr_header *) block_buf;
        }
        region_free(region);
  
 +      /*
 +       * We only get here if there was no other errors that were fixed.
 +       * If there was a checksum fail, ask to correct it.
 +       */
 +      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->ino);
 +              if (pctx->errcode)
 +                      return 0;
 +      }
 +
        count = header->h_refcount - 1;
        if (count)
                ea_refcount_store(ctx->refcount, blk, count);
@@@ -1859,7 -1771,6 +1859,7 @@@ static void scan_extent_node(e2fsck_t c
        int                     is_dir, is_leaf;
        problem_t               problem;
        struct ext2_extent_info info;
 +      int                     failed_csum;
  
        pctx->errcode = ext2fs_extent_get_info(ehandle, &info);
        if (pctx->errcode)
  
        pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB,
                                          &extent);
 -      while (!pctx->errcode && info.num_entries-- > 0) {
 +      while ((pctx->errcode == 0 ||
 +              pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) &&
 +             info.num_entries-- > 0) {
 +              failed_csum = 0;
                is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF;
                is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);
                last_lblk = extent.e_lblk + extent.e_len - 1;
  
                problem = 0;
 +              /* Ask to clear a corrupt extent block */
 +              if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) {
 +                      pctx->blk = extent.e_pblk;
 +                      pctx->blk2 = extent.e_lblk;
 +                      pctx->num = extent.e_len;
 +                      problem = PR_1_EXTENT_CSUM_INVALID;
 +                      if (fix_problem(ctx, problem, pctx))
 +                              goto fix_problem_now;
 +                      failed_csum = 1;
 +              }
 +
                if (extent.e_pblk == 0 ||
                    extent.e_pblk < ctx->fs->super->s_first_data_block ||
                    extent.e_pblk >= ext2fs_blocks_count(ctx->fs->super))
                          (1 << (21 - ctx->fs->super->s_log_block_size))))
                        problem = PR_1_TOOBIG_DIR;
  
 +              /* Corrupt but passes checks?  Ask to fix checksum. */
 +              if (failed_csum) {
 +                      pctx->blk = extent.e_pblk;
 +                      pctx->blk2 = extent.e_lblk;
 +                      pctx->num = extent.e_len;
 +                      problem = 0;
 +                      if (fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID,
 +                                      pctx))
 +                              ext2fs_extent_replace(ehandle, 0, &extent);
 +              }
 +
                if (problem) {
  report_problem:
                        pctx->blk = extent.e_pblk;
                        pctx->num = extent.e_len;
                        pctx->blkcount = extent.e_lblk + extent.e_len;
                        if (fix_problem(ctx, problem, pctx)) {
 +fix_problem_now:
                                e2fsck_read_bitmaps(ctx);
                                pctx->errcode =
                                        ext2fs_extent_delete(ehandle, 0);
                        if (pctx->errcode) {
                                pctx->str = "EXT2_EXTENT_DOWN";
                                problem = PR_1_EXTENT_HEADER_INVALID;
 -                              if (pctx->errcode == EXT2_ET_EXTENT_HEADER_BAD)
 +                              if (pctx->errcode ==
 +                                      EXT2_ET_EXTENT_HEADER_BAD ||
 +                                  pctx->errcode ==
 +                                      EXT2_ET_EXTENT_CSUM_INVALID)
                                        goto report_problem;
                                return;
                        }
@@@ -2281,7 -2163,8 +2281,8 @@@ static void check_blocks(e2fsck_t ctx, 
                }
                pctx->num = 0;
        }
-       if (LINUX_S_ISREG(inode->i_mode) && EXT2_I_SIZE(inode) >= 0x80000000UL)
+       if (LINUX_S_ISREG(inode->i_mode) &&
+           ext2fs_needs_large_file_feature(EXT2_I_SIZE(inode)))
                ctx->large_files++;
        if ((pb.num_blocks != ext2fs_inode_i_blocks(fs, inode)) ||
            ((fs->super->s_feature_ro_compat &
        }
  
        if (ctx->dirs_to_hash && pb.is_dir &&
 +          !(ctx->lost_and_found && ctx->lost_and_found == ino) &&
            !(inode->i_flags & EXT2_INDEX_FL) &&
            ((inode->i_size / fs->blocksize) >= 3))
 -              ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
 +              e2fsck_rehash_dir_later(ctx, ino);
  
  out:
        if (dirty_inode)
diff --combined e2fsck/pass2.c
index 81a0f4bf0ed025f7814686396ff2a394803c6291,b7268e348dbb1431e61af54efdc1d8fc1eae00f4..804770b5e6b2b96af2c6e75ffab18c34cc7f3cb8
@@@ -302,9 -302,9 +302,9 @@@ static int dict_de_cmp(const void *a, c
        int     a_len, b_len;
  
        de_a = (const struct ext2_dir_entry *) a;
 -      a_len = de_a->name_len & 0xFF;
 +      a_len = ext2fs_dirent_name_len(de_a);
        de_b = (const struct ext2_dir_entry *) b;
 -      b_len = de_b->name_len & 0xFF;
 +      b_len = ext2fs_dirent_name_len(de_b);
  
        if (a_len != b_len)
                return (a_len - b_len);
@@@ -357,7 -357,7 +357,7 @@@ static int check_dot(e2fsck_t ctx
  
        if (!dirent->inode)
                problem = PR_2_MISSING_DOT;
 -      else if (((dirent->name_len & 0xFF) != 1) ||
 +      else if ((ext2fs_dirent_name_len(dirent) != 1) ||
                 (dirent->name[0] != '.'))
                problem = PR_2_1ST_NOT_DOT;
        else if (dirent->name[1] != '\0')
                        if (rec_len < 12)
                                rec_len = dirent->rec_len = 12;
                        dirent->inode = ino;
 -                      dirent->name_len = 1;
 +                      ext2fs_dirent_set_name_len(dirent, 1);
 +                      ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
                        dirent->name[0] = '.';
                        dirent->name[1] = '\0';
                        status = 1;
                                (void) ext2fs_set_rec_len(ctx->fs, new_len,
                                                          nextdir);
                                nextdir->inode = 0;
 -                              nextdir->name_len = 0;
 +                              ext2fs_dirent_set_name_len(nextdir, 0);
 +                              ext2fs_dirent_set_file_type(nextdir,
 +                                                          EXT2_FT_UNKNOWN);
                                status = 1;
                        }
                }
@@@ -418,7 -415,7 +418,7 @@@ static int check_dotdot(e2fsck_t ctx
  
        if (!dirent->inode)
                problem = PR_2_MISSING_DOT_DOT;
 -      else if (((dirent->name_len & 0xFF) != 2) ||
 +      else if ((ext2fs_dirent_name_len(dirent) != 2) ||
                 (dirent->name[0] != '.') ||
                 (dirent->name[1] != '.'))
                problem = PR_2_2ND_NOT_DOT_DOT;
                         * inode.  This will get fixed in pass 3.
                         */
                        dirent->inode = EXT2_ROOT_INO;
 -                      dirent->name_len = 2;
 +                      ext2fs_dirent_set_name_len(dirent, 2);
 +                      ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
                        dirent->name[0] = '.';
                        dirent->name[1] = '.';
                        dirent->name[2] = '\0';
@@@ -465,7 -461,7 +465,7 @@@ static int check_name(e2fsck_t ctx
        int     fixup = -1;
        int     ret = 0;
  
 -      for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
 +      for ( i = 0; i < ext2fs_dirent_name_len(dirent); i++) {
                if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
                        if (fixup < 0) {
                                fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
@@@ -487,7 -483,7 +487,7 @@@ static _INLINE_ int check_filetype(e2fs
                                   ext2_ino_t dir_ino EXT2FS_ATTR((unused)),
                                   struct problem_context *pctx)
  {
 -      int     filetype = dirent->name_len >> 8;
 +      int     filetype = ext2fs_dirent_file_type(dirent);
        int     should_be = EXT2_FT_UNKNOWN;
        struct ext2_inode       inode;
  
                if (filetype == 0 ||
                    !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
                        return 0;
 -              dirent->name_len = dirent->name_len & 0xFF;
 +              ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
                return 1;
        }
  
                        pctx) == 0)
                return 0;
  
 -      dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
 +      ext2fs_dirent_set_file_type(dirent, should_be);
        return 1;
  }
  
@@@ -531,7 -527,7 +531,7 @@@ static void parse_int_node(ext2_filsys 
                           struct ext2_db_entry2 *db,
                           struct check_dir_struct *cd,
                           struct dx_dir_info   *dx_dir,
 -                         char *block_buf)
 +                         char *block_buf, int failed_csum)
  {
        struct          ext2_dx_root_info  *root;
        struct          ext2_dx_entry *ent;
        ext2_dirhash_t  min_hash = 0xffffffff;
        ext2_dirhash_t  max_hash = 0;
        ext2_dirhash_t  hash = 0, prev_hash;
 +      int             csum_size = 0;
  
        if (db->blockcnt == 0) {
                root = (struct ext2_dx_root_info *) (block_buf + 24);
  #endif
  
                ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
 +
 +              if (failed_csum &&
 +                  (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
 +                   fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID,
 +                              &cd->pctx)))
 +                      goto clear_and_exit;
        } else {
                ent = (struct ext2_dx_entry *) (block_buf+8);
 +
 +              if (failed_csum &&
 +                  (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
 +                   fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID,
 +                              &cd->pctx)))
 +                      goto clear_and_exit;
        }
 +
        limit = (struct ext2_dx_countlimit *) ent;
  
  #ifdef DX_DEBUG
  #endif
  
        count = ext2fs_le16_to_cpu(limit->count);
 -      expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
 -              sizeof(struct ext2_dx_entry);
 +      if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                     EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
 +              csum_size = sizeof(struct ext2_dx_tail);
 +      expect_limit = (fs->blocksize -
 +                      (csum_size + ((char *) ent - block_buf))) /
 +                     sizeof(struct ext2_dx_entry);
        if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
                cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
                if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
  clear_and_exit:
        clear_htree(cd->ctx, cd->pctx.ino);
        dx_dir->numblocks = 0;
 +      e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino);
  }
  #endif /* ENABLE_HTREE */
  
@@@ -670,7 -647,7 +670,7 @@@ static void salvage_directory(ext2_fils
        char    *cp = (char *) dirent;
        int left;
        unsigned int rec_len, prev_rec_len;
 -      unsigned int name_len = dirent->name_len & 0xFF;
 +      unsigned int name_len = ext2fs_dirent_name_len(dirent);
  
        (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
        left = fs->blocksize - *offset - rec_len;
        } else {
                rec_len = fs->blocksize - *offset;
                (void) ext2fs_set_rec_len(fs, rec_len, dirent);
 -              dirent->name_len = 0;
 +              ext2fs_dirent_set_name_len(dirent, 0);
 +              ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
                dirent->inode = 0;
        }
  }
@@@ -758,9 -734,6 +758,9 @@@ static int check_dir_block(ext2_filsys 
        struct problem_context  pctx;
        int     dups_found = 0;
        int     ret;
 +      int     dx_csum_size = 0, de_csum_size = 0;
 +      int     failed_csum = 0;
 +      int     is_leaf = 1;
  
        cd = (struct check_dir_struct *) priv_data;
        buf = cd->buf;
        if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
                return DIRENT_ABORT;
  
 +      if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                     EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
 +              dx_csum_size = sizeof(struct ext2_dx_tail);
 +              de_csum_size = sizeof(struct ext2_dir_entry_tail);
 +      }
 +
        /*
         * Make sure the inode is still in use (could have been
         * deleted in the duplicate/bad blocks pass.
  #endif
  
        ehandler_operation(_("reading directory block"));
 -      cd->pctx.errcode = ext2fs_read_dir_block3(fs, block_nr, buf, 0);
 +      cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino);
        ehandler_operation(0);
        if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
                cd->pctx.errcode = 0; /* We'll handle this ourselves */
 +      else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) {
 +              cd->pctx.errcode = 0; /* We'll handle this ourselves */
 +              failed_csum = 1;
 +      }
        if (cd->pctx.errcode) {
 +              char *buf2;
                if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
                        ctx->flags |= E2F_FLAG_ABORT;
                        return DIRENT_ABORT;
                }
 -              memset(buf, 0, fs->blocksize);
 +              ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0,
 +                                   EXT2_ROOT_INO, &buf2);
 +              memcpy(buf, buf2, fs->blocksize);
 +              ext2fs_free_mem(&buf2);
        }
  #ifdef ENABLE_HTREE
        dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
                        dx_dir->depth = root->indirect_levels + 1;
                } else if ((dirent->inode == 0) &&
                           (rec_len == fs->blocksize) &&
 -                         (dirent->name_len == 0) &&
 +                         (ext2fs_dirent_name_len(dirent) == 0) &&
                           (ext2fs_le16_to_cpu(limit->limit) ==
 -                          ((fs->blocksize-8) /
 +                          ((fs->blocksize - (8 + dx_csum_size)) /
                             sizeof(struct ext2_dx_entry))))
                        dx_db->type = DX_DIRBLOCK_NODE;
 +              is_leaf = 0;
        }
  out_htree:
  #endif /* ENABLE_HTREE */
  
 +      /* Verify checksum. */
 +      if (is_leaf && de_csum_size) {
 +              /* No space for csum?  Rebuild dirs in pass 3A. */
 +              if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
 +                      de_csum_size = 0;
 +                      if (e2fsck_dir_will_be_rehashed(ctx, ino))
 +                              goto skip_checksum;
 +                      if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
 +                                       &cd->pctx))
 +                              goto skip_checksum;
 +                      e2fsck_rehash_dir_later(ctx, ino);
 +                      goto skip_checksum;
 +              }
 +              if (failed_csum) {
 +                      char *buf2;
 +                      if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID,
 +                                       &cd->pctx))
 +                              goto skip_checksum;
 +                      ext2fs_new_dir_block(fs,
 +                                           db->blockcnt == 0 ? ino : 0,
 +                                           EXT2_ROOT_INO, &buf2);
 +                      memcpy(buf, buf2, fs->blocksize);
 +                      ext2fs_free_mem(&buf2);
 +                      dir_modified++;
 +                      failed_csum = 0;
 +              }
 +      }
 +      /* htree nodes don't use fake dirents to store checksums */
 +      if (!is_leaf)
 +              de_csum_size = 0;
 +
 +skip_checksum:
        dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
        prev = 0;
        do {
                dgrp_t group;
                ext2_ino_t first_unused_inode;
 +              unsigned int name_len;
  
                problem = 0;
                dirent = (struct ext2_dir_entry *) (buf + offset);
                if (((offset + rec_len) > fs->blocksize) ||
                    (rec_len < 12) ||
                    ((rec_len % 4) != 0) ||
 -                  (((dirent->name_len & (unsigned) 0xFF)+8) > rec_len)) {
 +                  ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) {
                        if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
                                salvage_directory(fs, dirent, prev, &offset);
                                dir_modified++;
                /*
                 * Make sure the inode listed is a legal one.
                 */
 +              name_len = ext2fs_dirent_name_len(dirent);
                if (((dirent->inode != EXT2_ROOT_INO) &&
                     (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
                    (dirent->inode > fs->super->s_inodes_count)) {
                         * clear it.
                         */
                        problem = PR_2_BB_INODE;
 -              } else if ((dot_state > 1) &&
 -                         ((dirent->name_len & 0xFF) == 1) &&
 +              } else if ((dot_state > 1) && (name_len == 1) &&
                           (dirent->name[0] == '.')) {
                        /*
                         * If there's a '.' entry in anything other
                         * duplicate entry that should be removed.
                         */
                        problem = PR_2_DUP_DOT;
 -              } else if ((dot_state > 1) &&
 -                         ((dirent->name_len & 0xFF) == 2) &&
 +              } else if ((dot_state > 1) && (name_len == 2) &&
                           (dirent->name[0] == '.') &&
                           (dirent->name[1] == '.')) {
                        /*
                         * directory hasn't been created yet.
                         */
                        problem = PR_2_LINK_ROOT;
 -              } else if ((dot_state > 1) &&
 -                         (dirent->name_len & 0xFF) == 0) {
 +              } else if ((dot_state > 1) && (name_len == 0)) {
                        /*
                         * Don't allow zero-length directory names.
                         */
  #ifdef ENABLE_HTREE
                if (dx_db) {
                        ext2fs_dirhash(dx_dir->hashversion, dirent->name,
 -                                     (dirent->name_len & 0xFF),
 +                                     ext2fs_dirent_name_len(dirent),
                                       fs->super->s_hash_seed, &hash, 0);
                        if (hash < dx_db->min_hash)
                                dx_db->min_hash = hash;
                        pctx.ino = ino;
                        pctx.dirent = dirent;
                        fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
 -                      if (!ctx->dirs_to_hash)
 -                              ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
 -                      if (ctx->dirs_to_hash)
 -                              ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
 +                      e2fsck_rehash_dir_later(ctx, ino);
                        dups_found++;
                } else
                        dict_alloc_insert(&de_dict, dirent, dirent);
                        (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
                offset += rec_len;
                dot_state++;
 -      } while (offset < fs->blocksize);
 +      } while (offset < fs->blocksize - de_csum_size);
  #if 0
        printf("\n");
  #endif
                cd->pctx.dir = cd->pctx.ino;
                if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
                    (dx_db->type == DX_DIRBLOCK_NODE))
 -                      parse_int_node(fs, db, cd, dx_dir, buf);
 +                      parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
        }
  #endif /* ENABLE_HTREE */
 -      if (offset != fs->blocksize) {
 -              cd->pctx.num = rec_len - fs->blocksize + offset;
 +
 +      if (offset != fs->blocksize - de_csum_size) {
 +              cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
 +                             offset;
                if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
                        dirent->rec_len = cd->pctx.num;
                        dir_modified++;
                }
        }
        if (dir_modified) {
 -              cd->pctx.errcode = ext2fs_write_dir_block3(fs, block_nr, buf, 0);
 +              /* leaf block with no tail?  Rehash dirs later. */
 +              if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
 +                  is_leaf &&
 +                  !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf))
 +                      e2fsck_rehash_dir_later(ctx, ino);
 +
 +write_and_fix:
 +              if (e2fsck_dir_will_be_rehashed(ctx, ino))
 +                      ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
 +              cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
 +                                                         0, ino);
 +              if (e2fsck_dir_will_be_rehashed(ctx, ino))
 +                      ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
                if (cd->pctx.errcode) {
                        if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
                                         &cd->pctx))
                                goto abort_free_dict;
                }
                ext2fs_mark_changed(fs);
 +      } else if (is_leaf && failed_csum && !dir_modified) {
 +              /*
 +               * If a leaf node that fails csum makes it this far without
 +               * alteration, ask the user if the checksum should be fixed.
 +               */
 +              if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
 +                              &cd->pctx))
 +                      goto write_and_fix;
        }
        dict_free_nodes(&de_dict);
        return 0;
@@@ -1293,9 -1200,9 +1293,9 @@@ static void deallocate_inode(e2fsck_t c
  
        if (ext2fs_file_acl_block(fs, &inode) &&
            (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
 -              pctx.errcode = ext2fs_adjust_ea_refcount2(fs,
 -                                      ext2fs_file_acl_block(fs, &inode),
 -                                      block_buf, -1, &count);
 +              pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
 +                              ext2fs_file_acl_block(fs, &inode),
 +                              block_buf, -1, &count, ino);
                if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
                        pctx.errcode = 0;
                        count = 1;
        if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
                goto clear_inode;
  
-       if (LINUX_S_ISREG(inode.i_mode) && EXT2_I_SIZE(&inode) >= 0x80000000UL)
+       if (LINUX_S_ISREG(inode.i_mode) &&
+           ext2fs_needs_large_file_feature(EXT2_I_SIZE(&inode)))
                ctx->large_files--;
  
        del_block.ctx = ctx;
@@@ -1548,7 -1456,7 +1549,7 @@@ static int allocate_dir_block(e2fsck_t 
                return 1;
        }
  
 -      pctx->errcode = ext2fs_write_dir_block3(fs, blk, block, 0);
 +      pctx->errcode = ext2fs_write_dir_block4(fs, blk, block, 0, db->ino);
        ext2fs_free_mem(&block);
        if (pctx->errcode) {
                pctx->str = "ext2fs_write_dir_block";
diff --combined e2fsck/pass3.c
index fbaadcfc6d40e141b93d96efbba6660e4a28eb3e,d7a243e4b7692ef07f7a1f2e5c2398f7dd674757..dc9d7c19e6c9422a093e5714b26b45532299ea52
@@@ -53,7 -53,7 +53,7 @@@ static ext2fs_inode_bitmap inode_done_m
  void e2fsck_pass3(e2fsck_t ctx)
  {
        ext2_filsys fs = ctx->fs;
-       struct dir_info_iter *iter;
+       struct dir_info_iter *iter = NULL;
  #ifdef RESOURCE_TRACK
        struct resource_track   rtrack;
  #endif
                        if (check_directory(ctx, dir->ino, &pctx))
                                goto abort_exit;
        }
-       e2fsck_dir_info_iter_end(ctx, iter);
  
        /*
         * Force the creation of /lost+found if not present
        e2fsck_rehash_directories(ctx);
  
  abort_exit:
+       if (iter)
+               e2fsck_dir_info_iter_end(ctx, iter);
        e2fsck_free_dir_info(ctx);
        if (inode_loop_detect) {
                ext2fs_free_inode_bitmap(inode_loop_detect);
@@@ -198,10 -199,9 +199,10 @@@ static void check_root(e2fsck_t ctx
                return;
        }
  
 -      pctx.errcode = ext2fs_write_dir_block3(fs, blk, block, 0);
 +      pctx.errcode = ext2fs_write_dir_block4(fs, blk, block, 0,
 +                                             EXT2_ROOT_INO);
        if (pctx.errcode) {
 -              pctx.str = "ext2fs_write_dir_block3";
 +              pctx.str = "ext2fs_write_dir_block4";
                fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
                ctx->flags |= E2F_FLAG_ABORT;
                return;
@@@ -375,7 -375,7 +376,7 @@@ ext2_ino_t e2fsck_get_lost_and_found(e2
        if (retval && !fix)
                return 0;
        if (!retval) {
 -              if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, ino)) {
 +              if (ext2fs_check_directory(fs, ino) == 0) {
                        ctx->lost_and_found = ino;
                        return ino;
                }
                return 0;
        }
  
 -      retval = ext2fs_write_dir_block3(fs, blk, block, 0);
 +      retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
        ext2fs_free_mem(&block);
        if (retval) {
                pctx.errcode = retval;
@@@ -612,7 -612,7 +613,7 @@@ static int fix_dotdot_proc(struct ext2_
        errcode_t       retval;
        struct problem_context pctx;
  
 -      if ((dirent->name_len & 0xFF) != 2)
 +      if (ext2fs_dirent_name_len(dirent) != 2)
                return 0;
        if (strncmp(dirent->name, "..", 2))
                return 0;
        dirent->inode = fp->parent;
        if (fp->ctx->fs->super->s_feature_incompat &
            EXT2_FEATURE_INCOMPAT_FILETYPE)
 -              dirent->name_len = (dirent->name_len & 0xFF) |
 -                      (EXT2_FT_DIR << 8);
 +              ext2fs_dirent_set_file_type(dirent, EXT2_FT_DIR);
        else
 -              dirent->name_len = dirent->name_len & 0xFF;
 +              ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
  
        fp->done++;
        return DIRENT_ABORT | DIRENT_CHANGED;
@@@ -658,12 -659,8 +659,12 @@@ static void fix_dotdot(e2fsck_t ctx, ex
  
        clear_problem_context(&pctx);
        pctx.ino = ino;
 +      if (e2fsck_dir_will_be_rehashed(ctx, ino))
 +              ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
        retval = ext2fs_dir_iterate(fs, ino, DIRENT_FLAG_INCLUDE_EMPTY,
                                    0, fix_dotdot_proc, &fp);
 +      if (e2fsck_dir_will_be_rehashed(ctx, ino))
 +              ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
        if (retval || !fp.done) {
                pctx.errcode = retval;
                fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
@@@ -689,7 -686,6 +690,7 @@@ struct expand_dir_struct 
        blk64_t                 last_block;
        errcode_t               err;
        e2fsck_t                ctx;
 +      ext2_ino_t              dir;
  };
  
  static int expand_dir_proc(ext2_filsys fs,
                        return BLOCK_ABORT;
                }
                es->num--;
 -              retval = ext2fs_write_dir_block3(fs, new_blk, block, 0);
 +              retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
 +                                               es->dir);
        } else {
                retval = ext2fs_get_mem(fs->blocksize, &block);
                if (retval) {
                return BLOCK_CHANGED;
  }
  
+ /*
+  * Ensure that all blocks are marked in the block_found_map, since it's
+  * possible that the library allocated an extent node block or a block map
+  * block during the directory rebuilding; these new allocations are not
+  * captured in block_found_map.  This is bad since we could later use
+  * block_found_map to allocate more blocks.
+  */
+ static int find_new_blocks_proc(ext2_filsys fs,
+                               blk64_t *blocknr,
+                               e2_blkcnt_t     blockcnt,
+                               blk64_t ref_block EXT2FS_ATTR((unused)),
+                               int ref_offset EXT2FS_ATTR((unused)),
+                               void    *priv_data)
+ {
+       struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
+       e2fsck_t        ctx = es->ctx;
+       ext2fs_mark_block_bitmap2(ctx->block_found_map, *blocknr);
+       return 0;
+ }
  errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
                                  int num, int guaranteed_size)
  {
        errcode_t       retval;
        struct expand_dir_struct es;
        struct ext2_inode       inode;
-       blk64_t         sz;
+       blk64_t         sz, before, after;
  
        if (!(fs->flags & EXT2_FLAG_RW))
                return EXT2_ET_RO_FILSYS;
        es.err = 0;
        es.newblocks = 0;
        es.ctx = ctx;
 +      es.dir = dir;
  
+       before = ext2fs_free_blocks_count(fs->super);
        retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
                                       0, expand_dir_proc, &es);
  
        if (es.err)
                return es.err;
+       after = ext2fs_free_blocks_count(fs->super);
+       /*
+        * If the free block count has dropped by more than the blocks we
+        * allocated ourselves, then we must've allocated some extent/map
+        * blocks.  Therefore, we must iterate this dir's blocks again to
+        * ensure that all newly allocated blocks are captured in
+        * block_found_map.
+        */
+       if ((before - after) > es.newblocks) {
+               retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY,
+                                              0, find_new_blocks_proc, &es);
+               if (es.err)
+                       return es.err;
+       }
  
        /*
         * Update the size and block count fields in the inode.
diff --combined e2fsck/problem.c
index 897693a7fd821a4fa793f8ab1a5af45526cfc919,83584a08cd4eb70db9568212d1ab849dd9e3d86f..0f83421192914fa609f8b9c797f2f0781b712f66
@@@ -433,20 -433,6 +433,20 @@@ static struct e2fsck_problem problem_ta
          N_("ext2fs_check_desc: %m\n"),
          PROMPT_NONE, 0 },
  
 +      /*
 +       * metadata_csum implies uninit_bg; both feature bits cannot
 +       * be set simultaneously.
 +       */
 +      { PR_0_META_AND_GDT_CSUM_SET,
 +        N_("@S metadata_csum supersedes uninit_bg; both feature "
 +           "bits cannot be set simultaneously."),
 +        PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},
 +
 +      /* Superblock has invalid MMP checksum. */
 +      { PR_0_MMP_CSUM_INVALID,
 +        N_("@S MMP block checksum does not match MMP block.  "),
 +        PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},
 +
        /* 64bit is set but extents is unset. */
        { PR_0_64BIT_WITHOUT_EXTENTS,
          N_("@S 64bit filesystems needs extents to access the whole disk.  "),
          N_("@i %i has zero length extent\n\t(@n logical @b %c, physical @b %b)\n"),
          PROMPT_CLEAR, 0 },
  
 +      /* inode checksum does not match inode */
 +      { PR_1_INODE_CSUM_INVALID,
 +        N_("@i %i checksum does not match @i.  "),
 +        PROMPT_CLEAR, PR_PREEN_OK },
 +
 +      /* inode passes checks, but checksum does not match inode */
 +      { PR_1_INODE_ONLY_CSUM_INVALID,
 +        N_("@i %i passes checks, but checksum does not match @i.  "),
 +        PROMPT_FIX, PR_PREEN_OK },
 +
 +      /* Inode extent block checksum does not match extent */
 +      { PR_1_EXTENT_CSUM_INVALID,
 +        N_("@i %i extent block checksum does not match extent\n\t(logical @b "
 +           "%c, @n physical @b %b, len %N)\n"),
 +        PROMPT_CLEAR, 0 },
 +
 +      /*
 +       * Inode extent block passes checks, but checksum does not match
 +       * extent
 +       */
 +      { PR_1_EXTENT_ONLY_CSUM_INVALID,
 +        N_("@i %i extent block passes checks, but checksum does not match "
 +           "extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
 +        PROMPT_FIX, 0 },
 +
 +      /* Extended attribute block checksum for inode does not match. */
 +      { PR_1_EA_BLOCK_CSUM_INVALID,
 +        N_("Extended attribute @a @b %b checksum for @i %i does not "
 +           "match.  "),
 +        PROMPT_CLEAR, 0 },
 +
 +      /*
 +       * Extended attribute block passes checks, but checksum for inode does
 +       * not match.
 +       */
 +      { PR_1_EA_BLOCK_ONLY_CSUM_INVALID,
 +        N_("Extended attribute @a @b %b passes checks, but checksum for "
 +           "@i %i does not match.  "),
 +        PROMPT_FIX, 0 },
 +
        /*
         * Interior extent node logical offset doesn't match first node below it
         */
          N_("@n @i number for '.' in @d @i %i.\n"),
          PROMPT_FIX, 0 },
  
-       /* Directory entry has bad inode number */
+       /* Entry 'xxxx' in /a/b/c has bad inode number.*/
        { PR_2_BAD_INO,
          N_("@E has @n @i #: %Di.\n"),
          PROMPT_CLEAR, 0 },
  
-       /* Directory entry has deleted or unused inode */
+       /* Entry 'xxxx' in /a/b/c has deleted/unused inode nnnnn.*/
        { PR_2_UNUSED_INODE,
          N_("@E has @D/unused @i %Di.  "),
          PROMPT_CLEAR, PR_PREEN_OK },
          N_("i_file_acl_hi @F %N, @s zero.\n"),
          PROMPT_CLEAR, PR_PREEN_OK },
  
 +      /* htree root node fails checksum */
 +      { PR_2_HTREE_ROOT_CSUM_INVALID,
 +        N_("@p @h %d: root node fails checksum\n"),
 +        PROMPT_CLEAR_HTREE, PR_PREEN_OK },
 +
 +      /* htree internal node fails checksum */
 +      { PR_2_HTREE_NODE_CSUM_INVALID,
 +        N_("@p @h %d: internal node fails checksum\n"),
 +        PROMPT_CLEAR_HTREE, PR_PREEN_OK },
 +
 +      /* leaf node fails checksum */
 +      { PR_2_LEAF_NODE_CSUM_INVALID,
 +        N_("@d @i %i, %B, offset %N: @d fails checksum\n"),
 +        PROMPT_SALVAGE, PR_PREEN_OK },
 +
 +      /* leaf node has no checksum */
 +      { PR_2_LEAF_NODE_MISSING_CSUM,
 +        N_("@d @i %i, %B, offset %N: @d has no checksum\n"),
 +        PROMPT_FIX, PR_PREEN_OK },
 +
 +      /* leaf node passes checks but fails checksum */
 +      { PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
 +        N_("@d @i %i, %B, offset %N: @d passes checks but fails checksum\n"),
 +        PROMPT_FIX, PR_PREEN_OK },
 +
        /* Pass 3 errors */
  
        /* Pass 3: Checking directory connectivity */
          N_("@g %g @i(s) in use but @g is marked INODE_UNINIT\n"),
          PROMPT_FIX, PR_PREEN_OK },
  
 +      /* Group N inode bitmap does not match checksum */
 +      { PR_5_INODE_BITMAP_CSUM_INVALID,
 +        N_("@g %g @i bitmap does not match checksum\n"),
 +        PROMPT_FIX, PR_LATCH_IBITMAP | PR_PREEN_OK },
 +
 +      /* Group N block bitmap does not match checksum */
 +      { PR_5_BLOCK_BITMAP_CSUM_INVALID,
 +        N_("@g %g @b bitmap does not match checksum\n"),
 +        PROMPT_FIX, PR_LATCH_BBITMAP | PR_PREEN_OK },
 +
        /* Post-Pass 5 errors */
  
        /* Recreate journal if E2F_FLAG_JOURNAL_INODE flag is set */
diff --combined e2fsck/unix.c
index a6c8d25cb5ffb7b5860cd5892f1b566044be21d9,6c4e26de2b763e254a0864ddfd146eda5b4f295d..7a8fce2472dec952af9c0421f4ac8edcc8da8cbf
@@@ -869,6 -869,8 +869,8 @@@ static errcode_t PRS(int argc, char *ar
                case 'L':
                        replace_bad_blocks++;
                case 'l':
+                       if (bad_blocks_file)
+                               free(bad_blocks_file);
                        bad_blocks_file = string_copy(ctx, optarg, 0);
                        break;
                case 'd':
@@@ -1149,11 -1151,6 +1151,11 @@@ check_error
                        ext2fs_mmp_clear(fs);
                        retval = 0;
                }
 +      } else if (retval == EXT2_ET_MMP_CSUM_INVALID) {
 +              if (fix_problem(ctx, PR_0_MMP_CSUM_INVALID, &pctx)) {
 +                      ext2fs_mmp_clear(fs);
 +                      retval = 0;
 +              }
        }
        return retval;
  }
@@@ -1272,7 -1269,6 +1274,7 @@@ restart
        if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
            !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
            ((retval == EXT2_ET_BAD_MAGIC) ||
 +           (retval == EXT2_ET_SB_CSUM_INVALID) ||
             (retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
             ((retval == 0) && (retval2 = ext2fs_check_desc(fs))))) {
                if (retval) {
@@@ -1750,7 -1746,7 +1752,7 @@@ no_journal
        }
  
        if ((run_result & E2F_FLAG_CANCEL) == 0 &&
 -          sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM &&
 +          ext2fs_has_group_desc_csum(ctx->fs) &&
            !(ctx->options & E2F_OPT_READONLY)) {
                retval = ext2fs_set_gdt_csum(ctx->fs);
                if (retval) {
index 9cc1bd1e551d1e6beae4a8a41431fa4f1dc80eac,87812ab483c3ee26f508d68308904e3cf56876f7..699813a9bdeb7479f0d6bf20d53c90bf88b8f44e
@@@ -99,7 -99,7 +99,7 @@@ ec    EXT2_ET_BLOCK_BITMAP_WRITE
        "Can't write a block bitmap"
  
  ec    EXT2_ET_BLOCK_BITMAP_READ,
-       "Can't read an block bitmap"
+       "Can't read a block bitmap"
  
  ec    EXT2_ET_INODE_TABLE_WRITE,
        "Can't write an inode table"
@@@ -476,10 -476,4 +476,10 @@@ ec       EXT2_ET_MMP_CSUM_INVALID
  ec    EXT2_ET_FILE_EXISTS,
        "Ext2 file already exists"
  
 +ec    EXT2_ET_BLOCK_BITMAP_CSUM_INVALID,
 +      "Block bitmap checksum does not match bitmap"
 +
 +ec    EXT2_ET_INLINE_DATA_CANT_ITERATE,
 +      "Cannot block iterate on an inode containing inline data"
 +
        end
diff --combined lib/ext2fs/ext2fs.h
index 654247abe4c7ae5faab965ba5dd85f828038eb79,c982327ec2a79e5cb6be4d7c619fe07f59aa05e8..0624350d0e4f47f7c5eefa3ae06ee6287361d419
@@@ -191,7 -191,6 +191,7 @@@ typedef struct ext2_file *ext2_file_t
  #define EXT2_FLAG_PRINT_PROGRESS      0x40000
  #define EXT2_FLAG_DIRECT_IO           0x80000
  #define EXT2_FLAG_SKIP_MMP            0x100000
 +#define EXT2_FLAG_IGNORE_CSUM_ERRORS  0x200000
  
  /*
   * Special flag in the ext2 inode i_flag field that means that this is
@@@ -274,12 -273,6 +274,12 @@@ struct struct_ext2_filsys 
         * Time at which e2fsck last updated the MMP block.
         */
        long mmp_last_written;
 +
 +      /* progress operation functions */
 +      struct ext2fs_progress_ops *progress_ops;
 +
 +      /* Precomputed FS UUID checksum for seeding other checksums */
 +      __u32 csum_seed;
  };
  
  #if EXT2_FLAT_INCLUDES
@@@ -437,13 -430,11 +437,13 @@@ struct ext2_extent_info 
  
  #define DIRENT_FLAG_INCLUDE_EMPTY     1
  #define DIRENT_FLAG_INCLUDE_REMOVED   2
 +#define DIRENT_FLAG_INCLUDE_CSUM      4
  
  #define DIRENT_DOT_FILE               1
  #define DIRENT_DOT_DOT_FILE   2
  #define DIRENT_OTHER_FILE     3
  #define DIRENT_DELETED_FILE   4
 +#define DIRENT_CHECKSUM               5
  
  /*
   * Inode scan definitions
@@@ -569,33 -560,26 +569,33 @@@ typedef struct ext2_icount *ext2_icount
     environment at configure time. */
   #warning "Compression support is experimental"
  #endif
 -#define EXT2_LIB_FEATURE_INCOMPAT_SUPP        (EXT2_FEATURE_INCOMPAT_FILETYPE|\
 -                                       EXT2_FEATURE_INCOMPAT_COMPRESSION|\
 -                                       EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
 -                                       EXT2_FEATURE_INCOMPAT_META_BG|\
 -                                       EXT3_FEATURE_INCOMPAT_RECOVER|\
 -                                       EXT3_FEATURE_INCOMPAT_EXTENTS|\
 -                                       EXT4_FEATURE_INCOMPAT_FLEX_BG|\
 -                                       EXT4_FEATURE_INCOMPAT_MMP|\
 -                                       EXT4_FEATURE_INCOMPAT_64BIT)
 +#define EXT2_LIB_INCOMPAT_COMPRESSION EXT2_FEATURE_INCOMPAT_COMPRESSION
 +#else
 +#define EXT2_LIB_INCOMPAT_COMPRESSION (0)
 +#endif
 +
 +#ifdef CONFIG_MMP
 +#define EXT4_LIB_INCOMPAT_MMP         EXT4_FEATURE_INCOMPAT_MMP
 +#else
 +#define EXT4_LIB_INCOMPAT_MMP         (0)
 +#endif
 +
 +#ifdef CONFIG_QUOTA
 +#define EXT4_LIB_RO_COMPAT_QUOTA      EXT4_FEATURE_RO_COMPAT_QUOTA
  #else
 +#define EXT4_LIB_RO_COMPAT_QUOTA      (0)
 +#endif
 +
  #define EXT2_LIB_FEATURE_INCOMPAT_SUPP        (EXT2_FEATURE_INCOMPAT_FILETYPE|\
 +                                       EXT2_LIB_INCOMPAT_COMPRESSION|\
                                         EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
                                         EXT2_FEATURE_INCOMPAT_META_BG|\
                                         EXT3_FEATURE_INCOMPAT_RECOVER|\
                                         EXT3_FEATURE_INCOMPAT_EXTENTS|\
                                         EXT4_FEATURE_INCOMPAT_FLEX_BG|\
 -                                       EXT4_FEATURE_INCOMPAT_MMP|\
 +                                       EXT4_LIB_INCOMPAT_MMP|\
                                         EXT4_FEATURE_INCOMPAT_64BIT)
 -#endif
 -#ifdef CONFIG_QUOTA
 +
  #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP       (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
                                         EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
                                         EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\
                                         EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\
                                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM|\
                                         EXT4_FEATURE_RO_COMPAT_BIGALLOC|\
 -                                       EXT4_FEATURE_RO_COMPAT_QUOTA)
 -#else
 -#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP       (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
 -                                       EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
 -                                       EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\
 -                                       EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\
 -                                       EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\
 -                                       EXT4_FEATURE_RO_COMPAT_GDT_CSUM|\
 -                                       EXT4_FEATURE_RO_COMPAT_BIGALLOC)
 -#endif
 +                                       EXT4_LIB_RO_COMPAT_QUOTA|\
 +                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
  
  /*
   * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed
   * to ext2fs_openfs()
   */
 -#define EXT2_LIB_SOFTSUPP_INCOMPAT    (0)
 +#define EXT2_LIB_SOFTSUPP_INCOMPAT    (EXT4_FEATURE_INCOMPAT_INLINE_DATA)
  #define EXT2_LIB_SOFTSUPP_RO_COMPAT   (EXT4_FEATURE_RO_COMPAT_REPLICA)
  
  
@@@ -639,13 -631,13 +639,19 @@@ typedef struct stat ext2fs_struct_stat
  /*
   * function prototypes
   */
 +static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
 +{
 +      return EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                      EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
 +                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
 +}
  
+ /* The LARGE_FILE feature should be set if we have stored files 2GB+ in size */
+ static inline int ext2fs_needs_large_file_feature(unsigned long long file_size)
+ {
+       return file_size >= 0x80000000ULL;
+ }
  /* alloc.c */
  extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode,
                                  ext2fs_inode_bitmap map, ext2_ino_t *ret);
@@@ -812,8 -804,6 +818,8 @@@ extern errcode_t ext2fs_get_block_bitma
                                         void *out);
  
  /* blknum.c */
 +extern __u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group);
 +extern __u32 ext2fs_block_bitmap_checksum(ext2_filsys fs, dgrp_t group);
  extern dgrp_t ext2fs_group_of_blk2(ext2_filsys fs, blk64_t);
  extern blk64_t ext2fs_group_first_block2(ext2_filsys fs, dgrp_t group);
  extern blk64_t ext2fs_group_last_block2(ext2_filsys fs, dgrp_t group);
@@@ -841,11 -831,9 +847,11 @@@ extern void ext2fs_free_blocks_count_ad
  extern struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs,
                                          struct opaque_ext2_group_desc *gdp,
                                          dgrp_t group);
 +extern blk64_t ext2fs_block_bitmap_csum(ext2_filsys fs, dgrp_t group);
  extern blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group);
  extern void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group,
                                        blk64_t blk);
 +extern __u32 ext2fs_inode_bitmap_csum(ext2_filsys fs, dgrp_t group);
  extern blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group);
  extern void ext2fs_inode_bitmap_loc_set(ext2_filsys fs, dgrp_t group,
                                        blk64_t blk);
@@@ -951,65 -939,18 +957,65 @@@ extern int ext2fs_super_and_bgd_loc(ext
  extern void ext2fs_update_dynamic_rev(ext2_filsys fs);
  
  /* crc32c.c */
 -extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
 +extern __u32 ext2fs_crc32_be(__u32 crc, unsigned char const *p, size_t len);
  extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);
  
  /* csum.c */
 +extern errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp);
 +extern int ext2fs_mmp_csum_verify(ext2_filsys, struct mmp_struct *mmp);
 +extern int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb);
 +extern errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
 +                                          struct ext2_super_block *sb);
 +extern int ext2fs_superblock_csum_verify(ext2_filsys fs,
 +                                       struct ext2_super_block *sb);
 +extern errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs,
 +                                      ext2_ino_t inum, blk64_t block,
 +                                      struct ext2_ext_attr_header *hdr);
 +extern int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
 +                                           blk64_t block,
 +                                           struct ext2_ext_attr_header *hdr);
 +#define EXT2_DIRENT_TAIL(block, blocksize) \
 +      ((struct ext2_dir_entry_tail *)(((char *)(block)) + \
 +      (blocksize) - sizeof(struct ext2_dir_entry_tail)))
 +
 +extern void ext2fs_initialize_dirent_tail(ext2_filsys fs,
 +                                        struct ext2_dir_entry_tail *t);
 +extern int ext2fs_dirent_has_tail(ext2_filsys fs,
 +                                struct ext2_dir_entry *dirent);
 +extern int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
 +                                   struct ext2_dir_entry *dirent);
 +extern int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
 +                                      struct ext2_dir_entry *dirent);
 +extern errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
 +                                         struct ext2_dir_entry *dirent);
 +extern errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
 +                                        struct ext2_dir_entry *dirent,
 +                                        struct ext2_dx_countlimit **cc,
 +                                        int *offset);
 +extern errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs,
 +                                            ext2_ino_t inum,
 +                                            struct ext3_extent_header *eh);
 +extern int ext2fs_extent_block_csum_verify(ext2_filsys fs,
 +                                         ext2_ino_t inum,
 +                                         struct ext3_extent_header *eh);
 +extern errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
 +                                            char *bitmap, int size);
 +extern int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
 +                                         char *bitmap, int size);
 +extern errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
 +                                            char *bitmap, int size);
 +extern int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
 +                                         char *bitmap, int size);
 +extern errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
 +                                     struct ext2_inode_large *inode);
 +extern int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
 +                                  struct ext2_inode_large *inode);
  extern void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group);
  extern int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group);
  extern errcode_t ext2fs_set_gdt_csum(ext2_filsys fs);
  extern __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group);
  
  /* dblist.c */
 -
 -extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs);
  extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist);
  extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino,
                                      blk_t blk, int blockcnt);
@@@ -1064,16 -1005,12 +1070,16 @@@ extern errcode_t ext2fs_read_dir_block2
                                        void *buf, int flags);
  extern errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
                                        void *buf, int flags);
 +extern errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
 +                                      void *buf, int flags, ext2_ino_t ino);
  extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
                                        void *buf);
  extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
                                         void *buf, int flags);
  extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
                                         void *buf, int flags);
 +extern errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
 +                                       void *buf, int flags, ext2_ino_t ino);
  
  /* dirhash.c */
  extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
@@@ -1124,24 -1061,16 +1130,24 @@@ extern __u32 ext2fs_ext_attr_hash_entry
  extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
  extern errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block,
                                       void *buf);
 +extern errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block,
 +                                     void *buf, ext2_ino_t inum);
  extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block,
                                       void *buf);
  extern errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block,
                                       void *buf);
 +extern errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block,
 +                                     void *buf, ext2_ino_t inum);
  extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
                                           char *block_buf,
                                           int adjust, __u32 *newcount);
  extern errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
                                           char *block_buf,
                                           int adjust, __u32 *newcount);
 +extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
 +                                         char *block_buf,
 +                                         int adjust, __u32 *newcount,
 +                                         ext2_ino_t inum);
  
  /* extent.c */
  extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
@@@ -1247,6 -1176,10 +1253,6 @@@ extern errcode_t ext2fs_find_first_zero
                                                       __u32 *out);
  
  /* gen_bitmap64.c */
 -
 -/* Generate and print bitmap usage statistics */
 -#define BMAP_STATS
 -
  void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap);
  errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic,
                                    int type, __u64 start, __u64 end,
@@@ -1275,9 -1208,6 +1281,9 @@@ errcode_t ext2fs_set_generic_bmap_range
  errcode_t ext2fs_convert_subcluster_bitmap(ext2_filsys fs,
                                           ext2fs_block_bitmap *bitmap);
  
 +/* get_num_dirs.c */
 +extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs);
 +
  /* getsize.c */
  extern errcode_t ext2fs_get_device_size(const char *file, int blocksize,
                                        blk_t *retblocks);
@@@ -1340,7 -1270,6 +1346,7 @@@ extern errcode_t ext2fs_get_memalign(un
                                     unsigned long align, void *ptr);
  
  /* inode.c */
 +extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
  extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
  extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
                                            ext2_ino_t *ino,
@@@ -1472,7 -1401,6 +1478,7 @@@ errcode_t ext2fs_mmp_clear(ext2_filsys 
  errcode_t ext2fs_mmp_init(ext2_filsys fs);
  errcode_t ext2fs_mmp_start(ext2_filsys fs);
  errcode_t ext2fs_mmp_update(ext2_filsys fs);
 +errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately);
  errcode_t ext2fs_mmp_stop(ext2_filsys fs);
  unsigned ext2fs_mmp_new_seq(void);
  
@@@ -1497,8 -1425,6 +1503,8 @@@ extern errcode_t ext2fs_read_bb_FILE(ex
  extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
  
  /* swapfs.c */
 +extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
 +extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
  extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
                                 int has_header);
  extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
@@@ -1538,7 -1464,6 +1544,7 @@@ extern errcode_t ext2fs_write_bb_FILE(e
  
  /* inline functions */
  #ifdef NO_INLINE_FUNCS
 +extern void ext2fs_init_csum_seed(ext2_filsys fs);
  extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
  extern errcode_t ext2fs_get_memzero(unsigned long size, void *ptr);
  extern errcode_t ext2fs_get_array(unsigned long count,
@@@ -1589,20 -1514,10 +1595,20 @@@ extern __u64 ext2fs_div64_ceil(__u64 a
  #endif /* __STDC_VERSION__ >= 199901L */
  #endif
  
 +_INLINE_ void ext2fs_init_csum_seed(ext2_filsys fs)
 +{
 +      if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
 +              return;
 +
 +      fs->csum_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid,
 +                                       sizeof(fs->super->s_uuid));
 +}
 +
  #ifndef EXT2_CUSTOM_MEMORY_ROUTINES
  #include <string.h>
  /*
-  *  Allocate memory
+  *  Allocate memory.  The 'ptr' arg must point to a pointer.
   */
  _INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
  {
@@@ -1649,7 -1564,7 +1655,7 @@@ _INLINE_ errcode_t ext2fs_get_arrayzero
  }
  
  /*
-  * Free memory
+  * Free memory.  The 'ptr' arg must point to a pointer.
   */
  _INLINE_ errcode_t ext2fs_free_mem(void *ptr)
  {
  }
  
  /*
-  *  Resize memory
+  *  Resize memory.  The 'ptr' arg must point to a pointer.
   */
  _INLINE_ errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
                                     unsigned long size, void *ptr)
@@@ -1815,26 -1730,6 +1821,26 @@@ _INLINE_ __u64 ext2fs_div64_ceil(__u64 
        return ((a - 1) / b) + 1;
  }
  
 +_INLINE_ int ext2fs_dirent_name_len(const struct ext2_dir_entry *entry)
 +{
 +      return entry->name_len & 0xff;
 +}
 +
 +_INLINE_ void ext2fs_dirent_set_name_len(struct ext2_dir_entry *entry, int len)
 +{
 +      entry->name_len = (entry->name_len & 0xff00) | (len & 0xff);
 +}
 +
 +_INLINE_ int ext2fs_dirent_file_type(const struct ext2_dir_entry *entry)
 +{
 +      return entry->name_len >> 8;
 +}
 +
 +_INLINE_ void ext2fs_dirent_set_file_type(struct ext2_dir_entry *entry, int type)
 +{
 +      entry->name_len = (entry->name_len & 0xff) | (type << 8);
 +}
 +
  #undef _INLINE_
  #endif
  
diff --combined lib/ext2fs/ext2fsP.h
index 80d2d0a3370ce52aad31f4f2581a2e309b3ba746,99c0c1ba8eda009c499b2556f0da7d149bc4b26d..8c7983beb15008cd6602d9f70a4e227606384605
@@@ -75,7 -75,7 +75,7 @@@ struct ext2_inode_cache 
  
  struct ext2_inode_cache_ent {
        ext2_ino_t              ino;
 -      struct ext2_inode       inode;
 +      struct ext2_inode       *inode;
  };
  
  /* Function prototypes */
@@@ -95,23 -95,6 +95,23 @@@ struct ext2fs_numeric_progress_struct 
        int             skip_progress;
  };
  
 +/*
 + * progress callback functions
 + */
 +struct ext2fs_progress_ops {
 +      void (*init)(ext2_filsys fs,
 +                   struct ext2fs_numeric_progress_struct * progress,
 +                   const char *label, __u64 max);
 +      void (*update)(ext2_filsys fs,
 +                     struct ext2fs_numeric_progress_struct * progress,
 +                     __u64 val);
 +      void (*close)(ext2_filsys fs,
 +                    struct ext2fs_numeric_progress_struct * progress,
 +                    const char *message);
 +};
 +
 +extern struct ext2fs_progress_ops ext2fs_numeric_progress_ops;
 +
  extern void ext2fs_numeric_progress_init(ext2_filsys fs,
                                         struct ext2fs_numeric_progress_struct * progress,
                                         const char *label, __u64 max);
@@@ -158,3 -141,7 +158,7 @@@ extern errcode_t ext2fs_get_generic_bma
  extern void ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap,const char *func);
  
  extern int ext2fs_mem_is_zero(const char *mem, size_t len);
+ int ext2fs_file_block_offset_too_big(ext2_filsys fs,
+                                    struct ext2_inode *inode,
+                                    blk64_t offset);
index 2880afa268b4bbed7a5a3fa19a36408d686574e5,9ba770172b06f804379e738818459cf434c610f9..36e0240171a7e3fd3df7d697ef5eb2901cbde65c
@@@ -80,7 -80,7 +80,7 @@@ static void warn_bitmap(ext2fs_generic_
  #endif
  }
  
 -#ifdef BMAP_STATS_OPS
 +#ifdef ENABLE_BMAP_STATS_OPS
  #define INC_STAT(map, name) map->stats.name
  #else
  #define INC_STAT(map, name) ;;
@@@ -124,10 -124,11 +124,11 @@@ errcode_t ext2fs_alloc_generic_bmap(ext
        if (retval)
                return retval;
  
 -#ifdef BMAP_STATS
 +#ifdef ENABLE_BMAP_STATS
        if (gettimeofday(&bitmap->stats.created,
                         (struct timezone *) NULL) == -1) {
                perror("gettimeofday");
+               ext2fs_free_mem(&bitmap);
                return 1;
        }
        bitmap->stats.type = type;
        return 0;
  }
  
 -#ifdef BMAP_STATS
 +#ifdef ENABLE_BMAP_STATS
  static void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap bitmap)
  {
        struct ext2_bmap_statistics *stats = &bitmap->stats;
 -#ifdef BMAP_STATS_OPS
 +#ifdef ENABLE_BMAP_STATS_OPS
        float mark_seq_perc = 0.0, test_seq_perc = 0.0;
        float mark_back_perc = 0.0, test_back_perc = 0.0;
  #endif
        double inuse;
        struct timeval now;
  
 -#ifdef BMAP_STATS_OPS
 +#ifdef ENABLE_BMAP_STATS_OPS
        if (stats->test_count) {
                test_seq_perc = ((float)stats->test_seq /
                                 stats->test_count) * 100;
        fprintf(stderr, "\n[+] %s bitmap (type %d)\n", bitmap->description,
                stats->type);
        fprintf(stderr, "=================================================\n");
 -#ifdef BMAP_STATS_OPS
 +#ifdef ENABLE_BMAP_STATS_OPS
        fprintf(stderr, "%16llu bits long\n",
                bitmap->real_end - bitmap->start);
        fprintf(stderr, "%16lu copy_bmap\n%16lu resize_bmap\n",
        fprintf(stderr, "%16llu bits marked backwards (%.2f%%)\n"
                "%16.2f seconds in use\n",
                stats->mark_back, mark_back_perc, inuse);
 -#endif /* BMAP_STATS_OPS */
 +#endif /* ENABLE_BMAP_STATS_OPS */
  }
  #endif
  
@@@ -253,7 -254,7 +254,7 @@@ void ext2fs_free_generic_bmap(ext2fs_ge
        if (!EXT2FS_IS_64_BITMAP(bmap))
                return;
  
 -#ifdef BMAP_STATS
 +#ifdef ENABLE_BMAP_STATS
        if (getenv("E2FSPROGS_BITMAP_STATS")) {
                ext2fs_print_bmap_statistics(bmap);
                bmap->bitmap_ops->print_stats(bmap);
@@@ -293,13 -294,14 +294,14 @@@ errcode_t ext2fs_copy_generic_bmap(ext2
                return retval;
  
  
 -#ifdef BMAP_STATS_OPS
 +#ifdef ENABLE_BMAP_STATS_OPS
        src->stats.copy_count++;
  #endif
 -#ifdef BMAP_STATS
 +#ifdef ENABLE_BMAP_STATS
        if (gettimeofday(&new_bmap->stats.created,
                         (struct timezone *) NULL) == -1) {
                perror("gettimeofday");
+               ext2fs_free_mem(&new_bmap);
                return 1;
        }
        new_bmap->stats.type = src->stats.type;
                        ext2fs_free_mem(&new_bmap);
                        return retval;
                }
 -              sprintf(new_descr, "copy of %s", descr);
 +              strcpy(new_descr, "copy of ");
 +              strcat(new_descr, descr);
                new_bmap->description = new_descr;
        }
  
@@@ -443,7 -444,7 +445,7 @@@ int ext2fs_mark_generic_bmap(ext2fs_gen
  
        arg >>= bitmap->cluster_bits;
  
 -#ifdef BMAP_STATS_OPS
 +#ifdef ENABLE_BMAP_STATS_OPS
        if (arg == bitmap->stats.last_marked + 1)
                bitmap->stats.mark_seq++;
        if (arg < bitmap->stats.last_marked)
@@@ -510,7 -511,7 +512,7 @@@ int ext2fs_test_generic_bmap(ext2fs_gen
  
        arg >>= bitmap->cluster_bits;
  
 -#ifdef BMAP_STATS_OPS
 +#ifdef ENABLE_BMAP_STATS_OPS
        bitmap->stats.test_count++;
        if (arg == bitmap->stats.last_tested + 1)
                bitmap->stats.test_seq++;
diff --combined lib/ext2fs/newdir.c
index d134bdf04a6de35fdbf8ff7197fa882ee7876f8d,3e2c0dbec9a8e2658c9ab4aae1a8abff91427123..44e4ca9592d1f7ccad062c4970dd61112b686d34
@@@ -34,8 -34,6 +34,8 @@@ errcode_t ext2fs_new_dir_block(ext2_fil
        char                    *buf;
        int                     rec_len;
        int                     filetype = 0;
 +      struct ext2_dir_entry_tail      *t;
 +      int                     csum_size = 0;
  
        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
  
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
  
 -      retval = ext2fs_set_rec_len(fs, fs->blocksize, dir);
 +      if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                     EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
 +              csum_size = sizeof(struct ext2_dir_entry_tail);
 +
 +      retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir);
-       if (retval)
+       if (retval) {
+               ext2fs_free_mem(&buf);
                return retval;
+       }
  
        if (dir_ino) {
                if (fs->super->s_feature_incompat &
                    EXT2_FEATURE_INCOMPAT_FILETYPE)
 -                      filetype = EXT2_FT_DIR << 8;
 +                      filetype = EXT2_FT_DIR;
                /*
                 * Set up entry for '.'
                 */
                dir->inode = dir_ino;
 -              dir->name_len = 1 | filetype;
 +              ext2fs_dirent_set_name_len(dir, 1);
 +              ext2fs_dirent_set_file_type(dir, filetype);
                dir->name[0] = '.';
 -              rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1);
 +              rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
                dir->rec_len = EXT2_DIR_REC_LEN(1);
  
                /*
                 */
                dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
                retval = ext2fs_set_rec_len(fs, rec_len, dir);
-               if (retval)
+               if (retval) {
+                       ext2fs_free_mem(&buf);
                        return retval;
+               }
                dir->inode = parent_ino;
 -              dir->name_len = 2 | filetype;
 +              ext2fs_dirent_set_name_len(dir, 2);
 +              ext2fs_dirent_set_file_type(dir, filetype);
                dir->name[0] = '.';
                dir->name[1] = '.';
  
        }
 +
 +      if (csum_size) {
 +              t = EXT2_DIRENT_TAIL(buf, fs->blocksize);
 +              ext2fs_initialize_dirent_tail(fs, t);
 +      }
        *block = buf;
        return 0;
  }
diff --combined misc/dumpe2fs.c
index 8be7ce24356e5afaa5d2f1c163be3228650355bd,d0ea6c340174eaafe44eb93a917632b3bb88ee91..3dbfcb9cf61e7b831aaff4c9ddc6fc925aeca27d
@@@ -121,7 -121,7 +121,7 @@@ static void print_bg_opts(ext2_filsys f
  {
        int first = 1, bg_flags = 0;
  
 -      if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
 +      if (ext2fs_has_group_desc_csum(fs))
                bg_flags = ext2fs_bg_flags(fs, i);
  
        print_bg_opt(bg_flags, EXT2_BG_INODE_UNINIT, "INODE_UNINIT",
@@@ -162,6 -162,7 +162,7 @@@ static void list_desc (ext2_filsys fs
        int has_super;
        blk64_t         blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
        ext2_ino_t      ino_itr = 1;
+       errcode_t       retval;
  
        if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
                                       EXT4_FEATURE_RO_COMPAT_BIGALLOC))
                print_range(first_block, last_block);
                fputs(")", stdout);
                print_bg_opts(fs, i);
 -              if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
 +              if (ext2fs_has_group_desc_csum(fs)) {
                        unsigned csum = ext2fs_bg_checksum(fs, i);
                        unsigned exp_csum = ext2fs_group_desc_csum(fs, i);
  
                print_number(ext2fs_block_bitmap_loc(fs, i));
                print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0,
                                    first_block, last_block);
 +              if (fs->super->s_feature_ro_compat &
 +                  EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
 +                      printf(_(", csum 0x%08x"),
 +                             ext2fs_block_bitmap_checksum(fs, i));
                fputs(_(", Inode bitmap at "), stdout);
                print_number(ext2fs_inode_bitmap_loc(fs, i));
                print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,
                                    first_block, last_block);
 +              if (fs->super->s_feature_ro_compat &
 +                  EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
 +                      printf(_(", csum 0x%08x"),
 +                             ext2fs_inode_bitmap_checksum(fs, i));
                fputs(_("\n  Inode table at "), stdout);
                print_range(ext2fs_inode_table_loc(fs, i),
                            ext2fs_inode_table_loc(fs, i) +
                                ext2fs_bg_itable_unused(fs, i));
                if (block_bitmap) {
                        fputs(_("  Free blocks: "), stdout);
-                       ext2fs_get_block_bitmap_range2(fs->block_map,
+                       retval = ext2fs_get_block_bitmap_range2(fs->block_map,
                                 blk_itr, block_nbytes << 3, block_bitmap);
-                       print_free(i, block_bitmap,
-                                  fs->super->s_clusters_per_group,
-                                  fs->super->s_first_data_block,
-                                  EXT2FS_CLUSTER_RATIO(fs));
+                       if (retval)
+                               com_err("list_desc", retval,
+                                       "while reading block bitmap");
+                       else
+                               print_free(i, block_bitmap,
+                                          fs->super->s_clusters_per_group,
+                                          fs->super->s_first_data_block,
+                                          EXT2FS_CLUSTER_RATIO(fs));
                        fputc('\n', stdout);
                        blk_itr += fs->super->s_clusters_per_group;
                }
                if (inode_bitmap) {
                        fputs(_("  Free inodes: "), stdout);
-                       ext2fs_get_inode_bitmap_range2(fs->inode_map,
+                       retval = ext2fs_get_inode_bitmap_range2(fs->inode_map,
                                 ino_itr, inode_nbytes << 3, inode_bitmap);
-                       print_free(i, inode_bitmap,
-                                  fs->super->s_inodes_per_group, 1, 1);
+                       if (retval)
+                               com_err("list_desc", retval,
+                                       "while reading inode bitmap");
+                       else
+                               print_free(i, inode_bitmap,
+                                          fs->super->s_inodes_per_group,
+                                          1, 1);
                        fputc('\n', stdout);
                        ino_itr += fs->super->s_inodes_per_group;
                }
@@@ -324,16 -326,6 +334,16 @@@ static void list_bad_blocks(ext2_filsy
        ext2fs_badblocks_list_free(bb_list);
  }
  
 +static const char *journal_checksum_type_str(__u8 type)
 +{
 +      switch (type) {
 +      case JBD2_CRC32C_CHKSUM:
 +              return "crc32c";
 +      default:
 +              return "unknown";
 +      }
 +}
 +
  static void print_inline_journal_information(ext2_filsys fs)
  {
        journal_superblock_t    *jsb;
               (unsigned int)ntohl(jsb->s_maxlen),
               (unsigned int)ntohl(jsb->s_sequence),
               (unsigned int)ntohl(jsb->s_start));
 +      if (jsb->s_feature_compat &
 +          ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
 +              printf(_("Journal checksum type:    crc32\n"));
 +      if (jsb->s_feature_incompat &
 +          ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
 +              printf(_("Journal checksum type:    %s\n"
 +                       "Journal checksum:         0x%08x\n"),
 +                     journal_checksum_type_str(jsb->s_checksum_type),
 +                     ext2fs_be32_to_cpu(jsb->s_checksum));
        if (jsb->s_errno != 0)
                printf(_("Journal errno:            %d\n"),
                       (int) ntohl(jsb->s_errno));
@@@ -437,16 -420,6 +447,16 @@@ static void print_journal_information(e
                exit(1);
        }
  
 +      if (jsb->s_feature_compat &
 +          ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
 +              printf(_("Journal checksum type:    crc32\n"));
 +      if (jsb->s_feature_incompat &
 +          ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
 +              printf(_("Journal checksum type:    %s\n"
 +                       "Journal checksum:         0x%08x\n"),
 +                     journal_checksum_type_str(jsb->s_checksum_type),
 +                     ext2fs_be32_to_cpu(jsb->s_checksum));
 +
        printf(_("\nJournal block size:       %u\n"
                 "Journal length:           %u\n"
                 "Journal first block:      %u\n"
diff --combined misc/mke2fs.c
index 67c922528da05d81b5b8e849bafc89ae1dcec3a2,74df434316864c0b415945106b4ae44668bc46d0..c1cbcaafbc9ffb19c9e5a9fde664aa834dea9c13
@@@ -93,7 -93,7 +93,7 @@@ gid_t root_gid
  int   journal_size;
  int   journal_flags;
  int   lazy_itable_init;
- char  *bad_blocks_filename;
+ char  *bad_blocks_filename = NULL;
  __u32 fs_stride;
  int   quotatype = -1;  /* Initialize both user and group quotas by default */
  
@@@ -308,27 -308,6 +308,27 @@@ _("Warning: the backup superblock/grou
        ext2fs_badblocks_list_iterate_end(bb_iter);
  }
  
 +static void write_reserved_inodes(ext2_filsys fs)
 +{
 +      errcode_t       retval;
 +      ext2_ino_t      ino;
 +      struct ext2_inode *inode;
 +
 +      retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode);
 +      if (retval) {
 +              com_err("inode_init", retval,
 +                      "while allocating memory");
 +              exit(1);
 +      }
 +      bzero(inode, EXT2_INODE_SIZE(fs->super));
 +
 +      for (ino = 1; ino < EXT2_FIRST_INO(fs->super); ino++)
 +              ext2fs_write_inode_full(fs, ino, inode,
 +                                      EXT2_INODE_SIZE(fs->super));
 +
 +      ext2fs_free_mem(&inode);
 +}
 +
  static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed)
  {
        errcode_t       retval;
        ext2fs_zero_blocks2(0, 0, 0, 0, 0);
        ext2fs_numeric_progress_close(fs, &progress,
                                      _("done                            \n"));
 +
 +      /* Reserved inodes must always have correct checksums */
 +      if (fs->super->s_creator_os == EXT2_OS_LINUX &&
 +          fs->super->s_feature_ro_compat &
 +          EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
 +              write_reserved_inodes(fs);
  }
  
  static void create_root_dir(ext2_filsys fs)
@@@ -656,23 -629,6 +656,23 @@@ static void show_stats(ext2_filsys fs
        printf("\n\n");
  }
  
 +/*
 + * Returns true if making a file system for the Hurd, else 0
 + */
 +static int for_hurd(const char *os)
 +{
 +      if (!os) {
 +#ifdef __GNU__
 +              return 1;
 +#else
 +              return 0;
 +#endif
 +      }
 +      if (isdigit(*os))
 +              return (atoi(os) == EXT2_OS_HURD);
 +      return (strcasecmp(os, "GNU") == 0 || strcasecmp(os, "hurd") == 0);
 +}
 +
  /*
   * Set the S_CREATOR_OS field.  Return true if OS is known,
   * otherwise, 0.
@@@ -941,7 -897,7 +941,7 @@@ static __u32 ok_features[3] = 
  #ifdef CONFIG_QUOTA
                EXT4_FEATURE_RO_COMPAT_QUOTA|
  #endif
 -              0
 +              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
  };
  
  
@@@ -1070,11 -1026,15 +1070,11 @@@ static char **parse_fs_type(const char 
        const char      *size_type;
        struct str_list list;
        unsigned long long meg;
 -      int             is_hurd = 0;
 +      int             is_hurd = for_hurd(creator_os);
  
        if (init_list(&list))
                return 0;
  
 -      if (creator_os && (!strcasecmp(creator_os, "GNU") ||
 -                         !strcasecmp(creator_os, "hurd")))
 -              is_hurd = 1;
 -
        if (fs_type)
                ext_type = fs_type;
        else if (is_hurd)
  
        parse_str = malloc(strlen(usage_types)+1);
        if (!parse_str) {
+               free(profile_type);
                free(list.list);
                return 0;
        }
@@@ -1298,6 -1259,21 +1299,21 @@@ static void PRS(int argc, char *argv[]
        char *          fs_type = 0;
        char *          usage_types = 0;
        blk64_t         dev_size;
+       /*
+        * NOTE: A few words about fs_blocks_count and blocksize:
+        *
+        * Initially, blocksize is set to zero, which implies 1024.
+        * If -b is specified, blocksize is updated to the user's value.
+        *
+        * Next, the device size or the user's "blocks" command line argument
+        * is used to set fs_blocks_count; the units are blocksize.
+        *
+        * Later, if blocksize hasn't been set and the profile specifies a
+        * blocksize, then blocksize is updated and fs_blocks_count is scaled
+        * appropriately.  Note the change in units!
+        *
+        * Finally, we complain about fs_blocks_count > 2^32 on a non-64bit fs.
+        */
        blk64_t         fs_blocks_count = 0;
  #ifdef __linux__
        struct          utsname ut;
@@@ -1494,7 -1470,8 +1510,8 @@@ profile_error
                        discard = 0;
                        break;
                case 'l':
-                       bad_blocks_filename = malloc(strlen(optarg)+1);
+                       bad_blocks_filename = realloc(bad_blocks_filename,
+                                                     strlen(optarg) + 1);
                        if (!bad_blocks_filename) {
                                com_err(program_name, ENOMEM,
                                        _("in malloc for bad_blocks_filename"));
                tmp = get_string_from_profile(fs_types, "default_features",
                                              "");
        }
 +      /* Mask off features which aren't supported by the Hurd */
 +      if (for_hurd(creator_os)) {
 +              fs_param.s_feature_incompat &= ~EXT2_FEATURE_INCOMPAT_FILETYPE;
 +              fs_param.s_feature_ro_compat &=
 +                      ~(EXT4_FEATURE_RO_COMPAT_HUGE_FILE |
 +                        EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
 +      }
        edit_feature(fs_features ? fs_features : tmp,
                     &fs_param.s_feature_compat);
        if (tmp)
                free(tmp);
 +      /*
 +       * If the user specified features incompatible with the Hurd, complain
 +       */
 +      if (for_hurd(creator_os)) {
 +              if (fs_param.s_feature_incompat &
 +                  EXT2_FEATURE_INCOMPAT_FILETYPE) {
 +                      fprintf(stderr, _("The HURD does not support the "
 +                                        "filetype feature.\n"));
 +                      exit(1);
 +              }
 +              if (fs_param.s_feature_ro_compat &
 +                  EXT4_FEATURE_RO_COMPAT_HUGE_FILE) {
 +                      fprintf(stderr, _("The HURD does not support the "
 +                                        "huge_file feature.\n"));
 +                      exit(1);
 +              }
 +              if (fs_param.s_feature_ro_compat &
 +                  EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
 +                      fprintf(stderr, _("The HURD does not support the "
 +                                        "metadata_csum feature.\n"));
 +                      exit(1);
 +              }
 +      }
  
+       /* Get the hardware sector sizes, if available */
+       retval = ext2fs_get_device_sectsize(device_name, &lsector_size);
+       if (retval) {
+               com_err(program_name, retval,
+                       _("while trying to determine hardware sector size"));
+               exit(1);
+       }
+       retval = ext2fs_get_device_phys_sectsize(device_name, &psector_size);
+       if (retval) {
+               com_err(program_name, retval,
+                       _("while trying to determine physical sector size"));
+               exit(1);
+       }
+       tmp = getenv("MKE2FS_DEVICE_SECTSIZE");
+       if (tmp != NULL)
+               lsector_size = atoi(tmp);
+       tmp = getenv("MKE2FS_DEVICE_PHYS_SECTSIZE");
+       if (tmp != NULL)
+               psector_size = atoi(tmp);
+       /* Older kernels may not have physical/logical distinction */
+       if (!psector_size)
+               psector_size = lsector_size;
+       if (blocksize <= 0) {
+               use_bsize = get_int_from_profile(fs_types, "blocksize", 4096);
+               if (use_bsize == -1) {
+                       use_bsize = sys_page_size;
+                       if ((linux_version_code < (2*65536 + 6*256)) &&
+                           (use_bsize > 4096))
+                               use_bsize = 4096;
+               }
+               if (lsector_size && use_bsize < lsector_size)
+                       use_bsize = lsector_size;
+               if ((blocksize < 0) && (use_bsize < (-blocksize)))
+                       use_bsize = -blocksize;
+               blocksize = use_bsize;
+               fs_blocks_count /= (blocksize / 1024);
+       } else {
+               if (blocksize < lsector_size) {                 /* Impossible */
+                       com_err(program_name, EINVAL,
+                               _("while setting blocksize; too small "
+                                 "for device\n"));
+                       exit(1);
+               } else if ((blocksize < psector_size) &&
+                          (psector_size <= sys_page_size)) {   /* Suboptimal */
+                       fprintf(stderr, _("Warning: specified blocksize %d is "
+                               "less than device physical sectorsize %d\n"),
+                               blocksize, psector_size);
+               }
+       }
+       fs_param.s_log_block_size =
+               int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
        /*
         * We now need to do a sanity check of fs_blocks_count for
         * 32-bit vs 64-bit block number support.
         */
-       if ((fs_blocks_count > MAX_32_NUM) && (blocksize == 0)) {
-               fs_blocks_count /= 4; /* Try using a 4k blocksize */
-               blocksize = 4096;
-               fs_param.s_log_block_size = 2;
-       }
        if ((fs_blocks_count > MAX_32_NUM) &&
            !(fs_param.s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) &&
            get_bool_from_profile(fs_types, "auto_64-bit_support", 0)) {
        if ((fs_param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
            ((tmp = getenv("MKE2FS_FIRST_META_BG"))))
                fs_param.s_first_meta_bg = atoi(tmp);
-       /* Get the hardware sector sizes, if available */
-       retval = ext2fs_get_device_sectsize(device_name, &lsector_size);
-       if (retval) {
-               com_err(program_name, retval,
-                       _("while trying to determine hardware sector size"));
-               exit(1);
-       }
-       retval = ext2fs_get_device_phys_sectsize(device_name, &psector_size);
-       if (retval) {
-               com_err(program_name, retval,
-                       _("while trying to determine physical sector size"));
-               exit(1);
-       }
-       if ((tmp = getenv("MKE2FS_DEVICE_SECTSIZE")) != NULL)
-               lsector_size = atoi(tmp);
-       if ((tmp = getenv("MKE2FS_DEVICE_PHYS_SECTSIZE")) != NULL)
-               psector_size = atoi(tmp);
-       /* Older kernels may not have physical/logical distinction */
-       if (!psector_size)
-               psector_size = lsector_size;
-       if (blocksize <= 0) {
-               use_bsize = get_int_from_profile(fs_types, "blocksize", 4096);
-               if (use_bsize == -1) {
-                       use_bsize = sys_page_size;
-                       if ((linux_version_code < (2*65536 + 6*256)) &&
-                           (use_bsize > 4096))
-                               use_bsize = 4096;
-               }
-               if (lsector_size && use_bsize < lsector_size)
-                       use_bsize = lsector_size;
-               if ((blocksize < 0) && (use_bsize < (-blocksize)))
-                       use_bsize = -blocksize;
-               blocksize = use_bsize;
-               ext2fs_blocks_count_set(&fs_param,
-                                       ext2fs_blocks_count(&fs_param) /
-                                       (blocksize / 1024));
-       } else {
-               if (blocksize < lsector_size) {                 /* Impossible */
-                       com_err(program_name, EINVAL,
-                               _("while setting blocksize; too small "
-                                 "for device\n"));
-                       exit(1);
-               } else if ((blocksize < psector_size) &&
-                          (psector_size <= sys_page_size)) {   /* Suboptimal */
-                       fprintf(stderr, _("Warning: specified blocksize %d is "
-                               "less than device physical sectorsize %d\n"),
-                               blocksize, psector_size);
-               }
-       }
-       fs_param.s_log_block_size =
-               int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
        if (fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_BIGALLOC) {
                if (!cluster_size)
                        cluster_size = get_int_from_profile(fs_types,
        if (extended_opts)
                parse_extended_opts(&fs_param, extended_opts);
  
 +      /* Don't allow user to set both metadata_csum and uninit_bg bits. */
 +      if ((fs_param.s_feature_ro_compat &
 +           EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
 +          (fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
 +              fs_param.s_feature_ro_compat &=
 +                              ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
 +
        /* Can't support bigalloc feature without extents feature */
        if ((fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_BIGALLOC) &&
            !(fs_param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)) {
@@@ -2190,8 -2125,7 +2202,8 @@@ static int should_do_undo(const char *n
        int csum_flag, force_undo;
  
        csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(&fs_param,
 -                                             EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
 +                              EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
 +                              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
        force_undo = get_int_from_profile(fs_types, "force_undo", 0);
        if (!force_undo && (!csum_flag || !lazy_itable_init))
                return 0;
@@@ -2252,8 -2186,11 +2264,11 @@@ static int mke2fs_setup_tdb(const char 
        }
  
        if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0) ||
-           access(tdb_dir, W_OK))
+           access(tdb_dir, W_OK)) {
+               if (free_tdb_dir)
+                       free(tdb_dir);
                return 0;
+       }
  
        tmp_name = strdup(name);
        if (!tmp_name)
@@@ -2446,26 -2383,6 +2461,26 @@@ int main (int argc, char *argv[]
                com_err(device_name, retval, _("while setting up superblock"));
                exit(1);
        }
 +      fs->progress_ops = &ext2fs_numeric_progress_ops;
 +
 +      /* Check the user's mkfs options for metadata checksumming */
 +      if (!quiet &&
 +          EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                     EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
 +              if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
 +                              EXT3_FEATURE_INCOMPAT_EXTENTS))
 +                      printf(_("Extents are not enabled.  The file extent "
 +                               "tree can be checksummed, whereas block maps "
 +                               "cannot.  Not enabling extents reduces the "
 +                               "coverage of metadata checksumming.  "
 +                               "Pass -O extents to rectify.\n"));
 +              if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
 +                              EXT4_FEATURE_INCOMPAT_64BIT))
 +                      printf(_("64-bit filesystem support is not "
 +                               "enabled.  The larger fields afforded by "
 +                               "this feature enable full-strength "
 +                               "checksumming.  Pass -O 64bit to rectify.\n"));
 +      }
  
        /* Calculate journal blocks */
        if (!journal_device && ((journal_size) ||
            (fs_param.s_feature_ro_compat &
             (EXT4_FEATURE_RO_COMPAT_HUGE_FILE|EXT4_FEATURE_RO_COMPAT_GDT_CSUM|
              EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 +            EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|
              EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)))
                fs->super->s_kbytes_written = 1;
  
                }
        } else
                uuid_generate(fs->super->s_uuid);
 +      ext2fs_init_csum_seed(fs);
  
        /*
         * Initialize the directory index variables
                        sizeof(fs->super->s_last_mounted));
        }
  
 +      if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                     EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
 +              fs->super->s_checksum_type = EXT2_CRC32C_CHKSUM;
 +
        if (!quiet || noaction)
                show_stats(fs);
  
                 * inodes as unused; we want e2fsck to consider all
                 * inodes as potentially containing recoverable data.
                 */
 -              if (fs->super->s_feature_ro_compat &
 -                  EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
 +              if (ext2fs_has_group_desc_csum(fs)) {
                        for (i = 0; i < fs->group_desc_count; i++)
                                ext2fs_bg_itable_unused_set(fs, i, 0);
                }