[PATCH] fs: ext4: Add metadata checksums support
Fredrik Hallenberg
megahallon at gmail.com
Fri Feb 12 17:57:47 CET 2021
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 at 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(-)
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)
--
2.27.0
More information about the U-Boot
mailing list