Merge branch 'maint' into next

Conflicts:
	debian/changelog
	debian/e2fslibs.symbols
	version.h
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index e3db1b0..3ec2eab 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -1,3 +1,23 @@
+E2fsprogs 1.43-WIP (December 28, 2013) -- 38cc555a5fc
+======================================
+
+Add support for the ext4 metadata checksum feature.
+
+Check to make sure file system features which can not be supported by
+HURD are not enabled if the file system is created to be
+HURD-compatible.
+
+
+Programmer's Notes
+------------------
+
+Reduce the use of libc functions in libext2fs that may not be present
+in the boot loader environment, at least for those functions that are
+needed by boot loadsers such as yaboot.
+
+Support for the MMP feature can now be disabled at compile time.
+
+
 E2fsprogs 1.42.9 (December 28, 2013)
 ====================================
 
diff --git a/configure b/configure
index 965fe6b..814b2f4 100755
--- a/configure
+++ b/configure
@@ -852,6 +852,9 @@
 enable_e2initrd_helper
 enable_tls
 enable_uuidd
+enable_mmp
+enable_bmap_stats
+enable_bmap_stats_ops
 enable_nls
 with_gnu_ld
 enable_rpath
@@ -1508,6 +1511,9 @@
   --enable-e2initrd-helper build e2initrd-helper program
   --disable-tls           disable use of thread local support
   --disable-uuidd         disable building the uuid daemon
+  --disable-mmp           disable support mmp, Multi Mount Protection
+  --disable-bmap-stats    disable collection of bitmap stats.
+  --enable-bmap-stats-ops enable collection of additional bitmap stats
   --disable-nls           do not use Native Language Support
   --disable-rpath         do not hardcode runtime library paths
 
@@ -5619,6 +5625,77 @@
 fi
 
 
+
+# Check whether --enable-mmp was given.
+if test "${enable_mmp+set}" = set; then :
+  enableval=$enable_mmp; if test "$enableval" = "no"
+then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling mmp support" >&5
+$as_echo "Disabling mmp support" >&6; }
+else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling mmp support" >&5
+$as_echo "Enabling mmp support" >&6; }
+	$as_echo "#define CONFIG_MMP 1" >>confdefs.h
+
+fi
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling mmp support by default" >&5
+$as_echo "Enabling mmp support by default" >&6; }
+$as_echo "#define CONFIG_MMP 1" >>confdefs.h
+
+
+fi
+
+
+# Check whether --enable-bmap-stats was given.
+if test "${enable_bmap_stats+set}" = set; then :
+  enableval=$enable_bmap_stats; if test "$enableval" = "no"
+then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling bitmap statistics support" >&5
+$as_echo "Disabling bitmap statistics support" >&6; }
+else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling bitmap statistics support" >&5
+$as_echo "Enabling bitmap statistics support" >&6; }
+	$as_echo "#define ENABLE_BMAP_STATS 1" >>confdefs.h
+
+fi
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling bitmap statistics support by default" >&5
+$as_echo "Enabling bitmap statistics support by default" >&6; }
+$as_echo "#define ENABLE_BMAP_STATS 1" >>confdefs.h
+
+
+fi
+
+
+# Check whether --enable-bmap-stats-ops was given.
+if test "${enable_bmap_stats_ops+set}" = set; then :
+  enableval=$enable_bmap_stats_ops; if test "$enableval" = "no"
+then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling additional bitmap statistics" >&5
+$as_echo "Disabling additional bitmap statistics" >&6; }
+else
+		if test "x${enable_bmap_stats}" = "xno"; then :
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "Error --enable-bmap-stats-ops requires bmap-stats
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling additional bitmap statistics" >&5
+$as_echo "Enabling additional bitmap statistics" >&6; }
+	$as_echo "#define ENABLE_BMAP_STATS_OPS 1" >>confdefs.h
+
+fi
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling additional bitmap statistics by default" >&5
+$as_echo "Disabling additional bitmap statistics by default" >&6; }
+
+fi
+
 MAKEFILE_LIBRARY=$srcdir/lib/Makefile.library
 
 GETTEXT_PACKAGE=e2fsprogs
diff --git a/configure.in b/configure.in
index 44dc3ad..93c581a 100644
--- a/configure.in
+++ b/configure.in
@@ -798,6 +798,60 @@
 )
 AC_SUBST(UUIDD_CMT)
 dnl
+dnl handle --disable-mmp
+dnl
+AH_TEMPLATE([CONFIG_MMP], [Define to 1 to enable mmp support])
+AC_ARG_ENABLE([mmp],
+[  --disable-mmp           disable support mmp, Multi Mount Protection],
+if test "$enableval" = "no"
+then
+	AC_MSG_RESULT([Disabling mmp support])
+else
+	AC_MSG_RESULT([Enabling mmp support])
+	AC_DEFINE(CONFIG_MMP, 1)
+fi
+,
+AC_MSG_RESULT([Enabling mmp support by default])
+AC_DEFINE(CONFIG_MMP, 1)
+)
+dnl
+dnl handle --disable-bmap-stats
+dnl
+AH_TEMPLATE([ENABLE_BMAP_STATS], [Define to 1 to enable bitmap stats.])
+AC_ARG_ENABLE([bmap-stats],
+[  --disable-bmap-stats    disable collection of bitmap stats.],
+if test "$enableval" = "no"
+then
+	AC_MSG_RESULT([Disabling bitmap statistics support])
+else
+	AC_MSG_RESULT([Enabling bitmap statistics support])
+	AC_DEFINE(ENABLE_BMAP_STATS, 1)
+fi
+,
+AC_MSG_RESULT([Enabling bitmap statistics support by default])
+AC_DEFINE(ENABLE_BMAP_STATS, 1)
+)
+dnl
+dnl handle --enable-bmap-stats-ops
+dnl
+AH_TEMPLATE([ENABLE_BMAP_STATS_OPS], [Define to 1 to enable bitmap stats.])
+AC_ARG_ENABLE([bmap-stats-ops],
+[  --enable-bmap-stats-ops enable collection of additional bitmap stats],
+if test "$enableval" = "no"
+then
+	AC_MSG_RESULT([Disabling additional bitmap statistics])
+else
+	dnl There has to be a better way!
+	AS_IF([test "x${enable_bmap_stats}" = "xno"],
+	 AC_MSG_FAILURE([Error --enable-bmap-stats-ops requires bmap-stats]))
+
+	AC_MSG_RESULT([Enabling additional bitmap statistics])
+	AC_DEFINE(ENABLE_BMAP_STATS_OPS, 1)
+fi
+,
+AC_MSG_RESULT([Disabling additional bitmap statistics by default])
+)
+dnl
 dnl
 dnl
 MAKEFILE_LIBRARY=$srcdir/lib/Makefile.library
diff --git a/debian/changelog b/debian/changelog
index ce5020f..2d00c30 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+e2fsprogs (1.43~WIP-2014-02-04-1) unstable; urgency=low
+
+  * Merge in updates from the maint branch (changes from 1.42.9-3)
+
+ -- Theodore Y. Ts'o <tytso@mit.edu>  Wed, 04 Feb 2014 23:31:56 -0500
+
 e2fsprogs (1.42.9-3) unstable; urgency=medium
 
   * Add the ability for mke2fs to create hugefiles
@@ -175,6 +181,12 @@
 
  -- Theodore Y. Ts'o <tytso@mit.edu>  Tue, 21 Jan 2013 21:52:58 -0500
 
+e2fsprogs (1.43~WIP-2012-09-22-1) unstable; urgency=low
+
+  * Add metadata checksum feature
+
+ -- Theodore Y. Ts'o <tytso@mit.edu>  Sat, 22 Sep 2012 21:50:20 -0400
+
 e2fsprogs (1.42.6-1) unstable; urgency=low
 
   * New upstream version
diff --git a/debian/e2fslibs.symbols b/debian/e2fslibs.symbols
index 5422544..8a88efe 100644
--- a/debian/e2fslibs.symbols
+++ b/debian/e2fslibs.symbols
@@ -47,6 +47,7 @@
  ext2fs_add_journal_inode2@Base 1.42.9-3~
  ext2fs_add_journal_inode@Base 1.37
  ext2fs_adjust_ea_refcount2@Base 1.42
+ ext2fs_adjust_ea_refcount3@Base 1.43~WIP-2012-08-01
  ext2fs_adjust_ea_refcount@Base 1.37
  ext2fs_alloc_block2@Base 1.42
  ext2fs_alloc_block@Base 1.37
@@ -88,6 +89,9 @@
  ext2fs_blkmap64_rbtree@Base 1.42.1
  ext2fs_block_alloc_stats2@Base 1.42
  ext2fs_block_alloc_stats@Base 1.37
+ ext2fs_block_bitmap_checksum@Base 1.43~WIP-2012-08-01
+ ext2fs_block_bitmap_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_block_bitmap_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_block_alloc_stats_range@Base 1.42.9-3~
  ext2fs_block_bitmap_loc@Base 1.42
  ext2fs_block_bitmap_loc_set@Base 1.42
@@ -122,7 +126,9 @@
  ext2fs_copy_generic_bitmap@Base 1.41.0
  ext2fs_copy_generic_bmap@Base 1.42
  ext2fs_crc16@Base 1.41.1
- ext2fs_crc32c_be@Base 1.42
+ ext2fs_crc32_be@Base 1.43~WIP-2012-08-01
+#Removed in e2fsprogs 1.43
+#ext2fs_crc32c_be@Base 1.42
  ext2fs_crc32c_le@Base 1.42
  ext2fs_create_icount2@Base 1.37
  ext2fs_create_icount@Base 1.37
@@ -142,14 +148,22 @@
  ext2fs_default_journal_size@Base 1.40
  ext2fs_descriptor_block_loc2@Base 1.42
  ext2fs_descriptor_block_loc@Base 1.37
+ ext2fs_dir_block_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_dir_block_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_dir_iterate2@Base 1.37
  ext2fs_dir_iterate@Base 1.37
+ ext2fs_dirent_csum_verify@Base 1.43~WIP-2012-08-01
+ ext2fs_dirent_has_tail@Base 1.43~WIP-2012-08-01
  ext2fs_dirhash@Base 1.37
  ext2fs_div64_ceil@Base 1.42
  ext2fs_div_ceil@Base 1.40
  ext2fs_dup_handle@Base 1.37
  ext2fs_expand_dir@Base 1.37
+ ext2fs_ext_attr_block_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_ext_attr_block_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_ext_attr_hash_entry@Base 1.41.0
+ ext2fs_extent_block_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_extent_block_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_extent_delete@Base 1.41.0
  ext2fs_extent_fix_parents@Base 1.42.7
  ext2fs_extent_free@Base 1.41.0
@@ -250,6 +264,7 @@
  ext2fs_get_device_size2@Base 1.41.4
  ext2fs_get_device_size@Base 1.37
  ext2fs_get_dio_alignment@Base 1.42.3
+ ext2fs_get_dx_countlimit@Base 1.43~WIP-2012-08-01
  ext2fs_get_free_blocks2@Base 1.42
  ext2fs_get_free_blocks@Base 1.37
  ext2fs_get_generic_bitmap_end@Base 1.41.0
@@ -300,12 +315,19 @@
  ext2fs_image_inode_write@Base 1.37
  ext2fs_image_super_read@Base 1.37
  ext2fs_image_super_write@Base 1.37
+ ext2fs_init_csum_seed@Base 1.43~WIP-2012-08-01
  ext2fs_init_dblist@Base 1.37
  ext2fs_initialize@Base 1.37
+ ext2fs_initialize_dirent_tail@Base 1.43~WIP-2012-08-01
  ext2fs_inode_alloc_stats2@Base 1.37
  ext2fs_inode_alloc_stats@Base 1.37
+ ext2fs_inode_bitmap_checksum@Base 1.43~WIP-2012-08-01
+ ext2fs_inode_bitmap_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_inode_bitmap_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_inode_bitmap_loc@Base 1.42
  ext2fs_inode_bitmap_loc_set@Base 1.42
+ ext2fs_inode_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_inode_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_inode_data_blocks2@Base 1.42
  ext2fs_inode_data_blocks@Base 1.37
  ext2fs_inode_has_valid_blocks2@Base 1.42
@@ -338,11 +360,14 @@
  ext2fs_mem_is_zero@Base 1.42
  ext2fs_mkdir@Base 1.37
  ext2fs_mmp_clear@Base 1.42
+ ext2fs_mmp_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_mmp_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_mmp_init@Base 1.42
  ext2fs_mmp_new_seq@Base 1.42
  ext2fs_mmp_read@Base 1.42
  ext2fs_mmp_start@Base 1.42
  ext2fs_mmp_stop@Base 1.42
+ ext2fs_mmp_update2@Base 1.43~WIP-2012-08-01
  ext2fs_mmp_update@Base 1.42
  ext2fs_mmp_write@Base 1.42
  ext2fs_namei@Base 1.37
@@ -354,6 +379,7 @@
  ext2fs_new_inode@Base 1.37
  ext2fs_numeric_progress_close@Base 1.42
  ext2fs_numeric_progress_init@Base 1.42
+ ext2fs_numeric_progress_ops@Base 1.43~WIP-2012-08-01
  ext2fs_numeric_progress_update@Base 1.42
  ext2fs_open2@Base 1.37
  ext2fs_open@Base 1.37
@@ -382,8 +408,10 @@
  ext2fs_read_block_bitmap@Base 1.37
  ext2fs_read_dir_block2@Base 1.37
  ext2fs_read_dir_block3@Base 1.42
+ ext2fs_read_dir_block4@Base 1.43~WIP-2012-08-01
  ext2fs_read_dir_block@Base 1.37
  ext2fs_read_ext_attr2@Base 1.42
+ ext2fs_read_ext_attr3@Base 1.43~WIP-2012-08-01
  ext2fs_read_ext_attr@Base 1.37
  ext2fs_read_ind_block@Base 1.37
  ext2fs_read_inode@Base 1.37
@@ -420,6 +448,8 @@
  ext2fs_stat@Base 1.42
  ext2fs_super_and_bgd_loc2@Base 1.42
  ext2fs_super_and_bgd_loc@Base 1.37
+ ext2fs_superblock_csum_set@Base 1.43~WIP-2012-08-01
+ ext2fs_superblock_csum_verify@Base 1.43~WIP-2012-08-01
  ext2fs_swab16@Base 1.37
  ext2fs_swab32@Base 1.37
  ext2fs_swab64@Base 1.40
@@ -516,6 +546,7 @@
  ext2fs_unmark_valid@Base 1.37
  ext2fs_update_bb_inode@Base 1.37
  ext2fs_update_dynamic_rev@Base 1.37
+ ext2fs_verify_csum_type@Base 1.43~WIP-2012-08-01
  ext2fs_warn_bitmap2@Base 1.37
  ext2fs_warn_bitmap32@Base 1.42
  ext2fs_warn_bitmap@Base 1.37
@@ -524,8 +555,10 @@
  ext2fs_write_block_bitmap@Base 1.37
  ext2fs_write_dir_block2@Base 1.37
  ext2fs_write_dir_block3@Base 1.42
+ ext2fs_write_dir_block4@Base 1.43~WIP-2012-08-01
  ext2fs_write_dir_block@Base 1.37
  ext2fs_write_ext_attr2@Base 1.42
+ ext2fs_write_ext_attr3@Base 1.43~WIP-2012-08-01
  ext2fs_write_ext_attr@Base 1.37
  ext2fs_write_ind_block@Base 1.37
  ext2fs_write_inode@Base 1.37
diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index 73254d3..7cda819 100644
--- a/debugfs/debugfs.8.in
+++ b/debugfs/debugfs.8.in
@@ -8,7 +8,7 @@
 .SH SYNOPSIS
 .B debugfs
 [
-.B \-DVwci
+.B \-DVwcin
 ]
 [
 .B \-b
@@ -48,6 +48,11 @@
 Specifies that the file system should be opened in read-write mode.
 Without this option, the file system is opened in read-only mode.
 .TP
+.I \-n
+Disables metadata checksum verification.  This should only be used if
+you believe the metadata to be correct despite the complaints of
+e2fsprogs.
+.TP
 .I \-c
 Specifies that the file system should be opened in catastrophic mode, in
 which the inode and group bitmaps are not read initially.  This can be
@@ -416,10 +421,13 @@
 .I \-b
 options.
 .TP
-.BI ls " [-d] [-l] [-p] filespec"
+.BI ls " [-l] [-c] [-d] [-p] filespec"
 Print a listing of the files in the directory
 .IR filespec .
 The
+.I \-c
+flag causes directory block checksums (if present) to be displayed.
+The
 .I \-d
 flag will list deleted entries in the directory.
 The
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 0e56ead..d8d6d76 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -372,8 +372,7 @@
 		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, "
@@ -818,6 +817,19 @@
 	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);
@@ -1971,9 +1983,9 @@
 
 	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;
@@ -2312,6 +2324,7 @@
 
 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;
@@ -2350,6 +2363,11 @@
 	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)
@@ -2414,9 +2432,9 @@
 	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)
@@ -2443,6 +2461,9 @@
 		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;
diff --git a/debugfs/dump.c b/debugfs/dump.c
index 51bc734..952a752 100644
--- a/debugfs/dump.c
+++ b/debugfs/dump.c
@@ -315,7 +315,7 @@
 	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 --git a/debugfs/filefrag.c b/debugfs/filefrag.c
index 0adea40..6219d7c 100644
--- a/debugfs/filefrag.c
+++ b/debugfs/filefrag.c
@@ -183,7 +183,7 @@
 	if (entry == DIRENT_DELETED_FILE)
 		return 0;
 
-	thislen = dirent->name_len & 0xFF;
+	thislen = ext2fs_dirent_name_len(dirent);
 	strncpy(name, dirent->name, thislen);
 	name[thislen] = '\0';
 	ino = dirent->inode;
diff --git a/debugfs/htree.c b/debugfs/htree.c
index 24f8250..4f0118d 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -44,6 +44,11 @@
 	ext2_dirhash_t 	hash, minor_hash;
 	unsigned int	rec_len;
 	int		hash_alg;
+	int		csum_size = 0;
+
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		csum_size = sizeof(struct ext2_dir_entry_tail);
 
 	errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk);
 	if (errcode) {
@@ -53,7 +58,7 @@
 	}
 
 	fprintf(pager, "Reading directory block %llu, phys %llu\n", blk, pblk);
-	errcode = ext2fs_read_dir_block2(current_fs, pblk, buf, 0);
+	errcode = ext2fs_read_dir_block4(current_fs, pblk, buf, 0, ino);
 	if (errcode) {
 		com_err("htree_dump_leaf_node", errcode,
 			"while reading block %llu (%llu)\n",
@@ -74,15 +79,15 @@
 				(unsigned long) blk);
 			return;
 		}
+		thislen = ext2fs_dirent_name_len(dirent);
 		if (((offset + rec_len) > fs->blocksize) ||
 		    (rec_len < 8) ||
 		    ((rec_len % 4) != 0) ||
-		    ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) {
+		    (thislen + 8 > rec_len)) {
 			fprintf(pager, "Corrupted directory block (%llu)!\n",
 				blk);
 			break;
 		}
