diff mbox series

fs: ext4: Add metadata checksums support

Message ID 20210212165747.207979-1-megahallon@gmail.com
State Changes Requested
Delegated to: Tom Rini
Headers show
Series fs: ext4: Add metadata checksums support | expand

Commit Message

Fredrik Hallenberg Feb. 12, 2021, 4:57 p.m. UTC
Support crc32c checksums in ext4 filesystems with metadata_csum flag
active. This includes superblock, inodes, inode and block group tables,
directory blocks and journal.

Signed-off-by: Fredrik Hallenberg <megahallon@gmail.com>
---
 fs/ext4/Kconfig                   |   1 +
 fs/ext4/ext4_common.c             |  91 ++++++++++-------
 fs/ext4/ext4_common.h             |   7 +-
 fs/ext4/ext4_journal.c            | 135 ++++++++++++++++++++-----
 fs/ext4/ext4_journal.h            |  40 +++++++-
 fs/ext4/ext4_write.c              | 162 ++++++++++++++++++++++++++++--
 include/ext4fs.h                  |   7 ++
 include/ext_common.h              |  77 +++++++++++++-
 test/py/tests/test_env.py         |   3 -
 test/py/tests/test_fs/conftest.py |   4 -
 10 files changed, 441 insertions(+), 86 deletions(-)

Comments

Tom Rini Feb. 12, 2021, 8:48 p.m. UTC | #1
On Fri, Feb 12, 2021 at 05:57:47PM +0100, Fredrik Hallenberg wrote:

> Support crc32c checksums in ext4 filesystems with metadata_csum flag
> active. This includes superblock, inodes, inode and block group tables,
> directory blocks and journal.
> 
> Signed-off-by: Fredrik Hallenberg <megahallon@gmail.com>

I've initially put this in a local branch and run the fs tests on
sandbox both with default options and forcing metadata_csum to be
enabled, and it worked in both cases.  Thanks for posting the patch!
Can you let us know on what systems you've tested this?
Fredrik Hallenberg Feb. 13, 2021, 9:51 a.m. UTC | #2
Thanks for trying it out. I have only used it on a custom arm-based
board. I have done quite a lot of writing with and without metadata
flag and have also tried to trigger errors that require journal
recovery. It has worked well but of course it is hard to be sure that
the code will handle every case.

On Fri, Feb 12, 2021 at 9:48 PM Tom Rini <trini@konsulko.com> wrote:
>
> On Fri, Feb 12, 2021 at 05:57:47PM +0100, Fredrik Hallenberg wrote:
>
> > Support crc32c checksums in ext4 filesystems with metadata_csum flag
> > active. This includes superblock, inodes, inode and block group tables,
> > directory blocks and journal.
> >
> > Signed-off-by: Fredrik Hallenberg <megahallon@gmail.com>
>
> I've initially put this in a local branch and run the fs tests on
> sandbox both with default options and forcing metadata_csum to be
> enabled, and it worked in both cases.  Thanks for posting the patch!
> Can you let us know on what systems you've tested this?
>
> --
> Tom
Fredrik Hallenberg Feb. 22, 2021, 4:54 p.m. UTC | #3
Anything I can do to improve the patch?

On Sat, Feb 13, 2021 at 10:51 AM Fredrik Hallenberg
<megahallon@gmail.com> wrote:
>
> Thanks for trying it out. I have only used it on a custom arm-based
> board. I have done quite a lot of writing with and without metadata
> flag and have also tried to trigger errors that require journal
> recovery. It has worked well but of course it is hard to be sure that
> the code will handle every case.
>
> On Fri, Feb 12, 2021 at 9:48 PM Tom Rini <trini@konsulko.com> wrote:
> >
> > On Fri, Feb 12, 2021 at 05:57:47PM +0100, Fredrik Hallenberg wrote:
> >
> > > Support crc32c checksums in ext4 filesystems with metadata_csum flag
> > > active. This includes superblock, inodes, inode and block group tables,
> > > directory blocks and journal.
> > >
> > > Signed-off-by: Fredrik Hallenberg <megahallon@gmail.com>
> >
> > I've initially put this in a local branch and run the fs tests on
> > sandbox both with default options and forcing metadata_csum to be
> > enabled, and it worked in both cases.  Thanks for posting the patch!
> > Can you let us know on what systems you've tested this?
> >
> > --
> > Tom
Tom Rini Feb. 22, 2021, 5:40 p.m. UTC | #4
On Mon, Feb 22, 2021 at 05:54:45PM +0100, Fredrik Hallenberg wrote:

> Anything I can do to improve the patch?

Barring further comments from others, I will put this in to -next when I
open that merge window after the next -rc release.  Thanks!
Tom Rini April 7, 2021, 2:20 p.m. UTC | #5
On Fri, Feb 12, 2021 at 05:57:47PM +0100, Fredrik Hallenberg wrote:

> Support crc32c checksums in ext4 filesystems with metadata_csum flag
> active. This includes superblock, inodes, inode and block group tables,
> directory blocks and journal.
> 
> Signed-off-by: Fredrik Hallenberg <megahallon@gmail.com>

This fails broadly with an error like:
   aarch64:  +   ls1046ardb_qspi
+(ls1046ardb_qspi) In file included from ../include/linux/bitops.h:22,
+(ls1046ardb_qspi)                  from include/asm/arch/config.h:14,
+(ls1046ardb_qspi)                  from ../include/configs/ls1046a_common.h:32,
+(ls1046ardb_qspi)                  from ../include/configs/ls1046ardb.h:10,
+(ls1046ardb_qspi)                  from include/config.h:4,
+(ls1046ardb_qspi)                  from ../include/common.h:16,
+(ls1046ardb_qspi)                  from ../fs/ext4/ext4_common.c:21:
+(ls1046ardb_qspi) ../fs/ext4/ext4_common.c: In function 'ext4fs_read_inode':
+(ls1046ardb_qspi) ../include/linux/kernel.h:184:17: error: comparison of distinct pointer types lacks a cast [-Werror]
+(ls1046ardb_qspi)   184 |  (void) (&_min1 == &_min2);  \
+(ls1046ardb_qspi)       |                 ^~
+(ls1046ardb_qspi) ../fs/ext4/ext4_common.c:1655:9: note: in expansion of macro 'min'
+(ls1046ardb_qspi)  1655 |  size = min(sizeof(struct ext2_inode), fs->inodesz);
+(ls1046ardb_qspi)       |         ^~~
+(ls1046ardb_qspi) cc1: all warnings being treated as errors
+(ls1046ardb_qspi) make[3]: *** [fs/ext4/ext4_common.o] Error 1
+(ls1046ardb_qspi) make[2]: *** [fs/ext4] Error 2
+(ls1046ardb_qspi) make[1]: *** [fs] Error 2
+(ls1046ardb_qspi) make: *** [sub-make] Error 2
diff mbox series

