Whamcloud - gitweb
Merge branch 'maint' into next
authorTheodore Ts'o <tytso@mit.edu>
Wed, 3 Dec 2014 03:15:25 +0000 (22:15 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 3 Dec 2014 03:15:25 +0000 (22:15 -0500)
1  2 
lib/ext2fs/alloc_stats.c
lib/ext2fs/bmap.c
misc/dumpe2fs.c
misc/mk_hugefiles.c
misc/tune2fs.c

diff --combined lib/ext2fs/alloc_stats.c
@@@ -20,13 -20,13 +20,13 @@@ void ext2fs_inode_alloc_stats2(ext2_fil
  {
        int     group = ext2fs_group_of_ino(fs, ino);
  
- #ifndef OMIT_COM_ERR
        if (ino > fs->super->s_inodes_count) {
+ #ifndef OMIT_COM_ERR
                com_err("ext2fs_inode_alloc_stats2", 0,
                        "Illegal inode number: %lu", (unsigned long) ino);
+ #endif
                return;
        }
- #endif
        if (inuse > 0)
                ext2fs_mark_inode_bitmap2(fs->inode_map, ino);
        else
@@@ -38,7 -38,8 +38,7 @@@
        /* We don't strictly need to be clearing the uninit flag if inuse < 0
         * (i.e. freeing inodes) but it also means something is bad. */
        ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT);
 -      if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 -                                     EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
 +      if (ext2fs_has_group_desc_csum(fs)) {
                ext2_ino_t first_unused_inode = fs->super->s_inodes_per_group -
                        ext2fs_bg_itable_unused(fs, group) +
                        group * fs->super->s_inodes_per_group + 1;
@@@ -62,13 -63,13 +62,13 @@@ void ext2fs_block_alloc_stats2(ext2_fil
  {
        int     group = ext2fs_group_of_blk2(fs, blk);
  
- #ifndef OMIT_COM_ERR
        if (blk >= ext2fs_blocks_count(fs->super)) {
+ #ifndef OMIT_COM_ERR
                com_err("ext2fs_block_alloc_stats", 0,
                        "Illegal block number: %lu", (unsigned long) blk);
+ #endif
                return;
        }
- #endif
        if (inuse > 0)
                ext2fs_mark_block_bitmap2(fs->block_map, blk);
        else
@@@ -129,7 -130,7 +129,7 @@@ void ext2fs_block_alloc_stats_range(ext
        while (num) {
                int group = ext2fs_group_of_blk2(fs, blk);
                blk64_t last_blk = ext2fs_group_last_block2(fs, group);
 -              blk_t n = num;
 +              blk64_t n = num;
  
                if (blk + num > last_blk)
                        n = last_blk - blk + 1;
diff --combined lib/ext2fs/bmap.c
@@@ -67,7 -67,7 +67,7 @@@ static _BMAP_INLINE_ errcode_t block_in
  #endif
  
        if (!b && (flags & BMAP_ALLOC)) {
-               b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
+               b = nr ? ext2fs_le32_to_cpu(((blk_t *)block_buf)[nr - 1]) : ind;
                retval = ext2fs_alloc_block(fs, b,
                                            block_buf + fs->blocksize, &b);
                if (retval)
@@@ -321,13 -321,6 +321,13 @@@ errcode_t ext2fs_bmap2(ext2_filsys fs, 
        if (ext2fs_file_block_offset_too_big(fs, inode, block))
                return EXT2_ET_FILE_TOO_BIG;
  
 +      /*
 +       * If an inode has inline data, that means that it doesn't have
 +       * any blocks and we shouldn't map any blocks for it.
 +       */
 +      if (inode->i_flags & EXT4_INLINE_DATA_FL)
 +              return EXT2_ET_INLINE_DATA_NO_BLOCK;
 +
        if (!block_buf) {
                retval = ext2fs_get_array(2, fs->blocksize, &buf);
                if (retval)
diff --combined misc/dumpe2fs.c
@@@ -42,7 -42,6 +42,7 @@@ extern int optind
  
  #include "../version.h"
  #include "nls-enable.h"
 +#include "plausible.h"
  
  #define in_use(m, x)  (ext2fs_test_bit ((x), (m)))
  
@@@ -53,9 -52,9 +53,9 @@@ static int blocks64 = 0
  
  static void usage(void)
  {
 -      fprintf (stderr, _("Usage: %s [-bfhixV] [-o superblock=<num>] "
 +      fprintf(stderr, _("Usage: %s [-bfghixV] [-o superblock=<num>] "
                 "[-o blocksize=<num>] device\n"), program_name);
 -      exit (1);
 +      exit(1);
  }
  
  static void print_number(unsigned long long num)
@@@ -122,7 -121,7 +122,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",
@@@ -151,7 -150,7 +151,7 @@@ static void print_bg_rel_offset(ext2_fi
        }
  }
  
 -static void list_desc (ext2_filsys fs)
 +static void list_desc(ext2_filsys fs, int grp_only)
  {
        unsigned long i;
        blk64_t first_block, last_block;
                old_desc_blocks = fs->super->s_first_meta_bg;
        else
                old_desc_blocks = fs->desc_blocks;
 +      if (grp_only)
 +              printf("group:block:super:gdt:bbitmap:ibitmap:itable\n");
        for (i = 0; i < fs->group_desc_count; i++) {
                first_block = ext2fs_group_first_block2(fs, i);
                last_block = ext2fs_group_last_block2(fs, i);
                ext2fs_super_and_bgd_loc2(fs, i, &super_blk,
                                          &old_desc_blk, &new_desc_blk, 0);
  
 +              if (grp_only) {
 +                      printf("%lu:%llu:", i, first_block);
 +                      if (i == 0 || super_blk)
 +                              printf("%llu:", super_blk);
 +                      else
 +                              printf("-1:");
 +                      if (old_desc_blk) {
 +                              print_range(old_desc_blk,
 +                                          old_desc_blk + old_desc_blocks - 1);
 +                              printf(":");
 +                      } else if (new_desc_blk)
 +                              printf("%llu:", new_desc_blk);
 +                      else
 +                              printf("-1:");
 +                      printf("%llu:%llu:%llu\n",
 +                             ext2fs_block_bitmap_loc(fs, i),
 +                             ext2fs_inode_bitmap_loc(fs, i),
 +                             ext2fs_inode_table_loc(fs, i));
 +                      continue;
 +              }
 +
                printf (_("Group %lu: (Blocks "), i);
                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) +
@@@ -358,16 -326,6 +358,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("%s", _("Journal checksum type:    crc32\n"));
 +      if ((jsb->s_feature_incompat &
 +           ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V3)) ||
 +          (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));
@@@ -457,9 -404,8 +457,9 @@@ static void print_journal_information(e
        errcode_t       retval;
        char            buf[1024];
        char            str[80];
 -      unsigned int    i;
 +      unsigned int    i, j, printed = 0;
        journal_superblock_t    *jsb;
 +      __u32                   *mask_ptr, mask, m;
  
        /* Get the journal superblock */
        if ((retval = io_channel_read_blk64(fs->io,
                exit(1);
        }
  
 +      if (jsb->s_feature_compat &
 +          ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
 +              printf("%s", _("Journal checksum type:    crc32\n"));
 +      if ((jsb->s_feature_incompat &
 +           ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V3)) ||
 +          (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("%s", _("Journal features:        "));
 +      for (i = 0, mask_ptr = &jsb->s_feature_compat; i < 3; i++, mask_ptr++) {
 +              mask = be32_to_cpu(*mask_ptr);
 +              for (j = 0, m = 1; j < 32; j++, m <<= 1) {
 +                      if (mask & m) {
 +                              printf(" %s", e2p_jrnl_feature2string(i, m));
 +                              printed++;
 +                      }
 +              }
 +      }
 +
        printf(_("\nJournal block size:       %u\n"
                 "Journal length:           %u\n"
                 "Journal first block:      %u\n"
@@@ -608,7 -531,6 +608,7 @@@ int main (int argc, char ** argv
        int             flags;
        int             header_only = 0;
        int             c;
 +      int             grp_only = 0;
  
  #ifdef ENABLE_NLS
        setlocale(LC_MESSAGES, "");
        if (argc && *argv)
                program_name = *argv;
  
 -      while ((c = getopt (argc, argv, "bfhixVo:")) != EOF) {
 +      while ((c = getopt(argc, argv, "bfghixVo:")) != EOF) {
                switch (c) {
                case 'b':
                        print_badblocks++;
                case 'f':
                        force++;
                        break;
 +              case 'g':
 +                      grp_only++;
 +                      break;
                case 'h':
                        header_only++;
                        break;
                        usage();
                }
        }
-       if (argc - 1 > optind) {
+       if (optind != argc - 1) {
                usage();
                exit(1);
        }
                flags |= EXT2_FLAG_FORCE;
        if (image_dump)
                flags |= EXT2_FLAG_IMAGE_FILE;
 -
 +try_open_again:
        if (use_superblock && !use_blocksize) {
                for (use_blocksize = EXT2_MIN_BLOCK_SIZE;
                     use_blocksize <= EXT2_MAX_BLOCK_SIZE;
        } else
                retval = ext2fs_open (device_name, flags, use_superblock,
                                      use_blocksize, unix_io_manager, &fs);
 +      if (retval && !(flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
 +              flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
 +              goto try_open_again;
 +      }
 +      if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
 +              printf("%s", _("\n*** Checksum errors detected in filesystem!  Run e2fsck now!\n\n"));
 +      flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
        if (retval) {
                com_err (program_name, retval, _("while trying to open %s"),
                         device_name);
                printf("%s", _("Couldn't find valid filesystem superblock.\n"));
 +              if (retval == EXT2_ET_BAD_MAGIC)
 +                      check_plausibility(device_name, CHECK_FS_EXIST, NULL);
                exit (1);
        }
        fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
        if (print_badblocks) {
                list_bad_blocks(fs, 1);
        } else {
 +              if (grp_only)
 +                      goto just_descriptors;
                list_super (fs->super);
                if (fs->super->s_feature_incompat &
                      EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
                        ext2fs_close_free(&fs);
                        exit (0);
                }
 +              fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
 +try_bitmaps_again:
                retval = ext2fs_read_bitmaps (fs);
 -              list_desc (fs);
 +              if (retval && !(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
 +                      fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
 +                      goto try_bitmaps_again;
 +              }
 +              if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
 +                      printf("%s", _("\n*** Checksum errors detected in bitmaps!  Run e2fsck now!\n\n"));
 +just_descriptors:
 +              list_desc(fs, grp_only);
                if (retval) {
                        printf(_("\n%s: %s: error reading bitmaps: %s\n"),
                               program_name, device_name,
diff --combined misc/mk_hugefiles.c
@@@ -437,7 -437,6 +437,6 @@@ static blk64_t get_start_block(ext2_fil
                                                blk, last_blk, &next);
                if (retval)
                        next = last_blk;
-               next--;
  
                if (next - blk > slack) {
                        blk += slack;
@@@ -530,10 -529,10 +529,10 @@@ errcode_t mk_hugefiles(ext2_filsys fs, 
  
        fs_blocks = ext2fs_free_blocks_count(fs->super);
        if (fs_blocks < num_slack + align)
 -              return ENOMEM;
 +              return ENOSPC;
        fs_blocks -= num_slack + align;
        if (num_blocks && num_blocks > fs_blocks)
 -              return ENOMEM;
 +              return ENOSPC;
        if (num_blocks == 0 && num_files == 0)
                num_files = 1;
  
diff --combined misc/tune2fs.c
@@@ -59,7 -59,6 +59,7 @@@ extern int optind
  #include "e2p/e2p.h"
  #include "jfs_user.h"
  #include "util.h"
 +#include "plausible.h"
  #include "blkid/blkid.h"
  #include "quota/quotaio.h"
  
@@@ -96,7 -95,6 +96,7 @@@ static char *extended_cmd
  static unsigned long new_inode_size;
  static char *ext_mount_opts;
  static int usrquota, grpquota;
 +static int rewrite_checksums;
  
  int journal_size, journal_flags;
  char *journal_device;
@@@ -112,8 -110,6 +112,8 @@@ struct blk_move 
  
  
  static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
 +static const char *please_dir_fsck =
 +              N_("Please run e2fsck -D on the filesystem.\n");
  
  #ifdef CONFIG_BUILD_FINDFS
  void do_findfs(int argc, char **argv);
@@@ -153,11 -149,10 +153,11 @@@ static __u32 ok_features[3] = 
                EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
                EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
                EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
 +              EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
  #ifdef CONFIG_QUOTA
                EXT4_FEATURE_RO_COMPAT_QUOTA |
  #endif
 -              EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
 +              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
  };
  
  static __u32 clear_ok_features[3] = {
                EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
                EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
                EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
 +              EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
  #ifdef CONFIG_QUOTA
                EXT4_FEATURE_RO_COMPAT_QUOTA |
  #endif
 -              EXT4_FEATURE_RO_COMPAT_GDT_CSUM
 +              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
  };
  
  /**
  static int get_journal_sb(ext2_filsys jfs, char buf[SUPERBLOCK_SIZE])
  {
        int retval;
 -      int start;
        journal_superblock_t *jsb;
  
        if (!(jfs->super->s_feature_incompat &
@@@ -379,7 -374,6 +379,7 @@@ static errcode_t remove_journal_inode(e
                return retval;
        }
        fs->super->s_journal_inum = 0;
 +      memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks));
        ext2fs_mark_super_dirty(fs);
  
        return 0;
@@@ -412,18 -406,6 +412,18 @@@ static int check_fsck_needed(ext2_filsy
        return 1;
  }
  
 +static void request_dir_fsck_afterwards(ext2_filsys fs)
 +{
 +      static int requested;
 +
 +      if (requested++)
 +              return;
 +      fs->super->s_state &= ~EXT2_VALID_FS;
 +      printf("\n%s\n", _(please_dir_fsck));
 +      if (mount_flags & EXT2_MF_READONLY)
 +              printf("%s", _("(and reboot afterwards!)\n"));
 +}
 +
  static void request_fsck_afterwards(ext2_filsys fs)
  {
        static int requested = 0;
                printf("%s", _("(and reboot afterwards!)\n"));
  }
  
 +/* Rewrite extents */
 +static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino,
 +                               struct ext2_inode *inode)
 +{
 +      ext2_extent_handle_t    handle;
 +      struct ext2fs_extent    extent;
 +      errcode_t               errcode;
 +      struct ext2_extent_info info;
 +
 +      if (!(inode->i_flags & EXT4_EXTENTS_FL) ||
 +          !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
 +              return 0;
 +
 +      errcode = ext2fs_extent_open(fs, ino, &handle);
 +      if (errcode)
 +              return errcode;
 +
 +      errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
 +      if (errcode)
 +              goto out;
 +
 +      do {
 +              errcode = ext2fs_extent_get_info(handle, &info);
 +              if (errcode)
 +                      break;
 +
 +              /*
 +               * If this is the first extent in an extent block that we
 +               * haven't visited, rewrite the extent to force the ETB
 +               * checksum to be rewritten.
 +               */
 +              if (info.curr_entry == 1 && info.curr_level != 0 &&
 +                  !(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)) {
 +                      errcode = ext2fs_extent_replace(handle, 0, &extent);
 +                      if (errcode)
 +                              break;
 +              }
 +
 +              /* Skip to the end of a block of leaf nodes */
 +              if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
 +                      errcode = ext2fs_extent_get(handle,
 +                                                  EXT2_EXTENT_LAST_SIB,
 +                                                  &extent);
 +                      if (errcode)
 +                              break;
 +              }
 +
 +              errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
 +      } while (errcode == 0);
 +
 +out:
 +      /* Ok if we run off the end */
 +      if (errcode == EXT2_ET_EXTENT_NO_NEXT)
 +              errcode = 0;
 +      ext2fs_extent_free(handle);
 +      return errcode;
 +}
 +
 +/*
 + * Rewrite directory blocks with checksums
 + */
 +struct rewrite_dir_context {
 +      char *buf;
 +      errcode_t errcode;
 +      ext2_ino_t dir;
 +      int is_htree;
 +};
 +
 +static int rewrite_dir_block(ext2_filsys fs,
 +                           blk64_t    *blocknr,
 +                           e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
 +                           blk64_t    ref_block EXT2FS_ATTR((unused)),
 +                           int        ref_offset EXT2FS_ATTR((unused)),
 +                           void       *priv_data)
 +{
 +      struct ext2_dx_countlimit *dcl = NULL;
 +      struct rewrite_dir_context *ctx = priv_data;
 +      int dcl_offset, changed = 0;
 +
 +      ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
 +                                            ctx->dir);
 +      if (ctx->errcode)
 +              return BLOCK_ABORT;
 +
 +      /* if htree node... */
 +      if (ctx->is_htree)
 +              ext2fs_get_dx_countlimit(fs, (struct ext2_dir_entry *)ctx->buf,
 +                                       &dcl, &dcl_offset);
 +      if (dcl) {
 +              if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
 +                      /* Ensure limit is the max size */
 +                      int max_entries = (fs->blocksize - dcl_offset) /
 +                                        sizeof(struct ext2_dx_entry);
 +                      if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) {
 +                              changed = 1;
 +                              dcl->limit = ext2fs_cpu_to_le16(max_entries);
 +                      }
 +              } else {
 +                      /* If htree block is full then rebuild the dir */
 +                      if (ext2fs_le16_to_cpu(dcl->count) ==
 +                          ext2fs_le16_to_cpu(dcl->limit)) {
 +                              request_dir_fsck_afterwards(fs);
 +                              return 0;
 +                      }
 +                      /*
 +                       * Ensure dcl->limit is small enough to leave room for
 +                       * the checksum tail.
 +                       */
 +                      int max_entries = (fs->blocksize - (dcl_offset +
 +                                              sizeof(struct ext2_dx_tail))) /
 +                                        sizeof(struct ext2_dx_entry);
 +                      if (ext2fs_le16_to_cpu(dcl->limit) != max_entries)
 +                              dcl->limit = ext2fs_cpu_to_le16(max_entries);
 +                      /* Always rewrite checksum */
 +                      changed = 1;
 +              }
 +      } else {
 +              unsigned int rec_len, name_size;
 +              char *top = ctx->buf + fs->blocksize;
 +              struct ext2_dir_entry *de = (struct ext2_dir_entry *)ctx->buf;
 +              struct ext2_dir_entry *last_de = NULL, *penultimate_de = NULL;
 +
 +              /* Find last and penultimate dirent */
 +              while ((char *)de < top) {
 +                      penultimate_de = last_de;
 +                      last_de = de;
 +                      ctx->errcode = ext2fs_get_rec_len(fs, de, &rec_len);
 +                      if (!ctx->errcode && !rec_len)
 +                              ctx->errcode = EXT2_ET_DIR_CORRUPTED;
 +                      if (ctx->errcode)
 +                              return BLOCK_ABORT;
 +                      de = (struct ext2_dir_entry *)(((char *)de) + rec_len);
 +              }
 +              ctx->errcode = ext2fs_get_rec_len(fs, last_de, &rec_len);
 +              if (ctx->errcode)
 +                      return BLOCK_ABORT;
 +              name_size = ext2fs_dirent_name_len(last_de);
 +
 +              if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
 +                      if (!penultimate_de)
 +                              return 0;
 +                      if (last_de->inode ||
 +                          name_size ||
 +                          rec_len != sizeof(struct ext2_dir_entry_tail))
 +                              return 0;
 +                      /*
 +                       * The last dirent is unused and the right length to
 +                       * have stored a checksum.  Erase it.
 +                       */
 +                      ctx->errcode = ext2fs_get_rec_len(fs, penultimate_de,
 +                                                        &rec_len);
 +                      if (!rec_len)
 +                              ctx->errcode = EXT2_ET_DIR_CORRUPTED;
 +                      if (ctx->errcode)
 +                              return BLOCK_ABORT;
 +                      ext2fs_set_rec_len(fs, rec_len +
 +                                      sizeof(struct ext2_dir_entry_tail),
 +                                      penultimate_de);
 +                      changed = 1;
 +              } else {
 +                      unsigned csum_size = sizeof(struct ext2_dir_entry_tail);
 +                      struct ext2_dir_entry_tail *t;
 +
 +                      /*
 +                       * If the last dirent looks like the tail, just update
 +                       * the checksum.
 +                       */
 +                      if (!last_de->inode &&
 +                          rec_len == csum_size) {
 +                              t = (struct ext2_dir_entry_tail *)last_de;
 +                              t->det_reserved_name_len =
 +                                              EXT2_DIR_NAME_LEN_CSUM;
 +                              changed = 1;
 +                              goto out;
 +                      }
 +                      if (name_size & 3)
 +                              name_size = (name_size & ~3) + 4;
 +                      /* If there's not enough space for the tail, e2fsck */
 +                      if (rec_len <= (8 + name_size + csum_size)) {
 +                              request_dir_fsck_afterwards(fs);
 +                              return 0;
 +                      }
 +                      /* Shorten that last de and insert the tail */
 +                      ext2fs_set_rec_len(fs, rec_len - csum_size, last_de);
 +                      t = EXT2_DIRENT_TAIL(ctx->buf, fs->blocksize);
 +                      ext2fs_initialize_dirent_tail(fs, t);
 +
 +                      /* Always update checksum */
 +                      changed = 1;
 +              }
 +      }
 +
 +out:
 +      if (!changed)
 +              return 0;
 +
 +      ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
 +                                             0, ctx->dir);
 +      if (ctx->errcode)
 +              return BLOCK_ABORT;
 +
 +      return 0;
 +}
 +
 +static errcode_t rewrite_directory(ext2_filsys fs, ext2_ino_t dir,
 +                                 struct ext2_inode *inode)
 +{
 +      errcode_t       retval;
 +      struct rewrite_dir_context ctx;
 +
 +      retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
 +      if (retval)
 +              return retval;
 +
 +      ctx.is_htree = (inode->i_flags & EXT2_INDEX_FL);
 +      ctx.dir = dir;
 +      ctx.errcode = 0;
 +      retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY |
 +                                              BLOCK_FLAG_DATA_ONLY,
 +                                     0, rewrite_dir_block, &ctx);
 +
 +      ext2fs_free_mem(&ctx.buf);
 +      if (retval)
 +              return retval;
 +
 +      return ctx.errcode;
 +}
 +
 +/*
 + * Forcibly set checksums in all inodes.
 + */
 +static void rewrite_inodes(ext2_filsys fs)
 +{
 +      int length = EXT2_INODE_SIZE(fs->super);
 +      struct ext2_inode *inode, *zero;
 +      char            *ea_buf;
 +      ext2_inode_scan scan;
 +      errcode_t       retval;
 +      ext2_ino_t      ino;
 +      blk64_t         file_acl_block;
 +      int             inode_dirty;
 +
 +      if (fs->super->s_creator_os != EXT2_OS_LINUX)
 +              return;
 +
 +      retval = ext2fs_open_inode_scan(fs, 0, &scan);
 +      if (retval) {
 +              com_err("set_csum", retval, "while opening inode scan");
 +              exit(1);
 +      }
 +
 +      retval = ext2fs_get_mem(length, &inode);
 +      if (retval) {
 +              com_err("set_csum", retval, "while allocating memory");
 +              exit(1);
 +      }
 +
 +      retval = ext2fs_get_memzero(length, &zero);
 +      if (retval) {
 +              com_err("set_csum", retval, "while allocating memory");
 +              exit(1);
 +      }
 +
 +      retval = ext2fs_get_mem(fs->blocksize, &ea_buf);
 +      if (retval) {
 +              com_err("set_csum", retval, "while allocating memory");
 +              exit(1);
 +      }
 +
 +      do {
 +              retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
 +              if (retval) {
 +                      com_err("set_csum", retval, "while getting next inode");
 +                      exit(1);
 +              }
 +              if (!ino)
 +                      break;
 +              if (ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
 +                      inode_dirty = 1;
 +              } else {
 +                      if (memcmp(inode, zero, length) != 0) {
 +                              memset(inode, 0, length);
 +                              inode_dirty = 1;
 +                      } else {
 +                              inode_dirty = 0;
 +                      }
 +              }
 +
 +              if (inode_dirty) {
 +                      retval = ext2fs_write_inode_full(fs, ino, inode,
 +                                                       length);
 +                      if (retval) {
 +                              com_err("set_csum", retval, "while writing "
 +                                      "inode");
 +                              exit(1);
 +                      }
 +              }
 +
 +              retval = rewrite_extents(fs, ino, inode);
 +              if (retval) {
 +                      com_err("rewrite_extents", retval,
 +                              "while rewriting extents");
 +                      exit(1);
 +              }
 +
 +              if (LINUX_S_ISDIR(inode->i_mode) &&
 +                  ext2fs_inode_has_valid_blocks2(fs, inode)) {
 +                      retval = rewrite_directory(fs, ino, inode);
 +                      if (retval) {
 +                              com_err("rewrite_directory", retval,
 +                                      "while rewriting directories");
 +                              exit(1);
 +                      }
 +              }
 +
 +              file_acl_block = ext2fs_file_acl_block(fs, inode);
 +              if (!file_acl_block)
 +                      continue;
 +              retval = ext2fs_read_ext_attr3(fs, file_acl_block, ea_buf, ino);
 +              if (retval) {
 +                      com_err("rewrite_eablock", retval,
 +                              "while rewriting extended attribute");
 +                      exit(1);
 +              }
 +              retval = ext2fs_write_ext_attr3(fs, file_acl_block, ea_buf,
 +                                              ino);
 +              if (retval) {
 +                      com_err("rewrite_eablock", retval,
 +                              "while rewriting extended attribute");
 +                      exit(1);
 +              }
 +      } while (ino);
 +
 +      ext2fs_free_mem(&zero);
 +      ext2fs_free_mem(&inode);
 +      ext2fs_free_mem(&ea_buf);
 +      ext2fs_close_inode_scan(scan);
 +}
 +
 +static void rewrite_metadata_checksums(ext2_filsys fs)
 +{
 +      errcode_t retval;
 +      dgrp_t i;
 +
 +      fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
 +      ext2fs_init_csum_seed(fs);
 +      for (i = 0; i < fs->group_desc_count; i++)
 +              ext2fs_group_desc_csum_set(fs, i);
 +      retval = ext2fs_read_bitmaps(fs);
 +      if (retval) {
 +              com_err("rewrite_metadata_checksums", retval,
 +                      "while reading bitmaps");
 +              exit(1);
 +      }
 +      rewrite_inodes(fs);
 +      ext2fs_mark_ib_dirty(fs);
 +      ext2fs_mark_bb_dirty(fs);
 +      ext2fs_mmp_update2(fs, 1);
 +      fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
 +      fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
 +      if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                                     EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
 +              fs->super->s_checksum_type = EXT2_CRC32C_CHKSUM;
 +      else
 +              fs->super->s_checksum_type = 0;
 +      ext2fs_mark_super_dirty(fs);
 +}
 +
 +static void enable_uninit_bg(ext2_filsys fs)
 +{
 +      struct ext2_group_desc *gd;
 +      dgrp_t i;
 +
 +      for (i = 0; i < fs->group_desc_count; i++) {
 +              gd = ext2fs_group_desc(fs, fs->group_desc, i);
 +              gd->bg_itable_unused = 0;
 +              gd->bg_flags = EXT2_BG_INODE_ZEROED;
 +              ext2fs_group_desc_csum_set(fs, i);
 +      }
 +      fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
 +}
 +
 +static errcode_t zero_empty_inodes(ext2_filsys fs)
 +{
 +      int length = EXT2_INODE_SIZE(fs->super);
 +      struct ext2_inode *inode = NULL;
 +      ext2_inode_scan scan;
 +      errcode_t       retval;
 +      ext2_ino_t      ino;
 +
 +      retval = ext2fs_open_inode_scan(fs, 0, &scan);
 +      if (retval)
 +              goto out;
 +
 +      retval = ext2fs_get_mem(length, &inode);
 +      if (retval)
 +              goto out;
 +
 +      do {
 +              retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
 +              if (retval)
 +                      goto out;
 +              if (!ino)
 +                      break;
 +              if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
 +                      memset(inode, 0, length);
 +                      retval = ext2fs_write_inode_full(fs, ino, inode,
 +                                                       length);
 +                      if (retval)
 +                              goto out;
 +              }
 +      } while (1);
 +
 +out:
 +      ext2fs_free_mem(&inode);
 +      ext2fs_close_inode_scan(scan);
 +      return retval;
 +}
 +
 +static errcode_t disable_uninit_bg(ext2_filsys fs, __u32 csum_feature_flag)
 +{
 +      struct ext2_group_desc *gd;
 +      dgrp_t i;
 +      errcode_t retval;
 +      blk64_t b, c, d;
 +
 +      /* Load bitmaps to ensure that the uninit ones get written out */
 +      fs->super->s_feature_ro_compat |= csum_feature_flag;
 +      retval = ext2fs_read_bitmaps(fs);
 +      fs->super->s_feature_ro_compat &= ~csum_feature_flag;
 +      if (retval) {
 +              com_err("disable_uninit_bg", retval,
 +                      "while reading bitmaps");
 +              request_fsck_afterwards(fs);
 +              return retval;
 +      }
 +      ext2fs_mark_ib_dirty(fs);
 +      ext2fs_mark_bb_dirty(fs);
 +
 +      /* If we're only turning off uninit_bg, zero the inodes */
 +      if (csum_feature_flag == EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
 +              retval = zero_empty_inodes(fs);
 +              if (retval) {
 +                      com_err("disable_uninit_bg", retval,
 +                              "while zeroing unused inodes");
 +                      request_fsck_afterwards(fs);
 +                      return retval;
 +              }
 +      }
 +
 +      /* The bbitmap is zeroed; we must mark group metadata blocks in use */
 +      for (i = 0; i < fs->group_desc_count; i++) {
 +              b = ext2fs_block_bitmap_loc(fs, i);
 +              ext2fs_mark_block_bitmap2(fs->block_map, b);
 +              b = ext2fs_inode_bitmap_loc(fs, i);
 +              ext2fs_mark_block_bitmap2(fs->block_map, b);
 +
 +              retval = ext2fs_super_and_bgd_loc2(fs, i, &b, &c, &d, NULL);
 +              if (retval == 0 && b)
 +                      ext2fs_mark_block_bitmap2(fs->block_map, b);
 +              if (retval == 0 && c)
 +                      ext2fs_mark_block_bitmap2(fs->block_map, c);
 +              if (retval == 0 && d)
 +                      ext2fs_mark_block_bitmap2(fs->block_map, d);
 +              if (retval) {
 +                      com_err("disable_uninit_bg", retval,
 +                              "while initializing block bitmaps");
 +                      request_fsck_afterwards(fs);
 +              }
 +
 +              gd = ext2fs_group_desc(fs, fs->group_desc, i);
 +              gd->bg_itable_unused = 0;
 +              gd->bg_flags = 0;
 +              ext2fs_group_desc_csum_set(fs, i);
 +      }
 +      fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
 +      ext2fs_mark_super_dirty(fs);
 +
 +      return 0;
 +}
 +
  /*
   * Update the feature set as provided by the user.
   */
  static int update_feature_set(ext2_filsys fs, char *features)
  {
        struct ext2_super_block *sb = fs->super;
 -      struct ext2_group_desc *gd;
        __u32           old_features[3];
 -      dgrp_t          i;
        int             type_err;
        unsigned int    mask_err;
 +      errcode_t       err;
  
  #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
                                ((&sb->s_feature_compat)[(type)] & (mask)))
@@@ -1107,76 -606,33 +1107,76 @@@ mmp_error
        }
  
        if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
 +                     EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
 +              if (check_fsck_needed(fs))
 +                      exit(1);
 +              if (mount_flags & EXT2_MF_MOUNTED)
 +                      fputs(_("Cannot enable metadata_csum on a mounted "
 +                              "filesystem!\n"), stderr);
 +              rewrite_checksums = 1;
 +              /* metadata_csum supersedes uninit_bg */
 +              fs->super->s_feature_ro_compat &=
 +                      ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
 +
 +              /* if uninit_bg was previously off, rewrite group desc */
 +              if (!(old_features[E2P_FEATURE_RO_INCOMPAT] &
 +                    EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
 +                      enable_uninit_bg(fs);
 +
 +              /*
 +               * Since metadata_csum supersedes uninit_bg, pretend like
 +               * uninit_bg has been off all along.
 +               */
 +              old_features[E2P_FEATURE_RO_INCOMPAT] &=
 +                      ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
 +      }
 +
 +      if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
 +                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
 +              if (check_fsck_needed(fs))
 +                      exit(1);
 +              if (mount_flags & EXT2_MF_MOUNTED)
 +                      fputs(_("Cannot disable metadata_csum on a mounted "
 +                              "filesystem!\n"), stderr);
 +              rewrite_checksums = 1;
 +              /*
 +               * If we're turning off metadata_csum and not turning on
 +               * uninit_bg, rewrite group desc.
 +               */
 +              if (!(fs->super->s_feature_ro_compat &
 +                    EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
 +                      err = disable_uninit_bg(fs,
 +                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
 +                      if (err)
 +                              return 1;
 +              } else
 +                      /*
 +                       * metadata_csum previously provided uninit_bg, so if
 +                       * we're also setting the uninit_bg feature bit,
 +                       * pretend like it was previously enabled.  Checksums
 +                       * will be rewritten with crc16 later.
 +                       */
 +                      old_features[E2P_FEATURE_RO_INCOMPAT] |=
 +                              EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
 +      }
 +
 +      if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
                       EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
 -              for (i = 0; i < fs->group_desc_count; i++) {
 -                      gd = ext2fs_group_desc(fs, fs->group_desc, i);
 -                      gd->bg_itable_unused = 0;
 -                      gd->bg_flags = EXT2_BG_INODE_ZEROED;
 -                      ext2fs_group_desc_csum_set(fs, i);
 -              }
 -              fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
 +              /* Do not enable uninit_bg when metadata_csum enabled */
 +              if (fs->super->s_feature_ro_compat &
 +                  EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
 +                      fs->super->s_feature_ro_compat &=
 +                              ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
 +              else
 +                      enable_uninit_bg(fs);
        }
  
        if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
                        EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
 -              for (i = 0; i < fs->group_desc_count; i++) {
 -                      gd = ext2fs_group_desc(fs, fs->group_desc, i);
 -                      if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) {
 -                              /* 
 -                               * XXX what we really should do is zap
 -                               * uninitialized inode tables instead.
 -                               */
 -                              request_fsck_afterwards(fs);
 -                              break;
 -                      }
 -                      gd->bg_itable_unused = 0;
 -                      gd->bg_flags = 0;
 -                      gd->bg_checksum = 0;
 -              }
 -              fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
 +              err = disable_uninit_bg(fs,
 +                              EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
 +              if (err)
 +                      return 1;
        }
  
        if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
@@@ -2147,6 -1603,7 +2147,7 @@@ static int inode_scan_and_fix(ext2_fils
  
  err_out:
        ext2fs_free_mem(&block_buf);
+       ext2fs_close_inode_scan(scan);
  
        return retval;
  }
@@@ -2575,7 -2032,7 +2576,7 @@@ retry_open
        if ((open_flag & EXT2_FLAG_RW) == 0 || f_flag)
                open_flag |= EXT2_FLAG_SKIP_MMP;
  
 -      open_flag |= EXT2_FLAG_64BITS;
 +      open_flag |= EXT2_FLAG_64BITS | EXT2_FLAG_JOURNAL_DEV_OK;
  
        /* keep the filesystem struct around to dump MMP data */
        open_flag |= EXT2_FLAG_NOFREE_ON_ERROR;
                        fprintf(stderr,
                                _("MMP block magic is bad. Try to fix it by "
                                  "running:\n'e2fsck -f %s'\n"), device_name);
 +              else if (retval == EXT2_ET_BAD_MAGIC)
 +                      check_plausibility(device_name, CHECK_FS_EXIST, NULL);
                else if (retval != EXT2_ET_MMP_FAILED)
                        fprintf(stderr, "%s",
                             _("Couldn't find valid filesystem superblock.\n"));
                ext2fs_free(fs);
                exit(1);
        }
 +      if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
 +                                    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
 +              fprintf(stderr, "%s", _("Cannot modify a journal device.\n"));
 +              ext2fs_free(fs);
 +              exit(1);
 +      }
        fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
  
        if (I_flag && !io_ptr_orig) {
                char buf[SUPERBLOCK_SIZE];
                __u8 old_uuid[UUID_SIZE];
  
 -              if (sb->s_feature_ro_compat &
 -                  EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
 +              if (ext2fs_has_group_desc_csum(fs)) {
                        /*
                         * Changing the UUID requires rewriting all metadata,
                         * which can race with a mounted fs.  Don't allow that.
                        rc = 1;
                        goto closefs;
                }
 +              ext2fs_init_csum_seed(fs);
                if (set_csum) {
                        for (i = 0; i < fs->group_desc_count; i++)
                                ext2fs_group_desc_csum_set(fs, i);
                }
  
                ext2fs_mark_super_dirty(fs);
 +              if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 +                              EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
 +                      rewrite_checksums = 1;
        }
 +      if (rewrite_checksums)
 +              rewrite_metadata_checksums(fs);
        if (I_flag) {
                if (mount_flags & EXT2_MF_MOUNTED) {
                        fputs(_("The inode size may only be "