-		thislen = dirent->name_len & 0xFF;
 		strncpy(name, dirent->name, thislen);
 		name[thislen] = '\0';
 		errcode = ext2fs_dirhash(hash_alg, name,
@@ -91,8 +96,23 @@
 		if (errcode)
 			com_err("htree_dump_leaf_node", errcode,
 				"while calculating hash");
-		snprintf(tmp, EXT2_NAME_LEN + 64, "%u 0x%08x-%08x (%d) %s   ",
-			dirent->inode, hash, minor_hash, rec_len, name);
+		if ((offset == fs->blocksize - csum_size) &&
+		    (dirent->inode == 0) &&
+		    (dirent->rec_len == csum_size) &&
+		    (dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) {
+			struct ext2_dir_entry_tail *t;
+
+			t = (struct ext2_dir_entry_tail *) dirent;
+
+			snprintf(tmp, EXT2_NAME_LEN + 64,
+				 "leaf block checksum: 0x%08x  ",
+				 t->det_checksum);
+		} else {
+			snprintf(tmp, EXT2_NAME_LEN + 64,
+				 "%u 0x%08x-%08x (%d) %s   ",
+				 dirent->inode, hash, minor_hash,
+				 rec_len, name);
+		}
 		thislen = strlen(tmp);
 		if (col + thislen > 80) {
 			fprintf(pager, "\n");
@@ -120,8 +140,9 @@
 {
 	struct ext2_dx_countlimit	limit;
 	struct ext2_dx_entry		e;
+	struct ext2_dx_tail		*tail;
 	int				hash, i;
-
+	int				remainder;
 
 	limit = *((struct ext2_dx_countlimit *) ent);
 	limit.count = ext2fs_le16_to_cpu(limit.count);
@@ -130,6 +151,20 @@
 	fprintf(pager, "Number of entries (count): %d\n", limit.count);
 	fprintf(pager, "Number of entries (limit): %d\n", limit.limit);
 
+	remainder = fs->blocksize - (limit.limit *
+				     sizeof(struct ext2_dx_entry));
+	if (ent == (struct ext2_dx_entry *)(rootnode + 1))
+		remainder -= sizeof(struct ext2_dx_root_info) + 24;
+	else
+		remainder -= 8;
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+	    remainder == sizeof(struct ext2_dx_tail)) {
+		tail = (struct ext2_dx_tail *)(ent + limit.limit);
+		fprintf(pager, "Checksum: 0x%08x\n",
+			ext2fs_le32_to_cpu(tail->dt_checksum));
+	}
+
 	for (i=0; i < limit.count; i++) {
 		hash = i ? ext2fs_le32_to_cpu(ent[i].hash) : 0;
 		fprintf(pager, "Entry #%d: Hash 0x%08x%s, block %u\n", i,
@@ -393,7 +428,7 @@
 			return BLOCK_ABORT;
 		}
 		if (dirent->inode &&
-		    p->len == (dirent->name_len & 0xFF) &&
+		    p->len == ext2fs_dirent_name_len(dirent) &&
 		    strncmp(p->search_name, dirent->name,
 			    p->len) == 0) {
 			printf("Entry found at logical block %lld, "
diff --git a/debugfs/logdump.c b/debugfs/logdump.c
index d2c3b30..2d0efaf 100644
--- a/debugfs/logdump.c
+++ b/debugfs/logdump.c
@@ -500,7 +500,7 @@
 			break;
 
 		tag_block = be32_to_cpu(tag->t_blocknr);
-		tag_flags = be32_to_cpu(tag->t_flags);
+		tag_flags = be16_to_cpu(tag->t_flags);
 
 		if (!(tag_flags & JFS_FLAG_SAME_UUID))
 			offset += 16;
diff --git a/debugfs/ls.c b/debugfs/ls.c
index b4036de..69c7897 100644
--- a/debugfs/ls.c
+++ b/debugfs/ls.c
@@ -30,8 +30,7 @@
  */
 
 #define LONG_OPT	0x0001
-#define DELETED_OPT	0x0002
-#define PARSE_OPT	0x0004
+#define PARSE_OPT	0x0002
 
 struct list_dir_struct {
 	FILE	*f;
@@ -60,8 +59,9 @@
 	char			lbr, rbr;
 	int			thislen;
 	struct list_dir_struct *ls = (struct list_dir_struct *) private;
+	struct ext2_dir_entry_tail *t = (struct ext2_dir_entry_tail *) dirent;
 
-	thislen = dirent->name_len & 0xFF;
+	thislen = ext2fs_dirent_name_len(dirent);
 	strncpy(name, dirent->name, thislen);
 	name[thislen] = '\0';
 	ino = dirent->inode;
@@ -99,8 +99,14 @@
 			strcpy(datestr, "                 ");
 			memset(&inode, 0, sizeof(struct ext2_inode));
 		}
-		fprintf(ls->f, "%c%6u%c %6o (%d)  %5d  %5d   ", lbr, ino, rbr,
-			inode.i_mode, dirent->name_len >> 8,
+		fprintf(ls->f, "%c%6u%c %6o ", lbr, ino, rbr, inode.i_mode);
+		if (entry == DIRENT_CHECKSUM) {
+			fprintf(ls->f, "(dirblock checksum: 0x%08x)\n",
+				t->det_checksum);
+			return 0;
+		}
+		fprintf(ls->f, "(%d)  %5d  %5d   ",
+			ext2fs_dirent_file_type(dirent),
 			inode_uid(inode), inode_gid(inode));
 		if (LINUX_S_ISDIR(inode.i_mode))
 			fprintf(ls->f, "%5d", inode.i_size);
@@ -108,8 +114,13 @@
 			fprintf(ls->f, "%5llu", EXT2_I_SIZE(&inode));
 		fprintf (ls->f, " %s %s\n", datestr, name);
 	} else {
-		sprintf(tmp, "%c%u%c (%d) %s   ", lbr, dirent->inode, rbr,
-			dirent->rec_len, name);
+		if (entry == DIRENT_CHECKSUM)
+			sprintf(tmp, "%c%u%c (dirblock checksum: 0x%08x)   ",
+				lbr, dirent->inode, rbr, t->det_checksum);
+		else
+			sprintf(tmp, "%c%u%c (%d) %s   ",
+				lbr, dirent->inode, rbr,
+				dirent->rec_len, name);
 		thislen = strlen(tmp);
 
 		if (ls->col + thislen > 80) {
@@ -127,7 +138,7 @@
 	ext2_ino_t	inode;
 	int		retval;
 	int		c;
-	int		flags;
+	int		flags = DIRENT_FLAG_INCLUDE_EMPTY;
 	struct list_dir_struct ls;
 
 	ls.options = 0;
@@ -135,13 +146,16 @@
 		return;
 
 	reset_getopt();
-	while ((c = getopt (argc, argv, "dlp")) != EOF) {
+	while ((c = getopt (argc, argv, "cdlp")) != EOF) {
 		switch (c) {
+		case 'c':
+			flags |= DIRENT_FLAG_INCLUDE_CSUM;
+			break;
 		case 'l':
 			ls.options |= LONG_OPT;
 			break;
 		case 'd':
-			ls.options |= DELETED_OPT;
+			flags |= DIRENT_FLAG_INCLUDE_REMOVED;
 			break;
 		case 'p':
 			ls.options |= PARSE_OPT;
@@ -166,9 +180,6 @@
 
 	ls.f = open_pager();
 	ls.col = 0;
-	flags = DIRENT_FLAG_INCLUDE_EMPTY;
-	if (ls.options & DELETED_OPT)
-		flags |= DIRENT_FLAG_INCLUDE_REMOVED;
 
 	retval = ext2fs_dir_iterate2(current_fs, inode, flags,
 				    0, list_dir_proc, &ls);
diff --git a/debugfs/ncheck.c b/debugfs/ncheck.c
index 58f3a50..5d9b5d2 100644
--- a/debugfs/ncheck.c
+++ b/debugfs/ncheck.c
@@ -45,7 +45,7 @@
 	struct inode_walk_struct *iw = (struct inode_walk_struct *) private;
 	struct ext2_inode inode;
 	errcode_t	retval;
-	int		filetype = dirent->name_len >> 8;
+	int		filetype = ext2fs_dirent_file_type(dirent);
 	int		i;
 
 	iw->position++;
@@ -66,11 +66,13 @@
 			if (iw->parent)
 				printf("%u\t%s/%.*s", iw->iarray[i],
 				       iw->parent,
-				       (dirent->name_len & 0xFF), dirent->name);
+				       ext2fs_dirent_name_len(dirent),
+				       dirent->name);
 			else
 				printf("%u\t<%u>/%.*s", iw->iarray[i],
 				       iw->dir,
-				       (dirent->name_len & 0xFF), dirent->name);
+				       ext2fs_dirent_name_len(dirent),
+				       dirent->name);
 			if (iw->check_dirent && filetype) {
 				if (!debugfs_read_inode(dirent->inode, &inode,
 							"ncheck") &&
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index ffbda74..762e655 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -153,6 +153,7 @@
 	{ "backup_bgs", &set_sb.s_backup_bgs[0], NULL, 4, parse_uint,
 	  FLAG_ARRAY, 2 },
 	{ "checksum", &set_sb.s_checksum, NULL, 4, parse_uint },
+	{ "checksum_type", &set_sb.s_checksum_type, NULL, 1, parse_uint },
 	{ 0, 0, 0, 0 }
 };
 
@@ -257,6 +258,7 @@
 	{ "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)
@@ -771,6 +773,7 @@
 
 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.";
@@ -825,5 +828,9 @@
 				 &set_mmp);
 		*mmp_s = set_mmp;
 	}
+#else
+	fprintf(stdout, "MMP is unsupported, please recompile with "
+	                "--enable-mmp\n");
+#endif
 }
 
diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in
index 897bc2b..8ca329b 100644
--- a/e2fsck/Makefile.in
+++ b/e2fsck/Makefile.in
@@ -66,7 +66,7 @@
 #
 #MCHECK= -DMCHECK
 
-OBJS= crc32.o dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o \
+OBJS= dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o \
 	pass3.o pass4.o pass5.o journal.o badblocks.o util.o dirinfo.o \
 	dx_dirinfo.o ehandler.o problem.o message.o quota.o recovery.o \
 	region.o revoke.o ea_refcount.o rehash.o profile.o prof_err.o \
@@ -80,12 +80,10 @@
 	profiled/message.o profiled/problem.o profiled/quota.o \
 	profiled/recovery.o profiled/region.o profiled/revoke.o \
 	profiled/ea_refcount.o profiled/rehash.o profiled/profile.o \
-	profiled/crc32.o profiled/prof_err.o profiled/logfile.o \
+	profiled/prof_err.o profiled/logfile.o \
 	profiled/sigcatcher.o
 
 SRCS= $(srcdir)/e2fsck.c \
-	$(srcdir)/crc32.c \
-	$(srcdir)/gen_crc32table.c \
 	$(srcdir)/dict.c \
 	$(srcdir)/super.c \
 	$(srcdir)/pass1.c \
@@ -136,15 +134,6 @@
 	$(Q) $(LD) $(ALL_LDFLAGS) -g -pg -o e2fsck.profiled $(PROFILED_OBJS) \
 		$(PROFILED_LIBS) 
 
-gen_crc32table: $(srcdir)/gen_crc32table.c
-	$(E) "	CC $@"
-	$(Q) $(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o gen_crc32table \
-		$(srcdir)/gen_crc32table.c
-
-crc32table.h: gen_crc32table
-	$(E) "	GEN32TABLE $@"
-	$(Q) ./gen_crc32table > crc32table.h
-
 tst_sigcatcher: $(srcdir)/sigcatcher.c sigcatcher.o
 	$(E) "	CC $@"
 	$(Q) $(CC) $(BUILD_LDFLAGS) $(ALL_CFLAGS) $(RDYNAMIC) \
@@ -156,10 +145,6 @@
 		$(srcdir)/problem.c -DUNITTEST $(LIBEXT2FS) $(LIBCOM_ERR) \
 		$(LIBINTL) $(SYSLIBS)
 
-tst_crc32: $(srcdir)/crc32.c $(LIBEXT2FS) $(DEPLIBCOM_ERR)
-	$(Q) $(CC) $(BUILD_LDFLAGS) $(ALL_CFLAGS) -o tst_crc32 $(srcdir)/crc32.c \
-		-DUNITTEST $(LIBEXT2FS) $(LIBCOM_ERR) $(SYSLIBS)
-
 tst_refcount: ea_refcount.c $(DEPLIBCOM_ERR)
 	$(E) "	LD $@"
 	$(Q) $(CC) -o tst_refcount $(srcdir)/ea_refcount.c \
@@ -176,10 +161,9 @@
 	$(Q) $(CC) -o tst_region $(srcdir)/region.c \
 		$(ALL_CFLAGS) -DTEST_PROGRAM $(LIBCOM_ERR) $(SYSLIBS)
 
-check:: tst_refcount tst_region tst_crc32 tst_problem
+check:: tst_refcount tst_region tst_problem
 	LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_refcount
 	LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_region
-	LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_crc32
 	LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_problem
 
 extend: extend.o
@@ -276,9 +260,8 @@
 clean:
 	$(RM) -f $(PROGS) \#* *\# *.s *.o *.a *~ core e2fsck.static \
 		e2fsck.shared e2fsck.profiled flushb e2fsck.8 \
-		tst_problem tst_crc32 tst_region tst_refcount gen_crc32table \
-		crc32table.h e2fsck.conf.5 prof_err.c prof_err.h \
-		test_profile
+		tst_problem tst_region tst_refcount e2fsck.conf.5 \
+		prof_err.c prof_err.h test_profile
 	$(RM) -rf profiled
 
 mostlyclean: clean
@@ -301,18 +284,6 @@
  $(top_srcdir)/lib/quota/quotaio.h $(top_srcdir)/lib/quota/dqblk_v2.h \
  $(top_srcdir)/lib/quota/quotaio_tree.h $(top_srcdir)/lib/../e2fsck/dict.h \
  $(srcdir)/problem.h
-crc32.o: $(srcdir)/crc32.c $(top_builddir)/lib/config.h \
- $(top_builddir)/lib/dirpaths.h $(srcdir)/e2fsck.h \
- $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
- $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
- $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
- $(top_builddir)/lib/ext2fs/ext2_err.h \
- $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
- $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/quota/mkquota.h \
- $(top_srcdir)/lib/quota/quotaio.h $(top_srcdir)/lib/quota/dqblk_v2.h \
- $(top_srcdir)/lib/quota/quotaio_tree.h $(top_srcdir)/lib/../e2fsck/dict.h \
- $(srcdir)/crc32defs.h crc32table.h
-gen_crc32table.o: $(srcdir)/gen_crc32table.c $(srcdir)/crc32defs.h
 dict.o: $(srcdir)/dict.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/dict.h
 super.o: $(srcdir)/super.c $(top_builddir)/lib/config.h \
diff --git a/e2fsck/crc32.c b/e2fsck/crc32.c
deleted file mode 100644
index 0497a38..0000000
--- a/e2fsck/crc32.c
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * crc32.c --- CRC32 function
- *
- * Copyright (C) 2008 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-/*
- * Oct 15, 2000 Matt Domsch <Matt_Domsch@dell.com>
- * Nicer crc32 functions/docs submitted by linux@horizon.com.  Thanks!
- * Code was from the public domain, copyright abandoned.  Code was
- * subsequently included in the kernel, thus was re-licensed under the
- * GNU GPL v2.
- *
- * Oct 12, 2000 Matt Domsch <Matt_Domsch@dell.com>
- * Same crc32 function was used in 5 other places in the kernel.
- * I made one version, and deleted the others.
- * There are various incantations of crc32().  Some use a seed of 0 or ~0.
- * Some xor at the end with ~0.  The generic crc32() function takes
- * seed as an argument, and doesn't xor at the end.  Then individual
- * users can do whatever they need.
- *   drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0.
- *   fs/jffs2 uses seed 0, doesn't xor with ~0.
- *   fs/partitions/efi.c uses seed ~0, xor's with ~0.
- *
- * This source code is licensed under the GNU General Public License,
- * Version 2.  See the file COPYING for more details.
- */
-
-#include "config.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-
-#ifdef UNITTEST
-#undef ENABLE_NLS
-#endif
-#include "e2fsck.h"
-
-#include "crc32defs.h"
-#if CRC_LE_BITS == 8
-#define tole(x) __constant_cpu_to_le32(x)
-#define tobe(x) __constant_cpu_to_be32(x)
-#else
-#define tole(x) (x)
-#define tobe(x) (x)
-#endif
-#include "crc32table.h"
-
-#ifdef UNITTEST
-
-/**
- * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
- * @crc: seed value for computation.  ~0 for Ethernet, sometimes 0 for
- *	other uses, or the previous crc32 value if computing incrementally.
- * @p: pointer to buffer over which CRC is run
- * @len: length of buffer @p
- */
-__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len);
-
-#if CRC_LE_BITS == 1
-/*
- * In fact, the table-based code will work in this case, but it can be
- * simplified by inlining the table in ?: form.
- */
-
-__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
-{
-	int i;
-	while (len--) {
-		crc ^= *p++;
-		for (i = 0; i < 8; i++)
-			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
-	}
-	return crc;
-}
-#else				/* Table-based approach */
-
-__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
-{
-# if CRC_LE_BITS == 8
-	const __u32      *b =(__u32 *)p;
-	const __u32      *tab = crc32table_le;
-
-# ifdef WORDS_BIGENDIAN
-#  define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8)
-# else
-#  define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)
-# endif
-
-	crc = __cpu_to_le32(crc);
-	/* Align it */
-	if(unlikely(((long)b)&3 && len)){
-		do {
-			__u8 *p = (__u8 *)b;
-			DO_CRC(*p++);
-			b = (void *)p;
-		} while ((--len) && ((long)b)&3 );
-	}
-	if(likely(len >= 4)){
-		/* load data 32 bits wide, xor data 32 bits wide. */
-		size_t save_len = len & 3;
-	        len = len >> 2;
-		--b; /* use pre increment below(*++b) for speed */
-		do {
-			crc ^= *++b;
-			DO_CRC(0);
-			DO_CRC(0);
-			DO_CRC(0);
-			DO_CRC(0);
-		} while (--len);
-		b++; /* point to next byte(s) */
-		len = save_len;
-	}
-	/* And the last few bytes */
-	if(len){
-		do {
-			__u8 *p = (__u8 *)b;
-			DO_CRC(*p++);
-			b = (void *)p;
-		} while (--len);
-	}
-
-	return __le32_to_cpu(crc);
-#undef ENDIAN_SHIFT
-#undef DO_CRC
-
-# elif CRC_LE_BITS == 4
-	while (len--) {
-		crc ^= *p++;
-		crc = (crc >> 4) ^ crc32table_le[crc & 15];
-		crc = (crc >> 4) ^ crc32table_le[crc & 15];
-	}
-	return crc;
-# elif CRC_LE_BITS == 2
-	while (len--) {
-		crc ^= *p++;
-		crc = (crc >> 2) ^ crc32table_le[crc & 3];
-		crc = (crc >> 2) ^ crc32table_le[crc & 3];
-		crc = (crc >> 2) ^ crc32table_le[crc & 3];
-		crc = (crc >> 2) ^ crc32table_le[crc & 3];
-	}
-	return crc;
-# endif
-}
-#endif
-
-#endif /* UNITTEST */
-
-/**
- * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
- * @crc: seed value for computation.  ~0 for Ethernet, sometimes 0 for
- *	other uses, or the previous crc32 value if computing incrementally.
- * @p: pointer to buffer over which CRC is run
- * @len: length of buffer @p
- */
-__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len);
-
-#if CRC_BE_BITS == 1
-/*
- * In fact, the table-based code will work in this case, but it can be
- * simplified by inlining the table in ?: form.
- */
-
-__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len)
-{
-	int i;
-	while (len--) {
-		crc ^= *p++ << 24;
-		for (i = 0; i < 8; i++)
-			crc =
-			    (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE :
-					  0);
-	}
-	return crc;
-}
-
-#else				/* Table-based approach */
-__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len)
-{
-# if CRC_BE_BITS == 8
-	const __u32      *b =(const __u32 *)p;
-	const __u32      *tab = crc32table_be;
-
-# ifdef WORDS_BIGENDIAN
-#  define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8)
-# else
-#  define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)
-# endif
-
-	crc = __cpu_to_be32(crc);
-	/* Align it */
-	if(unlikely(((long)b)&3 && len)){
-		do {
-			const __u8 *q = (const __u8 *)b;
-			DO_CRC(*q++);
-			b = (const __u32 *)q;
-		} while ((--len) && ((long)b)&3 );
-	}
-	if(likely(len >= 4)){
-		/* load data 32 bits wide, xor data 32 bits wide. */
-		size_t save_len = len & 3;
-	        len = len >> 2;
-		--b; /* use pre increment below(*++b) for speed */
-		do {
-			crc ^= *++b;
-			DO_CRC(0);
-			DO_CRC(0);
-			DO_CRC(0);
-			DO_CRC(0);
-		} while (--len);
-		b++; /* point to next byte(s) */
-		len = save_len;
-	}
-	/* And the last few bytes */
-	if(len){
-		do {
-			const __u8 *q = (const __u8 *)b;
-			DO_CRC(*q++);
-			b = (const void *)q;
-		} while (--len);
-	}
-	return __be32_to_cpu(crc);
-#undef ENDIAN_SHIFT
-#undef DO_CRC
-
-# elif CRC_BE_BITS == 4
-	while (len--) {
-		crc ^= *p++ << 24;
-		crc = (crc << 4) ^ crc32table_be[crc >> 28];
-		crc = (crc << 4) ^ crc32table_be[crc >> 28];
-	}
-	return crc;
-# elif CRC_BE_BITS == 2
-	while (len--) {
-		crc ^= *p++ << 24;
-		crc = (crc << 2) ^ crc32table_be[crc >> 30];
-		crc = (crc << 2) ^ crc32table_be[crc >> 30];
-		crc = (crc << 2) ^ crc32table_be[crc >> 30];
-		crc = (crc << 2) ^ crc32table_be[crc >> 30];
-	}
-	return crc;
-# endif
-}
-#endif
-
-/*
- * A brief CRC tutorial.
- *
- * A CRC is a long-division remainder.  You add the CRC to the message,
- * and the whole thing (message+CRC) is a multiple of the given
- * CRC polynomial.  To check the CRC, you can either check that the
- * CRC matches the recomputed value, *or* you can check that the
- * remainder computed on the message+CRC is 0.  This latter approach
- * is used by a lot of hardware implementations, and is why so many
- * protocols put the end-of-frame flag after the CRC.
- *
- * It's actually the same long division you learned in school, except that
- * - We're working in binary, so the digits are only 0 and 1, and
- * - When dividing polynomials, there are no carries.  Rather than add and
- *   subtract, we just xor.  Thus, we tend to get a bit sloppy about
- *   the difference between adding and subtracting.
- *
- * A 32-bit CRC polynomial is actually 33 bits long.  But since it's
- * 33 bits long, bit 32 is always going to be set, so usually the CRC
- * is written in hex with the most significant bit omitted.  (If you're
- * familiar with the IEEE 754 floating-point format, it's the same idea.)
- *
- * Note that a CRC is computed over a string of *bits*, so you have
- * to decide on the endianness of the bits within each byte.  To get
- * the best error-detecting properties, this should correspond to the
- * order they're actually sent.  For example, standard RS-232 serial is
- * little-endian; the most significant bit (sometimes used for parity)
- * is sent last.  And when appending a CRC word to a message, you should
- * do it in the right order, matching the endianness.
- *
- * Just like with ordinary division, the remainder is always smaller than
- * the divisor (the CRC polynomial) you're dividing by.  Each step of the
- * division, you take one more digit (bit) of the dividend and append it
- * to the current remainder.  Then you figure out the appropriate multiple
- * of the divisor to subtract to being the remainder back into range.
- * In binary, it's easy - it has to be either 0 or 1, and to make the
- * XOR cancel, it's just a copy of bit 32 of the remainder.
- *
- * When computing a CRC, we don't care about the quotient, so we can
- * throw the quotient bit away, but subtract the appropriate multiple of
- * the polynomial from the remainder and we're back to where we started,
- * ready to process the next bit.
- *
- * A big-endian CRC written this way would be coded like:
- * for (i = 0; i < input_bits; i++) {
- * 	multiple = remainder & 0x80000000 ? CRCPOLY : 0;
- * 	remainder = (remainder << 1 | next_input_bit()) ^ multiple;
- * }
- * Notice how, to get at bit 32 of the shifted remainder, we look
- * at bit 31 of the remainder *before* shifting it.
- *
- * But also notice how the next_input_bit() bits we're shifting into
- * the remainder don't actually affect any decision-making until
- * 32 bits later.  Thus, the first 32 cycles of this are pretty boring.
- * Also, to add the CRC to a message, we need a 32-bit-long hole for it at
- * the end, so we have to add 32 extra cycles shifting in zeros at the
- * end of every message,
- *
- * So the standard trick is to rearrage merging in the next_input_bit()
- * until the moment it's needed.  Then the first 32 cycles can be precomputed,
- * and merging in the final 32 zero bits to make room for the CRC can be
- * skipped entirely.
- * This changes the code to:
- * for (i = 0; i < input_bits; i++) {
- *      remainder ^= next_input_bit() << 31;
- * 	multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
- * 	remainder = (remainder << 1) ^ multiple;
- * }
- * With this optimization, the little-endian code is simpler:
- * for (i = 0; i < input_bits; i++) {
- *      remainder ^= next_input_bit();
- * 	multiple = (remainder & 1) ? CRCPOLY : 0;
- * 	remainder = (remainder >> 1) ^ multiple;
- * }
- *
- * Note that the other details of endianness have been hidden in CRCPOLY
- * (which must be bit-reversed) and next_input_bit().
- *
- * However, as long as next_input_bit is returning the bits in a sensible
- * order, we can actually do the merging 8 or more bits at a time rather
- * than one bit at a time:
- * for (i = 0; i < input_bytes; i++) {
- * 	remainder ^= next_input_byte() << 24;
- * 	for (j = 0; j < 8; j++) {
- * 		multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
- * 		remainder = (remainder << 1) ^ multiple;
- * 	}
- * }
- * Or in little-endian:
- * for (i = 0; i < input_bytes; i++) {
- * 	remainder ^= next_input_byte();
- * 	for (j = 0; j < 8; j++) {
- * 		multiple = (remainder & 1) ? CRCPOLY : 0;
- * 		remainder = (remainder << 1) ^ multiple;
- * 	}
- * }
- * If the input is a multiple of 32 bits, you can even XOR in a 32-bit
- * word at a time and increase the inner loop count to 32.
- *
- * You can also mix and match the two loop styles, for example doing the
- * bulk of a message byte-at-a-time and adding bit-at-a-time processing
- * for any fractional bytes at the end.
- *
- * The only remaining optimization is to the byte-at-a-time table method.
- * Here, rather than just shifting one bit of the remainder to decide
- * in the correct multiple to subtract, we can shift a byte at a time.
- * This produces a 40-bit (rather than a 33-bit) intermediate remainder,
- * but again the multiple of the polynomial to subtract depends only on
- * the high bits, the high 8 bits in this case.
- *
- * The multiple we need in that case is the low 32 bits of a 40-bit
- * value whose high 8 bits are given, and which is a multiple of the
- * generator polynomial.  This is simply the CRC-32 of the given
- * one-byte message.
- *
- * Two more details: normally, appending zero bits to a message which
- * is already a multiple of a polynomial produces a larger multiple of that
- * polynomial.  To enable a CRC to detect this condition, it's common to
- * invert the CRC before appending it.  This makes the remainder of the
- * message+crc come out not as zero, but some fixed non-zero value.
- *
- * The same problem applies to zero bits prepended to the message, and
- * a similar solution is used.  Instead of starting with a remainder of
- * 0, an initial remainder of all ones is used.  As long as you start
- * the same way on decoding, it doesn't make a difference.
- */
-
-#ifdef UNITTEST
-
-#include <stdlib.h>
-#include <stdio.h>
-
-const __u8 byte_rev_table[256] = {
-	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
-	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
-	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
-	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
-	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
-	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
-	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
-	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
-	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
-	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
-	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
-	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
-	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
-	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
-	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
-	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
-	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
-	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
-	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
-	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
-	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
-	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
-	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
-	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
-	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
-	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
-	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
-	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
-	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
-	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
-	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
-	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
-};
-
-static inline __u8 bitrev8(__u8 byte)
-{
-	return byte_rev_table[byte];
-}
-
-static inline __u16 bitrev16(__u16 x)
-{
-	return (bitrev8(x & 0xff) << 8) | bitrev8(x >> 8);
-}
-
-/**
- * bitrev32 - reverse the order of bits in a u32 value
- * @x: value to be bit-reversed
- */
-static __u32 bitrev32(__u32 x)
-{
-	return (bitrev16(x & 0xffff) << 16) | bitrev16(x >> 16);
-}
-
-#if 0				/*Not used at present */
-
-static void
-buf_dump(char const *prefix, unsigned char const *buf, size_t len)
-{
-	fputs(prefix, stdout);
-	while (len--)
-		printf(" %02x", *buf++);
-	putchar('\n');
-
-}
-#endif
-
-static void bytereverse(unsigned char *buf, size_t len)
-{
-	while (len--) {
-		unsigned char x = bitrev8(*buf);
-		*buf++ = x;
-	}
-}
-
-static void random_garbage(unsigned char *buf, size_t len)
-{
-	while (len--)
-		*buf++ = (unsigned char) random();
-}
-
-#if 0				/* Not used at present */
-static void store_le(__u32 x, unsigned char *buf)
-{
-	buf[0] = (unsigned char) x;
-	buf[1] = (unsigned char) (x >> 8);
-	buf[2] = (unsigned char) (x >> 16);
-	buf[3] = (unsigned char) (x >> 24);
-}
-#endif
-
-static void store_be(__u32 x, unsigned char *buf)
-{
-	buf[0] = (unsigned char) (x >> 24);
-	buf[1] = (unsigned char) (x >> 16);
-	buf[2] = (unsigned char) (x >> 8);
-	buf[3] = (unsigned char) x;
-}
-
-/*
- * This checks that CRC(buf + CRC(buf)) = 0, and that
- * CRC commutes with bit-reversal.  This has the side effect
- * of bytewise bit-reversing the input buffer, and returns
- * the CRC of the reversed buffer.
- */
-static __u32 test_step(__u32 init, unsigned char *buf, size_t len)
-{
-	__u32 crc1, crc2;
-	size_t i;
-
-	crc1 = crc32_be(init, buf, len);
-	store_be(crc1, buf + len);
-	crc2 = crc32_be(init, buf, len + 4);
-	if (crc2)
-		printf("\nCRC cancellation fail: 0x%08x should be 0\n",
-		       crc2);
-
-	for (i = 0; i <= len + 4; i++) {
-		crc2 = crc32_be(init, buf, i);
-		crc2 = crc32_be(crc2, buf + i, len + 4 - i);
-		if (crc2)
-			printf("\nCRC split fail: 0x%08x\n", crc2);
-	}
-
-	/* Now swap it around for the other test */
-
-	bytereverse(buf, len + 4);
-	init = bitrev32(init);
-	crc2 = bitrev32(crc1);
-	if (crc1 != bitrev32(crc2))
-		printf("\nBit reversal fail: 0x%08x -> 0x%08x -> 0x%08x\n",
-		       crc1, crc2, bitrev32(crc2));
-	crc1 = crc32_le(init, buf, len);
-	if (crc1 != crc2)
-		printf("\nCRC endianness fail: 0x%08x != 0x%08x\n", crc1,
-		       crc2);
-	crc2 = crc32_le(init, buf, len + 4);
-	if (crc2)
-		printf("\nCRC cancellation fail: 0x%08x should be 0\n",
-		       crc2);
-
-	for (i = 0; i <= len + 4; i++) {
-		crc2 = crc32_le(init, buf, i);
-		crc2 = crc32_le(crc2, buf + i, len + 4 - i);
-		if (crc2)
-			printf("\nCRC split fail: 0x%08x\n", crc2);
-	}
-
-	return crc1;
-}
-
-#define SIZE 64
-#define INIT1 0
-#define INIT2 0
-
-int main(int argc, char **argv)
-{
-	unsigned char buf1[SIZE + 4];
-	unsigned char buf2[SIZE + 4];
-	unsigned char buf3[SIZE + 4];
-	int i, j;
-	__u32 crc1, crc2, crc3;
-	int exit_status = 0;
-
-	for (i = 0; i <= SIZE; i++) {
-		printf("\rTesting length %d...", i);
-		fflush(stdout);
-		random_garbage(buf1, i);
-		random_garbage(buf2, i);
-		for (j = 0; j < i; j++)
-			buf3[j] = buf1[j] ^ buf2[j];
-
-		crc1 = test_step(INIT1, buf1, i);
-		crc2 = test_step(INIT2, buf2, i);
-		/* Now check that CRC(buf1 ^ buf2) = CRC(buf1) ^ CRC(buf2) */
-		crc3 = test_step(INIT1 ^ INIT2, buf3, i);
-		if (crc3 != (crc1 ^ crc2)) {
-			printf("CRC XOR fail: 0x%08x != 0x%08x ^ 0x%08x\n",
-			       crc3, crc1, crc2);
-			exit_status++;
-		}
-	}
-	printf("\nAll test complete.  No failures expected.\n");
-	return 0;
-}
-
-#endif				/* UNITTEST */
diff --git a/e2fsck/crc32defs.h b/e2fsck/crc32defs.h
deleted file mode 100644
index 27414d2..0000000
--- a/e2fsck/crc32defs.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * There are multiple 16-bit CRC polynomials in common use, but this is
- * *the* standard CRC-32 polynomial, first popularized by Ethernet.
- * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
- */
-#define CRCPOLY_LE 0xedb88320
-#define CRCPOLY_BE 0x04c11db7
-
-/* How many bits at a time to use.  Requires a table of 4<<CRC_xx_BITS bytes. */
-/* For less performance-sensitive, use 4 */
-#ifndef CRC_LE_BITS
-# define CRC_LE_BITS 8
-#endif
-#ifndef CRC_BE_BITS
-# define CRC_BE_BITS 8
-#endif
-
-/*
- * Little-endian CRC computation.  Used with serial bit streams sent
- * lsbit-first.  Be sure to use cpu_to_le32() to append the computed CRC.
- */
-#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
-# error CRC_LE_BITS must be a power of 2 between 1 and 8
-#endif
-
-/*
- * Big-endian CRC computation.  Used with serial bit streams sent
- * msbit-first.  Be sure to use cpu_to_be32() to append the computed CRC.
- */
-#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
-# error CRC_BE_BITS must be a power of 2 between 1 and 8
-#endif
-
-#define ___constant_swab32(x) \
-	((__u32)( \
-		(((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
-		(((__u32)(x) & (__u32)0x0000ff00UL) <<  8) | \
-		(((__u32)(x) & (__u32)0x00ff0000UL) >>  8) | \
-		(((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
-
-
-#ifdef WORDS_BIGENDIAN
-#define __constant_cpu_to_le32(x) ___constant_swab32((x))
-#define __constant_cpu_to_be32(x) (x)
-#define __be32_to_cpu(x) (x)
-#define __cpu_to_be32(x) (x)
-#define __cpu_to_le32(x) (ext2fs_swab32((x)))
-#define __le32_to_cpu(x) (ext2fs_swab32((x)))
-#else
-#define __constant_cpu_to_le32(x) (x)
-#define __constant_cpu_to_be32(x) ___constant_swab32((x))
-#define __be32_to_cpu(x) (ext2fs_swab32((x)))
-#define __cpu_to_be32(x) (ext2fs_swab32((x)))
-#define __cpu_to_le32(x) (x)
-#define __le32_to_cpu(x) (x)
-#endif
-
-#if (__GNUC__ >= 3)
-#define likely(x)	__builtin_expect(!!(x), 1)
-#define unlikely(x)	__builtin_expect(!!(x), 0)
-#else
-#define likely(x)	(x)
-#define unlikely(x)	(x)
-#endif
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 913a596..dbd6ea8 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -405,9 +405,6 @@
 extern void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
 				 int replace_bad_blocks);
 
-/* crc32.c */
-extern __u32 crc32_be(__u32 crc, unsigned char const *p, size_t len);
-
 /* dirinfo.c */
 extern void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent);
 extern void e2fsck_free_dir_info(e2fsck_t ctx);
@@ -493,6 +490,8 @@
 extern int region_allocate(region_t region, region_addr_t start, int n);
 
 /* rehash.c */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino);
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino);
 errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
 void e2fsck_rehash_directories(e2fsck_t ctx);
 
diff --git a/e2fsck/gen_crc32table.c b/e2fsck/gen_crc32table.c
deleted file mode 100644
index 2c1aa8e..0000000
--- a/e2fsck/gen_crc32table.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * gen_crc32table.c --- Generate CRC32 tables.
- *
- * Copyright (C) 2008 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include "crc32defs.h"
-#include <inttypes.h>
-
-#define ENTRIES_PER_LINE 4
-
-#define LE_TABLE_SIZE (1 << CRC_LE_BITS)
-#define BE_TABLE_SIZE (1 << CRC_BE_BITS)
-
-static uint32_t crc32table_le[LE_TABLE_SIZE];
-static uint32_t crc32table_be[BE_TABLE_SIZE];
-
-/**
- * crc32init_le() - allocate and initialize LE table data
- *
- * crc is the crc of the byte i; other entries are filled in based on the
- * fact that crctable[i^j] = crctable[i] ^ crctable[j].
- *
- */
-static void crc32init_le(void)
-{
-	unsigned i, j;
-	uint32_t crc = 1;
-
-	crc32table_le[0] = 0;
-
-	for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) {
-		crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
-		for (j = 0; j < LE_TABLE_SIZE; j += 2 * i)
-			crc32table_le[i + j] = crc ^ crc32table_le[j];
-	}
-}
-
-/**
- * crc32init_be() - allocate and initialize BE table data
- */
-static void crc32init_be(void)
-{
-	unsigned i, j;
-	uint32_t crc = 0x80000000;
-
-	crc32table_be[0] = 0;
-
-	for (i = 1; i < BE_TABLE_SIZE; i <<= 1) {
-		crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
-		for (j = 0; j < i; j++)
-			crc32table_be[i + j] = crc ^ crc32table_be[j];
-	}
-}
-
-static void output_table(uint32_t table[], int len, const char *trans)
-{
-	int i;
-
-	for (i = 0; i < len - 1; i++) {
-		if (i % ENTRIES_PER_LINE == 0)
-			printf("\n");
-		printf("%s(0x%8.8xL), ", trans, table[i]);
-	}
-	printf("%s(0x%8.8xL)\n", trans, table[len - 1]);
-}
-
-#ifdef __GNUC__
-#define ATTR(x) __attribute__(x)
-#else
-#define ATTR(x)
-#endif
-
-int main(int argc ATTR((unused)), char** argv ATTR((unused)))
-{
-	printf("/* this file is generated - do not edit */\n\n");
-
-	printf("#ifdef UNITTEST\n");
-	if (CRC_LE_BITS > 1) {
-		crc32init_le();
-		printf("static const __u32 crc32table_le[] = {");
-		output_table(crc32table_le, LE_TABLE_SIZE, "tole");
-		printf("};\n");
-	}
-	printf("#endif /* UNITTEST */\n");
-
-	if (CRC_BE_BITS > 1) {
-		crc32init_be();
-		printf("static const __u32 crc32table_be[] = {");
-		output_table(crc32table_be, BE_TABLE_SIZE, "tobe");
-		printf("};\n");
-	}
-
-	return 0;
-}
diff --git a/e2fsck/jfs_user.h b/e2fsck/jfs_user.h
index bfc1bcd..3cccd3f 100644
--- a/e2fsck/jfs_user.h
+++ b/e2fsck/jfs_user.h
@@ -115,17 +115,6 @@
 	free(cache);
 }
 
-/*
- * helper functions to deal with 32 or 64bit block numbers.
- */
-_INLINE_ size_t journal_tag_bytes(journal_t *journal)
-{
-	if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
-		return JBD_TAG_SIZE64;
-	else
-		return JBD_TAG_SIZE32;
-}
-
 #undef _INLINE_
 #endif
 
diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index 11ccec5..a7b1150 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -40,6 +40,56 @@
  */
 #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.
@@ -574,6 +624,15 @@
 	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. */
@@ -641,6 +700,7 @@
 	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);
@@ -681,6 +741,7 @@
 		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);
@@ -826,6 +887,7 @@
 		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);
 	}
 
diff --git a/e2fsck/message.c b/e2fsck/message.c
index 7b84359..6c1d0bf 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -374,7 +374,7 @@
 		fprintf(f, "%u", dirent->inode);
 		break;
 	case 'n':
-		len = dirent->name_len & 0xFF;
+		len = ext2fs_dirent_name_len(dirent);
 		if ((ext2fs_get_rec_len(fs, dirent, &rec_len) == 0) &&
 		    (len > rec_len))
 			len = rec_len;
@@ -385,10 +385,10 @@
 		fprintf(f, "%u", rec_len);
 		break;
 	case 'l':
-		fprintf(f, "%u", dirent->name_len & 0xFF);
+		fprintf(f, "%u", ext2fs_dirent_name_len(dirent));
 		break;
 	case 't':
-		fprintf(f, "%u", dirent->name_len >> 8);
+		fprintf(f, "%u", ext2fs_dirent_file_type(dirent));
 		break;
 	default:
 	no_dirent:
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 38b221c..7554f4e 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -473,7 +473,7 @@
 
 	/* 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;
@@ -482,7 +482,7 @@
 	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) ||
@@ -494,7 +494,7 @@
 	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,6 +540,40 @@
 		*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;
@@ -561,6 +595,7 @@
 	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);
@@ -689,7 +724,8 @@
 	}
 	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);
@@ -717,6 +753,9 @@
 		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))
@@ -735,7 +774,8 @@
 			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;
 			goto endit;
@@ -745,6 +785,14 @@
 		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);
@@ -1141,6 +1189,20 @@
 		} 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)
 			goto endit;
 
@@ -1237,6 +1299,12 @@
 	if (inode)
 		ext2fs_free_mem(&inode);
 
+	/*
+	 * The l+f inode may have been cleared, so zap it now and
+	 * later passes will recalculate it if necessary
+	 */
+	ctx->lost_and_found = 0;
+
 	if ((ctx->flags & E2F_FLAG_SIGNAL_MASK) == 0)
 		print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
 }
@@ -1467,7 +1535,8 @@
 		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;
@@ -1478,8 +1547,9 @@
 		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);
@@ -1504,6 +1574,7 @@
 	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)
@@ -1577,7 +1648,12 @@
 	 * 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;
@@ -1659,6 +1735,18 @@
 	}
 	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);
@@ -1773,6 +1861,7 @@
 	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)
@@ -1780,12 +1869,26 @@
 
 	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))
@@ -1807,6 +1910,17 @@
 			  (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;
@@ -1814,6 +1928,7 @@
 			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);
@@ -1843,7 +1958,10 @@
 			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;
 			}
@@ -2183,9 +2301,10 @@
 	}
 
 	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 --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c
index 13bd9e9..41a82cf 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -654,9 +654,9 @@
 	if (ext2fs_file_acl_block(fs, &dp->inode) &&
 	    (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
 		count = 1;
-		pctx.errcode = ext2fs_adjust_ea_refcount2(fs,
+		pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
 					ext2fs_file_acl_block(fs, &dp->inode),
-						   block_buf, -1, &count);
+					block_buf, -1, &count, ino);
 		if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
 			pctx.errcode = 0;
 			count = 1;
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 7277a7b..5a2745a 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -302,9 +302,9 @@
 	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 @@
 
 	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')
@@ -369,7 +369,8 @@
 			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;
@@ -393,7 +394,9 @@
 				(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;
 			}
 		}
@@ -415,7 +418,7 @@
 
 	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;
@@ -433,7 +436,8 @@
 			 * 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';
@@ -461,7 +465,7 @@
 	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);
@@ -483,7 +487,7 @@
 				   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;
 
@@ -492,7 +496,7 @@
 		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;
 	}
 
@@ -518,7 +522,7 @@
 			pctx) == 0)
 		return 0;
 
-	dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
+	ext2fs_dirent_set_file_type(dirent, should_be);
 	return 1;
 }
 
@@ -527,7 +531,7 @@
 			   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;
@@ -538,6 +542,7 @@
 	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);
@@ -552,9 +557,22 @@
 #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
@@ -565,8 +583,12 @@
 #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))
@@ -632,6 +654,7 @@
 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 */
 
@@ -647,7 +670,7 @@
 	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;
@@ -701,7 +724,8 @@
 	} 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;
 	}
 }
@@ -734,6 +758,9 @@
 	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;
@@ -745,6 +772,12 @@
 	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.
@@ -780,16 +813,24 @@
 #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);
@@ -832,20 +873,54 @@
 			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);
@@ -855,7 +930,7 @@
 		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++;
@@ -887,6 +962,7 @@
 		/*
 		 * 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)) {
@@ -899,8 +975,7 @@
 			 * 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
@@ -908,8 +983,7 @@
 			 * 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] == '.')) {
 			/*
@@ -927,8 +1001,7 @@
 			 * 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.
 			 */
@@ -1041,7 +1114,7 @@
 #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;
@@ -1088,10 +1161,7 @@
 			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);
@@ -1107,7 +1177,7 @@
 			(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
@@ -1121,24 +1191,47 @@
 		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;
@@ -1200,9 +1293,9 @@
 
 	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;
@@ -1456,7 +1549,7 @@
 		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 --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 32c05b5..aaf177c 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -199,9 +199,10 @@
 		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 +376,7 @@
 	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;
 		}
@@ -445,7 +446,7 @@
 		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 +613,7 @@
 	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;
@@ -632,10 +633,9 @@
 	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;
@@ -659,8 +659,12 @@
 
 	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 :
@@ -686,6 +690,7 @@
 	blk64_t			last_block;
 	errcode_t		err;
 	e2fsck_t		ctx;
+	ext2_ino_t		dir;
 };
 
 static int expand_dir_proc(ext2_filsys fs,
@@ -737,7 +742,8 @@
 			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) {
@@ -810,6 +816,7 @@
 	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,
diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index b31490f..04d8843 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -27,6 +27,8 @@
 static void check_inode_bitmaps(e2fsck_t ctx);
 static void check_inode_end(e2fsck_t ctx);
 static void check_block_end(e2fsck_t ctx);
+static void check_inode_bitmap_checksum(e2fsck_t ctx);
+static void check_block_bitmap_checksum(e2fsck_t ctx);
 
 void e2fsck_pass5(e2fsck_t ctx)
 {
@@ -64,6 +66,9 @@
 	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
 		return;
 
+	check_inode_bitmap_checksum(ctx);
+	check_block_bitmap_checksum(ctx);
+
 	ext2fs_free_inode_bitmap(ctx->inode_used_map);
 	ctx->inode_used_map = 0;
 	ext2fs_free_inode_bitmap(ctx->inode_dir_map);
@@ -74,6 +79,120 @@
 	print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
 }
 
+static void check_inode_bitmap_checksum(e2fsck_t ctx)
+{
+	struct problem_context	pctx;
+	char		*buf;
+	dgrp_t		i;
+	int		nbytes;
+	ext2_ino_t	ino_itr;
+	errcode_t	retval;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return;
+
+	/* If bitmap is dirty from being fixed, checksum will be corrected */
+	if (ext2fs_test_ib_dirty(ctx->fs))
+		return;
+
+	nbytes = (size_t)(EXT2_INODES_PER_GROUP(ctx->fs->super) / 8);
+	retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize,
+				     &buf);
+	if (retval) {
+		com_err(ctx->program_name, 0,
+		    _("check_inode_bitmap_checksum: Memory allocation error"));
+		fatal_error(ctx, 0);
+	}
+
+	clear_problem_context(&pctx);
+	for (i = 0; i < ctx->fs->group_desc_count; i++) {
+		if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_INODE_UNINIT))
+			continue;
+
+		ino_itr = 1 + (i * (nbytes << 3));
+		retval = ext2fs_get_inode_bitmap_range2(ctx->fs->inode_map,
+							ino_itr, nbytes << 3,
+							buf);
+		if (retval)
+			break;
+
+		if (ext2fs_inode_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+			continue;
+		pctx.group = i;
+		if (!fix_problem(ctx, PR_5_INODE_BITMAP_CSUM_INVALID, &pctx))
+			continue;
+
+		/*
+		 * Fixing one checksum will rewrite all of them.  The bitmap
+		 * will be checked against the one we made during pass1 for
+		 * discrepancies, and fixed if need be.
+		 */
+		ext2fs_mark_ib_dirty(ctx->fs);
+		break;
+	}
+
+	ext2fs_free_mem(&buf);
+}
+
+static void check_block_bitmap_checksum(e2fsck_t ctx)
+{
+	struct problem_context	pctx;
+	char		*buf;
+	dgrp_t		i;
+	int		nbytes;
+	blk64_t		blk_itr;
+	errcode_t	retval;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return;
+
+	/* If bitmap is dirty from being fixed, checksum will be corrected */
+	if (ext2fs_test_bb_dirty(ctx->fs))
+		return;
+
+	nbytes = (size_t)(EXT2_CLUSTERS_PER_GROUP(ctx->fs->super) / 8);
+	retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize,
+				     &buf);
+	if (retval) {
+		com_err(ctx->program_name, 0,
+		    _("check_block_bitmap_checksum: Memory allocation error"));
+		fatal_error(ctx, 0);
+	}
+
+	clear_problem_context(&pctx);
+	for (i = 0; i < ctx->fs->group_desc_count; i++) {
+		if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_BLOCK_UNINIT))
+			continue;
+
+		blk_itr = EXT2FS_B2C(ctx->fs,
+				     ctx->fs->super->s_first_data_block) +
+			  ((blk64_t) i * (nbytes << 3));
+		retval = ext2fs_get_block_bitmap_range2(ctx->fs->block_map,
+							blk_itr, nbytes << 3,
+							buf);
+		if (retval)
+			break;
+
+		if (ext2fs_block_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+			continue;
+		pctx.group = i;
+		if (!fix_problem(ctx, PR_5_BLOCK_BITMAP_CSUM_INVALID, &pctx))
+			continue;
+
+		/*
+		 * Fixing one checksum will rewrite all of them.  The bitmap
+		 * will be checked against the one we made during pass1 for
+		 * discrepancies, and fixed if need be.
+		 */
+		ext2fs_mark_bb_dirty(ctx->fs);
+		break;
+	}
+
+	ext2fs_free_mem(&buf);
+}
+
 static void e2fsck_discard_blocks(e2fsck_t ctx, blk64_t start,
 				  blk64_t count)
 {
@@ -253,8 +372,7 @@
 		goto errout;
 	}
 
-	csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+	csum_flag = ext2fs_has_group_desc_csum(fs);
 redo_counts:
 	had_problem = 0;
 	save_problem = 0;
@@ -505,8 +623,7 @@
 		goto errout;
 	}
 