Patch

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 1a913d2b6d..da033428b2 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -8,6 +8,7 @@  config FS_EXT4
 config EXT4_WRITE
 	bool "Enable ext4 filesystem write support"
 	depends on FS_EXT4
+	select CRC32C
 	help
 	  This provides support for creating and writing new files to an
 	  existing ext4 filesystem partition.
diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c
index c52cc400e1..6e23e47c97 100644
--- a/fs/ext4/ext4_common.c
+++ b/fs/ext4/ext4_common.c
@@ -45,6 +45,8 @@  __le32 *ext4fs_indir3_block;
 int ext4fs_indir3_size;
 int ext4fs_indir3_blkno = -1;
 struct ext2_inode *g_parent_inode;
+int g_parent_inode_no;
+
 static int symlinknest;
 
 #if defined(CONFIG_EXT4_WRITE)
@@ -416,32 +418,6 @@  void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index)
 		*ptr = *ptr & ~(operand);
 }
 
-uint16_t ext4fs_checksum_update(uint32_t i)
-{
-	struct ext2_block_group *desc;
-	struct ext_filesystem *fs = get_fs();
-	uint16_t crc = 0;
-	__le32 le32_i = cpu_to_le32(i);
-
-	desc = ext4fs_get_group_descriptor(fs, i);
-	if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
-		int offset = offsetof(struct ext2_block_group, bg_checksum);
-
-		crc = ext2fs_crc16(~0, fs->sb->unique_id,
-				   sizeof(fs->sb->unique_id));
-		crc = ext2fs_crc16(crc, &le32_i, sizeof(le32_i));
-		crc = ext2fs_crc16(crc, desc, offset);
-		offset += sizeof(desc->bg_checksum);	/* skip checksum */
-		assert(offset == sizeof(*desc));
-		if (offset < fs->gdsize) {
-			crc = ext2fs_crc16(crc, (__u8 *)desc + offset,
-					   fs->gdsize - offset);
-		}
-	}
-
-	return crc;
-}
-
 static int check_void_in_dentry(struct ext2_dirent *dir, char *filename)
 {
 	int dentry_length;
@@ -472,6 +448,33 @@  static int check_void_in_dentry(struct ext2_dirent *dir, char *filename)
 	return 0;
 }
 
