From mboxrd@z Thu Jan 1 00:00:00 1970 From: Fredrik Hallenberg Date: Fri, 12 Feb 2021 17:57:47 +0100 Subject: [PATCH] fs: ext4: Add metadata checksums support Message-ID: <20210212165747.207979-1-megahallon@gmail.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 --- 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 #include #include "ext4_common.h" +#include 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