-	csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+	csum_flag = ext2fs_has_group_desc_csum(fs);
 redo_counts:
 	had_problem = 0;
 	save_problem = 0;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 43a5d7b..be9d3ec 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -435,6 +435,20 @@
 	  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.  "),
@@ -953,6 +967,46 @@
 	  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
 	 */
@@ -1389,6 +1443,31 @@
 	  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 */
@@ -1705,6 +1784,16 @@
 	  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 --git a/e2fsck/problem.h b/e2fsck/problem.h
index 6cb09cf..8999a64 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -253,6 +253,16 @@
 #define PR_0_64BIT_WITHOUT_EXTENTS		0x000048
 
 /*
+ * metadata_csum supersedes uninit_bg; both feature bits cannot be set
+ * simultaneously.
+ */
+#define PR_0_META_AND_GDT_CSUM_SET		0x000046
+
+/* Superblock has invalid MMP checksum. */
+#define PR_0_MMP_CSUM_INVALID			0x000047
+
+
+/*
  * Pass 1 errors
  */
 
@@ -561,6 +571,24 @@
 /* Extent has zero length */
 #define PR_1_EXTENT_LENGTH_ZERO		0x010066
 
+/* inode checksum does not match inode */
+#define PR_1_INODE_CSUM_INVALID                0x010067
+
+/* inode passes checks, but checksum does not match inode */
+#define PR_1_INODE_ONLY_CSUM_INVALID   0x010068
+
+/* extent block checksum does not match extent block */
+#define PR_1_EXTENT_CSUM_INVALID       0x010069
+
+/* extent block passes checks, but checksum does not match extent block */
+#define PR_1_EXTENT_ONLY_CSUM_INVALID  0x01006A
+
+/* ea block checksum invalid */
+#define PR_1_EA_BLOCK_CSUM_INVALID     0x01006B
+
+/* ea block passes checks, but checksum invalid */
+#define PR_1_EA_BLOCK_ONLY_CSUM_INVALID        0x01006C
+
 /* Index start doesn't match start of next extent down */
 #define PR_1_EXTENT_INDEX_START_INVALID	0x01006D
 
@@ -829,6 +857,21 @@
 /* i_file_acl_hi should be zero */
 #define PR_2_I_FILE_ACL_HI_ZERO		0x020048
 
+/* htree root node fails checksum */
+#define PR_2_HTREE_ROOT_CSUM_INVALID	0x020049
+
+/* htree node fails checksum */
+#define PR_2_HTREE_NODE_CSUM_INVALID	0x02004A
+
+/* dir leaf node fails checksum */
+#define PR_2_LEAF_NODE_CSUM_INVALID	0x02004B
+
+/* no space in leaf for checksum */
+#define PR_2_LEAF_NODE_MISSING_CSUM	0x02004C
+
+/* dir leaf node passes checks, but fails checksum */
+#define PR_2_LEAF_NODE_ONLY_CSUM_INVALID	0x02004D
+
 /*
  * Pass 3 errors
  */
@@ -1027,6 +1070,12 @@
 /* Inode in use but group is marked INODE_UNINIT */
 #define PR_5_INODE_UNINIT		0x050019
 
+/* Inode bitmap checksum does not match */
+#define PR_5_INODE_BITMAP_CSUM_INVALID	0x05001A
+
+/* Block bitmap checksum does not match */
+#define PR_5_BLOCK_BITMAP_CSUM_INVALID	0x05001B
+
 /*
  * Post-Pass 5 errors
  */
diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index e4e5ae1..54579c2 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -174,6 +174,27 @@
 	return 0;
 }
 
+static int jbd2_descr_block_csum_verify(journal_t *j,
+					void *buf)
+{
+	struct journal_block_tail *tail;
+	__u32 provided, calculated;
+
+	if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+		return 1;
+
+	tail = (struct journal_block_tail *)((char *)buf + j->j_blocksize -
+			sizeof(struct journal_block_tail));
+	provided = tail->t_checksum;
+	tail->t_checksum = 0;
+	calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+				      sizeof(j->j_superblock->s_uuid));
+	calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize);
+	tail->t_checksum = provided;
+
+	provided = ext2fs_be32_to_cpu(provided);
+	return provided == calculated;
+}
 
 /*
  * Count the number of in-use tags in a journal descriptor block.
@@ -186,6 +207,9 @@
 	int			nr = 0, size = journal->j_blocksize;
 	int			tag_bytes = journal_tag_bytes(journal);
 
+	if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2))
+		size -= sizeof(struct journal_block_tail);
+
 	tagp = &bh->b_data[sizeof(journal_header_t)];
 
 	while ((tagp - bh->b_data + tag_bytes) <= size) {
@@ -193,10 +217,10 @@
 
 		nr++;
 		tagp += tag_bytes;
-		if (!(tag->t_flags & cpu_to_be32(JFS_FLAG_SAME_UUID)))
+		if (!(tag->t_flags & cpu_to_be16(JFS_FLAG_SAME_UUID)))
 			tagp += 16;
 
-		if (tag->t_flags & cpu_to_be32(JFS_FLAG_LAST_TAG))
+		if (tag->t_flags & cpu_to_be16(JFS_FLAG_LAST_TAG))
 			break;
 	}
 
@@ -329,7 +353,8 @@
 
 	num_blks = count_tags(journal, bh);
 	/* Calculate checksum of the descriptor block. */
-	*crc32_sum = crc32_be(*crc32_sum, (void *)bh->b_data, bh->b_size);
+	*crc32_sum = ext2fs_crc32_be(*crc32_sum, (void *)bh->b_data,
+				     bh->b_size);
 
 	for (i = 0; i < num_blks; i++) {
 		io_block = (*next_log_block)++;
@@ -340,14 +365,56 @@
 				"%llu in log\n", err, io_block);
 			return 1;
 		} else {
-			*crc32_sum = crc32_be(*crc32_sum, (void *)obh->b_data,
-				     obh->b_size);
+			*crc32_sum = ext2fs_crc32_be(*crc32_sum,
+						     (void *)obh->b_data,
+						     obh->b_size);
 		}
 		brelse(obh);
 	}
 	return 0;
 }
 
+static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
+{
+	struct commit_header *h;
+	__u32 provided, calculated;
+
+	if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+		return 1;
+
+	h = buf;
+	provided = h->h_chksum[0];
+	h->h_chksum[0] = 0;
+	calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+				      sizeof(j->j_superblock->s_uuid));
+	calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize);
+	h->h_chksum[0] = provided;
+
+	provided = ext2fs_be32_to_cpu(provided);
+	return provided == calculated;
+}
+
+static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
+				      void *buf, __u32 sequence)
+{
+	__u32 calculated;
+	__u16 provided, crc;
+
+	if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+		return 1;
+
+	sequence = ext2fs_cpu_to_be32(sequence);
+	calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+				      sizeof(j->j_superblock->s_uuid));
+	calculated = ext2fs_crc32c_le(calculated, (__u8 *)&sequence,
+				      sizeof(sequence));
+	calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize) & 0xffff;
+	crc = calculated & 0xFFFF;
+	provided = ext2fs_be16_to_cpu(tag->t_checksum);
+
+	return provided == crc;
+}
+
 static int do_one_pass(journal_t *journal,
 			struct recovery_info *info, enum passtype pass)
 {
@@ -361,6 +428,7 @@
 	int			blocktype;
 	int			tag_bytes = journal_tag_bytes(journal);
 	__u32			crc32_sum = ~0; /* Transactional Checksums */
+	int			descr_csum_size = 0;
 
 	/*
 	 * First thing is to establish what we expect to find in the log
@@ -446,6 +514,18 @@
 
 		switch(blocktype) {
 		case JFS_DESCRIPTOR_BLOCK:
+			/* Verify checksum first */
+			if (JFS_HAS_INCOMPAT_FEATURE(journal,
+					JFS_FEATURE_INCOMPAT_CSUM_V2))
+				descr_csum_size =
+					sizeof(struct journal_block_tail);
+			if (descr_csum_size > 0 &&
+			    !jbd2_descr_block_csum_verify(journal,
+							  bh->b_data)) {
+				err = -EIO;
+				goto failed;
+			}
+
 			/* If it is a valid descriptor block, replay it
 			 * in pass REPLAY; if journal_checksums enabled, then
 			 * calculate checksums in PASS_SCAN, otherwise,
@@ -476,11 +556,11 @@
 
 			tagp = &bh->b_data[sizeof(journal_header_t)];
 			while ((tagp - bh->b_data + tag_bytes)
-			       <= journal->j_blocksize) {
+			       <= journal->j_blocksize - descr_csum_size) {
 				unsigned long long io_block;
 
 				tag = (journal_block_tag_t *) tagp;
-				flags = be32_to_cpu(tag->t_flags);
+				flags = be16_to_cpu(tag->t_flags);
 
 				io_block = next_log_block++;
 				wrap(journal, next_log_block);
@@ -511,6 +591,19 @@
 						goto skip_write;
 					}
 
+					/* Look for block corruption */
+					if (!jbd2_block_tag_csum_verify(
+						journal, tag, obh->b_data,
+						be32_to_cpu(tmp->h_sequence))) {
+						brelse(obh);
+						success = -EIO;
+						printk(KERN_ERR "JBD: Invalid "
+						       "checksum recovering "
+						       "block %lld in log\n",
+						       blocknr);
+						continue;
+					}
+
 					/* Find a buffer for the new
 					 * data being restored */
 					nbh = __getblk(journal->j_fs_dev,
@@ -652,6 +745,19 @@
 				}
 				crc32_sum = ~0;
 			}
+			if (pass == PASS_SCAN &&
+			    !jbd2_commit_block_csum_verify(journal,
+							   bh->b_data)) {
+				info->end_transaction = next_commit_ID;
+
+				if (!JFS_HAS_INCOMPAT_FEATURE(journal,
+				     JFS_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
+					journal->j_failed_commit =
+						next_commit_ID;
+					brelse(bh);
+					break;
+				}
+			}
 			brelse(bh);
 			next_commit_ID++;
 			continue;
@@ -708,6 +814,27 @@
 	return err;
 }
 
+static int jbd2_revoke_block_csum_verify(journal_t *j,
+					 void *buf)
+{
+	struct journal_revoke_tail *tail;
+	__u32 provided, calculated;
+
+	if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+		return 1;
+
+	tail = (struct journal_revoke_tail *)((char *)buf + j->j_blocksize -
+			sizeof(struct journal_revoke_tail));
+	provided = tail->r_checksum;
+	tail->r_checksum = 0;
+	calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+				      sizeof(j->j_superblock->s_uuid));
+	calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize);
+	tail->r_checksum = provided;
+
+	provided = ext2fs_be32_to_cpu(provided);
+	return provided == calculated;
+}
 
 /* Scan a revoke record, marking all blocks mentioned as revoked. */
 
@@ -722,6 +849,9 @@
 	offset = sizeof(journal_revoke_header_t);
 	max = be32_to_cpu(header->r_count);
 
+	if (!jbd2_revoke_block_csum_verify(journal, header))
+		return -EINVAL;
+
 	if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
 		record_len = 8;
 
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 3aafbb1..9b90353 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -52,6 +52,25 @@
 #include "e2fsck.h"
 #include "problem.h"
 
+/* Schedule a dir to be rebuilt during pass 3A. */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
+{
+	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);
+}
+
+/* Ask if a dir will be rebuilt during pass 3A. */
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
+{
+	if (ctx->options & E2F_OPT_COMPRESS_DIRS)
+		return 1;
+	if (!ctx->dirs_to_hash)
+		return 0;
+	return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
+}
+
 struct fill_dir_struct {
 	char *buf;
 	struct ext2_inode *inode;
@@ -62,6 +81,7 @@
 	unsigned int dir_size;
 	int compress;
 	ino_t parent;
+	ext2_ino_t dir;
 };
 
 struct hash_entry {
@@ -89,7 +109,7 @@
 	struct hash_entry 	*new_array, *ent;
 	struct ext2_dir_entry 	*dirent;
 	char			*dir;
-	unsigned int		offset, dir_offset, rec_len;
+	unsigned int		offset, dir_offset, rec_len, name_len;
 	int			hash_alg;
 
 	if (blockcnt < 0)
@@ -106,7 +126,10 @@
 		dirent = (struct ext2_dir_entry *) dir;
 		(void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
 	} else {
-		fd->err = ext2fs_read_dir_block3(fs, *block_nr, dir, 0);
+		fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+		fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
+						 fd->dir);
+		fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
 		if (fd->err)
 			return BLOCK_ABORT;
 	}
@@ -119,20 +142,21 @@
 	while (dir_offset < fs->blocksize) {
 		dirent = (struct ext2_dir_entry *) (dir + dir_offset);
 		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+		name_len = ext2fs_dirent_name_len(dirent);
 		if (((dir_offset + rec_len) > fs->blocksize) ||
 		    (rec_len < 8) ||
 		    ((rec_len % 4) != 0) ||
-		    (((dirent->name_len & 0xFF)+8U) > rec_len)) {
+		    (name_len + 8 > rec_len)) {
 			fd->err = EXT2_ET_DIR_CORRUPTED;
 			return BLOCK_ABORT;
 		}
 		dir_offset += rec_len;
 		if (dirent->inode == 0)
 			continue;
-		if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
+		if (!fd->compress && (name_len == 1) &&
 		    (dirent->name[0] == '.'))
 			continue;
-		if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
+		if (!fd->compress && (name_len == 2) &&
 		    (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
 			fd->parent = dirent->inode;
 			continue;
@@ -149,13 +173,13 @@
 		}
 		ent = fd->harray + fd->num_array++;
 		ent->dir = dirent;
-		fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+		fd->dir_size += EXT2_DIR_REC_LEN(name_len);
 		ent->ino = dirent->inode;
 		if (fd->compress)
 			ent->hash = ent->minor_hash = 0;
 		else {
 			fd->err = ext2fs_dirhash(hash_alg, dirent->name,
-						 dirent->name_len & 0xFF,
+						 name_len,
 						 fs->super->s_hash_seed,
 						 &ent->hash, &ent->minor_hash);
 			if (fd->err)
@@ -180,18 +204,21 @@
 {
 	const struct hash_entry *he_a = (const struct hash_entry *) a;
 	const struct hash_entry *he_b = (const struct hash_entry *) b;
+	unsigned int he_a_len, he_b_len;
 	int	ret;
 	int	min_len;
 
-	min_len = he_a->dir->name_len;
-	if (min_len > he_b->dir->name_len)
-		min_len = he_b->dir->name_len;
+	he_a_len = ext2fs_dirent_name_len(he_a->dir);
+	he_b_len = ext2fs_dirent_name_len(he_b->dir);
+	min_len = he_a_len;
+	if (min_len > he_b_len)
+		min_len = he_b_len;
 
 	ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
 	if (ret == 0) {
-		if (he_a->dir->name_len > he_b->dir->name_len)
+		if (he_a_len > he_b_len)
 			ret = 1;
-		else if (he_a->dir->name_len < he_b->dir->name_len)
+		else if (he_a_len < he_b_len)
 			ret = -1;
 		else
 			ret = he_b->dir->inode - he_a->dir->inode;
@@ -274,10 +301,10 @@
  * expand the length of the filename beyond the padding available in
  * the directory entry.
  */
-static void mutate_name(char *str, __u16 *len)
+static void mutate_name(char *str, unsigned int *len)
 {
 	int	i;
-	__u16	l = *len & 0xFF, h = *len & 0xff00;
+	unsigned int l = *len;
 
 	/*
 	 * First check to see if it looks the name has been mutated
@@ -294,7 +321,7 @@
 			l = (l+3) & ~3;
 		str[l-2] = '~';
 		str[l-1] = '0';
-		*len = l | h;
+		*len = l;
 		return;
 	}
 	for (i = l-1; i >= 0; i--) {
@@ -337,7 +364,7 @@
 	int			i, j;
 	int			fixed = 0;
 	char			new_name[256];
-	__u16			new_len;
+	unsigned int		new_len;
 	int			hash_alg;
 
 	clear_problem_context(&pctx);
@@ -352,10 +379,10 @@
 		ent = fd->harray + i;
 		prev = ent - 1;
 		if (!ent->dir->inode ||
-		    ((ent->dir->name_len & 0xFF) !=
-		     (prev->dir->name_len & 0xFF)) ||
-		    (strncmp(ent->dir->name, prev->dir->name,
-			     ent->dir->name_len & 0xFF)))
+		    (ext2fs_dirent_name_len(ent->dir) !=
+		     ext2fs_dirent_name_len(prev->dir)) ||
+		    strncmp(ent->dir->name, prev->dir->name,
+			     ext2fs_dirent_name_len(ent->dir)))
 			continue;
 		pctx.dirent = ent->dir;
 		if ((ent->dir->inode == prev->dir->inode) &&
@@ -365,27 +392,25 @@
 			fixed++;
 			continue;
 		}
-		memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
-		new_len = ent->dir->name_len;
+		new_len = ext2fs_dirent_name_len(ent->dir);
+		memcpy(new_name, ent->dir->name, new_len);
 		mutate_name(new_name, &new_len);
 		for (j=0; j < fd->num_array; j++) {
 			if ((i==j) ||
-			    ((new_len & 0xFF) !=
-			     (fd->harray[j].dir->name_len & 0xFF)) ||
-			    (strncmp(new_name, fd->harray[j].dir->name,
-				     new_len & 0xFF)))
+			    (new_len !=
+			     ext2fs_dirent_name_len(fd->harray[j].dir)) ||
+			    strncmp(new_name, fd->harray[j].dir->name, new_len))
 				continue;
 			mutate_name(new_name, &new_len);
 
 			j = -1;
 		}
-		new_name[new_len & 0xFF] = 0;
+		new_name[new_len] = 0;
 		pctx.str = new_name;
 		if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
-			memcpy(ent->dir->name, new_name, new_len & 0xFF);
-			ent->dir->name_len = new_len;
-			ext2fs_dirhash(hash_alg, ent->dir->name,
-				       ent->dir->name_len & 0xFF,
+			memcpy(ent->dir->name, new_name, new_len);
+			ext2fs_dirent_set_name_len(ent->dir, new_len);
+			ext2fs_dirhash(hash_alg, ent->dir->name, new_len,
 				       fs->super->s_hash_seed,
 				       &ent->hash, &ent->minor_hash);
 			fixed++;
@@ -407,6 +432,8 @@
 	unsigned int		rec_len, prev_rec_len, left, slack, offset;
 	int			i;
 	ext2_dirhash_t		prev_hash;
+	int			csum_size = 0;
+	struct			ext2_dir_entry_tail *t;
 
 	if (ctx->htree_slack_percentage == 255) {
 		profile_get_uint(ctx->profile, "options",
@@ -417,6 +444,10 @@
 			ctx->htree_slack_percentage = 20;
 	}
 
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		csum_size = sizeof(struct ext2_dir_entry_tail);
+
 	outdir->max = 0;
 	retval = alloc_size_dir(fs, outdir,
 				(fd->dir_size / fs->blocksize) + 2);
@@ -431,16 +462,16 @@
 	dirent = (struct ext2_dir_entry *) block_start;
 	prev_rec_len = 0;
 	rec_len = 0;
-	left = fs->blocksize;
+	left = fs->blocksize - csum_size;
 	slack = fd->compress ? 12 :
-		(fs->blocksize * ctx->htree_slack_percentage)/100;
+		((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100;
 	if (slack < 12)
 		slack = 12;
 	for (i = 0; i < fd->num_array; i++) {
 		ent = fd->harray + i;
 		if (ent->dir->inode == 0)
 			continue;
-		rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
+		rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir));
 		if (rec_len > left) {
 			if (left) {
 				left += prev_rec_len;
@@ -448,12 +479,17 @@
 				if (retval)
 					return retval;
 			}
+			if (csum_size) {
+				t = EXT2_DIRENT_TAIL(block_start,
+						     fs->blocksize);
+				ext2fs_initialize_dirent_tail(fs, t);
+			}
 			if ((retval = get_next_block(fs, outdir,
 						      &block_start)))
 				return retval;
 			offset = 0;
 		}
-		left = fs->blocksize - offset;
+		left = (fs->blocksize - csum_size) - offset;
 		dirent = (struct ext2_dir_entry *) (block_start + offset);
 		if (offset == 0) {
 			if (ent->hash == prev_hash)
@@ -462,12 +498,16 @@
 				outdir->hashes[outdir->num-1] = ent->hash;
 		}
 		dirent->inode = ent->dir->inode;
-		dirent->name_len = ent->dir->name_len;
+		ext2fs_dirent_set_name_len(dirent,
+					   ext2fs_dirent_name_len(ent->dir));
+		ext2fs_dirent_set_file_type(dirent,
+					    ext2fs_dirent_file_type(ent->dir));
 		retval = ext2fs_set_rec_len(fs, rec_len, dirent);
 		if (retval)
 			return retval;
 		prev_rec_len = rec_len;
-		memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
+		memcpy(dirent->name, ent->dir->name,
+		       ext2fs_dirent_name_len(dirent));
 		offset += rec_len;
 		left -= rec_len;
 		if (left < slack) {
@@ -482,6 +522,10 @@
 	}
 	if (left)
 		retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
+	if (csum_size) {
+		t = EXT2_DIRENT_TAIL(block_start, fs->blocksize);
+		ext2fs_initialize_dirent_tail(fs, t);
+	}
 
 	return retval;
 }
@@ -494,21 +538,24 @@
 	struct ext2_dx_root_info  	*root;
 	struct ext2_dx_countlimit	*limits;
 	int				filetype = 0;
+	int				csum_size = 0;
 
 	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
-		filetype = EXT2_FT_DIR << 8;
+		filetype = EXT2_FT_DIR;
 
 	memset(buf, 0, fs->blocksize);
 	dir = (struct ext2_dir_entry *) buf;
 	dir->inode = ino;
 	dir->name[0] = '.';
-	dir->name_len = 1 | filetype;
+	ext2fs_dirent_set_name_len(dir, 1);
+	ext2fs_dirent_set_file_type(dir, filetype);
 	dir->rec_len = 12;
 	dir = (struct ext2_dir_entry *) (buf + 12);
 	dir->inode = parent;
 	dir->name[0] = '.';
 	dir->name[1] = '.';
-	dir->name_len = 2 | filetype;
+	ext2fs_dirent_set_name_len(dir, 2);
+	ext2fs_dirent_set_file_type(dir, filetype);
 	dir->rec_len = fs->blocksize - 12;
 
 	root = (struct ext2_dx_root_info *) (buf+24);
@@ -518,8 +565,13 @@
 	root->indirect_levels = 0;
 	root->unused_flags = 0;
 
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		csum_size = sizeof(struct ext2_dx_tail);
+
 	limits = (struct ext2_dx_countlimit *) (buf+32);
-	limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+	limits->limit = (fs->blocksize - (32 + csum_size)) /
+			sizeof(struct ext2_dx_entry);
 	limits->count = 0;
 
 	return root;
@@ -530,14 +582,20 @@
 {
 	struct ext2_dir_entry 		*dir;
 	struct ext2_dx_countlimit	*limits;
+	int				csum_size = 0;
 
 	memset(buf, 0, fs->blocksize);
 	dir = (struct ext2_dir_entry *) buf;
 	dir->inode = 0;
 	(void) 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_dx_tail);
+
 	limits = (struct ext2_dx_countlimit *) (buf+8);
-	limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+	limits->limit = (fs->blocksize - (8 + csum_size)) /
+			sizeof(struct ext2_dx_entry);
 	limits->count = 0;
 
 	return (struct ext2_dx_entry *) limits;
@@ -627,6 +685,7 @@
 	errcode_t	err;
 	e2fsck_t	ctx;
 	blk64_t		cleared;
+	ext2_ino_t	dir;
 };
 
 /*
@@ -641,11 +700,23 @@
 {
 	struct write_dir_struct	*wd = (struct write_dir_struct *) priv_data;
 	blk64_t	blk;
-	char	*dir;
+	char	*dir, *buf = 0;
 
 	if (*block_nr == 0)
 		return 0;
-	if (blockcnt >= wd->outdir->num) {
+	if (blockcnt < 0)
+		return 0;
+	if (blockcnt < wd->outdir->num)
+		dir = wd->outdir->buf + (blockcnt * fs->blocksize);
+	else if (wd->ctx->lost_and_found == wd->dir) {
+		/* Don't release any extra directory blocks for lost+found */
+		wd->err = ext2fs_new_dir_block(fs, 0, 0, &buf);
+		if (wd->err)
+			return BLOCK_ABORT;
+		dir = buf;
+		wd->outdir->num++;
+	} else {
+		/* We don't need this block, so release it */
 		e2fsck_read_bitmaps(wd->ctx);
 		blk = *block_nr;
 		/*
@@ -662,11 +733,11 @@
 		*block_nr = 0;
 		return BLOCK_CHANGED;
 	}
-	if (blockcnt < 0)
-		return 0;
 
-	dir = wd->outdir->buf + (blockcnt * fs->blocksize);
-	wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0);
+	wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir);
+	if (buf)
+		ext2fs_free_mem(&buf);
+
 	if (wd->err)
 		return BLOCK_ABORT;
 	return 0;
@@ -688,6 +759,7 @@
 	wd.err = 0;
 	wd.ctx = ctx;
 	wd.cleared = 0;
+	wd.dir = ino;
 
 	retval = ext2fs_block_iterate3(fs, ino, 0, 0,
 				       write_dir_block, &wd);
@@ -740,6 +812,7 @@
 	fd.err = 0;
 	fd.dir_size = 0;
 	fd.compress = 0;
+	fd.dir = ino;
 	if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
 	    (inode.i_size / fs->blocksize) < 2)
 		fd.compress = 1;
@@ -843,7 +916,7 @@
 	if (!ctx->dirs_to_hash && !all_dirs)
 		return;
 
-	e2fsck_get_lost_and_found(ctx, 0);
+	(void) e2fsck_get_lost_and_found(ctx, 0);
 
 	clear_problem_context(&pctx);
 
@@ -871,8 +944,7 @@
 			if (!ext2fs_u32_list_iterate(iter, &ino))
 				break;
 		}
-		if (ino == ctx->lost_and_found)
-			continue;
+
 		pctx.dir = ino;
 		if (first) {
 			fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
diff --git a/e2fsck/sigcatcher.c b/e2fsck/sigcatcher.c
index e4d60ce..421cd3e 100644
--- a/e2fsck/sigcatcher.c
+++ b/e2fsck/sigcatcher.c
@@ -392,6 +392,7 @@
 	sigaction(SIGILL, &sa, 0);
 	sigaction(SIGBUS, &sa, 0);
 	sigaction(SIGSEGV, &sa, 0);
+	sigaction(SIGABRT, &sa, 0);
 }	
 
 
diff --git a/e2fsck/super.c b/e2fsck/super.c
index b0cc440..59c1f97 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -201,9 +201,9 @@
 		ext2fs_iblk_sub_blocks(fs, inode, pb.truncated_blocks);
 
 	if (ext2fs_file_acl_block(fs, inode)) {
-		retval = ext2fs_adjust_ea_refcount2(fs,
-					ext2fs_file_acl_block(fs, inode),
-					block_buf, -1, &count);
+		retval = ext2fs_adjust_ea_refcount3(fs,
+				ext2fs_file_acl_block(fs, inode),
+				block_buf, -1, &count, ino);
 		if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
 			retval = 0;
 			count = 1;
@@ -580,6 +580,19 @@
 		}
 	}
 
+	/* Are metadata_csum and uninit_bg both set? */
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+	    EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+	    fix_problem(ctx, PR_0_META_AND_GDT_CSUM_SET, &pctx)) {
+		fs->super->s_feature_ro_compat &=
+			~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
+		ext2fs_mark_super_dirty(fs);
+		for (i = 0; i < fs->group_desc_count; i++)
+			ext2fs_group_desc_csum_set(fs, i);
+	}
+
 	/* Is 64bit set and extents unset? */
 	if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
 				      EXT4_FEATURE_INCOMPAT_64BIT) &&
@@ -597,8 +610,7 @@
 	first_block = sb->s_first_data_block;
 	last_block = ext2fs_blocks_count(sb)-1;
 
-	csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+	csum_flag = ext2fs_has_group_desc_csum(fs);
 	for (i = 0; i < fs->group_desc_count; i++) {
 		pctx.group = i;
 
@@ -726,6 +738,7 @@
 	if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
 		if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
 			uuid_generate(sb->s_uuid);
+			ext2fs_init_csum_seed(fs);
 			fs->flags |= EXT2_FLAG_DIRTY;
 			fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
 		}
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 6f94644..429f1cd 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1153,6 +1153,11 @@
 			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;
 }
@@ -1271,6 +1276,7 @@
 	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) {
@@ -1753,7 +1759,7 @@
 	}
 
 	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) {
diff --git a/e2fsck/util.c b/e2fsck/util.c
index 9f920b2..e7e8704 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -278,7 +278,9 @@
 	old_op = ehandler_operation(_("reading inode and block bitmaps"));
 	e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE, "fs_bitmaps",
 			       &save_type);
+	ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
 	retval = ext2fs_read_bitmaps(fs);
+	ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
 	fs->default_bitmap_type = save_type;
 	ehandler_operation(old_op);
 	if (retval) {
diff --git a/lib/blkid/probe.h b/lib/blkid/probe.h
index 37e80ef..d6809e1 100644
--- a/lib/blkid/probe.h
+++ b/lib/blkid/probe.h
@@ -110,6 +110,7 @@
 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK	0x0020
 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE	0x0040
 #define EXT4_FEATURE_RO_COMPAT_QUOTA		0x0100
+#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM	0x0400
 
 /* for s_feature_incompat */
 #define EXT2_FEATURE_INCOMPAT_FILETYPE		0x0002
diff --git a/lib/config.h.in b/lib/config.h.in
index b575a5c..12ac1e0 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -12,6 +12,9 @@
 /* Define to 1 if debugging ext3/4 journal code */
 #undef CONFIG_JBD_DEBUG
 
+/* Define to 1 to enable mmp support */
+#undef CONFIG_MMP
+
 /* Define to 1 to enable quota support */
 #undef CONFIG_QUOTA
 
@@ -29,6 +32,12 @@
 /* Define to 1 to disable use of backtrace */
 #undef DISABLE_BACKTRACE
 
+/* Define to 1 to enable bitmap stats. */
+#undef ENABLE_BMAP_STATS
+
+/* Define to 1 to enable bitmap stats. */
+#undef ENABLE_BMAP_STATS_OPS
+
 /* Define to 1 if ext2 compression enabled */
 #undef ENABLE_COMPRESSION
 
diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index 1d3e689..d0e29b8 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -95,7 +95,7 @@
 			"dirdata"},
 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_LARGEDIR,
 			"large_dir"},
-	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINEDATA,
+	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINE_DATA,
 			"inline_data"},
 	{	0, 0, 0 },
 };
@@ -110,6 +110,8 @@
                        "journal_64bit" },
        {       E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT,
                        "journal_async_commit" },
+       {       E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_CSUM_V2,
+                       "journal_checksum_v2" },
        {       0, 0, 0 },
 };
 
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 6f741c0..a7ea38a 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -196,6 +196,16 @@
 #define EXT2_GOOD_OLD_REV 0
 #endif
 
+static const char *checksum_type(__u8 type)
+{
+	switch (type) {
+	case EXT2_CRC32C_CHKSUM:
+		return "crc32c";
+	default:
+		return "unknown";
+	}
+}
+
 void list_super2(struct ext2_super_block * sb, FILE *f)
 {
 	int inode_blocks_per_group;
@@ -431,9 +441,12 @@
 		fprintf(f, "Group quota inode:        %u\n",
 			sb->s_grp_quota_inum);
 
-	if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+	if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+		fprintf(f, "Checksum type:            %s\n",
+			checksum_type(sb->s_checksum_type));
 		fprintf(f, "Checksum:                 0x%08x\n",
 			sb->s_checksum);
+	}
 }
 
 void list_super (struct ext2_super_block * s)
diff --git a/lib/e2p/pf.c b/lib/e2p/pf.c
index e2f8ce5..f116ac3 100644
--- a/lib/e2p/pf.c
+++ b/lib/e2p/pf.c
@@ -50,6 +50,7 @@
 	{ EXT4_EXTENTS_FL, "e", "Extents" },
 	{ EXT4_HUGE_FILE_FL, "h", "Huge_file" },
 	{ FS_NOCOW_FL, "C", "No_COW" },
+	{ EXT4_INLINE_DATA_FL, "N", "Inline_Data" },
 	{ 0, NULL, NULL }
 };
 
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 1063e62..92b6ab0 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -50,6 +50,7 @@
 	freefs.o \
 	gen_bitmap.o \
 	gen_bitmap64.o \
+	get_num_dirs.o \
 	get_pathname.o \
 	getsize.o \
 	getsectsize.o \
@@ -123,6 +124,7 @@
 	$(srcdir)/freefs.c \
 	$(srcdir)/gen_bitmap.c \
 	$(srcdir)/gen_bitmap64.c \
+	$(srcdir)/get_num_dirs.c \
 	$(srcdir)/get_pathname.c \
 	$(srcdir)/getsize.c \
 	$(srcdir)/getsectsize.c \
@@ -710,6 +712,13 @@
  $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
  $(srcdir)/bitops.h $(srcdir)/bmap64.h
+get_num_dirs.o: $(srcdir)/get_num_dirs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/bitops.h
 get_pathname.o: $(srcdir)/get_pathname.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c