+static void ext4fs_set_dirent_tail_csum(struct ext_filesystem *fs, uint8_t *block)
+{
+	struct ext4_dir_entry_tail *tail;
+	uint32_t crc32;
+	uint32_t inode_seed;
+	__le32 inum = cpu_to_le32(g_parent_inode_no);
+	__le32 gen = g_parent_inode->generation;
+	int size;
+
+	if (!(le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return;
+
+	tail = (struct ext4_dir_entry_tail *)(block + fs->blksz
+					      - sizeof(struct ext4_dir_entry_tail));
+	if (tail->det_reserved_ft != 0xde) {
+		printf("Bad dirent tail\n");
+		return;
+	}
+
+	inode_seed = ext4_csum(fs->csum_seed, (uint8_t *)&inum, sizeof(inum));
+	inode_seed = ext4_csum(inode_seed, (uint8_t *)&gen, sizeof(gen));
+
+	size = (uint8_t *)tail - block;
+	crc32 = ext4_csum(inode_seed, block, size);
+	tail->det_checksum = cpu_to_le32(crc32);
+}
+
 int ext4fs_update_parent_dentry(char *filename, int file_type)
 {
 	unsigned int *zero_buffer = NULL;
@@ -492,6 +495,10 @@  int ext4fs_update_parent_dentry(char *filename, int file_type)
 	uint32_t new_size;
 	uint32_t new_blockcnt;
 	uint32_t directory_blocks;
+	struct ext4_dir_entry_tail *tail;
+	int has_metadata_chksum = le32_to_cpu(fs->sb->feature_ro_compat) &
+		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM ? 1 : 0;
+	uint32_t max_size = fs->blksz;
 
 	zero_buffer = zalloc(fs->blksz);
 	if (!zero_buffer) {
@@ -529,12 +536,20 @@  restart_read:
 	dir = (struct ext2_dirent *)root_first_block_buffer;
 	totalbytes = 0;
 
+	if (has_metadata_chksum) {
+		tail = (struct ext4_dir_entry_tail *)(root_first_block_buffer + fs->blksz
+						      - sizeof(struct ext4_dir_entry_tail));
+		if (tail->det_reserved_ft != 0xde)
+			printf("Bad dirent tail\n");
+		max_size -= sizeof(struct ext4_dir_entry_tail);
+	}
+
 	while (le16_to_cpu(dir->direntlen) > 0) {
 		unsigned short used_len = ROUND(dir->namelen +
 		    sizeof(struct ext2_dirent), 4);
 
 		/* last entry of block */
-		if (fs->blksz - totalbytes == le16_to_cpu(dir->direntlen)) {
+		if (max_size - totalbytes == le16_to_cpu(dir->direntlen)) {
 
 			/* check if new entry fits */
 			if ((used_len + new_entry_byte_reqd) <=
@@ -608,7 +623,7 @@  restart_read:
 	if (sizeof_void_space)
 		dir->direntlen = cpu_to_le16(sizeof_void_space);
 	else
-		dir->direntlen = cpu_to_le16(fs->blksz - totalbytes);
+		dir->direntlen = cpu_to_le16(max_size - totalbytes);
 
 	dir->namelen = strlen(filename);
 	dir->filetype = file_type;
@@ -616,7 +631,8 @@  restart_read:
 	temp_dir = temp_dir + sizeof(struct ext2_dirent);
 	memcpy(temp_dir, filename, strlen(filename));
 
-	/* update or write  the 1st block of root inode */
+	/* update or write the 1st block of root inode */
+	ext4fs_set_dirent_tail_csum(fs, (uint8_t *)root_first_block_buffer);
 	if (ext4fs_put_metadata(root_first_block_buffer,
 				first_block_no_of_root))
 		goto fail;
@@ -925,6 +941,7 @@  static int unlink_filename(char *filename, unsigned int blknr)
 			/* invalidate dir entry */
 			dir->inode = 0;
 		}
+		ext4fs_set_dirent_tail_csum(fs, (uint8_t *)block_buffer);
 		if (ext4fs_put_metadata(block_buffer, blknr))
 			goto fail;
 		ret = inodeno;
@@ -1101,6 +1118,8 @@  int ext4fs_get_new_inode_no(void)
 		goto fail;
 	int has_gdt_chksum = le32_to_cpu(fs->sb->feature_ro_compat) &
 		EXT4_FEATURE_RO_COMPAT_GDT_CSUM ? 1 : 0;
+	int has_metadata_chksum = le32_to_cpu(fs->sb->feature_ro_compat) &
+		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM ? 1 : 0;
 
 	if (fs->first_pass_ibmap == 0) {
 		for (i = 0; i < fs->no_blkgrp; i++) {
@@ -1112,7 +1131,7 @@  int ext4fs_get_new_inode_no(void)
 				uint16_t bg_flags = ext4fs_bg_get_flags(bgd);
 				uint64_t i_bitmap_blk =
 					ext4fs_bg_get_inode_id(bgd, fs);
-				if (has_gdt_chksum)
+				if (has_gdt_chksum || has_metadata_chksum)
 					bgd->bg_itable_unused = free_inodes;
 				if (bg_flags & EXT4_BG_INODE_UNINIT) {
 					put_ext4(i_bitmap_blk * fs->blksz,
@@ -1131,7 +1150,7 @@  int ext4fs_get_new_inode_no(void)
 							(i * inodes_per_grp);
 				fs->first_pass_ibmap++;
 				ext4fs_bg_free_inodes_dec(bgd, fs);
-				if (has_gdt_chksum)
+				if (has_gdt_chksum || has_metadata_chksum)
 					ext4fs_bg_itable_unused_dec(bgd, fs);
 				ext4fs_sb_free_inodes_dec(fs->sb);
 				status = ext4fs_devread(i_bitmap_blk *
@@ -1187,7 +1206,7 @@  restart:
 			prev_inode_bitmap_index = ibmap_idx;
 		}
 		ext4fs_bg_free_inodes_dec(bgd, fs);
-		if (has_gdt_chksum)
+		if (has_gdt_chksum || has_metadata_chksum)
 			bgd->bg_itable_unused = bgd->free_inodes;
 		ext4fs_sb_free_inodes_dec(fs->sb);
 		goto success;
@@ -1598,7 +1617,7 @@  int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode)
 	struct ext2_sblock *sblock = &data->sblock;
 	struct ext_filesystem *fs = get_fs();
 	int log2blksz = get_fs()->dev_desc->log2blksz;
-	int inodes_per_block, status;
+	int inodes_per_block, status, size;
 	long int blkno;
 	unsigned int blkoff;
 
@@ -1633,9 +1652,9 @@  int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode)
 	free(blkgrp);
 
 	/* Read the inode. */
+	size = min(sizeof(struct ext2_inode), fs->inodesz);
 	status = ext4fs_devread((lbaint_t)blkno << (LOG2_BLOCK_SIZE(data) -
-				log2blksz), blkoff,
-				sizeof(struct ext2_inode), (char *)inode);
+				log2blksz), blkoff, size, (char *)inode);
 	if (status == 0)
 		return 0;
 
@@ -2368,7 +2387,7 @@  int ext4fs_mount(unsigned part_length)
 	struct ext2_data *data;
 	int status;
 	struct ext_filesystem *fs = get_fs();
-	data = zalloc(SUPERBLOCK_SIZE);
+	data = zalloc(sizeof(struct ext2_data));
 	if (!data)
 		return 0;
 
diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h
index beaee9c80b..afcd9a072d 100644
--- a/fs/ext4/ext4_common.h
+++ b/fs/ext4/ext4_common.h
@@ -41,6 +41,8 @@ 
 #define SUPERBLOCK_SIZE	1024
 #define F_FILE			1
 
+#define EXT4_OLD_INODE_SIZE 128
+
 static inline void *zalloc(size_t size)
 {
 	void *p = memalign(ARCH_DMA_MINALIGN, size);
@@ -59,7 +61,6 @@  int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
 
 #if defined(CONFIG_EXT4_WRITE)
 uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n);
-uint16_t ext4fs_checksum_update(unsigned int i);
 int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags);
 int ext4fs_update_parent_dentry(char *filename, int file_type);
 uint32_t ext4fs_get_new_blk_no(void);
@@ -86,5 +87,9 @@  uint64_t ext4fs_sb_get_free_blocks(const struct ext2_sblock *sb);
 void ext4fs_sb_set_free_blocks(struct ext2_sblock *sb, uint64_t free_blocks);
 uint32_t ext4fs_bg_get_free_blocks(const struct ext2_block_group *bg,
 	const struct ext_filesystem *fs);
+void ext4fs_set_superblock_csum(struct ext2_sblock *sb);
+uint32_t ext4_csum(uint32_t crc, const uint8_t *data, unsigned int length);
+void ext4fs_set_journal_superblock_csum(struct journal_superblock_t *sb);
+
 #endif
 #endif
diff --git a/fs/ext4/ext4_journal.c b/fs/ext4/ext4_journal.c
index 1a340b4764..aace78a795 100644
--- a/fs/ext4/ext4_journal.c
+++ b/fs/ext4/ext4_journal.c
@@ -184,6 +184,7 @@  int ext4fs_log_journal(char *journal_buffer, uint32_t blknr)
 int ext4fs_put_metadata(char *metadata_buffer, uint32_t blknr)
 {
 	struct ext_filesystem *fs = get_fs();
+
 	if (!metadata_buffer) {
 		printf("Invalid input arguments %s\n", __func__);
 		return -EINVAL;
@@ -336,6 +337,7 @@  void recover_transaction(int prev_desc_logical_no)
 	int ofs, flags;
 	int i;
 	struct ext3_journal_block_tag *tag;
+	int tag_size = fs->journal_tag_size;
 	char *temp_buff = zalloc(fs->blksz);
 	char *metadata_buff = zalloc(fs->blksz);
 	if (!temp_buff || !metadata_buff)
@@ -353,12 +355,14 @@  void recover_transaction(int prev_desc_logical_no)
 
 	do {
 		tag = (struct ext3_journal_block_tag *)(p_jdb + ofs);
-		ofs += sizeof(struct ext3_journal_block_tag);
+		ofs += tag_size;
 
 		if (ofs > fs->blksz)
 			break;
 
-		flags = be32_to_cpu(tag->flags);
+		flags = be16_to_cpu(tag->flags);
+
+		/* skip uuid */
 		if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID))
 			ofs += 16;
 
@@ -388,6 +392,15 @@  void print_jrnl_status(int recovery_flag)
 		printf("Journal Scan Completed\n");
 }
 
+void ext4fs_set_journal_superblock_csum(struct journal_superblock_t *sb)
+{
+	uint32_t csum;
+
+	sb->s_checksum = 0;
+	csum = ext4_csum(~0, (uint8_t *)sb, sizeof(struct journal_superblock_t));
+	sb->s_checksum = cpu_to_be32(csum);
+}
+
 int ext4fs_check_journal_state(int recovery_flag)
 {
 	int i;
@@ -405,9 +418,7 @@  int ext4fs_check_journal_state(int recovery_flag)
 	char *temp_buff = NULL;
 	char *temp_buff1 = NULL;
 	struct ext_filesystem *fs = get_fs();
-
-	if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
-		return 0;
+	int tag_size = sizeof(struct ext3_journal_block_tag3);
 
 	temp_buff = zalloc(fs->blksz);
 	if (!temp_buff)
@@ -425,6 +436,11 @@  int ext4fs_check_journal_state(int recovery_flag)
 		       temp_buff);
 	jsb = (struct journal_superblock_t *) temp_buff;
 
+	if (be32_to_cpu(jsb->s_header.h_magic) != EXT3_JOURNAL_MAGIC_NUMBER) {
+		printf("Bad ext4 journal magic number\n");
+		return -ENODEV;
+	}
+
 	if (le32_to_cpu(fs->sb->feature_incompat) & EXT3_FEATURE_INCOMPAT_RECOVER) {
 		if (recovery_flag == RECOVER)
 			printf("Recovery required\n");
@@ -437,10 +453,23 @@  int ext4fs_check_journal_state(int recovery_flag)
 	if (be32_to_cpu(jsb->s_start) == 0)
 		goto end;
 
-	if (!(jsb->s_feature_compat &
-				cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM)))
-		jsb->s_feature_compat |=
-				cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM);
+	fs->journal_csum_seed = ext4_csum(~0, jsb->s_uuid, sizeof(jsb->s_uuid));
+
+	if (be32_to_cpu(jsb->s_feature_incompat) & JBD2_FEATURE_INCOMPAT_CSUM_V3) {
+		fs->journal_tag_size = sizeof(struct ext3_journal_block_tag3);
+		fs->journal_csum_version = 3;
+	} else {
+		fs->journal_tag_size = sizeof(struct ext3_journal_block_tag);
+		fs->journal_csum_version = 0;
+		if (be32_to_cpu(jsb->s_feature_compat) & JBD2_FEATURE_COMPAT_CHECKSUM)
+			fs->journal_csum_version = 1;
+		if (be32_to_cpu(jsb->s_feature_incompat) & JBD2_FEATURE_INCOMPAT_CSUM_V2) {
+			fs->journal_tag_size += sizeof(uint16_t);
+			fs->journal_csum_version = 2;
+		}
+		if (!(be32_to_cpu(jsb->s_feature_incompat) & JBD2_FEATURE_INCOMPAT_64BIT))
+			fs->journal_tag_size -= sizeof(uint32_t);
+	}
 
 	i = be32_to_cpu(jsb->s_first);
 	while (1) {
@@ -468,10 +497,11 @@  int ext4fs_check_journal_state(int recovery_flag)
 			do {
 				tag = (struct ext3_journal_block_tag *)
 				    (p_jdb + ofs);
-				ofs += sizeof(struct ext3_journal_block_tag);
+				ofs += tag_size;
 				if (ofs > fs->blksz)
 					break;
-				flags = be32_to_cpu(tag->flags);
+				flags = be16_to_cpu(tag->flags);
+				/* skip uuid */
 				if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID))
 					ofs += 16;
 				i++;
@@ -532,16 +562,15 @@  end:
 		fs->sb->feature_incompat = cpu_to_le32(new_feature_incompat);
 
 		/* Update the super block */
-		put_ext4((uint64_t) (SUPERBLOCK_SIZE),
-			 (struct ext2_sblock *)fs->sb,
-			 (uint32_t) SUPERBLOCK_SIZE);
+		ext4fs_set_superblock_csum(fs->sb);
+		put_ext4(SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE);
 		ext4_read_superblock((char *)fs->sb);
 
 		blknr = read_allocated_block(&inode_journal,
 					 EXT2_JOURNAL_SUPERBLOCK, NULL);
+		ext4fs_set_journal_superblock_csum(jsb);
 		put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
-			 (struct journal_superblock_t *)temp_buff,
-			 (uint32_t) fs->blksz);
+			 jsb, (uint32_t)fs->blksz);
 		ext4fs_free_revoke_blks();
 	}
 	free(temp_buff);
@@ -550,18 +579,58 @@  end:
 	return 0;
 }
 
+static void descriptor_block_csum_set(const uint8_t *block)
+{
+	struct ext_filesystem *fs = get_fs();
+	struct ext3_journal_block_tail *tail;
+	__u32 csum;
+
+	if (fs->journal_csum_version < 2)
+		return;
+
+	tail = (struct ext3_journal_block_tail *)(block + fs->blksz -
+						  sizeof(struct ext3_journal_block_tail));
+	tail->t_checksum = 0;
+	csum = ext4_csum(fs->journal_csum_seed, block, fs->blksz);
+	tail->t_checksum = cpu_to_be32(csum);
+}
+
+static void block_tag_csum_set(struct ext3_journal_block_tag3 *tag, const uint8_t *data,
+			       uint32_t sequence)
+{
+	struct ext_filesystem *fs = get_fs();
+	struct ext3_journal_block_tag *tag2 = (struct ext3_journal_block_tag *)tag;
+	uint32_t crc32;
+	__be32 seq;
+
+	if (fs->journal_csum_version < 2)
+		return;
+
+	seq = cpu_to_be32(sequence);
+	crc32 = ext4_csum(fs->journal_csum_seed, (uint8_t *)&seq, sizeof(seq));
+	crc32 = ext4_csum(crc32, data, fs->blksz);
+
+	if (fs->journal_csum_version == 3)
+		tag->checksum = cpu_to_be32(crc32);
+	else
+		tag2->checksum = cpu_to_be16(crc32 & 0xffff);
+}
+
 static void update_descriptor_block(long int blknr)
 {
 	int i;
 	long int jsb_blknr;
 	struct journal_header_t jdb;
-	struct ext3_journal_block_tag tag;
+	struct ext3_journal_block_tag3 tag;
 	struct ext2_inode inode_journal;
 	struct journal_superblock_t *jsb = NULL;
 	char *buf = NULL;
 	char *temp = NULL;
 	struct ext_filesystem *fs = get_fs();
 	char *temp_buff = zalloc(fs->blksz);
+	int tag_size = fs->journal_tag_size;
+	uint32_t sequence;
+
 	if (!temp_buff)
 		return;
 
@@ -575,6 +644,9 @@  static void update_descriptor_block(long int blknr)
 	jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_DESCRIPTOR_BLOCK);
 	jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER);
 	jdb.h_sequence = jsb->s_sequence;