index 7dcfd3c..1be4ecc 100644
--- a/lib/ext2fs/alloc.c
+++ b/lib/ext2fs/alloc.c
@@ -31,8 +31,7 @@
  */
 static void clear_block_uninit(ext2_filsys fs, dgrp_t group)
 {
-	if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
+	if (!ext2fs_has_group_desc_csum(fs) ||
 	    !(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
 		return;
 
@@ -52,8 +51,7 @@
 {
 	ext2_ino_t	i, ino;
 
-	if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
+	if (!ext2fs_has_group_desc_csum(fs) ||
 	    !(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)))
 		return;
 
diff --git a/lib/ext2fs/alloc_stats.c b/lib/ext2fs/alloc_stats.c
index 1f58e00..5bb86ef 100644
--- a/lib/ext2fs/alloc_stats.c
+++ b/lib/ext2fs/alloc_stats.c
@@ -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;
diff --git a/lib/ext2fs/alloc_tables.c b/lib/ext2fs/alloc_tables.c
index fec9003..474a61a 100644
--- a/lib/ext2fs/alloc_tables.c
+++ b/lib/ext2fs/alloc_tables.c
@@ -222,16 +222,19 @@
 	dgrp_t		i;
 	struct ext2fs_numeric_progress_struct progress;
 
-	ext2fs_numeric_progress_init(fs, &progress, NULL,
-				     fs->group_desc_count);
+	if (fs->progress_ops && fs->progress_ops->init)
+		(fs->progress_ops->init)(fs, &progress, NULL,
+					 fs->group_desc_count);
 
 	for (i = 0; i < fs->group_desc_count; i++) {
-		ext2fs_numeric_progress_update(fs, &progress, i);
+		if (fs->progress_ops && fs->progress_ops->update)
+			(fs->progress_ops->update)(fs, &progress, i);
 		retval = ext2fs_allocate_group_table(fs, i, fs->block_map);
 		if (retval)
 			return retval;
 	}
-	ext2fs_numeric_progress_close(fs, &progress, NULL);
+	if (fs->progress_ops && fs->progress_ops->close)
+		(fs->progress_ops->close)(fs, &progress, NULL);
 	return 0;
 }
 
diff --git a/lib/ext2fs/blkmap64_ba.c b/lib/ext2fs/blkmap64_ba.c
index 894293a..87946c7 100644
--- a/lib/ext2fs/blkmap64_ba.c
+++ b/lib/ext2fs/blkmap64_ba.c
@@ -310,12 +310,16 @@
 	       (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
 }
 
+#ifdef ENABLE_BMAP_STATS
 static void ba_print_stats(ext2fs_generic_bitmap bitmap)
 {
 	fprintf(stderr, "%16llu Bytes used by bitarray\n",
 		((bitmap->real_end - bitmap->start) >> 3) + 1 +
 		sizeof(struct ext2fs_ba_private_struct));
 }
+#else
+static void ba_print_stats(ext2fs_generic_bitmap bitmap) {}
+#endif
 
 /* Find the first zero bit between start and end, inclusive. */
 static errcode_t ba_find_first_zero(ext2fs_generic_bitmap bitmap,
diff --git a/lib/ext2fs/blkmap64_rb.c b/lib/ext2fs/blkmap64_rb.c
index 148f856..28820cb 100644
--- a/lib/ext2fs/blkmap64_rb.c
+++ b/lib/ext2fs/blkmap64_rb.c
@@ -41,7 +41,7 @@
 	struct bmap_rb_extent *wcursor;
 	struct bmap_rb_extent *rcursor;
 	struct bmap_rb_extent *rcursor_next;
-#ifdef BMAP_STATS_OPS
+#ifdef ENABLE_BMAP_STATS_OPS
 	__u64 mark_hit;
 	__u64 test_hit;
 #endif
@@ -146,10 +146,8 @@
 
 	retval = ext2fs_get_mem(sizeof (struct bmap_rb_extent),
 				&new_ext);
-	if (retval) {
-		perror("ext2fs_get_mem");
-		exit(1);
-	}
+	if (retval)
+		abort();
 
 	new_ext->start = start;
 	new_ext->count = count;
@@ -183,7 +181,7 @@
 	bp->rcursor_next = NULL;
 	bp->wcursor = NULL;
 
-#ifdef BMAP_STATS_OPS
+#ifdef ENABLE_BMAP_STATS_OPS
 	bp->test_hit = 0;
 	bp->mark_hit = 0;
 #endif
@@ -331,7 +329,7 @@
 		goto search_tree;
 
 	if (bit >= rcursor->start && bit < rcursor->start + rcursor->count) {
-#ifdef BMAP_STATS_OPS
+#ifdef ENABLE_BMAP_STATS_OPS
 		bp->test_hit++;
 #endif
 		return 1;
@@ -396,7 +394,7 @@
 	if (ext) {
 		if (start >= ext->start &&
 		    start <= (ext->start + ext->count)) {
-#ifdef BMAP_STATS_OPS
+#ifdef ENABLE_BMAP_STATS_OPS
 			bp->mark_hit++;
 #endif
 			goto got_extent;
@@ -896,7 +894,7 @@
 	return ENOENT;
 }
 
-#ifdef BMAP_STATS
+#ifdef ENABLE_BMAP_STATS
 static void rb_print_stats(ext2fs_generic_bitmap bitmap)
 {
 	struct ext2fs_rb_private *bp;
@@ -907,7 +905,7 @@
 	__u64 min_size = ULONG_MAX;
 	__u64 size = 0, avg_size = 0;
 	double eff;
-#ifdef BMAP_STATS_OPS
+#ifdef ENABLE_BMAP_STATS_OPS
 	__u64 mark_all, test_all;
 	double m_hit = 0.0, t_hit = 0.0;
 #endif
@@ -931,7 +929,7 @@
 		min_size = 0;
 	eff = (double)((count * sizeof(struct bmap_rb_extent)) << 3) /
 	      (bitmap->real_end - bitmap->start);
-#ifdef BMAP_STATS_OPS
+#ifdef ENABLE_BMAP_STATS_OPS
 	mark_all = bitmap->stats.mark_count + bitmap->stats.mark_ext_count;
 	test_all = bitmap->stats.test_count + bitmap->stats.test_ext_count;
 	if (mark_all)
@@ -958,7 +956,9 @@
 		eff);
 }
 #else
-static void rb_print_stats(ext2fs_generic_bitmap bitmap){}
+static void rb_print_stats(ext2fs_generic_bitmap bitmap EXT2FS_ATTR((unused)))
+{
+}
 #endif
 
 struct ext2_bitmap_ops ext2fs_blkmap64_rbtree = {
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 8ced1ee..aced897 100644
--- a/lib/ext2fs/blknum.c
+++ b/lib/ext2fs/blknum.c
@@ -200,6 +200,21 @@
 }
 
 /*
+ * Return the block bitmap checksum of a group
+ */
+__u32 ext2fs_block_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+	struct ext4_group_desc *gdp;
+	__u32 csum;
+
+	gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+	csum = gdp->bg_block_bitmap_csum_lo;
+	if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+		csum |= ((__u32)gdp->bg_block_bitmap_csum_hi << 16);
+	return csum;
+}
+
+/*
  * Return the block bitmap block of a group
  */
 blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group)
@@ -227,6 +242,21 @@
 }
 
 /*
+ * Return the inode bitmap checksum of a group
+ */
+__u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+	struct ext4_group_desc *gdp;
+	__u32 csum;
+
+	gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+	csum = gdp->bg_inode_bitmap_csum_lo;
+	if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+		csum |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16);
+	return csum;
+}
+
+/*
  * Return the inode bitmap block of a group
  */
 blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group)
diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c
index b8c6879..601129d 100644
--- a/lib/ext2fs/block.c
+++ b/lib/ext2fs/block.c
@@ -345,6 +345,13 @@
 		return ctx.errcode;
 
 	/*
+	 * An inode with inline data has no blocks over which to
+	 * iterate, so return an error code indicating this fact.
+	 */
+	if (inode.i_flags & EXT4_INLINE_DATA_FL)
+		return EXT2_ET_INLINE_DATA_CANT_ITERATE;
+
+	/*
 	 * Check to see if we need to limit large files
 	 */
 	if (flags & BLOCK_FLAG_NO_LARGE) {
diff --git a/lib/ext2fs/bmap64.h b/lib/ext2fs/bmap64.h
index 9deba46..d8c7a3c 100644
--- a/lib/ext2fs/bmap64.h
+++ b/lib/ext2fs/bmap64.h
@@ -13,7 +13,7 @@
 	int		type;
 	struct timeval	created;
 
-#ifdef BMAP_STATS_OPS
+#ifdef ENABLE_BMAP_STATS_OPS
 	unsigned long	copy_count;
 	unsigned long	resize_count;
 	unsigned long	mark_count;
@@ -33,7 +33,7 @@
 
 	unsigned long	mark_seq;
 	unsigned long	test_seq;
-#endif /* BMAP_STATS_OPS */
+#endif /* ENABLE_BMAP_STATS_OPS */
 };
 
 
@@ -48,7 +48,7 @@
 	char			*description;
 	void			*private;
 	errcode_t		base_error_code;
-#ifdef BMAP_STATS
+#ifdef ENABLE_BMAP_STATS
 	struct ext2_bmap_statistics	stats;
 #endif
 };
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 4e91778..c2eaec1 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -255,15 +255,19 @@
 				    blk64_t group_block,
 				    struct ext2_super_block *super_shadow)
 {
+	errcode_t retval;
 	dgrp_t	sgrp = group;
 
 	if (sgrp > ((1 << 16) - 1))
 		sgrp = (1 << 16) - 1;
+
+	super_shadow->s_block_group_nr = sgrp;
 #ifdef WORDS_BIGENDIAN
-	super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
-#else
-	fs->super->s_block_group_nr = sgrp;
+	ext2fs_swap_super(super_shadow);
 #endif
+	retval = ext2fs_superblock_csum_set(fs, super_shadow);
+	if (retval)
+		return retval;
 
 	return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE,
 				    super_shadow);
@@ -297,6 +301,23 @@
 
 	fs->super->s_wtime = fs->now ? fs->now : time(NULL);
 	fs->super->s_block_group_nr = 0;
+
+	/*
+	 * If the write_bitmaps() function is present, call it to
+	 * flush the bitmaps.  This is done this way so that a simple
+	 * program that doesn't mess with the bitmaps doesn't need to
+	 * drag in the bitmaps.c code.
+	 *
+	 * Bitmap checksums live in the group descriptor, so the
+	 * bitmaps need to be written before the descriptors.
+	 */
+	if (fs->write_bitmaps) {
+		retval = fs->write_bitmaps(fs);
+		if (retval)
+			goto errout;
+	}
+
+	/* Prepare the group descriptors for writing */
 #ifdef WORDS_BIGENDIAN
 	retval = EXT2_ET_NO_MEMORY;
 	retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
@@ -306,6 +327,7 @@
 				  &group_shadow);
 	if (retval)
 		goto errout;
+	memcpy(super_shadow, fs->super, sizeof(struct ext2_super_block));
 	memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize *
 	       fs->desc_blocks);
 
@@ -326,10 +348,6 @@
 	 */
 	fs->super->s_state &= ~EXT2_VALID_FS;
 	fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
-#ifdef WORDS_BIGENDIAN
-	*super_shadow = *fs->super;
-	ext2fs_swap_super(super_shadow);
-#endif
 
 	/*
 	 * If this is an external journal device, don't write out the
@@ -349,14 +367,16 @@
 	else
 		old_desc_blocks = fs->desc_blocks;
 
-	ext2fs_numeric_progress_init(fs, &progress, NULL,
-				     fs->group_desc_count);
+	if (fs->progress_ops && fs->progress_ops->init)
+		(fs->progress_ops->init)(fs, &progress, NULL,
+					 fs->group_desc_count);
 
 
 	for (i = 0; i < fs->group_desc_count; i++) {
 		blk64_t	super_blk, old_desc_blk, new_desc_blk;
 
-		ext2fs_numeric_progress_update(fs, &progress, i);
+		if (fs->progress_ops && fs->progress_ops->update)
+			(fs->progress_ops->update)(fs, &progress, i);
 		ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk,
 					 &new_desc_blk, 0);
 
@@ -385,19 +405,8 @@
 		}
 	}
 
-	ext2fs_numeric_progress_close(fs, &progress, NULL);
-
-	/*
-	 * If the write_bitmaps() function is present, call it to
-	 * flush the bitmaps.  This is done this way so that a simple
-	 * program that doesn't mess with the bitmaps doesn't need to
-	 * drag in the bitmaps.c code.
-	 */
-	if (fs->write_bitmaps) {
-		retval = fs->write_bitmaps(fs);
-		if (retval)
-			goto errout;
-	}
+	if (fs->progress_ops && fs->progress_ops->close)
+		(fs->progress_ops->close)(fs, &progress, NULL);
 
 write_primary_superblock_only:
 	/*
@@ -416,6 +425,10 @@
 	ext2fs_swap_super(super_shadow);
 #endif
 
+	retval = ext2fs_superblock_csum_set(fs, super_shadow);
+	if (retval)
+		return retval;
+
 	if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC))
 		retval = io_channel_flush(fs->io);
 	retval = write_primary_superblock(fs, super_shadow);
diff --git a/lib/ext2fs/crc32c.c b/lib/ext2fs/crc32c.c
index 2512528..624ad77 100644
--- a/lib/ext2fs/crc32c.c
+++ b/lib/ext2fs/crc32c.c
@@ -32,7 +32,6 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
-#define __force
 #define min(x, y)		((x) > (y) ? (y) : (x))
 #define __ALIGN_KERNEL_MASK(x, mask)	(((x) + (mask)) & ~(mask))
 #define __ALIGN_KERNEL(x, a)	__ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)
@@ -58,400 +57,189 @@
 #endif
 
 #if CRC_LE_BITS > 8
-# define tole(x) (__force uint32_t) __constant_cpu_to_le32(x)
+# define tole(x) __constant_cpu_to_le32(x)
 #else
 # define tole(x) (x)
 #endif
 
 #if CRC_BE_BITS > 8
-# define tobe(x) (__force uint32_t) __constant_cpu_to_be32(x)
+# define tobe(x) __constant_cpu_to_be32(x)
 #else
 # define tobe(x) (x)
 #endif
 
 #include "crc32c_table.h"
 
-#if CRC_LE_BITS == 32
-/* slice by 4 algorithm */
-static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len)
+#if CRC_LE_BITS > 8 || CRC_BE_BITS > 8
+
+/* implements slicing-by-4 or slicing-by-8 algorithm */
+static inline uint32_t
+crc32_body(uint32_t crc, unsigned char const *buf, size_t len,
+	   const uint32_t (*tab)[256])
 {
-	const uint8_t *p8;
-	const uint32_t *p32;
-	size_t init_bytes;
-	size_t words;
-	size_t end_bytes;
-	size_t i;
+# ifndef WORDS_BIGENDIAN
+#  define DO_CRC(x) (crc = t0[(crc ^ (x)) & 255] ^ (crc >> 8))
+#  define DO_CRC4 (t3[(q) & 255] ^ t2[(q >> 8) & 255] ^ \
+		   t1[(q >> 16) & 255] ^ t0[(q >> 24) & 255])
+#  define DO_CRC8 (t7[(q) & 255] ^ t6[(q >> 8) & 255] ^ \
+		   t5[(q >> 16) & 255] ^ t4[(q >> 24) & 255])
+# else
+#  define DO_CRC(x) (crc = t0[((crc >> 24) ^ (x)) & 255] ^ (crc << 8))
+#  define DO_CRC4 (t0[(q) & 255] ^ t1[(q >> 8) & 255] ^ \
+		   t2[(q >> 16) & 255] ^ t3[(q >> 24) & 255])
+#  define DO_CRC8 (t4[(q) & 255] ^ t5[(q >> 8) & 255] ^ \
+		   t6[(q >> 16) & 255] ^ t7[(q >> 24) & 255])
+# endif
+	const uint32_t *b;
+	size_t rem_len;
+	const uint32_t *t0 = tab[0], *t1 = tab[1], *t2 = tab[2], *t3 = tab[3];
+	const uint32_t *t4 = tab[4], *t5 = tab[5], *t6 = tab[6], *t7 = tab[7];
 	uint32_t q;
-	uint8_t i0, i1, i2, i3;
 
-	crc = (__force uint32_t) __cpu_to_le32(crc);
-
-	/* unroll loop into 'init_bytes' odd bytes followed by
-	 * 'words' aligned 4 byte words followed by
-	 * 'end_bytes' odd bytes at the end */
-	p8 = buf;
-	p32 = (uint32_t *)PTR_ALIGN(p8, 4);
-	init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
-	words = (len - init_bytes) >> 2;
-	end_bytes = (len - init_bytes) & 3;
-
-	for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_le[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_le[i0] ^ (crc << 8);
-#endif
+	/* Align it */
+	if (unlikely((long)buf & 3 && len)) {
+		do {
+			DO_CRC(*buf++);
+		} while ((--len) && ((long)buf)&3);
 	}
 
-	/* using pre-increment below slightly faster */
-	p32--;
+# if CRC_LE_BITS == 32
+	rem_len = len & 3;
+	len = len >> 2;
+# else
+	rem_len = len & 7;
+	len = len >> 3;
+# endif
 
-	for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
-		q = *++p32 ^ crc;
-		i3 = q;
-		i2 = q >> 8;
-		i1 = q >> 16;
-		i0 = q >> 24;
-		crc = t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#else
-		q = *++p32 ^ crc;
-		i3 = q >> 24;
-		i2 = q >> 16;
-		i1 = q >> 8;
-		i0 = q;
-		crc = t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#endif
+	b = (const uint32_t *)buf;
+	for (--b; len; --len) {
+		q = crc ^ *++b; /* use pre increment for speed */
+# if CRC_LE_BITS == 32
+		crc = DO_CRC4;
+# else
+		crc = DO_CRC8;
+		q = *++b;
+		crc ^= DO_CRC4;
+# endif
 	}
-
-	p8 = (uint8_t *)(++p32);
-
-	for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_le[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_le[i0] ^ (crc << 8);
-#endif
+	len = rem_len;
+	/* And the last few bytes */
+	if (len) {
+		const uint8_t *p = (const uint8_t *)(b + 1) - 1;
+		do {
+			DO_CRC(*++p); /* use pre increment for speed */
+		} while (--len);
 	}
-
-	return __le32_to_cpu((__force __le32)crc);
-}
-#endif
-
-#if CRC_BE_BITS == 32
-static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len)
-{
-	const uint8_t *p8;
-	const uint32_t *p32;
-	size_t init_bytes;
-	size_t words;
-	size_t end_bytes;
-	size_t i;
-	uint32_t q;
-	uint8_t i0, i1, i2, i3;
-
-	crc = (__force uint32_t) __cpu_to_be32(crc);
-
-	p8 = buf;
-	p32 = (uint32_t *)PTR_ALIGN(p8, 4);
-	init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
-	words = (len - init_bytes) >> 2;
-	end_bytes = (len - init_bytes) & 3;
-
-	for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_be[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_be[i0] ^ (crc << 8);
-#endif
-	}
-
-	p32--;
-
-	for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
-		q = *++p32 ^ crc;
-		i3 = q;
-		i2 = q >> 8;
-		i1 = q >> 16;
-		i0 = q >> 24;
-		crc = t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#else
-		q = *++p32 ^ crc;
-		i3 = q >> 24;
-		i2 = q >> 16;
-		i1 = q >> 8;
-		i0 = q;
-		crc = t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#endif
-	}
-
-	p8 = (uint8_t *)(++p32);
-
-	for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_be[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_be[i0] ^ (crc << 8);
-#endif
-	}
-
-	return __be32_to_cpu((__force __be32)crc);
-}
-#endif
-
-#if CRC_LE_BITS == 64
-/* slice by 8 algorithm */
-static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len)
-{
-	const uint8_t *p8;
-	const uint32_t *p32;
-	size_t init_bytes;
-	size_t words;
-	size_t end_bytes;
-	size_t i;
-	uint32_t q;
-	uint8_t i0, i1, i2, i3;
-
-	crc = (__force uint32_t) __cpu_to_le32(crc);
-
-	p8 = buf;
-	p32 = (const uint32_t *)PTR_ALIGN(p8, 8);
-	init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
-	words = (len - init_bytes) >> 3;
-	end_bytes = (len - init_bytes) & 7;
-
-	for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_le[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_le[i0] ^ (crc << 8);
-#endif
-	}
-
-	p32--;
-
-	for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
-		q = *++p32 ^ crc;
-		i3 = q;
-		i2 = q >> 8;
-		i1 = q >> 16;
-		i0 = q >> 24;
-		crc = t7_le[i3] ^ t6_le[i2] ^ t5_le[i1] ^ t4_le[i0];
-
-		q = *++p32;
-		i3 = q;
-		i2 = q >> 8;
-		i1 = q >> 16;
-		i0 = q >> 24;
-		crc ^= t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#else
-		q = *++p32 ^ crc;
-		i3 = q >> 24;
-		i2 = q >> 16;
-		i1 = q >> 8;
-		i0 = q;
-		crc = t7_le[i3] ^ t6_le[i2] ^ t5_le[i1] ^ t4_le[i0];
-
-		q = *++p32;
-		i3 = q >> 24;
-		i2 = q >> 16;
-		i1 = q >> 8;
-		i0 = q;
-		crc ^= t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#endif
-	}
-
-	p8 = (const uint8_t *)(++p32);
-
-	for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_le[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_le[i0] ^ (crc << 8);
-#endif
-	}
-
-	return __le32_to_cpu(crc);
-}
-#endif
-
-#if CRC_BE_BITS == 64
-static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len)
-{
-	const uint8_t *p8;
-	const uint32_t *p32;
-	size_t init_bytes;
-	size_t words;
-	size_t end_bytes;
-	size_t i;
-	uint32_t q;
-	uint8_t i0, i1, i2, i3;
-
-	crc = (__force uint32_t) __cpu_to_be32(crc);
-
-	p8 = buf;
-	p32 = (const uint32_t *)PTR_ALIGN(p8, 8);
-	init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
-	words = (len - init_bytes) >> 3;
-	end_bytes = (len - init_bytes) & 7;
-
-	for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_be[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_be[i0] ^ (crc << 8);
-#endif
-	}
-
-	p32--;
-
-	for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
-		q = *++p32 ^ crc;
-		i3 = q;
-		i2 = q >> 8;
-		i1 = q >> 16;
-		i0 = q >> 24;
-		crc = t7_be[i3] ^ t6_be[i2] ^ t5_be[i1] ^ t4_be[i0];
-
-		q = *++p32;
-		i3 = q;
-		i2 = q >> 8;
-		i1 = q >> 16;
-		i0 = q >> 24;
-		crc ^= t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#else
-		q = *++p32 ^ crc;
-		i3 = q >> 24;
-		i2 = q >> 16;
-		i1 = q >> 8;
-		i0 = q;
-		crc = t7_be[i3] ^ t6_be[i2] ^ t5_be[i1] ^ t4_be[i0];
-
-		q = *++p32;
-		i3 = q >> 24;
-		i2 = q >> 16;
-		i1 = q >> 8;
-		i0 = q;
-		crc ^= t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#endif
-	}
-
-	p8 = (const uint8_t *)(++p32);
-
-	for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
-		i0 = *p8++ ^ crc;
-		crc = t0_be[i0] ^ (crc >> 8);
-#else
-		i0 = *p8++ ^ (crc >> 24);
-		crc = t0_be[i0] ^ (crc << 8);
-#endif
-	}
-
-	return __be32_to_cpu(crc);
+	return crc;
+#undef DO_CRC
+#undef DO_CRC4
+#undef DO_CRC8
 }
 #endif
 
 /**
- * crc32c_le() - Calculate bitwise little-endian CRC32c.
- * @crc: seed value for computation.  ~0 for ext4, sometimes 0 for
- *	other uses, or the previous crc32c value if computing incrementally.
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *	other uses, or the previous crc32 value if computing incrementally.
  * @p: pointer to buffer over which CRC is run
  * @len: length of buffer @p
  */
-uint32_t ext2fs_crc32c_le(uint32_t crc, unsigned char const *p, size_t len)
+static inline uint32_t crc32_le_generic(uint32_t crc, unsigned char const *p,
+					size_t len, const uint32_t (*tab)[256],
+					uint32_t polynomial EXT2FS_ATTR((unused)))
 {
 #if CRC_LE_BITS == 1
 	int i;
 	while (len--) {
 		crc ^= *p++;
 		for (i = 0; i < 8; i++)
-			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+			crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0);
 	}
 # elif CRC_LE_BITS == 2
 	while (len--) {
 		crc ^= *p++;
-		crc = (crc >> 2) ^ t0_le[crc & 0x03];
-		crc = (crc >> 2) ^ t0_le[crc & 0x03];
-		crc = (crc >> 2) ^ t0_le[crc & 0x03];
-		crc = (crc >> 2) ^ t0_le[crc & 0x03];
+		crc = (crc >> 2) ^ tab[0][crc & 3];
+		crc = (crc >> 2) ^ tab[0][crc & 3];
+		crc = (crc >> 2) ^ tab[0][crc & 3];
+		crc = (crc >> 2) ^ tab[0][crc & 3];
 	}
 # elif CRC_LE_BITS == 4
 	while (len--) {
 		crc ^= *p++;
-		crc = (crc >> 4) ^ t0_le[crc & 0x0f];
-		crc = (crc >> 4) ^ t0_le[crc & 0x0f];
+		crc = (crc >> 4) ^ tab[0][crc & 15];
+		crc = (crc >> 4) ^ tab[0][crc & 15];
 	}
 # elif CRC_LE_BITS == 8
+	/* aka Sarwate algorithm */
 	while (len--) {
 		crc ^= *p++;
-		crc = (crc >> 8) ^ t0_le[crc & 0xff];
+		crc = (crc >> 8) ^ tab[0][crc & 255];
 	}
 # else
-	crc = crc32c_le_body(crc, p, len);
-# endif
+	crc = __cpu_to_le32(crc);
+	crc = crc32_body(crc, p, len, tab);
+	crc = __le32_to_cpu(crc);
+#endif
 	return crc;
 }
 
+uint32_t ext2fs_crc32c_le(uint32_t crc, unsigned char const *p, size_t len)
+{
+	return crc32_le_generic(crc, p, len, crc32ctable_le, CRC32C_POLY_LE);
+}
+
 /**
- * crc32c_be() - Calculate bitwise big-endian CRC32c.
- * @crc: seed value for computation.  ~0 for ext4, sometimes 0 for
- *	other uses, or the previous crc32c value if computing incrementally.
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *	other uses, or the previous crc32 value if computing incrementally.
  * @p: pointer to buffer over which CRC is run
  * @len: length of buffer @p
  */
-uint32_t ext2fs_crc32c_be(uint32_t crc, unsigned char const *p, size_t len)
+static inline uint32_t crc32_be_generic(uint32_t crc, unsigned char const *p,
+					size_t len, const uint32_t (*tab)[256],
+					uint32_t polynomial EXT2FS_ATTR((unused)))
 {
 #if CRC_BE_BITS == 1
 	int i;
 	while (len--) {
 		crc ^= *p++ << 24;
 		for (i = 0; i < 8; i++)
-			crc = (crc << 1) ^
-			      ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+			crc =
+			    (crc << 1) ^ ((crc & 0x80000000) ? polynomial :
+					  0);
 	}
 # elif CRC_BE_BITS == 2
 	while (len--) {
 		crc ^= *p++ << 24;
-		crc = (crc << 2) ^ t0_be[crc >> 30];
-		crc = (crc << 2) ^ t0_be[crc >> 30];
-		crc = (crc << 2) ^ t0_be[crc >> 30];
-		crc = (crc << 2) ^ t0_be[crc >> 30];
+		crc = (crc << 2) ^ tab[0][crc >> 30];
+		crc = (crc << 2) ^ tab[0][crc >> 30];
+		crc = (crc << 2) ^ tab[0][crc >> 30];
+		crc = (crc << 2) ^ tab[0][crc >> 30];
 	}
 # elif CRC_BE_BITS == 4
 	while (len--) {
 		crc ^= *p++ << 24;
-		crc = (crc << 4) ^ t0_be[crc >> 28];
-		crc = (crc << 4) ^ t0_be[crc >> 28];
+		crc = (crc << 4) ^ tab[0][crc >> 28];
+		crc = (crc << 4) ^ tab[0][crc >> 28];
 	}
 # elif CRC_BE_BITS == 8
 	while (len--) {
 		crc ^= *p++ << 24;
-		crc = (crc << 8) ^ t0_be[crc >> 24];
+		crc = (crc << 8) ^ tab[0][crc >> 24];
 	}
 # else
-	crc = crc32c_be_body(crc, p, len);
+	crc = __cpu_to_be32(crc);
+	crc = crc32_body(crc, p, len, tab);
+	crc = __be32_to_cpu(crc);
 # endif
 	return crc;
 }
 
+uint32_t ext2fs_crc32_be(uint32_t crc, unsigned char const *p, size_t len)
+{
+	return crc32_be_generic(crc, p, len, crc32table_be, CRCPOLY_BE);
+}
+
 #ifdef UNITTEST
 static uint8_t test_buf[] = {
 	0xd9, 0xd7, 0x6a, 0x13, 0x3a, 0xb1, 0x05, 0x48,
@@ -972,137 +760,137 @@
 	uint32_t crc;		/* random starting crc */
 	uint32_t start;		/* random offset in buf */
 	uint32_t length;	/* random length of test */
-	uint32_t crc_le;	/* expected crc32_le result */
-	uint32_t crc_be;	/* expected crc32_be result */
+	uint32_t crc32c_le;	/* expected crc32c_le result */
+	uint32_t crc32_be;	/* expected crc32_be result */
 } test[] = {
-	{0xffffffff, 0x00000000, 0x00001000, 0x13934bef, 0x14f3b75f},
-	{0xfe7328ea, 0x00000763, 0x00000717, 0xed2c0d70, 0x57531214},
-	{0x4c40684e, 0x00000721, 0x0000011e, 0xd7f46ccc, 0xedf12ec3},
-	{0x6b487f90, 0x00000264, 0x000007bc, 0x759e9939, 0x9af8e387},
-	{0x9f5810db, 0x00000afa, 0x00000255, 0x2685197f, 0x716de6ed},
-	{0xb15c4755, 0x00000d5b, 0x000002a4, 0xd8fadcb5, 0xfc34ae3f},
-	{0x06518253, 0x00000ffb, 0x00000004, 0xabee2433, 0xfa30ac9e},
-	{0xd9e71c55, 0x00000a2a, 0x00000259, 0x96682af2, 0xe5907ea3},
-	{0x0c1ae843, 0x00000ce4, 0x0000031b, 0x7b637c43, 0xe7f71b04},
-	{0xec3cd517, 0x000002ff, 0x00000566, 0x5d719a77, 0xed16e045},
-	{0x77828e95, 0x0000067f, 0x0000038f, 0x43ee5b6c, 0x35999927},
-	{0xec87b4e3, 0x00000d1c, 0x000002e3, 0x2ddd2eee, 0x9452d3f8},
-	{0x412158bb, 0x00000eee, 0x00000111, 0x67b38ba2, 0x177976d0},
-	{0x2e52de3e, 0x00000c4a, 0x000003b5, 0xbcc5d61d, 0xf60fee71},
-	{0x6ddaae8b, 0x00000d99, 0x00000266, 0x8b535544, 0x1dab8596},
-	{0x049b6cb1, 0x000009c5, 0x000000b0, 0xfc22cabc, 0x47ebc954},
-	{0x77d4b954, 0x0000028a, 0x000007fa, 0x71d00923, 0x905585ef},
-	{0x5e192355, 0x00000ac1, 0x000001fa, 0xb966b81a, 0x33c12903},
-	{0x7d80b71d, 0x00000213, 0x000001e0, 0x2bba371a, 0x5f4bd8d9},
-	{0x01f6f1e4, 0x000001d6, 0x00000395, 0xb7e8a647, 0x2a7943a1},
-	{0x1dfabb13, 0x00000e14, 0x000001eb, 0x53917fba, 0x8dee1e5d},
-	{0xb00a4449, 0x00000bf6, 0x00000409, 0xedecb577, 0x628e087d},
-	{0x7ecd3981, 0x0000083f, 0x0000016b, 0xefef62b9, 0xda4f94e6},
-	{0xf8f330d2, 0x000004be, 0x00000757, 0x9357c9f3, 0x8e2d5c2f},
-	{0x03c38af2, 0x00000d23, 0x000002dc, 0x360fa8c0, 0x6294c0d6},
-	{0x687bb79b, 0x00000f3d, 0x000000c2, 0x448d3be2, 0x08f48f3a},
-	{0x6710f550, 0x000009e9, 0x00000603, 0xdbfd1998, 0xc950ac29},
-	{0x873171d1, 0x00000787, 0x000004d5, 0xab7f1b62, 0xe66896ab},
-	{0x373b1314, 0x00000f0f, 0x000000f0, 0x184098ab, 0x4038e674},
-	{0x90fad9cd, 0x00000ead, 0x00000152, 0x23ce52ff, 0x9eff3974},
-	{0x19676fe7, 0x0000007d, 0x0000070d, 0xf8a76f1e, 0xfbc5c8a9},
-	{0x89facd45, 0x000005f3, 0x00000473, 0x4331a006, 0xb8f0f0cc},
-	{0x6f173747, 0x00000fc3, 0x0000003c, 0xb012f08e, 0x5126e378},
-	{0x4b44a106, 0x0000075a, 0x0000008b, 0xf6f7ac38, 0xf9b1781b},
-	{0xb620ad06, 0x00000774, 0x0000017e, 0xd34558e6, 0xb175edd3},
-	{0x976f21e9, 0x000008d7, 0x0000034a, 0xe533aa3a, 0x1e4367b9},
-	{0x687628c0, 0x000006c5, 0x0000061b, 0x3a840b15, 0xfb5989a0},
-	{0xe24ac108, 0x00000cd0, 0x0000032f, 0x51010ae8, 0xcdd8f182},
-	{0x361c44a3, 0x00000304, 0x00000719, 0xfd7bd481, 0x12de540f},
-	{0xd93ff95e, 0x00000db7, 0x0000008e, 0xcfbbc304, 0x42eecd5a},
-	{0xed752d12, 0x00000883, 0x00000091, 0x65a6c868, 0x9ebfa578},
-	{0xb4ff4b54, 0x000003d3, 0x000001c1, 0xf82597e7, 0xa8ad2b19},
-	{0x111b520f, 0x00000708, 0x000000eb, 0xc3e109f3, 0x323ace17},
-	{0x62c806f2, 0x00000ba3, 0x0000045c, 0x874d3a72, 0xaf1a1360},
-	{0x40d97470, 0x000005e1, 0x0000058d, 0x87a9684f, 0x524244a8},
-	{0x4312179c, 0x00000056, 0x0000070e, 0x809a00f5, 0xf9e940b0},
-	{0x13d5f84c, 0x00000a2d, 0x00000104, 0xf3d27578, 0x5d33341c},
-	{0x1f302cb2, 0x00000151, 0x00000014, 0x1e162693, 0x53c3cfc3},
-	{0xe491db24, 0x00000600, 0x000006f6, 0x7ff09615, 0xa300ecf7},
-	{0xf9a98069, 0x000002ba, 0x000002ad, 0x01af7387, 0x31c0911e},
-	{0xe9c477ad, 0x0000015f, 0x00000778, 0x6facf9a0, 0x1993b688},
-	{0x353f32b2, 0x0000087c, 0x00000783, 0x6cc964ea, 0x418db561},
-	{0x78e1b24f, 0x00000650, 0x000006a8, 0xb3bb7c27, 0xf2aad006},
-	{0x61aa400e, 0x00000049, 0x00000254, 0xb8cd1681, 0x79150b15},
-	{0xb84b10b0, 0x00000f73, 0x0000008c, 0x406a6450, 0x0c705222},
-	{0x9fa99c9c, 0x00000a7c, 0x000004d7, 0xfb3d21b4, 0xe4e789df},
-	{0x3fc9ebe3, 0x00000cd9, 0x000000d6, 0x43803f9c, 0x5a152be5},
-	{0x529879cd, 0x000002f2, 0x00000595, 0x78b4c6a6, 0xf7236ec4},
-	{0x3a933019, 0x00000516, 0x00000266, 0xdcb45436, 0x2c7935f5},
-	{0x887b4977, 0x00000227, 0x0000038d, 0xc5f7c3d9, 0x0d6d7df6},
-	{0x770745de, 0x000008c6, 0x00000739, 0xf69145e8, 0x47d5efc9},
-	{0x28be3b47, 0x00000c46, 0x0000032b, 0x764c028f, 0x1eb70d64},
-	{0x5013a050, 0x00000cf6, 0x00000309, 0xea8fe164, 0x186affa4},
-	{0x2ec4c9ba, 0x000006e8, 0x0000078d, 0xa35557a9, 0xb41f49ec},
-	{0xa9f950c9, 0x00000d33, 0x000002cc, 0x41ea8618, 0xab8dfae3},
-	{0x5b520229, 0x000007b2, 0x00000484, 0x44569f1f, 0x607a8052},
-	{0xd8dcbbfc, 0x0000002f, 0x0000048c, 0xdb88ab8b, 0xf1c411f1},
-	{0x25529792, 0x00000d1d, 0x000002e2, 0x20cda404, 0x32683a2d},
-	{0x9f3f6d71, 0x00000238, 0x0000079a, 0x0720443e, 0x4b8ba2ff},
-	{0x64121215, 0x000007ff, 0x0000038f, 0x6aacff2c, 0x3b84233b},
-	{0xfb6cdde0, 0x00000ef8, 0x00000107, 0xbd43a0f1, 0x926624d0},
-	{0x221c9d6f, 0x000007b6, 0x0000014f, 0xb67f834b, 0x2bdedda4},
-	{0x030e1de4, 0x00000836, 0x000004b4, 0x0d67d26a, 0x75a73b73},
-	{0xb56fa6cf, 0x00000c07, 0x000003f8, 0x60601ac1, 0x10a43f35},
-	{0xb55c89f5, 0x0000098e, 0x000001d4, 0x2400efbe, 0x006e28eb},
-	{0x5e90b6d5, 0x0000070b, 0x000003ea, 0x3bb5d6ea, 0xb175fa6b},
-	{0x2a7045ae, 0x00000961, 0x00000633, 0xfca89e4b, 0x962cd6d2},
-	{0x8b374ea9, 0x000006ba, 0x00000780, 0xbce036ed, 0x4dc8279b},
-	{0x8bd90bc9, 0x00000562, 0x00000369, 0xcb26a24b, 0x50dee743},
-	{0x5b1b1762, 0x000000fd, 0x0000051a, 0x33cdda07, 0xee75ff7b},
-	{0xa4153555, 0x0000058f, 0x000005c7, 0xbe50eeca, 0xe73fffcc},
-	{0x0be1f931, 0x00000651, 0x00000672, 0x95a25753, 0x4ad6270f},
-	{0xb7e78618, 0x00000a7f, 0x000002bb, 0xe06bcc1c, 0x1a35ee59},
-	{0x4a9bc41b, 0x00000e51, 0x000001ae, 0x709e8d2c, 0x75080ca8},
-	{0xfc359d13, 0x00000440, 0x000002f8, 0x0a58451f, 0x6fa3cfbf},
-	{0x5aa48619, 0x000006d1, 0x00000284, 0x928ead83, 0xbd600efc},
-	{0xa609afa8, 0x0000053e, 0x00000272, 0xb048c141, 0x184f80bb},
-	{0x3f108afb, 0x00000949, 0x00000150, 0x9a6bb5bc, 0x0ea02be1},
-	{0x79bec2d3, 0x000008ed, 0x00000712, 0x32692d57, 0x2eb13289},
-	{0x9429e067, 0x00000bc3, 0x0000043c, 0x5295ceff, 0x8a9014a7},
-	{0xae58b96a, 0x0000082d, 0x000007d2, 0xc2a681ba, 0x6af94089},
-	{0x95df24be, 0x00000985, 0x000004c1, 0x3a287765, 0x379fcb42},
-	{0x5e94976f, 0x00000596, 0x000004ed, 0xff00c489, 0x991fc1f5},
-	{0xf5e5f1de, 0x00000d31, 0x000002ce, 0x35f28e91, 0x543def1a},
-	{0xa2c219cf, 0x00000a3c, 0x00000374, 0x707d21eb, 0xa6d28bc1},
-	{0xf21b6ceb, 0x00000919, 0x00000135, 0x0847fb8b, 0x224468c2},
-	{0xaa988728, 0x00000787, 0x00000771, 0x885aeaa4, 0x814db00b},
-	{0xaa5dfaac, 0x000003e5, 0x0000051b, 0x52c48ab7, 0x725bef8a},
-	{0x0a053968, 0x00000d2a, 0x000002d5, 0x7a90256d, 0xc53b9402},
-	{0x1421dc20, 0x00000eef, 0x00000110, 0x97d6da24, 0x10846935},
-	{0xb47c2166, 0x00000a6a, 0x00000209, 0xcfd6cc52, 0x46e2797e},
-	{0x77dd1955, 0x000000de, 0x00000266, 0xba74bcaa, 0x4fa3fe9c},
-	{0x68a03cc2, 0x0000082f, 0x000007b0, 0x752bd5d8, 0x4f760c63},
-	{0x0226b0a3, 0x00000a5f, 0x000005a0, 0x82de4970, 0x8ee1310e},
-	{0x637bf3b1, 0x00000d93, 0x0000026c, 0x5c7115cb, 0x9f6a0ced},
-	{0x3b120edf, 0x00000c13, 0x000003ec, 0x80d7d20f, 0x241657d5},
-	{0xe2456780, 0x000002eb, 0x00000641, 0xc0a5d289, 0x74df96b4},
-	{0x9b2e7125, 0x00000c0c, 0x000003f3, 0xcc15f57e, 0x03e290bf},
-	{0x153033ef, 0x00000787, 0x000006b6, 0x3cde443b, 0x7bf1d121},
-	{0x18458b3f, 0x0000066c, 0x00000561, 0x9a2bd8c6, 0x9d564bef},
-	{0x4ff9d4b9, 0x00000c8f, 0x0000033a, 0xd0ee6d6d, 0xee00aa0b},
-	{0xdf84b5d9, 0x00000802, 0x0000029a, 0xdab0d74a, 0xd0cb63dc},
-	{0x81ee15df, 0x000003ce, 0x00000725, 0x9942e2de, 0xe48fb26b},
-	{0x5c768e04, 0x00000afd, 0x00000160, 0x36110831, 0x8dc74483},
-	{0xe5e18094, 0x00000b4b, 0x000000a0, 0xffa3e4a7, 0xc0145e1b},
-	{0xed7263b6, 0x00000d0d, 0x000002f2, 0xb0006a35, 0x5468ae3a},
-	{0x5bfde7d7, 0x000006fb, 0x00000554, 0xa4193b76, 0xb73d34b2},
-	{0x67f4a743, 0x00000b85, 0x0000047a, 0xf05c8d8f, 0x4f843e49},
-	{0xf13bdf22, 0x00000ff7, 0x00000008, 0x816351eb, 0x41f537f6},
-	{0x08ecc608, 0x00000d5d, 0x00000098, 0x90492772, 0xf5172204},
-	{0x296f52ba, 0x000004f9, 0x00000788, 0x5e5a4896, 0xe01d5b46},
-	{0xbe4624c2, 0x00000427, 0x000004ef, 0xcd267b94, 0x7b9069f4},
-	{0x906f7c7c, 0x00000a05, 0x0000003f, 0x03fcfc33, 0x7b6ff563},
-	{0x8f7b323e, 0x00000458, 0x000004c7, 0xcd4969c8, 0xd4c22ada},
-	{0x88d6593d, 0x00000597, 0x000005b5, 0xf199cd3b, 0x5c3e8ca2},
-	{0x978a7768, 0x00000268, 0x000001d3, 0xb28c95bd, 0x49a2cc67},
-	{0x857a621e, 0x000007a7, 0x000003a8, 0xf4bf84ab, 0xde26f369},
-	{0xb0e121ef, 0x000005be, 0x00000644, 0x28747c14, 0x61d4dc6b},
+	{0xffffffff, 0x00000000, 0x00001000, 0x13934bef, 0xd8ddcdc3},
+	{0xfe7328ea, 0x00000763, 0x00000717, 0xed2c0d70, 0xc863aef8},
+	{0x4c40684e, 0x00000721, 0x0000011e, 0xd7f46ccc, 0x173a11c4},
+	{0x6b487f90, 0x00000264, 0x000007bc, 0x759e9939, 0xd6307c56},
+	{0x9f5810db, 0x00000afa, 0x00000255, 0x2685197f, 0x2e5c9201},
+	{0xb15c4755, 0x00000d5b, 0x000002a4, 0xd8fadcb5, 0xf682c4be},
+	{0x06518253, 0x00000ffb, 0x00000004, 0xabee2433, 0x3d8abdf9},
+	{0xd9e71c55, 0x00000a2a, 0x00000259, 0x96682af2, 0x47b4d26c},
+	{0x0c1ae843, 0x00000ce4, 0x0000031b, 0x7b637c43, 0x62b47e8b},
+	{0xec3cd517, 0x000002ff, 0x00000566, 0x5d719a77, 0xff5bc5b7},
+	{0x77828e95, 0x0000067f, 0x0000038f, 0x43ee5b6c, 0x1a0cfacd},
+	{0xec87b4e3, 0x00000d1c, 0x000002e3, 0x2ddd2eee, 0x275118a7},
+	{0x412158bb, 0x00000eee, 0x00000111, 0x67b38ba2, 0xa74ecff5},
+	{0x2e52de3e, 0x00000c4a, 0x000003b5, 0xbcc5d61d, 0xbd800707},
+	{0x6ddaae8b, 0x00000d99, 0x00000266, 0x8b535544, 0xecbde1a1},
+	{0x049b6cb1, 0x000009c5, 0x000000b0, 0xfc22cabc, 0xfb78eb9f},
+	{0x77d4b954, 0x0000028a, 0x000007fa, 0x71d00923, 0x8c116f85},
+	{0x5e192355, 0x00000ac1, 0x000001fa, 0xb966b81a, 0x5aa17bbe},
+	{0x7d80b71d, 0x00000213, 0x000001e0, 0x2bba371a, 0xb5906aa6},
+	{0x01f6f1e4, 0x000001d6, 0x00000395, 0xb7e8a647, 0x3ad112b1},
+	{0x1dfabb13, 0x00000e14, 0x000001eb, 0x53917fba, 0xbaee0339},
+	{0xb00a4449, 0x00000bf6, 0x00000409, 0xedecb577, 0x6f3a3979},
+	{0x7ecd3981, 0x0000083f, 0x0000016b, 0xefef62b9, 0xe3e52eed},
+	{0xf8f330d2, 0x000004be, 0x00000757, 0x9357c9f3, 0x0835bc1b},
+	{0x03c38af2, 0x00000d23, 0x000002dc, 0x360fa8c0, 0x2ca885e6},
+	{0x687bb79b, 0x00000f3d, 0x000000c2, 0x448d3be2, 0x79be2f78},
+	{0x6710f550, 0x000009e9, 0x00000603, 0xdbfd1998, 0x1d25f627},
+	{0x873171d1, 0x00000787, 0x000004d5, 0xab7f1b62, 0xa76a5656},
+	{0x373b1314, 0x00000f0f, 0x000000f0, 0x184098ab, 0xba273974},
+	{0x90fad9cd, 0x00000ead, 0x00000152, 0x23ce52ff, 0xb7bc958c},
+	{0x19676fe7, 0x0000007d, 0x0000070d, 0xf8a76f1e, 0xf882b644},
+	{0x89facd45, 0x000005f3, 0x00000473, 0x4331a006, 0xe9dc1396},
+	{0x6f173747, 0x00000fc3, 0x0000003c, 0xb012f08e, 0xc6b888ee},
+	{0x4b44a106, 0x0000075a, 0x0000008b, 0xf6f7ac38, 0x60cd2b74},
+	{0xb620ad06, 0x00000774, 0x0000017e, 0xd34558e6, 0x3a0a615b},
+	{0x976f21e9, 0x000008d7, 0x0000034a, 0xe533aa3a, 0xa99e60be},
+	{0x687628c0, 0x000006c5, 0x0000061b, 0x3a840b15, 0x9bfcaef2},
+	{0xe24ac108, 0x00000cd0, 0x0000032f, 0x51010ae8, 0x20958672},
+	{0x361c44a3, 0x00000304, 0x00000719, 0xfd7bd481, 0xd70ff2b2},
+	{0xd93ff95e, 0x00000db7, 0x0000008e, 0xcfbbc304, 0xad716acd},
+	{0xed752d12, 0x00000883, 0x00000091, 0x65a6c868, 0x95c71c7b},
+	{0xb4ff4b54, 0x000003d3, 0x000001c1, 0xf82597e7, 0x44b7f99b},
+	{0x111b520f, 0x00000708, 0x000000eb, 0xc3e109f3, 0x71bc01ee},
+	{0x62c806f2, 0x00000ba3, 0x0000045c, 0x874d3a72, 0xc539b753},
+	{0x40d97470, 0x000005e1, 0x0000058d, 0x87a9684f, 0xea6073a5},
+	{0x4312179c, 0x00000056, 0x0000070e, 0x809a00f5, 0x209aea3b},
+	{0x13d5f84c, 0x00000a2d, 0x00000104, 0xf3d27578, 0xe087a8b6},
+	{0x1f302cb2, 0x00000151, 0x00000014, 0x1e162693, 0x95e4b90e},
+	{0xe491db24, 0x00000600, 0x000006f6, 0x7ff09615, 0x77611523},
+	{0xf9a98069, 0x000002ba, 0x000002ad, 0x01af7387, 0xea925faa},
+	{0xe9c477ad, 0x0000015f, 0x00000778, 0x6facf9a0, 0x1130f736},
+	{0x353f32b2, 0x0000087c, 0x00000783, 0x6cc964ea, 0x32459994},
+	{0x78e1b24f, 0x00000650, 0x000006a8, 0xb3bb7c27, 0x5a632f78},
+	{0x61aa400e, 0x00000049, 0x00000254, 0xb8cd1681, 0xdf2652d5},
+	{0xb84b10b0, 0x00000f73, 0x0000008c, 0x406a6450, 0x3619d31b},
+	{0x9fa99c9c, 0x00000a7c, 0x000004d7, 0xfb3d21b4, 0xea31c743},
+	{0x3fc9ebe3, 0x00000cd9, 0x000000d6, 0x43803f9c, 0x1f76a809},
+	{0x529879cd, 0x000002f2, 0x00000595, 0x78b4c6a6, 0x63b9b93f},
+	{0x3a933019, 0x00000516, 0x00000266, 0xdcb45436, 0x8f99c98c},
+	{0x887b4977, 0x00000227, 0x0000038d, 0xc5f7c3d9, 0xaf5e3091},
+	{0x770745de, 0x000008c6, 0x00000739, 0xf69145e8, 0x53d0dce1},
+	{0x28be3b47, 0x00000c46, 0x0000032b, 0x764c028f, 0x106d0905},
+	{0x5013a050, 0x00000cf6, 0x00000309, 0xea8fe164, 0x62180b57},
+	{0x2ec4c9ba, 0x000006e8, 0x0000078d, 0xa35557a9, 0xf44430a4},
+	{0xa9f950c9, 0x00000d33, 0x000002cc, 0x41ea8618, 0x587b4eb3},
+	{0x5b520229, 0x000007b2, 0x00000484, 0x44569f1f, 0x92406c32},
+	{0xd8dcbbfc, 0x0000002f, 0x0000048c, 0xdb88ab8b, 0x13bfe70e},
+	{0x25529792, 0x00000d1d, 0x000002e2, 0x20cda404, 0x19d3b4e4},
+	{0x9f3f6d71, 0x00000238, 0x0000079a, 0x0720443e, 0x3c107021},
+	{0x64121215, 0x000007ff, 0x0000038f, 0x6aacff2c, 0xb82fdc3e},
+	{0xfb6cdde0, 0x00000ef8, 0x00000107, 0xbd43a0f1, 0xab0d3c1d},
+	{0x221c9d6f, 0x000007b6, 0x0000014f, 0xb67f834b, 0x1371ad05},
+	{0x030e1de4, 0x00000836, 0x000004b4, 0x0d67d26a, 0xe2e72df1},
+	{0xb56fa6cf, 0x00000c07, 0x000003f8, 0x60601ac1, 0x039de73e},
+	{0xb55c89f5, 0x0000098e, 0x000001d4, 0x2400efbe, 0xfe39a2bb},
+	{0x5e90b6d5, 0x0000070b, 0x000003ea, 0x3bb5d6ea, 0xf0f794a0},
+	{0x2a7045ae, 0x00000961, 0x00000633, 0xfca89e4b, 0xe66ce41c},
+	{0x8b374ea9, 0x000006ba, 0x00000780, 0xbce036ed, 0x4cb28ef7},
+	{0x8bd90bc9, 0x00000562, 0x00000369, 0xcb26a24b, 0x40236d1d},
+	{0x5b1b1762, 0x000000fd, 0x0000051a, 0x33cdda07, 0xc32e420a},
+	{0xa4153555, 0x0000058f, 0x000005c7, 0xbe50eeca, 0x83a67f35},
+	{0x0be1f931, 0x00000651, 0x00000672, 0x95a25753, 0x88f1aac1},
+	{0xb7e78618, 0x00000a7f, 0x000002bb, 0xe06bcc1c, 0x74274f66},
+	{0x4a9bc41b, 0x00000e51, 0x000001ae, 0x709e8d2c, 0x54eff534},
+	{0xfc359d13, 0x00000440, 0x000002f8, 0x0a58451f, 0x55e9363f},
+	{0x5aa48619, 0x000006d1, 0x00000284, 0x928ead83, 0x31041c06},
+	{0xa609afa8, 0x0000053e, 0x00000272, 0xb048c141, 0x4704efba},
+	{0x3f108afb, 0x00000949, 0x00000150, 0x9a6bb5bc, 0x4e4430c8},
+	{0x79bec2d3, 0x000008ed, 0x00000712, 0x32692d57, 0x11d52a7b},
+	{0x9429e067, 0x00000bc3, 0x0000043c, 0x5295ceff, 0x04640f4d},
+	{0xae58b96a, 0x0000082d, 0x000007d2, 0xc2a681ba, 0xf7ca4a2c},
+	{0x95df24be, 0x00000985, 0x000004c1, 0x3a287765, 0x2c4af003},
+	{0x5e94976f, 0x00000596, 0x000004ed, 0xff00c489, 0x5ae11687},
+	{0xf5e5f1de, 0x00000d31, 0x000002ce, 0x35f28e91, 0x30d47957},
+	{0xa2c219cf, 0x00000a3c, 0x00000374, 0x707d21eb, 0x2a14a255},
+	{0xf21b6ceb, 0x00000919, 0x00000135, 0x0847fb8b, 0xcb8d3b93},
+	{0xaa988728, 0x00000787, 0x00000771, 0x885aeaa4, 0x6531b509},
+	{0xaa5dfaac, 0x000003e5, 0x0000051b, 0x52c48ab7, 0xe43cc5e9},
+	{0x0a053968, 0x00000d2a, 0x000002d5, 0x7a90256d, 0x8004765c},
+	{0x1421dc20, 0x00000eef, 0x00000110, 0x97d6da24, 0x1378f6ff},
+	{0xb47c2166, 0x00000a6a, 0x00000209, 0xcfd6cc52, 0x676e14a5},
+	{0x77dd1955, 0x000000de, 0x00000266, 0xba74bcaa, 0xc71b429c},
+	{0x68a03cc2, 0x0000082f, 0x000007b0, 0x752bd5d8, 0x19ed14aa},
+	{0x0226b0a3, 0x00000a5f, 0x000005a0, 0x82de4970, 0xf654d3ed},
+	{0x637bf3b1, 0x00000d93, 0x0000026c, 0x5c7115cb, 0x3cccb57e},
+	{0x3b120edf, 0x00000c13, 0x000003ec, 0x80d7d20f, 0x92132798},
+	{0xe2456780, 0x000002eb, 0x00000641, 0xc0a5d289, 0x6160c87a},
+	{0x9b2e7125, 0x00000c0c, 0x000003f3, 0xcc15f57e, 0x6f00f637},
+	{0x153033ef, 0x00000787, 0x000006b6, 0x3cde443b, 0xb46caa6e},
+	{0x18458b3f, 0x0000066c, 0x00000561, 0x9a2bd8c6, 0xb6c29121},
+	{0x4ff9d4b9, 0x00000c8f, 0x0000033a, 0xd0ee6d6d, 0xc81cf380},
+	{0xdf84b5d9, 0x00000802, 0x0000029a, 0xdab0d74a, 0xb2464559},
+	{0x81ee15df, 0x000003ce, 0x00000725, 0x9942e2de, 0x4ccf571b},
+	{0x5c768e04, 0x00000afd, 0x00000160, 0x36110831, 0xae0b305a},
+	{0xe5e18094, 0x00000b4b, 0x000000a0, 0xffa3e4a7, 0x6c8a4f09},
+	{0xed7263b6, 0x00000d0d, 0x000002f2, 0xb0006a35, 0x7e04af8c},
+	{0x5bfde7d7, 0x000006fb, 0x00000554, 0xa4193b76, 0xb3a91d12},
+	{0x67f4a743, 0x00000b85, 0x0000047a, 0xf05c8d8f, 0xfb472fdf},
+	{0xf13bdf22, 0x00000ff7, 0x00000008, 0x816351eb, 0xf347f235},
+	{0x08ecc608, 0x00000d5d, 0x00000098, 0x90492772, 0x0b7f1521},
+	{0x296f52ba, 0x000004f9, 0x00000788, 0x5e5a4896, 0x1cc67088},
+	{0xbe4624c2, 0x00000427, 0x000004ef, 0xcd267b94, 0x550caefd},
+	{0x906f7c7c, 0x00000a05, 0x0000003f, 0x03fcfc33, 0x9ed82a02},
+	{0x8f7b323e, 0x00000458, 0x000004c7, 0xcd4969c8, 0x633c38a8},
+	{0x88d6593d, 0x00000597, 0x000005b5, 0xf199cd3b, 0x0491452f},
+	{0x978a7768, 0x00000268, 0x000001d3, 0xb28c95bd, 0x1a42fe61},
+	{0x857a621e, 0x000007a7, 0x000003a8, 0xf4bf84ab, 0xcd0694c6},
+	{0xb0e121ef, 0x000005be, 0x00000644, 0x28747c14, 0xf0510c72},
 	{0, 0, 0, 0, 0},
 };
 
@@ -1114,15 +902,15 @@
 	while (t->length) {
 		uint32_t be, le;
 		le = ext2fs_crc32c_le(t->crc, test_buf + t->start, t->length);
-		be = ext2fs_crc32c_be(t->crc, test_buf + t->start, t->length);
-		if (le != t->crc_le) {
+		be = ext2fs_crc32_be(t->crc, test_buf + t->start, t->length);
+		if (le != t->crc32c_le) {
 			printf("Test %d LE fails, %x != %x\n",
-			       (int) (t - test), le, t->crc_le);
+			       (int) (t - test), le, t->crc32c_le);
 			failures++;
 		}
-		if (be != t->crc_be) {
+		if (be != t->crc32_be) {
 			printf("Test %d BE fails, %x != %x\n",
-			       (int) (t - test), be, t->crc_be);
+			       (int) (t - test), be, t->crc32_be);
 			failures++;
 		}
 		t++;
diff --git a/lib/ext2fs/crc32c_defs.h b/lib/ext2fs/crc32c_defs.h
index 023f2c0..3f9a09e 100644
--- a/lib/ext2fs/crc32c_defs.h
+++ b/lib/ext2fs/crc32c_defs.h
@@ -1,10 +1,18 @@
 /*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/*
  * This is the CRC32c polynomial, as outlined by Castagnoli.
  * x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+
  * x^8+x^6+x^0
  */
-#define CRCPOLY_LE 0x82F63B78
-#define CRCPOLY_BE 0x1EDC6F41
+#define CRC32C_POLY_LE 0x82F63B78
+#define CRC32C_POLY_BE 0x1EDC6F41
 
 /* How many bits at a time to use.  Valid values are 1, 2, 4, 8, 32 and 64. */
 /* For less performance-sensitive, use 4 */
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 6989880..9f08c38 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,695 @@
 #define STATIC static
 #endif
 
+static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp)
+{
+	int offset = offsetof(struct mmp_struct, mmp_checksum);
+
+	return ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)mmp, offset);
+}
+
+int ext2fs_mmp_csum_verify(ext2_filsys fs, struct mmp_struct *mmp)
+{
+	__u32 calculated;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+
+	calculated = ext2fs_mmp_csum(fs, mmp);
+
+	return ext2fs_le32_to_cpu(mmp->mmp_checksum) == calculated;
+}
+
+errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp)
+{
+	__u32 crc;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	crc = ext2fs_mmp_csum(fs, mmp);
+	mmp->mmp_checksum = ext2fs_cpu_to_le32(crc);
+
+	return 0;
+}
+
+int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb)
+{
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+
+	return sb->s_checksum_type == EXT2_CRC32C_CHKSUM;
+}
+
+static __u32 ext2fs_superblock_csum(ext2_filsys fs EXT2FS_ATTR((unused)),
+				    struct ext2_super_block *sb)
+{
+	int offset = offsetof(struct ext2_super_block, s_checksum);
+
+	return ext2fs_crc32c_le(~0, (unsigned char *)sb, offset);
+}
+
+int ext2fs_superblock_csum_verify(ext2_filsys fs, struct ext2_super_block *sb)
+{
+	__u32 calculated;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+
+	calculated = ext2fs_superblock_csum(fs, sb);
+
+	return ext2fs_le32_to_cpu(sb->s_checksum) == calculated;
+}
+
+errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
+				     struct ext2_super_block *sb)
+{
+	__u32 crc;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	crc = ext2fs_superblock_csum(fs, sb);
+	sb->s_checksum = ext2fs_cpu_to_le32(crc);
+
+	return 0;
+}
+
+static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs,
+					    ext2_ino_t inum EXT2FS_ATTR((unused)),
+					    blk64_t block,
+					    struct ext2_ext_attr_header *hdr,
+					    __u32 *crc)
+{
+	char *buf = (char *)hdr;
+	__u32 old_crc = hdr->h_checksum;
+
+	hdr->h_checksum = 0;
+	block = ext2fs_cpu_to_le64(block);
+	*crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&block,
+				sizeof(block));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, fs->blocksize);
+	hdr->h_checksum = old_crc;
+
+	return 0;
+}
+
+int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+				      blk64_t block,
+				      struct ext2_ext_attr_header *hdr)
+{
+	__u32 calculated;
+	errcode_t retval;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+
+	retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated);
+	if (retval)
+		return 0;
+
+	return ext2fs_le32_to_cpu(hdr->h_checksum) == calculated;
+}
+
+errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+					 blk64_t block,
+					 struct ext2_ext_attr_header *hdr)
+{
+	errcode_t retval;
+	__u32 crc;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &crc);
+	if (retval)
+		return retval;
+	hdr->h_checksum = ext2fs_cpu_to_le32(crc);
+	return 0;
+}
+
+static __u16 do_nothing16(__u16 x)
+{
+	return x;
+}
+
+static __u16 disk_to_host16(__u16 x)
+{
+	return ext2fs_le16_to_cpu(x);
+}
+
+static errcode_t __get_dx_countlimit(ext2_filsys fs,
+				     struct ext2_dir_entry *dirent,
+				     struct ext2_dx_countlimit **cc,
+				     int *offset,
+				     int need_swab)
+{
+	struct ext2_dir_entry *dp;
+	struct ext2_dx_root_info *root;
+	struct ext2_dx_countlimit *c;
+	int count_offset, max_sane_entries;
+	unsigned int rec_len;
+	__u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+	rec_len = translate(dirent->rec_len);
+
+	if (rec_len == fs->blocksize && translate(dirent->name_len) == 0)
+		count_offset = 8;
+	else if (rec_len == 12) {
+		dp = (struct ext2_dir_entry *)(((char *)dirent) + rec_len);
+		rec_len = translate(dp->rec_len);
+		if (rec_len != fs->blocksize - 12)
+			return EXT2_ET_DB_NOT_FOUND;
+		root = (struct ext2_dx_root_info *)(((char *)dp + 12));
+		if (root->reserved_zero ||
+		    root->info_length != sizeof(struct ext2_dx_root_info))
+			return EXT2_ET_DB_NOT_FOUND;
+		count_offset = 32;
+	} else
+		return EXT2_ET_DB_NOT_FOUND;
+
+	c = (struct ext2_dx_countlimit *)(((char *)dirent) + count_offset);
+	max_sane_entries = (fs->blocksize - count_offset) /
+			   sizeof(struct ext2_dx_entry);
+	if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries ||
+	    ext2fs_le16_to_cpu(c->count) > max_sane_entries)
+		return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+	if (offset)
+		*offset = count_offset;
+	if (cc)
+		*cc = c;
+
+	return 0;
+}
+
+errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+				   struct ext2_dir_entry *dirent,
+				   struct ext2_dx_countlimit **cc,
+				   int *offset)
+{
+	return __get_dx_countlimit(fs, dirent, cc, offset, 0);
+}
+
+void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+				   struct ext2_dir_entry_tail *t)
+{
+	memset(t, 0, sizeof(struct ext2_dir_entry_tail));
+	ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail),
+			   (struct ext2_dir_entry *)t);
+	t->det_reserved_name_len = EXT2_DIR_NAME_LEN_CSUM;
+}
+
+static errcode_t __get_dirent_tail(ext2_filsys fs,
+				   struct ext2_dir_entry *dirent,
+				   struct ext2_dir_entry_tail **tt,
+				   int need_swab)
+{
+	struct ext2_dir_entry *d;
+	void *top;
+	struct ext2_dir_entry_tail *t;
+	unsigned int rec_len;
+	errcode_t retval = 0;
+	__u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+	d = dirent;
+	top = EXT2_DIRENT_TAIL(dirent, fs->blocksize);
+
+	rec_len = translate(d->rec_len);
+	while (rec_len && !(rec_len & 0x3)) {
+		d = (struct ext2_dir_entry *)(((char *)d) + rec_len);
+		if ((void *)d >= top)
+			break;
+		rec_len = translate(d->rec_len);
+	}
+
+	if (d != top)
+		return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+	t = (struct ext2_dir_entry_tail *)d;
+	if (t->det_reserved_zero1 ||
+	    translate(t->det_rec_len) != sizeof(struct ext2_dir_entry_tail) ||
+	    translate(t->det_reserved_name_len) != EXT2_DIR_NAME_LEN_CSUM)
+		return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+	if (tt)
+		*tt = t;
+	return retval;
+}
+
+int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent)
+{
+	return __get_dirent_tail(fs, dirent, NULL, 0) == 0;
+}
+
+static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
+				    struct ext2_dir_entry *dirent, __u32 *crc,
+				    int size)
+{
+	errcode_t retval;
+	char *buf = (char *)dirent;
+	__u32 gen;
+	struct ext2_inode inode;
+
+	retval = ext2fs_read_inode(fs, inum, &inode);
+	if (retval)
+		return retval;
+
+	inum = ext2fs_cpu_to_le32(inum);
+	gen = ext2fs_cpu_to_le32(inode.i_generation);
+	*crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+				sizeof(inum));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+
+	return 0;
+}
+
+int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+			      struct ext2_dir_entry *dirent)
+{
+	errcode_t retval;
+	__u32 calculated;
+	struct ext2_dir_entry_tail *t;
+
+	retval = __get_dirent_tail(fs, dirent, &t, 1);
+	if (retval)
+		return 1;
+
+	/*
+	 * The checksum field is overlaid with the dirent->name field
+	 * so the swapfs.c functions won't change the endianness.
+	 */
+	retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated,
+				    (char *)t - (char *)dirent);
+	if (retval)
+		return 0;
+	return ext2fs_le32_to_cpu(t->det_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum,
+					struct ext2_dir_entry *dirent)
+{
+	errcode_t retval;
+	__u32 crc;
+	struct ext2_dir_entry_tail *t;
+
+	retval = __get_dirent_tail(fs, dirent, &t, 1);
+	if (retval)
+		return retval;
+
+	/* swapfs.c functions don't change the checksum endianness */
+	retval = ext2fs_dirent_csum(fs, inum, dirent, &crc,
+				    (char *)t - (char *)dirent);
+	if (retval)
+		return retval;
+	t->det_checksum = ext2fs_cpu_to_le32(crc);
+	return 0;
+}
+
+static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
+				struct ext2_dir_entry *dirent,
+				__u32 *crc, int count_offset, int count,
+				struct ext2_dx_tail *t)
+{
+	errcode_t retval;
+	char *buf = (char *)dirent;
+	int size;
+	__u32 old_csum, gen;
+	struct ext2_inode inode;
+
+	size = count_offset + (count * sizeof(struct ext2_dx_entry));
+	old_csum = t->dt_checksum;
+	t->dt_checksum = 0;
+
+	retval = ext2fs_read_inode(fs, inum, &inode);
+	if (retval)
+		return retval;
+
+	inum = ext2fs_cpu_to_le32(inum);
+	gen = ext2fs_cpu_to_le32(inode.i_generation);
+	*crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+				sizeof(inum));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)t,
+				sizeof(struct ext2_dx_tail));
+	t->dt_checksum = old_csum;
+
+	return 0;
+}
+
+static int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+				 struct ext2_dir_entry *dirent)
+{
+	__u32 calculated;
+	errcode_t retval;
+	struct ext2_dx_countlimit *c;
+	struct ext2_dx_tail *t;
+	int count_offset, limit, count;
+
+	retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+	if (retval)
+		return 1;
+	limit = ext2fs_le16_to_cpu(c->limit);
+	count = ext2fs_le16_to_cpu(c->count);
+	if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+	    fs->blocksize - sizeof(struct ext2_dx_tail))
+		return 0;
+	/* htree structs are accessed in LE order */
+	t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+	retval = ext2fs_dx_csum(fs, inum, dirent, &calculated, count_offset,
+				count, t);
+	if (retval)
+		return 0;
+
+	return ext2fs_le32_to_cpu(t->dt_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
+				    struct ext2_dir_entry *dirent)
+{
+	__u32 crc;
+	errcode_t retval = 0;
+	struct ext2_dx_countlimit *c;
+	struct ext2_dx_tail *t;
+	int count_offset, limit, count;
+
+	retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+	if (retval)
+		return retval;
+	limit = ext2fs_le16_to_cpu(c->limit);
+	count = ext2fs_le16_to_cpu(c->count);
+	if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+	    fs->blocksize - sizeof(struct ext2_dx_tail))
+		return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+	t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+
+	/* htree structs are accessed in LE order */
+	retval = ext2fs_dx_csum(fs, inum, dirent, &crc, count_offset, count, t);
+	if (retval)
+		return retval;
+	t->dt_checksum = ext2fs_cpu_to_le32(crc);
+	return retval;
+}
+
+int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+				 struct ext2_dir_entry *dirent)
+{
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+
+	if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+		return ext2fs_dirent_csum_verify(fs, inum, dirent);
+	if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+		return ext2fs_dx_csum_verify(fs, inum, dirent);
+
+	return 0;
+}
+
+errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+				    struct ext2_dir_entry *dirent)
+{
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+		return ext2fs_dirent_csum_set(fs, inum, dirent);
+	if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+		return ext2fs_dx_csum_set(fs, inum, dirent);
+
+	if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)
+		return 0;
+	return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+}
+
+#define EXT3_EXTENT_TAIL_OFFSET(hdr)	(sizeof(struct ext3_extent_header) + \
+	(sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
+
+static struct ext3_extent_tail *get_extent_tail(struct ext3_extent_header *h)
+{
+	return (struct ext3_extent_tail *)(((char *)h) +
+					   EXT3_EXTENT_TAIL_OFFSET(h));
+}
+
+static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum,
+					  struct ext3_extent_header *eh,
+					  __u32 *crc)
+{
+	int size;
+	__u32 gen;
+	errcode_t retval;
+	struct ext2_inode inode;
+
+	size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail,
+						      et_checksum);
+
+	retval = ext2fs_read_inode(fs, inum, &inode);
+	if (retval)
+		return retval;
+	inum = ext2fs_cpu_to_le32(inum);
+	gen = ext2fs_cpu_to_le32(inode.i_generation);
+	*crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+				sizeof(inum));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)eh, size);
+
+	return 0;
+}
+
+int ext2fs_extent_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+				    struct ext3_extent_header *eh)
+{
+	errcode_t retval;
+	__u32 provided, calculated;
+	struct ext3_extent_tail *t = get_extent_tail(eh);
+
+	/*
+	 * The extent tree structures are accessed in LE order, so we must
+	 * swap the checksum bytes here.
+	 */
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+
+	provided = ext2fs_le32_to_cpu(t->et_checksum);
+	retval = ext2fs_extent_block_csum(fs, inum, eh, &calculated);
+	if (retval)
+		return 0;
+
+	return provided == calculated;
+}
+
+errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+				       struct ext3_extent_header *eh)
+{
+	errcode_t retval;
+	__u32 crc;
+	struct ext3_extent_tail *t = get_extent_tail(eh);
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	/*
+	 * The extent tree structures are accessed in LE order, so we must
+	 * swap the checksum bytes here.
+	 */
+	retval = ext2fs_extent_block_csum(fs, inum, eh, &crc);
+	if (retval)
+		return retval;
+	t->et_checksum = ext2fs_cpu_to_le32(crc);
+	return retval;
+}
+
+int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+				    char *bitmap, int size)
+{
+	struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+			ext2fs_group_desc(fs, fs->group_desc, group);
+	__u32 provided, calculated;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+	provided = gdp->bg_inode_bitmap_csum_lo;
+	calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+				      size);
+	if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+		provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16;
+	else
+		calculated &= 0xFFFF;
+
+	return provided == calculated;
+}
+
+errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+				       char *bitmap, int size)
+{
+	__u32 crc;
+	struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+			ext2fs_group_desc(fs, fs->group_desc, group);
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+	gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF;
+	if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+		gdp->bg_inode_bitmap_csum_hi = crc >> 16;
+
+	return 0;
+}
+
+int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+				    char *bitmap, int size)
+{
+	struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+			ext2fs_group_desc(fs, fs->group_desc, group);
+	__u32 provided, calculated;
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+	provided = gdp->bg_block_bitmap_csum_lo;
+	calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+				      size);
+	if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+		provided |= (__u32)gdp->bg_block_bitmap_csum_hi << 16;
+	else
+		calculated &= 0xFFFF;
+
+	return provided == calculated;
+}
+
+errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+				       char *bitmap, int size)
+{
+	__u32 crc;
+	struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+			ext2fs_group_desc(fs, fs->group_desc, group);
+
+	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+	gdp->bg_block_bitmap_csum_lo = crc & 0xFFFF;
+	if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+		gdp->bg_block_bitmap_csum_hi = crc >> 16;
+
+	return 0;
+}
+
+static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
+			       struct ext2_inode_large *inode,
+			       __u32 *crc, int has_hi)
+{
+	__u32 gen;
+	struct ext2_inode_large *desc = inode;
+	size_t size = fs->super->s_inode_size;
+	__u16 old_lo;
+	__u16 old_hi = 0;
+
+	old_lo = inode->i_checksum_lo;
+	inode->i_checksum_lo = 0;
+	if (has_hi) {
+		old_hi = inode->i_checksum_hi;
+		inode->i_checksum_hi = 0;
+	}
+
+	inum = ext2fs_cpu_to_le32(inum);
+	gen = inode->i_generation;
+	*crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+				sizeof(inum));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+	*crc = ext2fs_crc32c_le(*crc, (unsigned char *)desc, size);
+
+	inode->i_checksum_lo = old_lo;
+	if (has_hi)
+		inode->i_checksum_hi = old_hi;
+	return 0;
+}
+
+int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+			     struct ext2_inode_large *inode)
+{
+	errcode_t retval;
+	__u32 provided, calculated;
+	unsigned int i, has_hi;
+	char *cp;
+
+	if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+	    !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 1;
+
+	has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+		  inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+	provided = ext2fs_le16_to_cpu(inode->i_checksum_lo);
+	retval = ext2fs_inode_csum(fs, inum, inode, &calculated, has_hi);
+	if (retval)
+		return 0;
+	if (has_hi) {
+		__u32 hi = ext2fs_le16_to_cpu(inode->i_checksum_hi);
+		provided |= hi << 16;
+	} else
+		calculated &= 0xFFFF;
+
+	if (provided == calculated)
+		return 1;
+
+	/*
+	 * If the checksum didn't match, it's possible it was due to
+	 * the inode being all zero's.  It's unlikely this is the
+	 * case, but it can happen.  So check for it here.  (We only
+	 * check the base inode since that's good enough, and it's not
+	 * worth the bother to figure out how much of the extended
+	 * inode, if any, is present.)
+	 */
+	for (cp = (char *) inode, i = 0;
+	     i < sizeof(struct ext2_inode);
+	     cp++, i++)
+		if (*cp)
+			return 0;
+	return 1;		/* Inode must have been all zero's */
+}
+
+errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+			   struct ext2_inode_large *inode)
+{
+	errcode_t retval;
+	__u32 crc;
+	int has_hi;
+
+	if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+	    !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return 0;
+
+	has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+		  inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+	retval = ext2fs_inode_csum(fs, inum, inode, &crc, has_hi);
+	if (retval)
+		return retval;
+	inode->i_checksum_lo = ext2fs_cpu_to_le16(crc & 0xFFFF);
+	if (has_hi)
+		inode->i_checksum_hi = ext2fs_cpu_to_le16(crc >> 16);
+	return 0;
+}
+
 __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
 {
 	struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc,
@@ -37,56 +726,74 @@
 	size_t size = EXT2_DESC_SIZE(fs->super);
 	size_t offset;
 	__u16 crc = 0;
-
-	if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
-		size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
-
 #ifdef WORDS_BIGENDIAN
-		struct ext4_group_desc swabdesc;
-		size_t save_size = size;
-		const size_t ext4_bg_size = sizeof(struct ext4_group_desc);
-		struct ext2_group_desc *save_desc = desc;
+	struct ext4_group_desc swabdesc;
+	size_t save_size = size;
+	const size_t ext4_bg_size = sizeof(struct ext4_group_desc);
+	struct ext2_group_desc *save_desc = desc;
 
-		/* Have to swab back to little-endian to do the checksum */
-		if (size > ext4_bg_size)
-			size = ext4_bg_size;
-		memcpy(&swabdesc, desc, size);
-		ext2fs_swap_group_desc2(fs,
-					(struct ext2_group_desc *) &swabdesc);
-		desc = (struct ext2_group_desc *) &swabdesc;
-
-		group = ext2fs_swab32(group);
+	/* Have to swab back to little-endian to do the checksum */
+	if (size > ext4_bg_size)
+		size = ext4_bg_size;
+	memcpy(&swabdesc, desc, size);
+	ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc);
+	desc = (struct ext2_group_desc *) &swabdesc;
+	group = ext2fs_swab32(group);
 #endif