+
+	sequence = be32_to_cpu(jsb->s_sequence);
+
 	buf = zalloc(fs->blksz);
 	if (!buf) {
 		free(temp_buff);
@@ -589,16 +661,19 @@  static void update_descriptor_block(long int blknr)
 			break;
 
 		tag.block = cpu_to_be32(journal_ptr[i]->blknr);
-		tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_SAME_UUID);
-		memcpy(temp, &tag, sizeof(struct ext3_journal_block_tag));
-		temp = temp + sizeof(struct ext3_journal_block_tag);
+		tag.flags = cpu_to_be16(EXT3_JOURNAL_FLAG_SAME_UUID);
+		block_tag_csum_set(&tag, (uint8_t *)journal_ptr[i]->buf, sequence);
+		memcpy(temp, &tag, tag_size);
+		temp = temp + tag_size;
 	}
 
 	tag.block = cpu_to_be32(journal_ptr[--i]->blknr);
-	tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_LAST_TAG);
-	memcpy(temp - sizeof(struct ext3_journal_block_tag), &tag,
-	       sizeof(struct ext3_journal_block_tag));
-	put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), buf, (uint32_t) fs->blksz);
+	tag.flags = cpu_to_be16(EXT3_JOURNAL_FLAG_LAST_TAG);
+	block_tag_csum_set(&tag, (uint8_t *)journal_ptr[i]->buf, sequence);
+	memcpy(temp - tag_size, &tag, tag_size);
+
+	descriptor_block_csum_set((uint8_t *)buf);
+	put_ext4((uint64_t)((uint64_t)blknr * (uint64_t)fs->blksz), buf, fs->blksz);
 
 	free(temp_buff);
 	free(buf);
@@ -606,7 +681,7 @@  static void update_descriptor_block(long int blknr)
 
 static void update_commit_block(long int blknr)
 {
-	struct journal_header_t jdb;
+	struct ext3_journal_commit_header jdb;
 	struct ext_filesystem *fs = get_fs();
 	char *buf = NULL;
 	struct ext2_inode inode_journal;
@@ -632,7 +707,15 @@  static void update_commit_block(long int blknr)
 		free(temp_buff);
 		return;
 	}
-	memcpy(buf, &jdb, sizeof(struct journal_header_t));
+
+	if (fs->journal_csum_version > 1) {
+		uint32_t csum = ext4_csum(fs->journal_csum_seed, (uint8_t *)temp_buff, fs->blksz);
+
+		jdb.h_chksum[0] = cpu_to_be32(csum);
+	}
+
+	memcpy(buf, &jdb, sizeof(struct ext3_journal_commit_header));
+
 	put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), buf, (uint32_t) fs->blksz);
 
 	free(temp_buff);
diff --git a/fs/ext4/ext4_journal.h b/fs/ext4/ext4_journal.h
index 43fb8e7664..9bdbf66971 100644
--- a/fs/ext4/ext4_journal.h
+++ b/fs/ext4/ext4_journal.h
@@ -22,7 +22,6 @@ 
 #define EXT2_JOURNAL_INO		8	/* Journal inode */
 #define EXT2_JOURNAL_SUPERBLOCK	0	/* Journal  Superblock number */
 
-#define JBD2_FEATURE_COMPAT_CHECKSUM	0x00000001
 #define EXT3_JOURNAL_MAGIC_NUMBER	0xc03b3998U
 #define TRANSACTION_RUNNING		1
 #define TRANSACTION_COMPLETE		0
@@ -37,6 +36,14 @@ 
 #define EXT3_JOURNAL_FLAG_DELETED	4
 #define EXT3_JOURNAL_FLAG_LAST_TAG	8
 