-		crc = ext2fs_crc16(~0, fs->super->s_uuid,
-				   sizeof(fs->super->s_uuid));
-		crc = ext2fs_crc16(crc, &group, sizeof(group));
-		crc = ext2fs_crc16(crc, desc, offset);
-		offset += sizeof(desc->bg_checksum); /* skip checksum */
-		/* for checksum of struct ext4_group_desc do the rest...*/
-		if (offset < size) {
-			crc = ext2fs_crc16(crc, (char *)desc + offset,
-					   size - offset);
-		}
+
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+			EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+		/* new metadata csum code */
+		__u16 old_crc;
+		__u32 crc32;
+
+		old_crc = desc->bg_checksum;
+		desc->bg_checksum = 0;
+		crc32 = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&group,
+					 sizeof(group));
+		crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)desc,
+					 size);
+		desc->bg_checksum = old_crc;
 #ifdef WORDS_BIGENDIAN
-		/*
-		 * If the size of the bg descriptor is greater than 64
-		 * bytes, which is the size of the traditional ext4 bg
-		 * descriptor, checksum the rest of the descriptor here
-		 */
 		if (save_size > ext4_bg_size)
-			crc = ext2fs_crc16(crc,
+			crc32 = ext2fs_crc32c_le(crc32,
 					   (char *)save_desc + ext4_bg_size,
 					   save_size - ext4_bg_size);
 #endif
+		crc = crc32 & 0xFFFF;
+		goto out;
 	}
 
+	/* old crc16 code */
+	offset = offsetof(struct ext2_group_desc, bg_checksum);
+	crc = ext2fs_crc16(~0, fs->super->s_uuid,
+			   sizeof(fs->super->s_uuid));
+	crc = ext2fs_crc16(crc, &group, sizeof(group));
+	crc = ext2fs_crc16(crc, desc, offset);
+	offset += sizeof(desc->bg_checksum); /* skip checksum */
+	/* for checksum of struct ext4_group_desc do the rest...*/
+	if (offset < size) {
+		crc = ext2fs_crc16(crc, (char *)desc + offset,
+				   size - offset);
+	}
+#ifdef WORDS_BIGENDIAN
+	/*
+	 * If the size of the bg descriptor is greater than 64
+	 * bytes, which is the size of the traditional ext4 bg
+	 * descriptor, checksum the rest of the descriptor here
+	 */
+	if (save_size > ext4_bg_size)
+		crc = ext2fs_crc16(crc, (char *)save_desc + ext4_bg_size,
+				   save_size - ext4_bg_size);
+#endif
+
+out:
 	return crc;
 }
 
 int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group)
 {
-	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+	if (ext2fs_has_group_desc_csum(fs) &&
 	    (ext2fs_bg_checksum(fs, group) !=
 	     ext2fs_group_desc_csum(fs, group)))
 		return 0;
@@ -96,8 +803,7 @@
 
 void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group)
 {
-	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+	if (!ext2fs_has_group_desc_csum(fs))
 		return;
 
 	/* ext2fs_bg_checksum_set() sets the actual checksum field but
@@ -131,8 +837,7 @@
 	if (!fs->inode_map)
 		return EXT2_ET_NO_INODE_BITMAP;
 
-	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+	if (!ext2fs_has_group_desc_csum(fs))
 		return 0;
 
 	for (i = 0; i < fs->group_desc_count; i++) {
diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
index 3f6ea50..942c4f0 100644
--- a/lib/ext2fs/dblist.c
+++ b/lib/ext2fs/dblist.c
@@ -25,34 +25,6 @@
 static EXT2_QSORT_TYPE (*sortfunc32)(const void *a, const void *b);
 
 /*
- * Returns the number of directories in the filesystem as reported by
- * the group descriptors.  Of course, the group descriptors could be
- * wrong!
- */
-errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs)
-{
-	dgrp_t	i;
-	ext2_ino_t	num_dirs, max_dirs;
-
-	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
-	num_dirs = 0;
-	max_dirs = fs->super->s_inodes_per_group;
-	for (i = 0; i < fs->group_desc_count; i++) {
-		if (ext2fs_bg_used_dirs_count(fs, i) > max_dirs)
-			num_dirs += max_dirs / 8;
-		else
-			num_dirs += ext2fs_bg_used_dirs_count(fs, i);
-	}
-	if (num_dirs > fs->super->s_inodes_count)
-		num_dirs = fs->super->s_inodes_count;
-
-	*ret_num_dirs = num_dirs;
-
-	return 0;
-}
-
-/*
  * helper function for making a new directory block list (for
  * initialize and copy).
  */
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 589af69..306ffb0 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -83,7 +83,7 @@
 		offset += rec_len;
 		if ((rec_len < 8) ||
 		    ((rec_len % 4) != 0) ||
-		    ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len))
+		    ((ext2fs_dirent_name_len(dirent)+8) > rec_len))
 			return 0;
 	}
 	return (offset == final_offset);
@@ -192,16 +192,22 @@
 	unsigned int	rec_len, size;
 	int		entry;
 	struct ext2_dir_entry *dirent;
+	int		csum_size = 0;
 
 	if (blockcnt < 0)
 		return 0;
 
 	entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
 
-	ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0);
+	ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+					      ctx->dir);
 	if (ctx->errcode)
 		return BLOCK_ABORT;
 
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		csum_size = sizeof(struct ext2_dir_entry_tail);
+
 	while (offset < fs->blocksize) {
 		dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
 		if (ext2fs_get_rec_len(fs, dirent, &rec_len))
@@ -209,13 +215,20 @@
 		if (((offset + rec_len) > fs->blocksize) ||
 		    (rec_len < 8) ||
 		    ((rec_len % 4) != 0) ||
-		    ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) {
+		    ((ext2fs_dirent_name_len(dirent)+8) > rec_len)) {
 			ctx->errcode = EXT2_ET_DIR_CORRUPTED;
 			return BLOCK_ABORT;
 		}
-		if (!dirent->inode &&
-		    !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
-			goto next;
+		if (!dirent->inode) {
+			if ((offset == fs->blocksize - csum_size) &&
+			    (dirent->rec_len == csum_size) &&
+			    (dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) {
+				if (!(ctx->flags & DIRENT_FLAG_INCLUDE_CSUM))
+					goto next;
+				entry = DIRENT_CHECKSUM;
+			} else if (!(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
+				goto next;
+		}
 
 		ret = (ctx->func)(ctx->dir,
 				  (next_real_entry > offset) ?
@@ -240,7 +253,7 @@
 			next_real_entry += rec_len;
 
  		if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
-			size = ((dirent->name_len & 0xFF) + 11) & ~3;
+			size = (ext2fs_dirent_name_len(dirent) + 11) & ~3;
 
 			if (rec_len != size)  {
 				unsigned int final_offset;
@@ -259,8 +272,8 @@
 	}
 
 	if (changed) {
-		ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf,
-						       0);
+		ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
+						       0, ctx->dir);
 		if (ctx->errcode)
 			return BLOCK_ABORT;
 	}
diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c
index cb3a104..54b2777 100644
--- a/lib/ext2fs/dirblock.c
+++ b/lib/ext2fs/dirblock.c
@@ -20,45 +20,36 @@
 #include "ext2_fs.h"
 #include "ext2fs.h"
 
-errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
-				 void *buf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
+				 void *buf, int flags EXT2FS_ATTR((unused)),
+				 ext2_ino_t ino)
 {
 	errcode_t	retval;
-	char		*p, *end;
-	struct ext2_dir_entry *dirent;
-	unsigned int	name_len, rec_len;
-
+	int		corrupt = 0;
 
 	retval = io_channel_read_blk64(fs->io, block, 1, buf);
 	if (retval)
 		return retval;
 
-	p = (char *) buf;
-	end = (char *) buf + fs->blocksize;
-	while (p < end-8) {
-		dirent = (struct ext2_dir_entry *) p;
+	if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+	    !ext2fs_dir_block_csum_verify(fs, ino,
+					  (struct ext2_dir_entry *)buf))
+		corrupt = 1;
+
 #ifdef WORDS_BIGENDIAN
-		dirent->inode = ext2fs_swab32(dirent->inode);
-		dirent->rec_len = ext2fs_swab16(dirent->rec_len);
-		dirent->name_len = ext2fs_swab16(dirent->name_len);
+	retval = ext2fs_dirent_swab_in(fs, buf, flags);
 #endif
-		name_len = dirent->name_len;
-#ifdef WORDS_BIGENDIAN
-		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
-			dirent->name_len = ext2fs_swab16(dirent->name_len);
-#endif
-		if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
-			return retval;
-		if ((rec_len < 8) || (rec_len % 4)) {
-			rec_len = 8;
-			retval = EXT2_ET_DIR_CORRUPTED;
-		} else if (((name_len & 0xFF) + 8) > rec_len)
-			retval = EXT2_ET_DIR_CORRUPTED;
-		p += rec_len;
-	}
+	if (!retval && corrupt)
+		retval = EXT2_ET_DIR_CSUM_INVALID;
 	return retval;
 }
 
+errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
+				 void *buf, int flags EXT2FS_ATTR((unused)))
+{
+	return ext2fs_read_dir_block4(fs, block, buf, flags, 0);
+}
+
 errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
 				 void *buf, int flags EXT2FS_ATTR((unused)))
 {
@@ -72,45 +63,40 @@
 }
 
 
-errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
-				  void *inbuf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
+				  void *inbuf, int flags EXT2FS_ATTR((unused)),
+				  ext2_ino_t ino)
 {
-#ifdef WORDS_BIGENDIAN
 	errcode_t	retval;
-	char		*p, *end;
-	char		*buf = 0;
-	unsigned int	rec_len;
-	struct ext2_dir_entry *dirent;
+	char		*buf = inbuf;
 
+#ifdef WORDS_BIGENDIAN
 	retval = ext2fs_get_mem(fs->blocksize, &buf);
 	if (retval)
 		return retval;
 	memcpy(buf, inbuf, fs->blocksize);
-	p = buf;
-	end = buf + fs->blocksize;
-	while (p < end) {
-		dirent = (struct ext2_dir_entry *) p;
-		if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
-			return retval;
-		if ((rec_len < 8) ||
-		    (rec_len % 4)) {
-			ext2fs_free_mem(&buf);
-			return (EXT2_ET_DIR_CORRUPTED);
-		}
-		p += rec_len;
-		dirent->inode = ext2fs_swab32(dirent->inode);
-		dirent->rec_len = ext2fs_swab16(dirent->rec_len);
-		dirent->name_len = ext2fs_swab16(dirent->name_len);
-
-		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
-			dirent->name_len = ext2fs_swab16(dirent->name_len);
-	}
-	retval = io_channel_write_blk64(fs->io, block, 1, buf);
-	ext2fs_free_mem(&buf);
-	return retval;
-#else
-	return io_channel_write_blk64(fs->io, block, 1, (char *) inbuf);
+	retval = ext2fs_dirent_swab_out(fs, buf, flags);
+	if (retval)
+		return retval;
 #endif
+	retval = ext2fs_dir_block_csum_set(fs, ino,
+					   (struct ext2_dir_entry *)buf);
+	if (retval)
+		goto out;
+
+	retval = io_channel_write_blk64(fs->io, block, 1, buf);
+
+out:
+#ifdef WORDS_BIGENDIAN
+	ext2fs_free_mem(&buf);
+#endif
+	return retval;
+}
+
+errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
+				  void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+	return ext2fs_write_dir_block4(fs, block, inbuf, flags, 0);
 }
 
 errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index 153b838..09a15fa 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -24,6 +24,7 @@
 	int		newblocks;
 	blk64_t		goal;
 	errcode_t	err;
+	ext2_ino_t	dir;
 };
 
 static int expand_dir_proc(ext2_filsys	fs,
@@ -63,7 +64,8 @@
 			return BLOCK_ABORT;
 		}
 		es->done = 1;
-		retval = ext2fs_write_dir_block(fs, new_blk, block);
+		retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
+						 es->dir);
 	} else {
 		retval = ext2fs_get_mem(fs->blocksize, &block);
 		if (retval) {
@@ -110,6 +112,7 @@
 	es.err = 0;
 	es.goal = 0;
 	es.newblocks = 0;
+	es.dir = dir;
 
 	retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
 				       0, expand_dir_proc, &es);
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 87812ab..93a1106 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -476,4 +476,10 @@
 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 iterate data blocks of an inode containing inline data"
+
 	end
diff --git a/lib/ext2fs/ext2_ext_attr.h b/lib/ext2fs/ext2_ext_attr.h
index ed548d1..bbb0aaa 100644
--- a/lib/ext2fs/ext2_ext_attr.h
+++ b/lib/ext2fs/ext2_ext_attr.h
@@ -20,7 +20,9 @@
 	__u32	h_refcount;	/* reference count */
 	__u32	h_blocks;	/* number of disk blocks used */
 	__u32	h_hash;		/* hash value of all attributes */
-	__u32	h_reserved[4];	/* zero right now */
+	__u32	h_checksum;	/* crc32c(uuid+id+xattrs) */
+				/* id = inum if refcount = 1, else blknum */
+	__u32	h_reserved[3];	/* zero right now */
 };
 
 struct ext2_ext_attr_entry {
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index d9e14d7..ac46ba8 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -192,6 +192,13 @@
 	__u32	bg_reserved;
 };
 
+#define EXT4_BG_INODE_BITMAP_CSUM_HI_END	\
+	(offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
+	 sizeof(__u16))
+#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION	\
+	(offsetof(struct ext4_group_desc, bg_block_bitmap_csum_hi) + \
+	 sizeof(__u16))
+
 #define EXT2_BG_INODE_UNINIT	0x0001 /* Inode table/bitmap not initialized */
 #define EXT2_BG_BLOCK_UNINIT	0x0002 /* Block bitmap not initialized */
 #define EXT2_BG_INODE_ZEROED	0x0004 /* On-disk itable initialized to zero */
@@ -235,6 +242,13 @@
 	__u16 count;
 };
 
+/*
+ * This goes at the end of each htree block.
+ */
+struct ext2_dx_tail {
+	__u32 dt_reserved;
+	__u32 dt_checksum;	/* crc32c(uuid+inum+dxblock) */
+};
 
 /*
  * Macro-instructions used to manage group descriptors
@@ -305,6 +319,7 @@
 #define EXT4_SNAPFILE_FL		0x01000000  /* Inode is a snapshot */
 #define EXT4_SNAPFILE_DELETED_FL	0x04000000  /* Snapshot is being deleted */
 #define EXT4_SNAPFILE_SHRUNK_FL		0x08000000  /* Snapshot shrink has completed */
+#define EXT4_INLINE_DATA_FL		0x10000000 /* Inode has inline data */
 #define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
 
 #define EXT2_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
@@ -454,8 +469,14 @@
 	__u32	i_version_hi;	/* high 32 bits for 64-bit version */
 };
 
+#define EXT4_INODE_CSUM_HI_EXTRA_END	\
+	(offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \
+	 EXT2_GOOD_OLD_INODE_SIZE)
+
 #define i_dir_acl	i_size_high
 
+#define i_checksum_lo	osd2.linux2.l_i_checksum_lo
+
 #if defined(__KERNEL__) || defined(__linux__)
 #define i_reserved1	osd1.linux1.l_i_reserved1
 #define i_frag		osd2.linux2.l_i_frag
@@ -535,6 +556,9 @@
 #define ext4_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 #endif
 
+/* Metadata checksum algorithms */
+#define EXT2_CRC32C_CHKSUM		1
+
 /*
  * Structure of the super block
  */
@@ -620,7 +644,7 @@
 	__u64   s_mmp_block;            /* Block for multi-mount protection */
 	__u32   s_raid_stripe_width;    /* blocks on all data disks (N*stride)*/
 	__u8	s_log_groups_per_flex;	/* FLEX_BG group size */
-	__u8    s_reserved_char_pad;
+	__u8    s_checksum_type;	/* metadata checksum algorithm */
 	__u16	s_reserved_pad;		/* Padding to next 32bits */
 	__u64	s_kbytes_written;	/* nr of lifetime kilobytes written */
 	__u32	s_snapshot_inum;	/* Inode number of active snapshot */
@@ -710,6 +734,11 @@
 #define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT	0x0080
 #define EXT4_FEATURE_RO_COMPAT_QUOTA		0x0100
 #define EXT4_FEATURE_RO_COMPAT_BIGALLOC		0x0200
+/*
+ * METADATA_CSUM implies GDT_CSUM.  When METADATA_CSUM is set, group
+ * descriptor checksums use the same algorithm as all other data
+ * structures' checksums.
+ */
 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM	0x0400
 #define EXT4_FEATURE_RO_COMPAT_REPLICA		0x0800
 
@@ -726,7 +755,7 @@
 #define EXT4_FEATURE_INCOMPAT_DIRDATA		0x1000
 /* 0x2000 was EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM but this was never used */
 #define EXT4_FEATURE_INCOMPAT_LARGEDIR		0x4000 /* >2GB or 3-lvl htree */
-#define EXT4_FEATURE_INCOMPAT_INLINEDATA	0x8000 /* data in inode */
+#define EXT4_FEATURE_INCOMPAT_INLINE_DATA	0x8000 /* data in inode */
 
 #define EXT2_FEATURE_COMPAT_SUPP	0
 #define EXT2_FEATURE_INCOMPAT_SUPP    (EXT2_FEATURE_INCOMPAT_FILETYPE| \
@@ -776,6 +805,14 @@
  * stored in intel byte order, and the name_len field could never be
  * bigger than 255 chars, it's safe to reclaim the extra byte for the
  * file_type field.
+ *
+ * This structure is deprecated due to endianity issues. Please use struct
+ * ext2_dir_entry and accessor functions
+ *   ext2fs_dirent_name_len
+ *   ext2fs_dirent_set_name_len
+ *   ext2fs_dirent_file_type
+ *   ext2fs_dirent_set_file_type
+ * to get and set name_len and file_type fields.
  */
 struct ext2_dir_entry_2 {
 	__u32	inode;			/* Inode number */
@@ -786,6 +823,17 @@
 };
 
 /*
+ * This is a bogus directory entry at the end of each leaf block that
+ * records checksums.
+ */
+struct ext2_dir_entry_tail {
+	__u32	det_reserved_zero1;	/* Pretend to be unused */
+	__u16	det_rec_len;		/* 12 */
+	__u16	det_reserved_name_len;	/* 0xDE00, fake namelen/filetype */
+	__u32	det_checksum;		/* crc32c(uuid+inode+dirent) */
+};
+
+/*
  * Ext2 directory file types.  Only the low 3 bits are used.  The
  * other bits are reserved for now.
  */
@@ -801,6 +849,14 @@
 #define EXT2_FT_MAX		8
 
 /*
+ * Annoyingly, e2fsprogs always swab16s ext2_dir_entry.name_len, so we
+ * have to build ext2_dir_entry_tail with that assumption too.  This
+ * constant helps to build the dir_entry_tail to look like it has an
+ * "invalid" file type.
+ */
+#define EXT2_DIR_NAME_LEN_CSUM	0xDE00
+
+/*
  * EXT2_DIR_PAD defines the directory entries boundaries
  *
  * NOTE: It must be a multiple of 4
@@ -841,7 +897,8 @@
 	char	mmp_bdevname[32];	/* Bdev which last updated MMP block */
 	__u16	mmp_check_interval;	/* Changed mmp_check_interval */
 	__u16	mmp_pad1;
-	__u32	mmp_pad2[227];
+	__u32	mmp_pad2[226];
+	__u32	mmp_checksum;		/* crc32c(uuid+mmp_block) */
 };
 
 /*
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 1491c62..7f7fd1f 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -191,6 +191,7 @@
 #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
@@ -273,6 +274,12 @@
 	 * 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
@@ -430,11 +437,13 @@
 
 #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
@@ -561,26 +570,33 @@
    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|\
@@ -588,22 +604,14 @@
 					 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)
 
 
@@ -632,6 +640,12 @@
 /*
  * 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)
@@ -807,6 +821,8 @@
 					 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);
@@ -834,9 +850,11 @@
 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);
@@ -945,18 +963,65 @@
 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);
@@ -1011,12 +1076,16 @@
 					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,
@@ -1067,16 +1136,24 @@
 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);
@@ -1185,10 +1262,6 @@
 						       __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,
@@ -1217,6 +1290,9 @@
 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);
@@ -1279,6 +1355,7 @@
 				     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,
@@ -1417,6 +1494,7 @@
 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);
 
@@ -1441,6 +1519,8 @@
 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,
@@ -1480,6 +1560,7 @@
 
 /* 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,
@@ -1530,6 +1611,16 @@
 #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>
 /*
@@ -1746,6 +1837,26 @@
 	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 --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index a88db93..319e9b0 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -75,7 +75,7 @@
 
 struct ext2_inode_cache_ent {
 	ext2_ino_t		ino;
-	struct ext2_inode	inode;
+	struct ext2_inode	*inode;
 };
 
 /* Function prototypes */
@@ -95,6 +95,23 @@
 	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);
diff --git a/lib/ext2fs/ext3_extents.h b/lib/ext2fs/ext3_extents.h
index 88fabc9..4163436 100644
--- a/lib/ext2fs/ext3_extents.h
+++ b/lib/ext2fs/ext3_extents.h
@@ -19,6 +19,17 @@
  */
 
 /*
+ * This is extent tail on-disk structure.
+ * All other extent structures are 12 bytes long.  It turns out that
+ * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which
+ * covers all valid ext4 block sizes.  Therefore, this tail structure can be
+ * crammed into the end of the block without having to rebalance the tree.
+ */
+struct ext3_extent_tail {
+	__u32	et_checksum;	/* crc32c(uuid+inum+extent_block) */
+};
+
+/*
  * this is extent on-disk structure
  * it's used at the bottom of the tree
  */
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 1889824..9649a14 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -61,17 +61,29 @@
 #undef NAME_HASH_SHIFT
 #undef VALUE_HASH_SHIFT
 
-errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
+errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf,
+				ext2_ino_t inum)
 {
 	errcode_t	retval;
 
 	retval = io_channel_read_blk64(fs->io, block, 1, buf);
 	if (retval)
 		return retval;
+
+	if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+	    !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf))
+		retval = EXT2_ET_EXT_ATTR_CSUM_INVALID;
+
 #ifdef WORDS_BIGENDIAN
 	ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
 #endif
-	return 0;
+
+	return retval;
+}
+
+errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
+{
+	return ext2fs_read_ext_attr3(fs, block, buf, 0);
 }
 
 errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
@@ -79,30 +91,40 @@
 	return ext2fs_read_ext_attr2(fs, block, buf);
 }
 
-errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
+errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, void *inbuf,
+				 ext2_ino_t inum)
 {
 	errcode_t	retval;
 	char		*write_buf;
-#ifdef WORDS_BIGENDIAN
-	char		*buf = NULL;
 
-	retval = ext2fs_get_mem(fs->blocksize, &buf);
+#ifdef WORDS_BIGENDIAN
+	retval = ext2fs_get_mem(fs->blocksize, &write_buf);
 	if (retval)
 		return retval;
-	write_buf = buf;
-	ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
+	ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1);
 #else
 	write_buf = (char *) inbuf;
 #endif
+
+	retval = ext2fs_ext_attr_block_csum_set(fs, inum, block,
+			(struct ext2_ext_attr_header *)write_buf);
+	if (retval)
+		return retval;
+
 	retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
 #ifdef WORDS_BIGENDIAN
-	ext2fs_free_mem(&buf);
+	ext2fs_free_mem(&write_buf);
 #endif
 	if (!retval)
 		ext2fs_mark_changed(fs);
 	return retval;
 }
 
+errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
+{
+	return ext2fs_write_ext_attr3(fs, block, inbuf, 0);
+}
+
 errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
 {
 	return ext2fs_write_ext_attr2(fs, block, inbuf);
@@ -111,9 +133,9 @@
 /*
  * This function adjusts the reference count of the EA block.
  */
-errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
+errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
 				    char *block_buf, int adjust,
-				    __u32 *newcount)
+				    __u32 *newcount, ext2_ino_t inum)
 {
 	errcode_t	retval;
 	struct ext2_ext_attr_header *header;
@@ -130,7 +152,7 @@
 		block_buf = buf;
 	}
 
-	retval = ext2fs_read_ext_attr2(fs, blk, block_buf);
+	retval = ext2fs_read_ext_attr3(fs, blk, block_buf, inum);
 	if (retval)
 		goto errout;
 
@@ -139,7 +161,7 @@
 	if (newcount)
 		*newcount = header->h_refcount;
 
-	retval = ext2fs_write_ext_attr2(fs, blk, block_buf);
+	retval = ext2fs_write_ext_attr3(fs, blk, block_buf, inum);
 	if (retval)
 		goto errout;
 
@@ -149,9 +171,18 @@
 	return retval;
 }
 
+errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
+				    char *block_buf, int adjust,
+				    __u32 *newcount)
+{
+	return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust,
+					  newcount, 0);
+}
+
 errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
 					char *block_buf, int adjust,
 					__u32 *newcount)
 {
-	return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount);
+	return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
+					  newcount);
 }
diff --git a/lib/ext2fs/extent.c b/lib/ext2fs/extent.c
index 6f4f1d2..e96fa5f 100644
--- a/lib/ext2fs/extent.c
+++ b/lib/ext2fs/extent.c
@@ -455,6 +455,13 @@
 			return retval;
 		}
 
+		if (!(handle->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+		    !ext2fs_extent_block_csum_verify(handle->fs, handle->ino,
+						     eh)) {
+			handle->level--;
+			return EXT2_ET_EXTENT_CSUM_INVALID;
+		}
+
 		newpath->left = newpath->entries =
 			ext2fs_le16_to_cpu(eh->eh_entries);
 		newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max);
@@ -541,6 +548,7 @@
 	blk64_t				blk;
 	errcode_t			retval;
 	struct ext3_extent_idx		*ix;
+	struct ext3_extent_header	*eh;
 
 	if (handle->level == 0) {
 		retval = ext2fs_write_inode(handle->fs, handle->ino,
@@ -550,6 +558,14 @@
 		blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
 			((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
 
+		/* then update the checksum */
+		eh = (struct ext3_extent_header *)
+				handle->path[handle->level].buf;
+		retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino,
+						      eh);
+		if (retval)
+			return retval;
+
 		retval = io_channel_write_blk64(handle->fs->io,
 				      blk, 1, handle->path[handle->level].buf);
 	}
@@ -963,6 +979,11 @@
 
 	new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block);
 
+	/* then update the checksum */
+	retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino, neweh);
+	if (retval)
+		goto done;
+
 	/* ...and write the new node block out to disk. */
 	retval = io_channel_write_blk64(handle->fs->io, new_node_pblk, 1,
 					block_buf);
diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c
index 1ad2d91..89a157b 100644
--- a/lib/ext2fs/freefs.c
+++ b/lib/ext2fs/freefs.c
@@ -18,8 +18,6 @@
 #include "ext2_fs.h"
 #include "ext2fsP.h"
 
-static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
-
 void ext2fs_free(ext2_filsys fs)
 {
 	if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS))
@@ -67,21 +65,6 @@
 }
 
 /*
- * Free the inode cache structure
- */
-static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
-{
-	if (--icache->refcount)
-		return;
-	if (icache->buffer)
-		ext2fs_free_mem(&icache->buffer);
-	if (icache->cache)
-		ext2fs_free_mem(&icache->cache);
-	icache->buffer_blk = 0;
-	ext2fs_free_mem(&icache);
-}
-
-/*
  * This procedure frees a badblocks list.
  */
 void ext2fs_u32_list_free(ext2_u32_list bb)
diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
index 9615f1e..4526a09 100644
--- a/lib/ext2fs/gen_bitmap64.c
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -80,7 +80,7 @@
 #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,7 +124,7 @@
 	if (retval)
 		return retval;
 
-#ifdef BMAP_STATS
+#ifdef ENABLE_BMAP_STATS
 	if (gettimeofday(&bitmap->stats.created,
 			 (struct timezone *) NULL) == -1) {
 		perror("gettimeofday");
@@ -174,18 +174,18 @@
 	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;
@@ -214,7 +214,7 @@
 	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",
@@ -237,7 +237,7 @@
 	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
 
@@ -254,7 +254,7 @@
 	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);
@@ -294,10 +294,10 @@
 		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");
@@ -324,7 +324,8 @@
 			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;
 	}
 
@@ -444,7 +445,7 @@
 
 	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)
@@ -511,7 +512,7 @@
 
 	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 --git a/lib/ext2fs/gen_crc32ctable.c b/lib/ext2fs/gen_crc32ctable.c
index 9996e9d..a0742ee 100644
--- a/lib/ext2fs/gen_crc32ctable.c
+++ b/lib/ext2fs/gen_crc32ctable.c
@@ -4,23 +4,27 @@
 
 #define ENTRIES_PER_LINE 4
 
-#if CRC_LE_BITS <= 8
-#define LE_TABLE_SIZE (1 << CRC_LE_BITS)
+#if CRC_LE_BITS > 8
+# define LE_TABLE_ROWS (CRC_LE_BITS/8)
+# define LE_TABLE_SIZE 256
 #else
-#define LE_TABLE_SIZE 256
+# define LE_TABLE_ROWS 1
+# define LE_TABLE_SIZE (1 << CRC_LE_BITS)
 #endif
 
-#if CRC_BE_BITS <= 8
-#define BE_TABLE_SIZE (1 << CRC_BE_BITS)
+#if CRC_BE_BITS > 8
+# define BE_TABLE_ROWS (CRC_BE_BITS/8)
+# define BE_TABLE_SIZE 256
 #else
-#define BE_TABLE_SIZE 256
+# define BE_TABLE_ROWS 1
+# define BE_TABLE_SIZE (1 << CRC_BE_BITS)
 #endif
 
-static uint32_t crc32ctable_le[8][256];
-static uint32_t crc32ctable_be[8][256];
+static uint32_t crc32table_be[BE_TABLE_ROWS][256];
+static uint32_t crc32ctable_le[LE_TABLE_ROWS][256];
 
 /**
- * crc32cinit_le() - allocate and initialize LE table data
+ * crc32init_le() - allocate and initialize LE table data
  *
  * crc is the crc of the byte i; other entries are filled in based on the
  * fact that crctable[i^j] = crctable[i] ^ crctable[j].
@@ -34,13 +38,13 @@
 	crc32ctable_le[0][0] = 0;
 
 	for (i = LE_TABLE_SIZE >> 1; i; i >>= 1) {
-		crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+		crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0);
 		for (j = 0; j < LE_TABLE_SIZE; j += 2 * i)
 			crc32ctable_le[0][i + j] = crc ^ crc32ctable_le[0][j];
 	}
 	for (i = 0; i < LE_TABLE_SIZE; i++) {
 		crc = crc32ctable_le[0][i];
-		for (j = 1; j < 8; j++) {
+		for (j = 1; j < LE_TABLE_ROWS; j++) {
 			crc = crc32ctable_le[0][crc & 0xff] ^ (crc >> 8);
 			crc32ctable_le[j][i] = crc;
 		}
@@ -48,75 +52,65 @@
 }
 
 /**
- * crc32cinit_be() - allocate and initialize BE table data
+ * crc32init_be() - allocate and initialize BE table data
  */
-static void crc32cinit_be(void)
+static void crc32init_be(void)
 {
 	unsigned i, j;
 	uint32_t crc = 0x80000000;
 
-	crc32ctable_be[0][0] = 0;
+	crc32table_be[0][0] = 0;
 
 	for (i = 1; i < BE_TABLE_SIZE; i <<= 1) {
 		crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
 		for (j = 0; j < i; j++)
-			crc32ctable_be[0][i + j] = crc ^ crc32ctable_be[0][j];
+			crc32table_be[0][i + j] = crc ^ crc32table_be[0][j];
 	}
 	for (i = 0; i < BE_TABLE_SIZE; i++) {
-		crc = crc32ctable_be[0][i];
-		for (j = 1; j < 8; j++) {
-			crc = crc32ctable_be[0][(crc >> 24) & 0xff] ^
-			      (crc << 8);
-			crc32ctable_be[j][i] = crc;
+		crc = crc32table_be[0][i];
+		for (j = 1; j < BE_TABLE_ROWS; j++) {
+			crc = crc32table_be[0][(crc >> 24) & 0xff] ^ (crc << 8);
+			crc32table_be[j][i] = crc;
 		}
 	}
 }
 
-static void output_table(uint32_t table[8][256], int len, char trans)
+static void output_table(uint32_t (*table)[256], int rows, int len, char *trans)
 {
 	int i, j;
 
-	for (j = 0 ; j < 8; j++) {
-		printf("static const uint32_t t%d_%ce[] = {", j, trans);
+	for (j = 0 ; j < rows; j++) {
+		printf("{");
 		for (i = 0; i < len - 1; i++) {
-			if ((i % ENTRIES_PER_LINE) == 0)
+			if (i % ENTRIES_PER_LINE == 0)
 				printf("\n");
-			printf("to%ce(0x%8.8xL),", trans, table[j][i]);
-			if ((i % ENTRIES_PER_LINE) != (ENTRIES_PER_LINE - 1))
-				printf(" ");
+			printf("%s(0x%8.8xL), ", trans, table[j][i]);
 		}
-		printf("to%ce(0x%8.8xL)};\n\n", trans, table[j][len - 1]);
-
-		if (trans == 'l') {
-			if ((j+1)*8 >= CRC_LE_BITS)
-				break;
-		} else {
-			if ((j+1)*8 >= CRC_BE_BITS)
-				break;
-		}
+		printf("%s(0x%8.8xL)},\n", trans, table[j][len - 1]);
 	}
 }
 
 int main(int argc, char **argv)
 {
-	printf("/*\n");
-	printf(" * crc32ctable.h - CRC32c tables\n");
-	printf(" *    this file is generated - do not edit\n");
-	printf(" *	# gen_crc32ctable > crc32c_table.h\n");
-	printf(" *    with\n");
-	printf(" *	CRC_LE_BITS = %d\n", CRC_LE_BITS);
-	printf(" *	CRC_BE_BITS = %d\n", CRC_BE_BITS);
-	printf(" */\n");
-	printf("#include <stdint.h>\n");
-
-	if (CRC_LE_BITS > 1) {
-		crc32cinit_le();
-		output_table(crc32ctable_le, LE_TABLE_SIZE, 'l');
-	}
+	printf("/* this file is generated - do not edit */\n\n");
 
 	if (CRC_BE_BITS > 1) {
-		crc32cinit_be();
-		output_table(crc32ctable_be, BE_TABLE_SIZE, 'b');
+		crc32init_be();
+		printf("static const uint32_t "
+		       "crc32table_be[%d][%d] = {",
+		       BE_TABLE_ROWS, BE_TABLE_SIZE);
+		output_table(crc32table_be, LE_TABLE_ROWS,
+			     BE_TABLE_SIZE, "tobe");
+		printf("};\n");
+	}
+	if (CRC_LE_BITS > 1) {
+		crc32cinit_le();
+		printf("static const uint32_t "
+		       "crc32ctable_le[%d][%d] = {",
+		       LE_TABLE_ROWS, LE_TABLE_SIZE);
+		output_table(crc32ctable_le, LE_TABLE_ROWS,
+			     LE_TABLE_SIZE, "tole");
+		printf("};\n");
 	}
 
 	return 0;
diff --git a/lib/ext2fs/get_num_dirs.c b/lib/ext2fs/get_num_dirs.c
new file mode 100644
index 0000000..f5644f8
--- /dev/null
+++ b/lib/ext2fs/get_num_dirs.c
@@ -0,0 +1,50 @@
+/*
+ * get_num_dirs.c -- calculate number of directories
+ *
+ * Copyright 1997 by Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * Returns the number of directories in the filesystem as reported by
+ * the group descriptors.  Of course, the group descriptors could be
+ * wrong!
+ */
+errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs)
+{
+	dgrp_t	i;
+	ext2_ino_t	num_dirs, max_dirs;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	num_dirs = 0;
+	max_dirs = fs->super->s_inodes_per_group;
+	for (i = 0; i < fs->group_desc_count; i++) {
+		if (ext2fs_bg_used_dirs_count(fs, i) > max_dirs)
+			num_dirs += max_dirs / 8;
+		else
+			num_dirs += ext2fs_bg_used_dirs_count(fs, i);
+	}
+	if (num_dirs > fs->super->s_inodes_count)
+		num_dirs = fs->super->s_inodes_count;
+
+	*ret_num_dirs = num_dirs;
+
+	return 0;
+}
+
diff --git a/lib/ext2fs/get_pathname.c b/lib/ext2fs/get_pathname.c
index 52aea62..4c9c765 100644
--- a/lib/ext2fs/get_pathname.c
+++ b/lib/ext2fs/get_pathname.c
@@ -49,21 +49,20 @@
 {
 	struct get_pathname_struct	*gp;
 	errcode_t			retval;
+	int name_len = ext2fs_dirent_name_len(dirent);
 
 	gp = (struct get_pathname_struct *) priv_data;
 
-	if (((dirent->name_len & 0xFF) == 2) &&
-	    !strncmp(dirent->name, "..", 2))
+	if ((name_len == 2) && !strncmp(dirent->name, "..", 2))
 		gp->parent = dirent->inode;
 	if (dirent->inode == gp->search_ino) {
-		retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1,
-					&gp->name);
+		retval = ext2fs_get_mem(name_len + 1, &gp->name);
 		if (retval) {
 			gp->errcode = retval;
 			return DIRENT_ABORT;
 		}
-		strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF));
-		gp->name[dirent->name_len & 0xFF] = '\0';
+		strncpy(gp->name, dirent->name, name_len);
+		gp->name[name_len] = '\0';
 		return DIRENT_ABORT;
 	}
 	return 0;
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index 36c94a9..75fbf8e 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -476,8 +476,7 @@
 	 * bitmaps will be accounted for when allocated).
 	 */
 	free_blocks = 0;
-	csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+	csum_flag = ext2fs_has_group_desc_csum(fs);
 	reserved_inos = super->s_first_ino;
 	for (i = 0; i < fs->group_desc_count; i++) {
 		/*
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 573a8fa..46c1c58 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -72,8 +72,28 @@
 	return 0;
 }
 
+/*
+ * Free the inode cache structure
+ */
+void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
+{
+	int i;
+
+	if (--icache->refcount)
+		return;
+	if (icache->buffer)
+		ext2fs_free_mem(&icache->buffer);
+	for (i = 0; i < icache->cache_size; i++)
+		ext2fs_free_mem(&icache->cache[i].inode);
+	if (icache->cache)
+		ext2fs_free_mem(&icache->cache);
+	icache->buffer_blk = 0;
+	ext2fs_free_mem(&icache);
+}
+
 static errcode_t create_icache(ext2_filsys fs)
 {
+	int		i;
 	errcode_t	retval;
 
 	if (fs->icache)
@@ -84,10 +104,9 @@
 
 	memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
 	retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
-	if (retval) {
-		ext2fs_free_mem(&fs->icache);
-		return retval;
-	}
+	if (retval)
+		goto errout;
+
 	fs->icache->buffer_blk = 0;
 	fs->icache->cache_last = -1;
 	fs->icache->cache_size = 4;
@@ -95,13 +114,22 @@
 	retval = ext2fs_get_array(fs->icache->cache_size,
 				  sizeof(struct ext2_inode_cache_ent),
 				  &fs->icache->cache);
-	if (retval) {
-		ext2fs_free_mem(&fs->icache->buffer);
-		ext2fs_free_mem(&fs->icache);
-		return retval;
+	if (retval)
+		goto errout;
+
+	for (i = 0; i < fs->icache->cache_size; i++) {
+		retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super),
+					&fs->icache->cache[i].inode);
+		if (retval)
+			goto errout;
 	}
+
 	ext2fs_flush_icache(fs);
 	return 0;
+errout:
+	ext2fs_free_inode_cache(fs->icache);
+	fs->icache = 0;
+	return retval;
 }
 
 errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
@@ -148,8 +176,7 @@
 						     scan->current_group);
 	scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
 	scan->blocks_left = scan->fs->inode_blocks_per_group;
-	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+	if (ext2fs_has_group_desc_csum(fs)) {
 		scan->inodes_left -=
 			ext2fs_bg_itable_unused(fs, scan->current_group);
 		scan->blocks_left =
@@ -174,8 +201,7 @@
 	}
 	if (scan->fs->badblocks && scan->fs->badblocks->num)
 		scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
-	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+	if (ext2fs_has_group_desc_csum(fs))
 		scan->scan_flags |= EXT2_SF_DO_LAZY;
 	*ret_scan = scan;
 	return 0;
@@ -241,8 +267,7 @@
 	scan->bytes_left = 0;
 	scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super);
 	scan->blocks_left = fs->inode_blocks_per_group;
-	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+	if (ext2fs_has_group_desc_csum(fs)) {
 		scan->inodes_left -=
 			ext2fs_bg_itable_unused(fs, scan->current_group);
 		scan->blocks_left =
@@ -407,6 +432,8 @@
 {
 	errcode_t	retval;
 	int		extra_bytes = 0;
+	const int	length = EXT2_INODE_SIZE(scan->fs->super);
+	struct ext2_inode_large	*iptr = (struct ext2_inode_large *)inode;
 
 	EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
 
@@ -469,6 +496,12 @@
 #endif
 	}
 
+	if (bufsize < length) {
+		retval = ext2fs_get_mem(length, &iptr);
+		if (retval)
+			return retval;
+	}
+
 	retval = 0;
 	if (extra_bytes) {
 		memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
@@ -476,27 +509,39 @@
 		scan->ptr += scan->inode_size - extra_bytes;
 		scan->bytes_left -= scan->inode_size - extra_bytes;
 
+		/* Verify the inode checksum. */
+		if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+		    !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+				(struct ext2_inode_large *)scan->temp_buffer))
+			retval = EXT2_ET_INODE_CSUM_INVALID;
+
 #ifdef WORDS_BIGENDIAN
-		memset(inode, 0, bufsize);
+		memset(iptr, 0, length);
 		ext2fs_swap_inode_full(scan->fs,
-			       (struct ext2_inode_large *) inode,
+			       (struct ext2_inode_large *) iptr,
 			       (struct ext2_inode_large *) scan->temp_buffer,
-			       0, bufsize);
+			       0, length);
 #else
-		*inode = *((struct ext2_inode *) scan->temp_buffer);
+		memcpy(iptr, scan->temp_buffer, length);
 #endif
 		if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
 			retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
 		scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
 	} else {
+		/* Verify the inode checksum. */
+		if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+		    !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+				(struct ext2_inode_large *)scan->ptr))
+			retval = EXT2_ET_INODE_CSUM_INVALID;
+
 #ifdef WORDS_BIGENDIAN
-		memset(inode, 0, bufsize);
+		memset(iptr, 0, length);
 		ext2fs_swap_inode_full(scan->fs,
-				(struct ext2_inode_large *) inode,
+				(struct ext2_inode_large *) iptr,
 				(struct ext2_inode_large *) scan->ptr,
-				0, bufsize);
+				0, length);
 #else
-		memcpy(inode, scan->ptr, bufsize);
+		memcpy(iptr, scan->ptr, length);
 #endif
 		scan->ptr += scan->inode_size;
 		scan->bytes_left -= scan->inode_size;
@@ -507,6 +552,10 @@
 	scan->inodes_left--;
 	scan->current_inode++;
 	*ino = scan->current_inode;
+	if (iptr != (struct ext2_inode_large *)inode) {
+		memcpy(inode, iptr, bufsize);
+		ext2fs_free_mem(&iptr);
+	}
 	return retval;
 }
 
@@ -527,8 +576,11 @@
 	unsigned long 	group, block, offset;
 	char 		*ptr;
 	errcode_t	retval;
-	int 		clen, i, inodes_per_block, length;
+	int		clen, i, inodes_per_block;
 	io_channel	io;
+	int		length = EXT2_INODE_SIZE(fs->super);
+	struct ext2_inode_large	*iptr;
+	int		cache_slot;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -549,13 +601,11 @@
 			return retval;
 	}
 	/* Check to see if it's in the inode cache */
-	if (bufsize == sizeof(struct ext2_inode)) {
-		/* only old good inode can be retrieved from the cache */
-		for (i=0; i < fs->icache->cache_size; i++) {
-			if (fs->icache->cache[i].ino == ino) {
-				*inode = fs->icache->cache[i].inode;
-				return 0;
-			}
+	for (i = 0; i < fs->icache->cache_size; i++) {
+		if (fs->icache->cache[i].ino == ino) {
+			memcpy(inode, fs->icache->cache[i].inode,
+			       (bufsize > length) ? length : bufsize);
+			return 0;
 		}
 	}
 	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
@@ -580,11 +630,10 @@
 	}
 	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
 
-	length = EXT2_INODE_SIZE(fs->super);
-	if (bufsize < length)
-		length = bufsize;
+	cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
+	iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode;
 
-	ptr = (char *) inode;
+	ptr = (char *) iptr;
 	while (length) {
 		clen = length;
 		if ((offset + length) > fs->blocksize)
@@ -606,18 +655,23 @@
 		ptr += clen;
 		block_nr++;
 	}
+	length = EXT2_INODE_SIZE(fs->super);
+
+	/* Verify the inode checksum. */
+	if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+	    !ext2fs_inode_csum_verify(fs, ino, iptr))
+		return EXT2_ET_INODE_CSUM_INVALID;
 
 #ifdef WORDS_BIGENDIAN
-	ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
-			       (struct ext2_inode_large *) inode,
-			       0, bufsize);
+	ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
+			       (struct ext2_inode_large *) iptr,
+			       0, length);
 #endif
 
-	/* Update the inode cache */
-	fs->icache->cache_last = (fs->icache->cache_last + 1) %
-		fs->icache->cache_size;
-	fs->icache->cache[fs->icache->cache_last].ino = ino;
-	fs->icache->cache[fs->icache->cache_last].inode = *inode;
+	/* Update the inode cache bookkeeping */
+	fs->icache->cache_last = cache_slot;
+	fs->icache->cache[cache_slot].ino = ino;
+	memcpy(inode, iptr, (bufsize > length) ? length : bufsize);
 
 	return 0;
 }
@@ -635,9 +689,10 @@
 	blk64_t block_nr;
 	unsigned long group, block, offset;
 	errcode_t retval = 0;
-	struct ext2_inode_large temp_inode, *w_inode;
+	struct ext2_inode_large *w_inode;
 	char *ptr;
-	int clen, i, length;
+	int clen, i;
+	int length = EXT2_INODE_SIZE(fs->super);
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -648,48 +703,54 @@
 			return retval;
 	}
 
+	if ((ino == 0) || (ino > fs->super->s_inodes_count))
+		return EXT2_ET_BAD_INODE_NUM;
+
+	/* Prepare our shadow buffer for read/modify/byteswap/write */
+	retval = ext2fs_get_mem(length, &w_inode);
+	if (retval)
+		return retval;
+
+	if (bufsize < length) {
+		int old_flags = fs->flags;
+		fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+		retval = ext2fs_read_inode_full(fs, ino,
+						(struct ext2_inode *)w_inode,
+						length);
+		fs->flags = old_flags;
+		if (retval)
+			goto errout;
+	}
+
 	/* Check to see if the inode cache needs to be updated */
 	if (fs->icache) {
 		for (i=0; i < fs->icache->cache_size; i++) {
 			if (fs->icache->cache[i].ino == ino) {
-				fs->icache->cache[i].inode = *inode;
+				memcpy(fs->icache->cache[i].inode, inode,
+				       (bufsize > length) ? length : bufsize);
 				break;
 			}
 		}
 	} else {
 		retval = create_icache(fs);
 		if (retval)
-			return retval;
+			goto errout;
+	}
+	memcpy(w_inode, inode, (bufsize > length) ? length : bufsize);
+
+	if (!(fs->flags & EXT2_FLAG_RW)) {
+		retval = EXT2_ET_RO_FILSYS;
+		goto errout;
 	}
 
-	if (!(fs->flags & EXT2_FLAG_RW))
-		return EXT2_ET_RO_FILSYS;
-
-	if ((ino == 0) || (ino > fs->super->s_inodes_count))
-		return EXT2_ET_BAD_INODE_NUM;
-
-	length = bufsize;
-	if (length < EXT2_INODE_SIZE(fs->super))
-		length = EXT2_INODE_SIZE(fs->super);
-
-	if (length > (int) sizeof(struct ext2_inode_large)) {
-		w_inode = malloc(length);
-		if (!w_inode) {
-			retval = ENOMEM;
-			goto errout;
-		}
-	} else
-		w_inode = &temp_inode;
-	memset(w_inode, 0, length);
-
 #ifdef WORDS_BIGENDIAN
-	ext2fs_swap_inode_full(fs, w_inode,
-			       (struct ext2_inode_large *) inode,
-			       1, bufsize);
-#else
-	memcpy(w_inode, inode, bufsize);
+	ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
 #endif
 
+	retval = ext2fs_inode_csum_set(fs, ino, w_inode);
+	if (retval)
+		goto errout;
+
 	group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
 	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
 		EXT2_INODE_SIZE(fs->super);
@@ -702,10 +763,6 @@
 
 	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
 
-	length = EXT2_INODE_SIZE(fs->super);
-	if (length > bufsize)
-		length = bufsize;
-
 	ptr = (char *) w_inode;
 
 	while (length) {
@@ -738,8 +795,7 @@
 
 	fs->flags |= EXT2_FLAG_CHANGED;
 errout:
-	if (w_inode && w_inode != &temp_inode)
-		free(w_inode);
+	ext2fs_free_mem(&w_inode);
 	return retval;
 }
 
diff --git a/lib/ext2fs/jfs_compat.h b/lib/ext2fs/jfs_compat.h
index 7b8aafd..7947ef5 100644
--- a/lib/ext2fs/jfs_compat.h
+++ b/lib/ext2fs/jfs_compat.h
@@ -17,6 +17,8 @@
 
 #define cpu_to_be32(n) htonl(n)
 #define be32_to_cpu(n) ntohl(n)
+#define cpu_to_be16(n) htons(n)
+#define be16_to_cpu(n) ntohs(n)
 
 typedef unsigned int tid_t;
 typedef struct journal_s journal_t;
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index 059bf8f..130c3a2 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -114,12 +114,24 @@
 #define JBD2_CRC32_CHKSUM   1
 #define JBD2_MD5_CHKSUM     2
 #define JBD2_SHA1_CHKSUM    3
+#define JBD2_CRC32C_CHKSUM  4
 
 #define JBD2_CRC32_CHKSUM_SIZE 4
 
 #define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32))
 /*
  * Commit block header for storing transactional checksums:
+ *
+ * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
+ * fields are used to store a checksum of the descriptor and data blocks.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
+ * field is used to store crc32c(uuid+commit_block).  Each journal metadata
+ * block gets its own checksum, and data block checksums are stored in
+ * journal_block_tag (in the descriptor).  The other h_chksum* fields are
+ * not used.
+ *
+ * Checksum v1 and v2 are mutually exclusive features.
  */
 struct commit_header {
 	__u32		h_magic;
@@ -139,13 +151,19 @@
 typedef struct journal_block_tag_s
 {
 	__u32		t_blocknr;	/* The on-disk block number */
-	__u32		t_flags;	/* See below */
+	__u16		t_checksum;	/* truncated crc32c(uuid+seq+block) */
+	__u16		t_flags;	/* See below */
 	__u32		t_blocknr_high; /* most-significant high 32bits. */
 } journal_block_tag_t;
 
 #define JBD_TAG_SIZE64 (sizeof(journal_block_tag_t))
 #define JBD_TAG_SIZE32 (8)
 
+/* Tail of descriptor block, for checksumming */
+struct journal_block_tail {
+	__u32		t_checksum;
+};
+
 /*
  * The revoke descriptor: used on disk to describe a series of blocks to
  * be revoked from the log
@@ -156,6 +174,10 @@
 	int		 r_count;	/* Count of bytes used in the block */
 } journal_revoke_header_t;
 
+/* Tail of revoke block, for checksumming */
+struct journal_revoke_tail {
+	__u32		r_checksum;
+};
 
 /* Definitions for the journal tag flags word: */
 #define JFS_FLAG_ESCAPE		1	/* on-disk block is escaped */
@@ -205,7 +227,10 @@
 	__u32	s_max_trans_data;	/* Limit of data blocks per trans. */
 
 /* 0x0050 */
-	__u32	s_padding[44];
+	__u8	s_checksum_type;	/* checksum type */
+	__u8	s_padding2[3];
+	__u32	s_padding[42];
+	__u32	s_checksum;		/* crc32c(superblock) */
 
 /* 0x0100 */
 	__u8	s_users[16*48];		/* ids of all fs'es sharing the log */
@@ -224,18 +249,56 @@
 
 #define JFS_FEATURE_COMPAT_CHECKSUM	0x00000001
 
-#define JFS_FEATURE_INCOMPAT_REVOKE	0x00000001
-
 #define JFS_FEATURE_INCOMPAT_REVOKE		0x00000001
 #define JFS_FEATURE_INCOMPAT_64BIT		0x00000002
 #define JFS_FEATURE_INCOMPAT_ASYNC_COMMIT	0x00000004
+#define JFS_FEATURE_INCOMPAT_CSUM_V2		0x00000008
 
 /* Features known to this kernel version: */
 #define JFS_KNOWN_COMPAT_FEATURES	0
 #define JFS_KNOWN_ROCOMPAT_FEATURES	0
 #define JFS_KNOWN_INCOMPAT_FEATURES	(JFS_FEATURE_INCOMPAT_REVOKE|\
 					 JFS_FEATURE_INCOMPAT_ASYNC_COMMIT|\
-					 JFS_FEATURE_INCOMPAT_64BIT)
+					 JFS_FEATURE_INCOMPAT_64BIT|\
+					 JFS_FEATURE_INCOMPAT_CSUM_V2)
+
+#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
+#ifdef E2FSCK_INCLUDE_INLINE_FUNCS
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ extern inline
+#else
+#define _INLINE_ inline
+#endif
+#else /* !E2FSCK_INCLUDE_INLINE FUNCS */
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ inline
+#else /* not C99 */
+#ifdef __GNUC__
+#define _INLINE_ extern __inline__
+#else				/* For Watcom C */
+#define _INLINE_ extern inline
+#endif /* __GNUC__ */
+#endif /* __STDC_VERSION__ >= 199901L */
+#endif /* INCLUDE_INLINE_FUNCS */
+
+/*
+ * helper functions to deal with 32 or 64bit block numbers.
+ */
+_INLINE_ size_t journal_tag_bytes(journal_t *journal)
+{
+	journal_block_tag_t tag;
+	size_t x = 0;
+
+	if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2))
+		x += sizeof(tag.t_checksum);
+
+	if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
+		return x + JBD_TAG_SIZE64;
+	else
+		return x + JBD_TAG_SIZE32;
+}
+#undef _INLINE_
+#endif
 
 #ifdef __KERNEL__
 
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index bf3c859..09e6cb4 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -41,6 +41,8 @@
 	struct ext2_dir_entry *next;
 	unsigned int rec_len, min_rec_len, curr_rec_len;
 	int ret = 0;
+	int csum_size = 0;
+	struct ext2_dir_entry_tail *t;
 
 	if (ls->done)
 		return DIRENT_ABORT;
@@ -51,12 +53,15 @@
 	if (ls->err)
 		return DIRENT_ABORT;
 
+	if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		csum_size = sizeof(struct ext2_dir_entry_tail);
 	/*
 	 * See if the following directory entry (if any) is unused;
 	 * if so, absorb it into this one.
 	 */
 	next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
-	if ((offset + (int) curr_rec_len < blocksize - 8) &&
+	if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
 	    (next->inode == 0) &&
 	    (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
 		curr_rec_len += next->rec_len;
@@ -67,12 +72,46 @@
 	}
 
 	/*
+	 * Since ext2fs_link blows away htree data, we need to be
+	 * careful -- if metadata_csum is enabled and we're passed in
+	 * a dirent that contains htree data, we need to create the
+	 * fake entry at the end of the block that hides the checksum.
+	 */
+
+	/* De-convert a dx_node block */
+	if (csum_size &&
+	    curr_rec_len == ls->fs->blocksize &&
+	    !dirent->inode) {
+		curr_rec_len -= csum_size;
+		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
+		if (ls->err)
+			return DIRENT_ABORT;
+		t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
+		ext2fs_initialize_dirent_tail(ls->fs, t);
+		ret = DIRENT_CHANGED;
+	}
+
+	/* De-convert a dx_root block */
+	if (csum_size &&
+	    curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) &&
+	    offset == EXT2_DIR_REC_LEN(1) &&
+	    dirent->name[0] == '.' && dirent->name[1] == '.') {
+		curr_rec_len -= csum_size;
+		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
+		if (ls->err)
+			return DIRENT_ABORT;
+		t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
+		ext2fs_initialize_dirent_tail(ls->fs, t);
+		ret = DIRENT_CHANGED;
+	}
+
+	/*
 	 * If the directory entry is used, see if we can split the
 	 * directory entry to make room for the new name.  If so,
 	 * truncate it and return.
 	 */
 	if (dirent->inode) {
-		min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+		min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent));
 		if (curr_rec_len < (min_rec_len + rec_len))
 			return ret;
 		rec_len = curr_rec_len - min_rec_len;
@@ -82,7 +121,8 @@
 		next = (struct ext2_dir_entry *) (buf + offset +
 						  dirent->rec_len);
 		next->inode = 0;
-		next->name_len = 0;
+		ext2fs_dirent_set_name_len(next, 0);
+		ext2fs_dirent_set_file_type(next, 0);
 		ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
 		if (ls->err)
 			return DIRENT_ABORT;
@@ -96,10 +136,10 @@
 	if (curr_rec_len < rec_len)
 		return ret;
 	dirent->inode = ls->inode;
-	dirent->name_len = ls->namelen;
+	ext2fs_dirent_set_name_len(dirent, ls->namelen);
 	strncpy(dirent->name, ls->name, ls->namelen);
 	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
-		dirent->name_len |= (ls->flags & 0x7) << 8;
+		ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7);
 
 	ls->done++;
 	return DIRENT_ABORT|DIRENT_CHANGED;
@@ -147,6 +187,11 @@
 	if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
 		return retval;
 
+	/*
+	 * If this function changes to preserve the htree, remove the
+	 * two hunks in link_proc that shove checksum tails into the
+	 * former dx_root/dx_node blocks.
+	 */
 	if (inode.i_flags & EXT2_INDEX_FL) {
 		inode.i_flags &= ~EXT2_INDEX_FL;
 		if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
diff --git a/lib/ext2fs/lookup.c b/lib/ext2fs/lookup.c
index 0e66e71..c1d802c 100644
--- a/lib/ext2fs/lookup.c
+++ b/lib/ext2fs/lookup.c
@@ -37,9 +37,9 @@
 {
 	struct lookup_struct *ls = (struct lookup_struct *) priv_data;
 
-	if (ls->len != (dirent->name_len & 0xFF))
+	if (ls->len != ext2fs_dirent_name_len(dirent))
 		return 0;
-	if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF)))
+	if (strncmp(ls->name, dirent->name, ext2fs_dirent_name_len(dirent)))
 		return 0;
 	*ls->inode = dirent->inode;
 	ls->found++;
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index b12bf2d..4a85439 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -93,12 +93,14 @@
 	inode.i_size = fs->blocksize;
 
 	/*
-	 * Write out the inode and inode data block
+	 * Write out the inode and inode data block.  The inode generation
+	 * number is assigned by write_new_inode, which means that the call
+	 * to write_dir_block must come after that.
 	 */
-	retval = ext2fs_write_dir_block(fs, blk, block);
+	retval = ext2fs_write_new_inode(fs, ino, &inode);
 	if (retval)
 		goto cleanup;
-	retval = ext2fs_write_new_inode(fs, ino, &inode);
+	retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
 	if (retval)
 		goto cleanup;
 
diff --git a/lib/ext2fs/mmp.c b/lib/ext2fs/mmp.c
index e4c7dcc..afb825c 100644
--- a/lib/ext2fs/mmp.c
+++ b/lib/ext2fs/mmp.c
@@ -33,6 +33,7 @@
 
 errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
 {
+#ifdef CONFIG_MMP
 	struct mmp_struct *mmp_cmp;
 	errcode_t retval = 0;
 
@@ -75,6 +76,11 @@
 	}
 
 	mmp_cmp = fs->mmp_cmp;
+
+	if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+	    !ext2fs_mmp_csum_verify(fs, mmp_cmp))
+		retval = EXT2_ET_MMP_CSUM_INVALID;
+
 #ifdef WORDS_BIGENDIAN
 	ext2fs_swap_mmp(mmp_cmp);
 #endif
@@ -89,10 +95,14 @@
 
 out:
 	return retval;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
 
 errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf)
 {
+#ifdef CONFIG_MMP
 	struct mmp_struct *mmp_s = buf;
 	struct timeval tv;
 	errcode_t retval = 0;
@@ -109,6 +119,10 @@
 	ext2fs_swap_mmp(mmp_s);
 #endif
 
+	retval = ext2fs_mmp_csum_set(fs, mmp_s);
+	if (retval)
+		return retval;
+
 	/* I was tempted to make this use O_DIRECT and the mmp_fd, but
 	 * this caused no end of grief, while leaving it as-is works. */
 	retval = io_channel_write_blk64(fs->io, mmp_blk, -(int)sizeof(struct mmp_struct), buf);
@@ -120,6 +134,9 @@
 	/* Make sure the block gets to disk quickly */
 	io_channel_flush(fs->io);
 	return retval;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
 
 #ifdef HAVE_SRANDOM
@@ -129,6 +146,7 @@
 
 unsigned ext2fs_mmp_new_seq(void)
 {
+#ifdef CONFIG_MMP
 	unsigned new_seq;
 	struct timeval tv;
 
@@ -145,6 +163,9 @@
 	} while (new_seq > EXT4_MMP_SEQ_MAX);
 
 	return new_seq;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
 
 static errcode_t ext2fs_mmp_reset(ext2_filsys fs)
@@ -181,8 +202,14 @@
 	return retval;
 }
 