+#define JBD2_FEATURE_COMPAT_CHECKSUM		0x00000001
+
+#define JBD2_FEATURE_INCOMPAT_REVOKE		0x00000001
+#define JBD2_FEATURE_INCOMPAT_64BIT		0x00000002
+#define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT	0x00000004
+#define JBD2_FEATURE_INCOMPAT_CSUM_V2		0x00000008
+#define JBD2_FEATURE_INCOMPAT_CSUM_V3		0x00000010
+
 /* Maximum entries in 1 journal transaction */
 #define MAX_JOURNAL_ENTRIES 100
 struct journal_log {
@@ -90,16 +97,45 @@  struct journal_superblock_t {
 	__be32 s_max_trans_data;	/* Limit of data blocks per trans. */
 
 	/* 0x0050 */
-	__be32 s_padding[44];
+	__u8 s_checksum_type;	/* checksum type */
+	__u8 s_padding2[3];
+	__u32 s_padding[42];
+	__be32 s_checksum;	/* crc32c(superblock) */
 
 	/* 0x0100 */
 	__u8 s_users[16 * 48];	/* ids of all fs'es sharing the log */
 	/* 0x0400 */
 } ;
 
+struct ext3_journal_commit_header {
+	__be32 h_magic;
+	__be32 h_blocktype;
+	__be32 h_sequence;
+	unsigned char h_chksum_type;
+	unsigned char h_chksum_size;
+	unsigned char h_padding[2];
+	__be32 h_chksum[8];
+	__be64 h_commit_sec;
+	__be32 h_commit_nsec;
+};
+
 struct ext3_journal_block_tag {
+	__be32 block;
+	__be16 checksum;	/* truncated crc32c(uuid+seq+block) */
+	__be16 flags;
+	__be32 blocknr_high;	/* only used when INCOMPAT_64BIT is set */
+};
+
+/* Use if FEATURE_INCOMPAT_CSUM_V3 is set */
+struct ext3_journal_block_tag3 {
 	__be32 block;
 	__be32 flags;
+	__be32 blocknr_high;
+	__be32 checksum;	/* crc32c(uuid+seq+block) */
+};
+
+struct ext3_journal_block_tail {
+	__be32 t_checksum;	/* crc32c(uuid+descr_block) */
 };
 
 struct journal_revoke_header_t {
diff --git a/fs/ext4/ext4_write.c b/fs/ext4/ext4_write.c
index f22af45d1b..fc248c0bc2 100644
--- a/fs/ext4/ext4_write.c
+++ b/fs/ext4/ext4_write.c
@@ -30,6 +30,7 @@ 
 #include <linux/stat.h>
 #include <div64.h>
 #include "ext4_common.h"
+#include <u-boot/crc.h>
 
 static inline void ext4fs_sb_free_inodes_inc(struct ext2_sblock *sb)
 {
@@ -67,6 +68,139 @@  static inline void ext4fs_bg_free_blocks_inc
 		bg->free_blocks_high = cpu_to_le16(free_blocks >> 16);
 }
 
+uint32_t ext4_csum(uint32_t crc, const uint8_t *data, unsigned int length)
+{
+	static uint32_t table[256];
+	static int init;
+
+	if (!init) {
+		crc32c_init(table, 0x82f63b78);
+		init = 1;
+	}
+
+	return crc32c_cal(crc, (const char *)data, length, table);
+}
+
+void ext4fs_set_superblock_csum(struct ext2_sblock *sb)
+{
+	int offset;
+
+	if (!(le32_to_cpu(sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return;
+
+	offset = offsetof(struct ext2_sblock, checksum);
+	sb->checksum = cpu_to_le32(ext4_csum(~0, (uint8_t *)sb, offset));
+}
+
+static uint32_t ext4_inode_csum(struct ext2_inode *inode, unsigned int inode_no)
+{
+	struct ext_filesystem *fs = get_fs();
+	uint32_t crc32;
+	uint16_t dummy_csum = 0;
+	unsigned int dummy_size = sizeof(dummy_csum);
+	int offset = offsetof(struct ext2_inode, checksum_lo);
+	__le32 inum = cpu_to_le32(inode_no);
+	__le32 gen = inode->generation;
+	uint32_t inode_seed;
+
+	inode_seed = ext4_csum(fs->csum_seed, (uint8_t *)&inum, sizeof(inum));
+	inode_seed = ext4_csum(inode_seed, (uint8_t *)&gen, sizeof(gen));
+
+	crc32 = ext4_csum(inode_seed, (uint8_t *)inode, offset);
+	crc32 = ext4_csum(crc32, (uint8_t *)&dummy_csum, dummy_size);
+	offset += dummy_size;
+	crc32 = ext4_csum(crc32, (uint8_t *)inode + offset, EXT4_OLD_INODE_SIZE - offset);
+
+	if (fs->inodesz > EXT4_OLD_INODE_SIZE) {
+		uint16_t extra_size;
+
+		offset = offsetof(struct ext2_inode, checksum_hi);
+		crc32 = ext4_csum(crc32, (uint8_t *)inode + EXT4_OLD_INODE_SIZE,
+				  offset - EXT4_OLD_INODE_SIZE);
+		extra_size = le16_to_cpu(inode->extra_isize);
+		if (extra_size + EXT4_OLD_INODE_SIZE >=
+		    offsetof(struct ext2_inode, checksum_hi) + sizeof(inode->checksum_hi)) {
+			crc32 = ext4_csum(crc32, (uint8_t *)&dummy_csum, dummy_size);
+			offset += dummy_size;
+		}
+		crc32 = ext4_csum(crc32, (uint8_t *)inode + offset, fs->inodesz - offset);
+	}
+
+	return crc32;
+}
+
+static void ext4fs_set_inode_csum(struct ext2_inode *inode, unsigned int inode_no)
+
+{
+	struct ext_filesystem *fs = get_fs();
+	uint32_t crc32;
+
+	if (!(le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		return;
+
+	crc32 = ext4_inode_csum(inode, inode_no);
+	inode->checksum_lo = cpu_to_le16(crc32 & 0xffff);
+
+	if (fs->inodesz > EXT4_OLD_INODE_SIZE) {
+		uint16_t extra_size = le16_to_cpu(inode->extra_isize);
+
+		if (extra_size + EXT4_OLD_INODE_SIZE >=
+		    offsetof(struct ext2_inode, checksum_hi) + sizeof(inode->checksum_hi))
+			inode->checksum_hi = cpu_to_le16(crc32 >> 16);
+	}
+}
+
+static void ext4fs_set_group_descriptor_csum(uint32_t i)
+{
+	struct ext_filesystem *fs = get_fs();
+	struct ext2_block_group *desc = ext4fs_get_group_descriptor(fs, i);
+	__le32 le32_i = cpu_to_le32(i);
+	int offset = offsetof(struct ext2_block_group, bg_checksum);
+	uint16_t crc = 0;
+
+	if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+		uint32_t crc32;
+		uint16_t dummy_csum = 0;
+		int sz;
+
+		/* inode bitmap */
+		sz = fs->sb->inodes_per_group / 8;
+		crc32 = ext4_csum(fs->csum_seed, fs->inode_bmaps[i], sz);
+		desc->bg_inode_id_csum = cpu_to_le16(crc32 & 0xFFFF);
+		if (fs->gdsize >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+			desc->bg_inode_id_csum_high = cpu_to_le16(crc32 >> 16);
+
+		/* block bitmap */
+		sz = fs->sb->fragments_per_group / 8;
+		crc32 = ext4_csum(fs->csum_seed, fs->blk_bmaps[i], sz);
+		desc->bg_block_id_csum = cpu_to_le16(crc32 & 0xFFFF);
+		if (fs->gdsize >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END)
+			desc->bg_block_id_csum_high = cpu_to_le16(crc32 >> 16);
+
+		crc32 = ext4_csum(fs->csum_seed, (uint8_t *)&le32_i, sizeof(le32_i));
+		crc32 = ext4_csum(crc32, (uint8_t *)desc, offset);
+		crc32 = ext4_csum(crc32, (uint8_t *)&dummy_csum, sizeof(dummy_csum));
+		offset += sizeof(dummy_csum);
+		if (offset < fs->gdsize) {
+			crc32 = ext4_csum(crc32, (uint8_t *)desc + offset,
+					  fs->gdsize - offset);
+		}
+		crc = crc32 & 0xffff;
+	} else if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
+		crc = ext2fs_crc16(~0, fs->sb->unique_id,
+				   sizeof(fs->sb->unique_id));
+		crc = ext2fs_crc16(crc, &le32_i, sizeof(le32_i));
+		crc = ext2fs_crc16(crc, desc, offset);
+		offset += sizeof(desc->bg_checksum);	/* skip checksum */
+		assert(offset == sizeof(*desc));
+		if (offset < fs->gdsize) {
+			crc = ext2fs_crc16(crc, (__u8 *)desc + offset,
+					   fs->gdsize - offset);
+		}
+	}
+	desc->bg_checksum = cpu_to_le16(crc);
+}
+
 static void ext4fs_update(void)
 {
 	short i;
@@ -74,14 +208,14 @@  static void ext4fs_update(void)
 	struct ext_filesystem *fs = get_fs();
 	struct ext2_block_group *bgd = NULL;
 
-	/* update  super block */
-	put_ext4((uint64_t)(SUPERBLOCK_SIZE),
-		 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
+	/* update super block */
+	ext4fs_set_superblock_csum(fs->sb);
+	put_ext4(SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE);
 
 	/* update block bitmaps */
 	for (i = 0; i < fs->no_blkgrp; i++) {
 		bgd = ext4fs_get_group_descriptor(fs, i);
-		bgd->bg_checksum = cpu_to_le16(ext4fs_checksum_update(i));
+		ext4fs_set_group_descriptor_csum(i);
 		uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
 		put_ext4(b_bitmap_blk * fs->blksz,
 			 fs->blk_bmaps[i], fs->blksz);
@@ -613,6 +747,11 @@  int ext4fs_init(void)
 	if (!ext4_read_superblock((char *)fs->sb))
 		goto fail;
 
+	if (le32_to_cpu(fs->sb->feature_incompat) & EXT4_FEATURE_INCOMPAT_CSUM_SEED)
+		fs->csum_seed = le32_to_cpu(fs->sb->checksum_seed);
+	else if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+		fs->csum_seed = ext4_csum(~0, fs->sb->unique_id, sizeof(fs->sb->unique_id));
+
 	/* init journal */
 	if (ext4fs_init_journal())
 		goto fail;
@@ -713,6 +852,7 @@  void ext4fs_deinit(void)
 			       temp_buff);
 		jsb = (struct journal_superblock_t *)temp_buff;
 		jsb->s_start = 0;
+		ext4fs_set_journal_superblock_csum(jsb);
 		put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
 			 (struct journal_superblock_t *)temp_buff, fs->blksz);
 		free(temp_buff);
@@ -724,8 +864,8 @@  void ext4fs_deinit(void)
 	new_feature_incompat = le32_to_cpu(fs->sb->feature_incompat);
 	new_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
 	fs->sb->feature_incompat = cpu_to_le32(new_feature_incompat);
-	put_ext4((uint64_t)(SUPERBLOCK_SIZE),
-		 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
+	ext4fs_set_superblock_csum(fs->sb);
+	put_ext4(SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE);
 	free(fs->sb);
 	fs->sb = NULL;
 
@@ -881,17 +1021,15 @@  int ext4fs_write(const char *fname, const char *buffer,
 		return -1;
 	}
 
-	if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
-		printf("Unsupported feature metadata_csum found, not writing.\n");
-		return -1;
-	}
-
 	inodes_per_block = fs->blksz / fs->inodesz;
 	parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE);
 	if (parent_inodeno == -1)
 		goto fail;
 	if (ext4fs_iget(parent_inodeno, g_parent_inode))
 		goto fail;
+
+	g_parent_inode_no = parent_inodeno;
+
 	/* do not mess up a directory using hash trees */
 	if (le32_to_cpu(g_parent_inode->flags) & EXT4_INDEX_FL) {
 		printf("hash tree directory\n");
@@ -963,6 +1101,8 @@  int ext4fs_write(const char *fname, const char *buffer,
 	file_inode->blockcnt = cpu_to_le32((blks_reqd_for_file * fs->blksz) >>
 					   LOG2_SECTOR_SIZE);
 
+	ext4fs_set_inode_csum(file_inode, inodeno);
+
 	temp_ptr = zalloc(fs->blksz);
 	if (!temp_ptr)
 		goto fail;
diff --git a/include/ext4fs.h b/include/ext4fs.h
index cb5d9cc0a5..5485388166 100644
--- a/include/ext4fs.h
+++ b/include/ext4fs.h
@@ -37,6 +37,7 @@  struct disk_partition;
 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
 #define EXT4_FEATURE_INCOMPAT_EXTENTS	0x0040
 #define EXT4_FEATURE_INCOMPAT_64BIT	0x0080
+#define EXT4_FEATURE_INCOMPAT_CSUM_SEED	0x2000
 #define EXT4_INDIRECT_BLOCKS		12
 
 #define EXT4_BG_INODE_UNINIT		0x0001
@@ -117,6 +118,11 @@  struct ext_filesystem {
 
 	/* Block Device Descriptor */
 	struct blk_desc *dev_desc;
+
+	uint32_t csum_seed;
+	uint32_t journal_csum_seed;
+	uint32_t journal_tag_size;
+	int journal_csum_version;
 };
 
 struct ext_block_cache {
@@ -130,6 +136,7 @@  extern struct ext2fs_node *ext4fs_file;
 
 #if defined(CONFIG_EXT4_WRITE)
 extern struct ext2_inode *g_parent_inode;
+extern int g_parent_inode_no;
 extern int gd_index;
 extern int gindex;
 
diff --git a/include/ext_common.h b/include/ext_common.h
index bc3324172a..c024b33e56 100644
--- a/include/ext_common.h
+++ b/include/ext_common.h
@@ -97,7 +97,7 @@  struct ext2_sblock {
 	__le32 feature_compatibility;
 	__le32 feature_incompat;
 	__le32 feature_ro_compat;
-	__le32 unique_id[4];
+	uint8_t unique_id[16];
 	char volume_name[16];
 	char last_mounted_on[64];
 	__le32 compression_info;
@@ -116,6 +116,8 @@  struct ext2_sblock {
 	__le32 first_meta_block_group;
 	__le32 mkfs_time;
 	__le32 journal_blocks[17];
+
+	/* 64 bit support */
 	__le32 total_blocks_high;
 	__le32 reserved_blocks_high;
 	__le32 free_blocks_high;
@@ -128,6 +130,43 @@  struct ext2_sblock {
 	__le32 raid_stripe_width;
 	uint8_t log2_groups_per_flex;
 	uint8_t checksum_type;
+	uint8_t encryption_level;
+	uint8_t reserved_pad;
+	__le64 kbytes_written;
+	__le32 snapshot_inum;
+	__le32 snapshot_id;
+	__le64 snapshot_r_blocks_count;
+	__le32 snapshot_list;
+	__le32 error_count;
+	__le32 first_error_time;
+	__le32 first_error_ino;
+	__le64 first_error_block;
+	uint8_t first_error_func[32];
+	__le32 first_error_line;
+	__le32 last_error_time;
+	__le32 last_error_ino;
+	__le32 last_error_line;
+	__le64 last_error_block;
+	uint8_t last_error_func[32];
+	uint8_t mount_opts[64];
+	__le32 usr_quota_inum;
+	__le32 grp_quota_inum;
+	__le32 overhead_clusters;
+	__le32 backup_bgs[2];
+	uint8_t encrypt_algos[4];
+	uint8_t encrypt_pw_salt[16];
+	__le32 lpf_ino;
+	__le32 prj_quota_inum;
+	__le32 checksum_seed;
+	uint8_t wtime_hi;
+	uint8_t mtime_hi;
+	uint8_t mkfs_time_hi;
+	uint8_t lastcheck_hi;
+	uint8_t first_error_time_hi;
+	uint8_t last_error_time_hi;
+	uint8_t pad[2];
+	__le32 reserved[96];
+	__le32 checksum;
 };
 
 struct ext2_block_group {
@@ -157,6 +196,13 @@  struct ext2_block_group {
 	__le32 bg_reserved;
 };
 
+#define EXT4_BG_INODE_BITMAP_CSUM_HI_END	\
+	(offsetof(struct ext2_block_group, bg_inode_id_csum_high) + \
+	 sizeof(__le16))
+#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_END	\
+	(offsetof(struct ext2_block_group, bg_block_id_csum_high) + \
+	 sizeof(__le16))
+
 /* The ext2 inode. */
 struct ext2_inode {
 	__le16 mode;
@@ -181,11 +227,28 @@  struct ext2_inode {
 		char symlink[60];
 		char inline_data[60];
 	} b;
-	__le32 version;
+	__le32 generation;
 	__le32 acl;
 	__le32 size_high;	/* previously dir_acl, but never used */
 	__le32 fragment_addr;
-	__le32 osd2[3];
+
+	__le16 blocks_high; /* were l_i_reserved1 */
+	__le16 file_acl_high;
+	__le16 uid_high;
+	__le16 gid_high;
+	__le16 checksum_lo; /* crc32c(uuid+inum+inode) LE */
+	__le16 reserved;
+
+	/* optional part */
+	__le16 extra_isize;
+	__le16 checksum_hi; /* crc32c(uuid+inum+inode) BE */
+	__le32 ctime_extra;
+	__le32 mtime_extra;
+	__le32 atime_extra;
+	__le32 crtime;
+	__le32 crtime_extra;
+	__le32 version_hi;
+	__le32 projid;
 };
 
 /* The header of an ext2 directory entry. */
@@ -196,6 +259,14 @@  struct ext2_dirent {
 	__u8 filetype;
 };
 
+struct ext4_dir_entry_tail {
+	__le32	det_reserved_zero1;	/* Pretend to be unused */
+	__le16	det_rec_len;		/* 12 */
+	__u8	det_reserved_zero2;	/* Zero name length */
+	__u8	det_reserved_ft;	/* 0xDE, fake file type */
+	__le32	det_checksum;		/* crc32c(uuid+inum+dirblock) */
+};
+
 struct ext2fs_node {
 	struct ext2_data *data;
 	struct ext2_inode inode;
diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py
index 940279651d..847b4fbcdd 100644
--- a/test/py/tests/test_env.py
+++ b/test/py/tests/test_env.py
@@ -417,9 +417,6 @@  def mk_env_ext4(state_test_env):
         try:
             u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent)
             u_boot_utils.run_and_log(c, 'mkfs.ext4 %s' % persistent)
-            sb_content = u_boot_utils.run_and_log(c, 'tune2fs -l %s' % persistent)
-            if 'metadata_csum' in sb_content:
-                u_boot_utils.run_and_log(c, 'tune2fs -O ^metadata_csum %s' % persistent)
         except CalledProcessError:
             call('rm -f %s' % persistent, shell=True)
             raise
diff --git a/test/py/tests/test_fs/conftest.py b/test/py/tests/test_fs/conftest.py
index ec70e8c4ef..272cbb639c 100644
--- a/test/py/tests/test_fs/conftest.py
+++ b/test/py/tests/test_fs/conftest.py
@@ -165,10 +165,6 @@  def mk_fs(config, fs_type, size, id):
             % (fs_img, count), shell=True)
         check_call('mkfs.%s %s %s'
             % (fs_lnxtype, mkfs_opt, fs_img), shell=True)
-        if fs_type == 'ext4':
-            sb_content = check_output('tune2fs -l %s' % fs_img, shell=True).decode()
-            if 'metadata_csum' in sb_content:
-                check_call('tune2fs -O ^metadata_csum %s' % fs_img, shell=True)
         return fs_img
     except CalledProcessError:
         call('rm -f %s' % fs_img, shell=True)