+errcode_t ext2fs_mmp_update(ext2_filsys fs)
+{
+	return ext2fs_mmp_update2(fs, 0);
+}
+
 errcode_t ext2fs_mmp_clear(ext2_filsys fs)
 {
+#ifdef CONFIG_MMP
 	errcode_t retval = 0;
 
 	if (!(fs->flags & EXT2_FLAG_RW))
@@ -191,10 +218,14 @@
 	retval = ext2fs_mmp_reset(fs);
 
 	return retval;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
 
 errcode_t ext2fs_mmp_init(ext2_filsys fs)
 {
+#ifdef CONFIG_MMP
 	struct ext2_super_block *sb = fs->super;
 	blk64_t mmp_block;
 	errcode_t retval;
@@ -223,6 +254,9 @@
 
 out:
 	return retval;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
 
 /*
@@ -230,6 +264,7 @@
  */
 errcode_t ext2fs_mmp_start(ext2_filsys fs)
 {
+#ifdef CONFIG_MMP
 	struct mmp_struct *mmp_s;
 	unsigned seq;
 	unsigned int mmp_check_interval;
@@ -319,6 +354,9 @@
 
 mmp_error:
 	return retval;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
 
 /*
@@ -329,6 +367,7 @@
  */
 errcode_t ext2fs_mmp_stop(ext2_filsys fs)
 {
+#ifdef CONFIG_MMP
 	struct mmp_struct *mmp, *mmp_cmp;
 	errcode_t retval = 0;
 
@@ -358,6 +397,9 @@
 	}
 
 	return retval;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
 
 #define EXT2_MIN_MMP_UPDATE_INTERVAL 60
@@ -365,8 +407,9 @@
 /*
  * Update the on-disk mmp buffer, after checking that it hasn't been changed.
  */
-errcode_t ext2fs_mmp_update(ext2_filsys fs)
+errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately)
 {
+#ifdef CONFIG_MMP
 	struct mmp_struct *mmp, *mmp_cmp;
 	struct timeval tv;
 	errcode_t retval = 0;
@@ -376,7 +419,8 @@
 		return 0;
 
 	gettimeofday(&tv, 0);
-	if (tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
+	if (!immediately &&
+	    tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
 		return 0;
 
 	retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, NULL);
@@ -395,4 +439,7 @@
 
 mmp_error:
 	return retval;
+#else
+	return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
 }
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index 3e2c0db..44e4ca9 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -34,6 +34,8 @@
 	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);
 
@@ -43,7 +45,11 @@
 	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) {
 		ext2fs_free_mem(&buf);
 		return retval;
@@ -52,14 +58,15 @@
 	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);
 
 		/*
@@ -72,11 +79,17 @@
 			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 --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 8035755..aba8a50 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -215,6 +215,16 @@
 	if (fs->orig_super)
 		memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);
 
+	if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
+		retval = 0;
+		if (!ext2fs_verify_csum_type(fs, fs->super))
+			retval = EXT2_ET_UNKNOWN_CSUM;
+		if (!ext2fs_superblock_csum_verify(fs, fs->super))
+			retval = EXT2_ET_SB_CSUM_INVALID;
+		if (retval)
+			goto cleanup;
+	}
+
 #ifdef WORDS_BIGENDIAN
 	fs->flags |= EXT2_FLAG_SWAP_BYTES;
 	ext2fs_swap_super(fs->super);
@@ -295,6 +305,21 @@
 		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
 		goto cleanup;
 	}
+
+	/* Enforce the block group descriptor size */
+	if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) {
+		if (fs->super->s_desc_size < EXT2_MIN_DESC_SIZE_64BIT) {
+			retval = EXT2_ET_BAD_DESC_SIZE;
+			goto cleanup;
+		}
+	} else {
+		if (fs->super->s_desc_size &&
+		    fs->super->s_desc_size != EXT2_MIN_DESC_SIZE) {
+			retval = EXT2_ET_BAD_DESC_SIZE;
+			goto cleanup;
+		}
+	}
+
 	fs->cluster_ratio_bits = fs->super->s_log_cluster_size -
 		fs->super->s_log_block_size;
 	if (EXT2_BLOCKS_PER_GROUP(fs->super) !=
@@ -332,6 +357,8 @@
 		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
 		goto cleanup;
 	}
+	/* Precompute the FS UUID to seed other checksums */
+	ext2fs_init_csum_seed(fs);
 
 	/*
 	 * Read group descriptors
@@ -418,8 +445,7 @@
 	 * If recovery is from backup superblock, Clear _UNININT flags &
 	 * reset bg_itable_unused to zero
 	 */
-	if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+	if (superblock > 1 && ext2fs_has_group_desc_csum(fs)) {
 		dgrp_t group;
 
 		for (group = 0; group < fs->group_desc_count; group++) {
diff --git a/lib/ext2fs/progress.c b/lib/ext2fs/progress.c
index 8c9a6f1..83556b1 100644
--- a/lib/ext2fs/progress.c
+++ b/lib/ext2fs/progress.c
@@ -19,6 +19,12 @@
 static char spaces[80], backspaces[80];
 static time_t last_update;
 
+struct ext2fs_progress_ops ext2fs_numeric_progress_ops = {
+	.init		= ext2fs_numeric_progress_init,
+	.update		= ext2fs_numeric_progress_update,
+	.close		= ext2fs_numeric_progress_close,
+};
+
 static int int_log10(unsigned int arg)
 {
 	int	l;
diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index d24ba9a..ad6cfc1 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -36,7 +36,7 @@
 	unsigned int	nbits;
 	errcode_t	retval;
 	char		*block_buf = NULL, *inode_buf = NULL;
-	int		csum_flag = 0;
+	int		csum_flag;
 	blk64_t		blk;
 	blk64_t		blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
 	ext2_ino_t	ino_itr = 1;
@@ -46,9 +46,7 @@
 	if (!(fs->flags & EXT2_FLAG_RW))
 		return EXT2_ET_RO_FILSYS;
 
-	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
-		csum_flag = 1;
+	csum_flag = ext2fs_has_group_desc_csum(fs);
 
 	inode_nbytes = block_nbytes = 0;
 	if (do_block) {
@@ -90,6 +88,13 @@
 				for (j = nbits; j < fs->blocksize * 8; j++)
 					ext2fs_set_bit(j, block_buf);
 		}
+
+		retval = ext2fs_block_bitmap_csum_set(fs, i, block_buf,
+						      block_nbytes);
+		if (retval)
+			return retval;
+		ext2fs_group_desc_csum_set(fs, i);
+
 		blk = ext2fs_block_bitmap_loc(fs, i);
 		if (blk) {
 			retval = io_channel_write_blk64(fs->io, blk, 1,
@@ -115,6 +120,12 @@
 		if (retval)
 			goto errout;
 
+		retval = ext2fs_inode_bitmap_csum_set(fs, i, inode_buf,
+						      inode_nbytes);
+		if (retval)
+			goto errout;
+		ext2fs_group_desc_csum_set(fs, i);
+
 		blk = ext2fs_inode_bitmap_loc(fs, i);
 		if (blk) {
 			retval = io_channel_write_blk64(fs->io, blk, 1,
@@ -190,7 +201,7 @@
 	errcode_t retval;
 	int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
 	int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8;
-	int csum_flag = 0;
+	int csum_flag;
 	unsigned int	cnt;
 	blk64_t	blk;
 	blk64_t	blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
@@ -206,9 +217,7 @@
 
 	fs->write_bitmaps = ext2fs_write_bitmaps;
 
-	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
-		csum_flag = 1;
+	csum_flag = ext2fs_has_group_desc_csum(fs);
 
 	retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
 	if (retval)
@@ -297,6 +306,15 @@
 					retval = EXT2_ET_BLOCK_BITMAP_READ;
 					goto cleanup;
 				}
+				/* verify block bitmap checksum */
+				if (!(fs->flags &
+				      EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+				    !ext2fs_block_bitmap_csum_verify(fs, i,
+						block_bitmap, block_nbytes)) {
+					retval =
+					EXT2_ET_BLOCK_BITMAP_CSUM_INVALID;
+					goto cleanup;
+				}
 			} else
 				memset(block_bitmap, 0, block_nbytes);
 			cnt = block_nbytes << 3;
@@ -319,6 +337,16 @@
 					retval = EXT2_ET_INODE_BITMAP_READ;
 					goto cleanup;
 				}
+
+				/* verify inode bitmap checksum */
+				if (!(fs->flags &
+				      EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+				    !ext2fs_inode_bitmap_csum_verify(fs, i,
+						inode_bitmap, inode_nbytes)) {
+					retval =
+					EXT2_ET_INODE_BITMAP_CSUM_INVALID;
+					goto cleanup;
+				}
 			} else
 				memset(inode_bitmap, 0, inode_nbytes);
 			cnt = inode_nbytes << 3;
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 2a7b768..d72b666 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -159,7 +159,8 @@
 	to_header->h_blocks   = ext2fs_swab32(from_header->h_blocks);
 	to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
 	to_header->h_hash     = ext2fs_swab32(from_header->h_hash);
-	for (n = 0; n < 4; n++)
+	to_header->h_checksum = ext2fs_swab32(from_header->h_checksum);
+	for (n = 0; n < 3; n++)
 		to_header->h_reserved[n] =
 			ext2fs_swab32(from_header->h_reserved[n]);
 }
@@ -350,6 +351,69 @@
 	mmp->mmp_seq = ext2fs_swab32(mmp->mmp_seq);
 	mmp->mmp_time = ext2fs_swab64(mmp->mmp_time);
 	mmp->mmp_check_interval = ext2fs_swab16(mmp->mmp_check_interval);
+	mmp->mmp_checksum = ext2fs_swab32(mmp->mmp_checksum);
+}
+
+errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
+{
+	errcode_t	retval;
+	char		*p, *end;
+	struct ext2_dir_entry *dirent;
+	unsigned int	name_len, rec_len;
+
+	p = (char *) buf;
+	end = (char *) buf + fs->blocksize;
+	while (p < end-8) {
+		dirent = (struct ext2_dir_entry *) p;
+		dirent->inode = ext2fs_swab32(dirent->inode);
+		dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+		dirent->name_len = ext2fs_swab16(dirent->name_len);
+		name_len = dirent->name_len;
+		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+			dirent->name_len = ext2fs_swab16(dirent->name_len);
+		retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+		if (retval)
+			return retval;
+		if ((rec_len < 8) || (rec_len % 4)) {
+			rec_len = 8;
+			retval = EXT2_ET_DIR_CORRUPTED;
+		} else if (((name_len & 0xFF) + 8) > rec_len)
+			retval = EXT2_ET_DIR_CORRUPTED;
+		p += rec_len;
+	}
+
+	return 0;
+}
+
+errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags)
+{
+	errcode_t	retval;
+	char		*p, *end;
+	unsigned int	rec_len;
+	struct ext2_dir_entry *dirent;
+
+	p = buf;
+	end = buf + fs->blocksize;
+	while (p < end) {
+		dirent = (struct ext2_dir_entry *) p;
+		retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+		if (retval)
+			return retval;
+		if ((rec_len < 8) ||
+		    (rec_len % 4)) {
+			ext2fs_free_mem(&buf);
+			return EXT2_ET_DIR_CORRUPTED;
+		}
+		p += rec_len;
+		dirent->inode = ext2fs_swab32(dirent->inode);
+		dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+		dirent->name_len = ext2fs_swab16(dirent->name_len);
+
+		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+			dirent->name_len = ext2fs_swab16(dirent->name_len);
+	}
+
+	return 0;
 }
 
 #endif
diff --git a/lib/ext2fs/tst_super_size.c b/lib/ext2fs/tst_super_size.c
index f9cec8a..b085bbb 100644
--- a/lib/ext2fs/tst_super_size.c
+++ b/lib/ext2fs/tst_super_size.c
@@ -113,7 +113,7 @@
 	check_field(s_mmp_block, 8);
 	check_field(s_raid_stripe_width, 4);
 	check_field(s_log_groups_per_flex, 1);
-	check_field(s_reserved_char_pad, 1);
+	check_field(s_checksum_type, 1);
 	check_field(s_reserved_pad, 2);
 	check_field(s_kbytes_written, 8);
 	check_field(s_snapshot_inum, 4);
diff --git a/lib/ext2fs/unlink.c b/lib/ext2fs/unlink.c
index d2d31cc..8ab27ee 100644
--- a/lib/ext2fs/unlink.c
+++ b/lib/ext2fs/unlink.c
@@ -44,9 +44,9 @@
 	ls->prev = dirent;
 
 	if (ls->name) {
-		if ((dirent->name_len & 0xFF) != ls->namelen)
+		if (ext2fs_dirent_name_len(dirent) != ls->namelen)
 			return 0;
-		if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF))
+		if (strncmp(ls->name, dirent->name, ext2fs_dirent_name_len(dirent)))
 			return 0;
 	}
 	if (ls->inode) {
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index d4bde8e..ae54f8a 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -121,7 +121,7 @@
 {
 	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",
@@ -198,7 +198,7 @@
 		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);
 
@@ -236,10 +236,18 @@
 		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) +
@@ -326,6 +334,16 @@
 	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;
@@ -394,6 +412,15 @@
 	       (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));
@@ -424,6 +451,16 @@
 		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 --git a/misc/e2image.c b/misc/e2image.c
index 6c51137..b768826 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -418,9 +418,7 @@
 		    ext2fs_inode_table_loc(fs, i)) {
 			unsigned int end = (unsigned) fs->inode_blocks_per_group;
 			/* skip unused blocks */
-			if (!output_is_blk &&
-			    EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-						       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+			if (!output_is_blk && ext2fs_has_group_desc_csum(fs))
 				end -= (ext2fs_bg_itable_unused(fs, i) /
 					EXT2_INODES_PER_BLOCK(fs->super));
 			for (j = 0, b = ext2fs_inode_table_loc(fs, i);
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 0ed02a5..7b69595 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -313,6 +313,27 @@
 	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 errcode_t packed_allocate_tables(ext2_filsys fs)
 {
 	errcode_t	retval;
@@ -392,6 +413,12 @@
 	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)
@@ -669,6 +696,23 @@
 }
 
 /*
+ * 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.
  */
@@ -1007,7 +1051,7 @@
 #ifdef CONFIG_QUOTA
 		EXT4_FEATURE_RO_COMPAT_QUOTA|
 #endif
-		0
+		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
 };
 
 
@@ -1136,15 +1180,11 @@
 	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)
@@ -1834,10 +1874,40 @@
 		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);
@@ -2101,6 +2171,13 @@
 	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)) {
@@ -2261,7 +2338,8 @@
 	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;
@@ -2531,6 +2609,26 @@
 			_("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) ||
@@ -2568,6 +2666,7 @@
 	    (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;
 
@@ -2588,6 +2687,7 @@
 		}
 	} else
 		uuid_generate(fs->super->s_uuid);
+	ext2fs_init_csum_seed(fs);
 
 	/*
 	 * Initialize the directory index variables
@@ -2664,6 +2764,10 @@
 			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);
 
@@ -2714,8 +2818,7 @@
 		 * 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);
 		}
diff --git a/misc/mke2fs.conf.in b/misc/mke2fs.conf.in
index 0871f77..178733f 100644
--- a/misc/mke2fs.conf.in
+++ b/misc/mke2fs.conf.in
@@ -16,7 +16,7 @@
 		inode_size = 256
 	}
 	ext4dev = {
-		features = has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize
+		features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize
 		inode_size = 256
 		options = test_fs=1
 	}
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 64a76f1..4327ca0 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -95,6 +95,7 @@
 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;
@@ -110,6 +111,8 @@
 
 
 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);
@@ -149,10 +152,11 @@
 		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] = {
@@ -169,10 +173,11 @@
 		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
 };
 
 /*
@@ -373,6 +378,18 @@
 	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(_("(and reboot afterwards!)\n"));
+}
+
 static void request_fsck_afterwards(ext2_filsys fs)
 {
 	static int requested = 0;
@@ -385,15 +402,476 @@
 		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;
+	int			op = EXT2_EXTENT_ROOT;
+	errcode_t		errcode;
+
+	if (!(inode->i_flags & EXT4_EXTENTS_FL))
+		return 0;
+
+	errcode = ext2fs_extent_open(fs, ino, &handle);
+	if (errcode)
+		return errcode;
+
+	while (1) {
+		errcode = ext2fs_extent_get(handle, op, &extent);
+		if (errcode)
+			break;
+
+		/* Root node is in the separately checksummed inode */
+		if (op == EXT2_EXTENT_ROOT) {
+			op = EXT2_EXTENT_NEXT;
+			continue;
+		}
+		op = EXT2_EXTENT_NEXT;
+
+		/* Only visit the first extent in each extent block */
+		if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
+			continue;
+		errcode = ext2fs_extent_replace(handle, 0, &extent);
+		if (errcode)
+			break;
+	}
+
+	/* Ok if we run off the end */
+	if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+		errcode = 0;
+	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)) {
+			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;
+	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 void 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;
+	int has_super;
+
+	/* 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);
+	if (retval) {
+		com_err("disable_uninit_bg", retval,
+			"while reading bitmaps");
+		return;
+	}
+	ext2fs_mark_ib_dirty(fs);
+	ext2fs_mark_bb_dirty(fs);
+	fs->super->s_feature_ro_compat &= ~csum_feature_flag;
+
+	/* 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);
+		}
+	}
+
+	/* 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);
+}
+
 /*
  * 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;
 
@@ -559,36 +1037,68 @@
 	}
 
 	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;
+		       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+		if (check_fsck_needed(fs))
+			exit(1);
+		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_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;
+			EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+		if (check_fsck_needed(fs))
+			exit(1);
+		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))
+			disable_uninit_bg(fs,
+				EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
+		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)) {
+		/* 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))
+		disable_uninit_bg(fs,
+				EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+
+	if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
 				EXT4_FEATURE_RO_COMPAT_QUOTA)) {
 		/*
 		 * Set the Q_flag here and handle the quota options in the code
@@ -2161,8 +2671,7 @@
 		int set_csum = 0;
 		dgrp_t i;
 
-		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.
@@ -2200,13 +2709,19 @@
 			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);
 			fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
 		}
 		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 "
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index b0c4b5e..498cf99 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -237,8 +237,7 @@
 	dgrp_t		g;
 	int		i;
 
-	if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)))
+	if (!ext2fs_has_group_desc_csum(fs))
 		return;
 
 	for (g=0; g < fs->group_desc_count; g++) {
@@ -544,8 +543,7 @@
 	 */
 	group_block = ext2fs_group_first_block2(fs,
 						old_fs->group_desc_count);
-	csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+	csum_flag = ext2fs_has_group_desc_csum(fs);
 	if (access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0)
 		lazy_itable_init = 1;
 	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
@@ -703,9 +701,7 @@
 	 * supports lazy inode initialization, we can skip
 	 * initializing the inode table.
 	 */
-	if (lazy_itable_init &&
-	    EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+	if (lazy_itable_init && ext2fs_has_group_desc_csum(fs)) {
 		retval = 0;
 		goto errout;
 	}
@@ -861,9 +857,9 @@
 			}
 		}
 	}
-	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
-		   (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) {
+
+	if (ext2fs_has_group_desc_csum(fs) &&
+	    (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) {
 		/*
 		 * If the block bitmap is uninitialized, which means
 		 * nothing other than standard metadata in use.
@@ -959,8 +955,7 @@
 	for (blk = ext2fs_blocks_count(fs->super);
 	     blk < ext2fs_blocks_count(old_fs->super); blk++) {
 		g = ext2fs_group_of_blk2(fs, blk);
-		if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+		if (ext2fs_has_group_desc_csum(fs) &&
 		    ext2fs_bg_flags_test(old_fs, g, EXT2_BG_BLOCK_UNINIT)) {
 			/*
 			 * The block bitmap is uninitialized, so skip
@@ -1402,10 +1397,12 @@
 struct process_block_struct {
 	ext2_resize_t 		rfs;
 	ext2_ino_t		ino;
+	ext2_ino_t		old_ino;
 	struct ext2_inode *	inode;
 	errcode_t		error;
 	int			is_dir;
 	int			changed;
+	int			has_extents;
 };
 
 static int process_block(ext2_filsys fs, blk64_t	*block_nr,
@@ -1429,11 +1426,23 @@
 #ifdef RESIZE2FS_DEBUG
 			if (pb->rfs->flags & RESIZE_DEBUG_BMOVE)
 				printf("ino=%u, blockcnt=%lld, %llu->%llu\n",
-				       pb->ino, blockcnt, block, new_block);
+				       pb->old_ino, blockcnt, block,
+				       new_block);
 #endif
 			block = new_block;
 		}
 	}
+
+	/*
+	 * If we moved inodes and metadata_csum is enabled, we must force the
+	 * extent block to be rewritten with new checksum.
+	 */
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+	    pb->has_extents &&
+	    pb->old_ino != pb->ino)
+		ret |= BLOCK_CHANGED;
+
 	if (pb->is_dir) {
 		retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
 					       block, (int) blockcnt);
@@ -1473,6 +1482,46 @@
 	return 0;
 }
 
+static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino,
+				  struct ext2_inode *inode, int *changed)
+{
+	char *buf;
+	blk64_t new_block;
+	errcode_t err = 0;
+
+	/* No EA block or no remapping?  Quit early. */
+	if (ext2fs_file_acl_block(rfs->old_fs, inode) == 0 && !rfs->bmap)
+		return 0;
+	new_block = extent_translate(rfs->old_fs, rfs->bmap,
+		ext2fs_file_acl_block(rfs->old_fs, inode));
+	if (new_block == 0)
+		return 0;
+
+	/* Set the new ACL block */
+	ext2fs_file_acl_block_set(rfs->old_fs, inode, new_block);
+
+	/* Update checksum */
+	if (EXT2_HAS_RO_COMPAT_FEATURE(rfs->new_fs->super,
+			EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+		err = ext2fs_get_mem(rfs->old_fs->blocksize, &buf);
+		if (err)
+			return err;
+		rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+		err = ext2fs_read_ext_attr3(rfs->old_fs, new_block, buf, ino);
+		rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+		if (err)
+			goto out;
+		err = ext2fs_write_ext_attr3(rfs->old_fs, new_block, buf, ino);
+		if (err)
+			goto out;
+	}
+	*changed = 1;
+
+out:
+	ext2fs_free_mem(&buf);
+	return err;
+}
+
 static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
 {
 	struct process_block_struct	pb;
@@ -1483,7 +1532,6 @@
 	char			*block_buf = 0;
 	ext2_ino_t		start_to_move;
 	blk64_t			orig_size;
-	blk64_t			new_block;
 	int			inode_size;
 
 	if ((rfs->old_fs->group_desc_count <=
@@ -1546,37 +1594,19 @@
 		pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
 		pb.changed = 0;
 
-		if (ext2fs_file_acl_block(rfs->old_fs, inode) && rfs->bmap) {
-			new_block = extent_translate(rfs->old_fs, rfs->bmap,
-				ext2fs_file_acl_block(rfs->old_fs, inode));
-			if (new_block) {
-				ext2fs_file_acl_block_set(rfs->old_fs, inode,
-							  new_block);
-				retval = ext2fs_write_inode_full(rfs->old_fs,
-							    ino, inode, inode_size);
-				if (retval) goto errout;
-			}
-		}
+		/* Remap EA block */
+		retval = migrate_ea_block(rfs, ino, inode, &pb.changed);
+		if (retval)
+			goto errout;
 
-		if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) &&
-		    (rfs->bmap || pb.is_dir)) {
-			pb.ino = ino;
-			retval = ext2fs_block_iterate3(rfs->old_fs,
-						       ino, 0, block_buf,
-						       process_block, &pb);
-			if (retval)
-				goto errout;
-			if (pb.error) {
-				retval = pb.error;
-				goto errout;
-			}
-		}
-
+		new_inode = ino;
 		if (ino <= start_to_move)
-			continue; /* Don't need to move it. */
+			goto remap_blocks; /* Don't need to move inode. */
 
 		/*
-		 * Find a new inode
+		 * Find a new inode.  Now that extents and directory blocks
+		 * are tied to the inode number through the checksum, we must
+		 * set up the new inode before we start rewriting blocks.
 		 */
 		retval = ext2fs_new_inode(rfs->new_fs, 0, 0, 0, &new_inode);
 		if (retval)
@@ -1584,16 +1614,12 @@
 
 		ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
 					  pb.is_dir);
-		if (pb.changed) {
-			/* Get the new version of the inode */
-			retval = ext2fs_read_inode_full(rfs->old_fs, ino,
-						inode, inode_size);
-			if (retval) goto errout;
-		}
 		inode->i_ctime = time(0);
 		retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
 						inode, inode_size);
-		if (retval) goto errout;
+		if (retval)
+			goto errout;
+		pb.changed = 0;
 
 #ifdef RESIZE2FS_DEBUG
 		if (rfs->flags & RESIZE_DEBUG_INODEMAP)
@@ -1605,6 +1631,37 @@
 				goto errout;
 		}
 		ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
+
+remap_blocks:
+		if (pb.changed)
+			retval = ext2fs_write_inode_full(rfs->old_fs,
+							 new_inode,
+							 inode, inode_size);
+		if (retval)
+			goto errout;
+
+		/*
+		 * Update inodes to point to new blocks; schedule directory
+		 * blocks for inode remapping.  Need to write out dir blocks
+		 * with new inode numbers if we have metadata_csum enabled.
+		 */
+		if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) &&
+		    (rfs->bmap || pb.is_dir)) {
+			pb.ino = new_inode;
+			pb.old_ino = ino;
+			pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL;
+			rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+			retval = ext2fs_block_iterate3(rfs->old_fs,
+						       new_inode, 0, block_buf,
+						       process_block, &pb);
+			rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+			if (retval)
+				goto errout;
+			if (pb.error) {
+				retval = pb.error;
+				goto errout;
+			}
+		}
 	}
 	io_channel_flush(rfs->old_fs->io);
 
@@ -1647,6 +1704,7 @@
 	struct ext2_inode 	inode;
 	ext2_ino_t		new_inode;
 	errcode_t		retval;
+	int			ret = 0;
 
 	if (is->rfs->progress && offset == 0) {
 		io_channel_flush(is->rfs->old_fs->io);
@@ -1657,17 +1715,26 @@
 			return DIRENT_ABORT;
 	}
 
+	/*
+	 * If we have checksums enabled and the inode wasn't present in the
+	 * old fs, then we must rewrite all dir blocks with new checksums.
+	 */
+	if (EXT2_HAS_RO_COMPAT_FEATURE(is->rfs->old_fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+	    !ext2fs_test_inode_bitmap2(is->rfs->old_fs->inode_map, dir))
+		ret |= DIRENT_CHANGED;
+
 	if (!dirent->inode)
-		return 0;
+		return ret;
 
 	new_inode = ext2fs_extent_translate(is->rfs->imap, dirent->inode);
 
 	if (!new_inode)
-		return 0;
+		return ret;
 #ifdef RESIZE2FS_DEBUG
 	if (is->rfs->flags & RESIZE_DEBUG_INODEMAP)
 		printf("Inode translate (dir=%u, name=%.*s, %u->%u)\n",
-		       dir, dirent->name_len&0xFF, dirent->name,
+		       dir, ext2fs_dirent_name_len(dirent), dirent->name,
 		       dirent->inode, new_inode);
 #endif
 
@@ -1679,10 +1746,10 @@
 		inode.i_mtime = inode.i_ctime = time(0);
 		is->err = ext2fs_write_inode(is->rfs->old_fs, dir, &inode);
 		if (is->err)
-			return DIRENT_ABORT;
+			return ret | DIRENT_ABORT;
 	}
 
-	return DIRENT_CHANGED;
+	return ret | DIRENT_CHANGED;
 }
 
 static errcode_t inode_ref_fix(ext2_resize_t rfs)
@@ -1709,9 +1776,11 @@
 			goto errout;
 	}
 
+	rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
 	retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist,
 					   DIRENT_FLAG_INCLUDE_EMPTY, 0,
 					   check_and_change_inodes, &is);
+	rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
 	if (retval)
 		goto errout;
 	if (is.err) {
diff --git a/tests/f_mmp/script b/tests/f_mmp/script
index d921672..105a305 100644
--- a/tests/f_mmp/script
+++ b/tests/f_mmp/script
@@ -1,5 +1,6 @@
 FSCK_OPT=-yf
 
+[ -f "$TMPFILE" ] && rm -f $TMPFILE
 TMPFILE=$test_name.tmp
 > $TMPFILE
 
diff --git a/tests/f_mmp_garbage/script b/tests/f_mmp_garbage/script
index 02cc12a..d4d2cb3 100644
--- a/tests/f_mmp_garbage/script
+++ b/tests/f_mmp_garbage/script
@@ -1,5 +1,6 @@
 FSCK_OPT=-yf
 
+[ -f "$TMPFILE" ] && rm -f $TMPFILE
 TMPFILE=$test_name.tmp
 > $TMPFILE
 
diff --git a/tests/filter.sed b/tests/filter.sed
index 91b956b..59fad4e 100644
--- a/tests/filter.sed
+++ b/tests/filter.sed
@@ -1,4 +1,9 @@
-/^[dbgumpe2fsckrsiz]* [1-9]\.[0-9]*[.-][^ ]* ([0-9]*-[A-Za-z]*-[0-9]*)/d
+/^debugfs [1-9]\.[0-9]*[.-][^ ]* ([0-9]*-[A-Za-z]*-[0-9]*)/d
+/^dumpe2fs [1-9]\.[0-9]*[.-][^ ]* ([0-9]*-[A-Za-z]*-[0-9]*)/d
+/^e2fsck [1-9]\.[0-9]*[.-][^ ]* ([0-9]*-[A-Za-z]*-[0-9]*)/d
+/^mke2fs [1-9]\.[0-9]*[.-][^ ]* ([0-9]*-[A-Za-z]*-[0-9]*)/d
+/^resize2fs [1-9]\.[0-9]*[.-][^ ]* ([0-9]*-[A-Za-z]*-[0-9]*)/d
+/^tune2fs [1-9]\.[0-9]*[.-][^ ]* ([0-9]*-[A-Za-z]*-[0-9]*)/d
 s/\\015//g
 /automatically checked/d
 /^Directory Hash Seed:/d
diff --git a/tests/m_mmp/script b/tests/m_mmp/script
index 02b0b4b..dff98f0 100644
--- a/tests/m_mmp/script
+++ b/tests/m_mmp/script
@@ -2,6 +2,7 @@
 FS_SIZE=65536
 MKE2FS_DEVICE_SECTSIZE=2048
 export MKE2FS_DEVICE_SECTSIZE
+[ -f "$TMPFILE" ] && rm -f $TMPFILE
 TMPFILE=$test_name.tmp
 > $TMPFILE
 stat -f $TMPFILE | grep -q "Type: tmpfs"
diff --git a/tests/t_mmp_1on/script b/tests/t_mmp_1on/script
index 8fc8158..d15b1e3 100644
--- a/tests/t_mmp_1on/script
+++ b/tests/t_mmp_1on/script
@@ -1,5 +1,6 @@
 FSCK_OPT=-yf
 
+[ -f "$TMPFILE" ] && rm -f $TMPFILE
 TMPFILE=$test_name.tmp
 > $TMPFILE
 
diff --git a/tests/t_mmp_2off/script b/tests/t_mmp_2off/script
index 1dee14e..572730b 100644
--- a/tests/t_mmp_2off/script
+++ b/tests/t_mmp_2off/script
@@ -1,5 +1,6 @@
 FSCK_OPT=-yf
 
+[ -f "$TMPFILE" ] && rm -f $TMPFILE
 TMPFILE=$test_name.tmp
 > $TMPFILE
 
diff --git a/tests/t_uninit_bg_rm/expect b/tests/t_uninit_bg_rm/expect
new file mode 100644
index 0000000..61e9eaa
--- /dev/null
+++ b/tests/t_uninit_bg_rm/expect
@@ -0,0 +1,21 @@
+mke2fs -q -t ext4 -F -o Linux -b 1024 test.img 1G
+tune2fs -f -O ^uninit_bg test.img
+ 
+fsck -yf -N test_filesys test.img
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/65536 files (0.0% non-contiguous), 52294/1048576 blocks
+ 
+mke2fs -q -t ext4 -O bigalloc -F -o Linux -b 1024 -C 8192 test.img 10G
+tune2fs -f -O ^uninit_bg test.img
+ 
+fsck -yf -N test_filesys test.img
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/655360 files (0.0% non-contiguous), 199864/10485760 blocks
diff --git a/tests/t_uninit_bg_rm/script b/tests/t_uninit_bg_rm/script
new file mode 100644
index 0000000..cd397c5
--- /dev/null
+++ b/tests/t_uninit_bg_rm/script
@@ -0,0 +1,50 @@
+test_description="remove uninit_bg"
+OUT=$test_name.log
+FSCK_OPT=-yf
+EXP=$test_dir/expect
+
+cp /dev/null $TMPFILE
+rm -f $OUT.new
+
+echo mke2fs -q -t ext4 -F -o Linux -b 1024 $TMPFILE 1G >> $OUT.new
+$MKE2FS -q -t ext4 -F -o Linux -b 1024 $TMPFILE 1G >> $OUT.new 2>&1
+
+echo "tune2fs -f -O ^uninit_bg $TMPFILE" >> $OUT.new
+$TUNE2FS -f -O ^uninit_bg $TMPFILE >> $OUT.new 2>&1
+
+echo " " >> $OUT.new
+echo fsck $FSCK_OPT -N test_filesys test.img >> $OUT.new
+$FSCK $FSCK_OPT -N test_filesys $TMPFILE >> $OUT.new 2>&1
+
+echo " " >> $OUT.new
+cp /dev/null $TMPFILE
+echo mke2fs -q -t ext4 -O bigalloc -F -o Linux -b 1024 -C 8192 $TMPFILE 10G >> $OUT.new
+$MKE2FS -q -t ext4 -O bigalloc -F -o Linux -b 1024 -C 8192 $TMPFILE 10G >> $OUT.new 2>&1
+
+echo "tune2fs -f -O ^uninit_bg $TMPFILE" >> $OUT.new
+$TUNE2FS -f -O ^uninit_bg $TMPFILE >> $OUT.new 2>&1
+
+echo " " >> $OUT.new
+echo fsck $FSCK_OPT -N test_filesys test.img >> $OUT.new
+$FSCK $FSCK_OPT -N test_filesys $TMPFILE >> $OUT.new 2>&1
+
+sed -f $cmd_dir/filter.sed -e "s;$TMPFILE;test.img;" $OUT.new > $OUT
+
+rm -f $OUT.new $TMPFILE
+
+#
+# Do the verification
+#
+
+cmp -s $OUT $EXP
+status=$?
+
+if [ "$status" = 0 ] ; then
+	echo "$test_name: $test_description: ok"
+	touch $test_name.ok
+else
+	echo "$test_name: $test_description: failed"
+	diff $DIFF_OPTS $EXP $OUT > $test_name.failed
+fi
+
+unset IMAGE FSCK_OPT OUT EXP
diff --git a/version.h b/version.h
index f99ce49..9eb6e94 100644
--- a/version.h
+++ b/version.h
@@ -7,5 +7,5 @@
  * file may be redistributed under the GNU Public License v2.
  */
 
-#define E2FSPROGS_VERSION "1.42.9"
+#define E2FSPROGS_VERSION "1.43-WIP"
 #define E2FSPROGS_DATE "4-Feb-2014"