diff --git a/sys/fs/ext2fs/ext2_alloc.c b/sys/fs/ext2fs/ext2_alloc.c index 8da86e63bbb3..f86944e41419 100644 --- a/sys/fs/ext2fs/ext2_alloc.c +++ b/sys/fs/ext2fs/ext2_alloc.c @@ -898,13 +898,21 @@ ext2_alloccg(struct inode *ip, int cg, daddr_t bpref, int size) EXT2_LOCK(ump); return (0); } - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) || + EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { error = ext2_cg_block_bitmap_init(fs, cg, bp); if (error) { brelse(bp); EXT2_LOCK(ump); return (0); } + ext2_gd_b_bitmap_csum_set(fs, cg, bp); + } + error = ext2_gd_b_bitmap_csum_verify(fs, cg, bp); + if (error) { + brelse(bp); + EXT2_LOCK(ump); + return (0); } if (e2fs_gd_get_nbfree(&fs->e2fs_gd[cg]) == 0) { /* @@ -1008,6 +1016,7 @@ ext2_alloccg(struct inode *ip, int cg, daddr_t bpref, int size) e2fs_gd_get_nbfree(&fs->e2fs_gd[cg]) - 1); fs->e2fs_fmod = 1; EXT2_UNLOCK(ump); + ext2_gd_b_bitmap_csum_set(fs, cg, bp); bdwrite(bp); return (((uint64_t)cg) * fs->e2fs->e2fs_fpg + fs->e2fs->e2fs_first_dblock + bno); } @@ -1187,11 +1196,13 @@ ext2_nodealloccg(struct inode *ip, int cg, daddr_t ipref, int mode) EXT2_LOCK(ump); return (0); } - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) || + EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { if (fs->e2fs_gd[cg].ext4bgd_flags & EXT2_BG_INODE_UNINIT) { memset(bp->b_data, 0, fs->e2fs_bsize); fs->e2fs_gd[cg].ext4bgd_flags &= ~EXT2_BG_INODE_UNINIT; } + ext2_gd_i_bitmap_csum_set(fs, cg, bp); error = ext2_zero_inode_table(ip, cg); if (error) { brelse(bp); @@ -1199,6 +1210,12 @@ ext2_nodealloccg(struct inode *ip, int cg, daddr_t ipref, int mode) return (0); } } + error = ext2_gd_i_bitmap_csum_verify(fs, cg, bp); + if (error) { + brelse(bp); + EXT2_LOCK(ump); + return (0); + } if (e2fs_gd_get_nifree(&fs->e2fs_gd[cg]) == 0) { /* * Another thread allocated the last i-node in this @@ -1234,7 +1251,8 @@ ext2_nodealloccg(struct inode *ip, int cg, daddr_t ipref, int mode) EXT2_LOCK(ump); e2fs_gd_set_nifree(&fs->e2fs_gd[cg], e2fs_gd_get_nifree(&fs->e2fs_gd[cg]) - 1); - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) || + EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) e2fs_gd_set_i_unused(&fs->e2fs_gd[cg], e2fs_gd_get_i_unused(&fs->e2fs_gd[cg]) - 1); fs->e2fs->e2fs_ficount--; @@ -1245,6 +1263,7 @@ ext2_nodealloccg(struct inode *ip, int cg, daddr_t ipref, int mode) fs->e2fs_total_dir++; } EXT2_UNLOCK(ump); + ext2_gd_i_bitmap_csum_set(fs, cg, bp); bdwrite(bp); return ((uint64_t)cg * fs->e2fs_ipg + ipref + 1); } @@ -1293,6 +1312,7 @@ ext2_blkfree(struct inode *ip, e4fs_daddr_t bno, long size) e2fs_gd_get_nbfree(&fs->e2fs_gd[cg]) + 1); fs->e2fs_fmod = 1; EXT2_UNLOCK(ump); + ext2_gd_b_bitmap_csum_set(fs, cg, bp); bdwrite(bp); } @@ -1338,7 +1358,8 @@ ext2_vfree(struct vnode *pvp, ino_t ino, int mode) fs->e2fs->e2fs_ficount++; e2fs_gd_set_nifree(&fs->e2fs_gd[cg], e2fs_gd_get_nifree(&fs->e2fs_gd[cg]) + 1); - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) || + EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) e2fs_gd_set_i_unused(&fs->e2fs_gd[cg], e2fs_gd_get_i_unused(&fs->e2fs_gd[cg]) + 1); if ((mode & IFMT) == IFDIR) { @@ -1348,6 +1369,7 @@ ext2_vfree(struct vnode *pvp, ino_t ino, int mode) } fs->e2fs_fmod = 1; EXT2_UNLOCK(ump); + ext2_gd_i_bitmap_csum_set(fs, cg, bp); bdwrite(bp); return (0); } diff --git a/sys/fs/ext2fs/ext2_csum.c b/sys/fs/ext2fs/ext2_csum.c index 69e2186e580a..db0b307160d9 100644 --- a/sys/fs/ext2fs/ext2_csum.c +++ b/sys/fs/ext2fs/ext2_csum.c @@ -43,9 +43,576 @@ #include #include +#include #include +#include +#include +#include #include +#define EXT2_BG_INODE_BITMAP_CSUM_HI_END \ + (offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \ + sizeof(uint16_t)) + +#define EXT2_INODE_CSUM_HI_EXTRA_END \ + (offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \ + E2FS_REV0_INODE_SIZE) + +#define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \ + (offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \ + sizeof(uint16_t)) + +void +ext2_sb_csum_set_seed(struct m_ext2fs *fs) +{ + + if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED)) + fs->e2fs_csum_seed = fs->e2fs->e4fs_chksum_seed; + else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { + fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid, + sizeof(fs->e2fs->e2fs_uuid)); + } + else + fs->e2fs_csum_seed = 0; +} + +int +ext2_sb_csum_verify(struct m_ext2fs *fs) +{ + + if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) { + printf( +"WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt); + return (EINVAL); + } + if (fs->e2fs->e4fs_sbchksum != + calculate_crc32c(~0, (const char *)fs->e2fs, + offsetof(struct ext2fs, e4fs_sbchksum))) { + printf( +"WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n", + fs->e2fs_fsmnt, fs->e2fs->e4fs_sbchksum, calculate_crc32c(~0, + (const char *)fs->e2fs, offsetof(struct ext2fs, e4fs_sbchksum))); + return (EINVAL); + } + + return (0); +} + +void +ext2_sb_csum_set(struct m_ext2fs *fs) +{ + + fs->e2fs->e4fs_sbchksum = calculate_crc32c(~0, (const char *)fs->e2fs, + offsetof(struct ext2fs, e4fs_sbchksum)); +} + +static uint32_t +ext2_extattr_blk_csum(struct inode *ip, uint64_t facl, + struct ext2fs_extattr_header *header) +{ + struct m_ext2fs *fs; + uint32_t crc, old_crc; + + fs = ip->i_e2fs; + + old_crc = header->h_checksum; + + header->h_checksum = 0; + crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl, sizeof(facl)); + crc = calculate_crc32c(crc, (uint8_t *)header, fs->e2fs_bsize); + header->h_checksum = old_crc; + + return (crc); +} + +int +ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp) +{ + struct ext2fs_extattr_header *header; + + header = (struct ext2fs_extattr_header *)bp->b_data; + + if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) && + (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) { + printf("WARNING: bad extattr csum detected, ip=%lu - run fsck\n", + (unsigned long)ip->i_number); + return (EIO); + } + + return (0); +} + +void +ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp) +{ + struct ext2fs_extattr_header *header; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return; + + header = (struct ext2fs_extattr_header *)bp->b_data; + header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header); +} + +static struct ext2fs_direct_tail * +ext2_get_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep) +{ + struct ext2fs_direct_tail *tp; + + tp = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize); + if (tp->e2dt_reserved_zero1 || + tp->e2dt_rec_len != sizeof(struct ext2fs_direct_tail) || + tp->e2dt_reserved_zero2 || + tp->e2dt_reserved_ft != EXT2_FT_DIR_CSUM) + return (NULL); + + return (tp); +} + +static uint32_t +ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size) +{ + struct m_ext2fs *fs; + char *buf; + uint32_t inum, gen, crc; + + fs = ip->i_e2fs; + + buf = (char *)ep; + + inum = ip->i_number; + gen = ip->i_gen; + crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); + crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); + crc = calculate_crc32c(crc, (uint8_t *)buf, size); + + return (crc); +} + +static int +ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) +{ + uint32_t calculated; + struct ext2fs_direct_tail *tp; + + tp = ext2_get_dirent_tail(ip, ep); + if (tp == NULL) + return (0); + + calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); + if (calculated != tp->e2dt_checksum) + return (EIO); + + return (0); +} + +static struct ext2fs_htree_count * +ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset) +{ + struct ext2fs_direct_2 *dp; + struct ext2fs_htree_root_info *root; + int count_offset; + + if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs)) + count_offset = 8; + else if (ep->e2d_reclen == 12) { + dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12); + if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12) + return (NULL); + + root = (struct ext2fs_htree_root_info *)(((char *)dp + 12)); + if (root->h_reserved1 || + root->h_info_len != sizeof(struct ext2fs_htree_root_info)) + return (NULL); + + count_offset = 32; + } else + return (NULL); + + if (offset) + *offset = count_offset; + + return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset)); +} + +static uint32_t +ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset, + int count, struct ext2fs_htree_tail *tp) +{ + struct m_ext2fs *fs; + char *buf; + int size; + uint32_t inum, old_csum, gen, crc; + + fs = ip->i_e2fs; + + buf = (char *)ep; + + size = count_offset + (count * sizeof(struct ext2fs_htree_entry)); + old_csum = tp->ht_checksum; + tp->ht_checksum = 0; + + inum = ip->i_number; + gen = ip->i_gen; + crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); + crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); + crc = calculate_crc32c(crc, (uint8_t *)buf, size); + crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail)); + tp->ht_checksum = old_csum; + + return (crc); +} + +static int +ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) +{ + uint32_t calculated; + struct ext2fs_htree_count *cp; + struct ext2fs_htree_tail *tp; + int count_offset, limit, count; + + cp = ext2_get_dx_count(ip, ep, &count_offset); + if (cp == NULL) + return (0); + + limit = cp->h_entries_max; + count = cp->h_entries_num; + if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > + ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) + return (EIO); + + tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); + calculated = ext2_dx_csum(ip, ep, count_offset, count, tp); + + if (tp->ht_checksum != calculated) + return (EIO); + + return (0); +} + +int +ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp) +{ + struct m_ext2fs *fs; + struct ext2fs_direct_2 *ep; + int error = 0; + + fs = ip->i_e2fs; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return (error); + + ep = (struct ext2fs_direct_2 *)bp->b_data; + + if (ext2_get_dirent_tail(ip, ep) != NULL) + error = ext2_dirent_csum_verify(ip, ep); + else if (ext2_get_dx_count(ip, ep, NULL) != NULL) + error = ext2_dx_csum_verify(ip, ep); + + if (error) + printf("WARNING: bad directory csum detected, ip=%lu" + " - run fsck\n", (unsigned long)ip->i_number); + + return (error); +} + +static void +ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) +{ + struct ext2fs_direct_tail *tp; + + tp = ext2_get_dirent_tail(ip, ep); + if (tp == NULL) + return; + + tp->e2dt_checksum = + ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); +} + +static void +ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) +{ + struct ext2fs_htree_count *cp; + struct ext2fs_htree_tail *tp; + int count_offset, limit, count; + + cp = ext2_get_dx_count(ip, ep, &count_offset); + if (cp == NULL) + return; + + limit = cp->h_entries_max; + count = cp->h_entries_num; + if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > + ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) + return; + + tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); + tp->ht_checksum = ext2_dx_csum(ip, ep, count_offset, count, tp); +} + +void +ext2_dir_blk_csum_set_mem(struct inode *ip, char *buf, int size) +{ + struct m_ext2fs *fs; + struct ext2fs_direct_2 *ep; + + fs = ip->i_e2fs; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return; + + ep = (struct ext2fs_direct_2 *)buf; + + if (ext2_htree_has_idx(ip)) { + if (ext2_get_dx_count(ip, ep, NULL) != NULL) + ext2_dx_csum_set(ip, ep); + } else { + if (ext2_get_dirent_tail(ip, ep) != NULL) + ext2_dirent_csum_set(ip, ep); + } +} + +void +ext2_dir_blk_csum_set(struct inode *ip, struct buf *bp) +{ + + ext2_dir_blk_csum_set_mem(ip, bp->b_data, bp->b_bufsize); +} + +static uint32_t +ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp) +{ + struct m_ext2fs *fs; + size_t size; + uint32_t inum, gen, crc; + + fs = ip->i_e2fs; + + size = EXT4_EXTENT_TAIL_OFFSET(ehp) + + offsetof(struct ext4_extent_tail, et_checksum); + + inum = ip->i_number; + gen = ip->i_gen; + crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); + crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); + crc = calculate_crc32c(crc, (uint8_t *)ehp, size); + + return (crc); +} + +int +ext2_extent_blk_csum_verify(struct inode *ip, void *data) +{ + struct m_ext2fs *fs; + struct ext4_extent_header *ehp; + struct ext4_extent_tail *etp; + uint32_t provided, calculated; + + fs = ip->i_e2fs; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return (0); + + ehp = (struct ext4_extent_header *)data; + etp = (struct ext4_extent_tail *)(((char *)ehp) + + EXT4_EXTENT_TAIL_OFFSET(ehp)); + + provided = etp->et_checksum; + calculated = ext2_extent_blk_csum(ip, ehp); + + if (provided != calculated) { + printf("WARNING: bad extent csum detected, ip=%lu - run fsck\n", + (unsigned long)ip->i_number); + return (EIO); + } + + return (0); +} + +void +ext2_extent_blk_csum_set(struct inode *ip, void *data) +{ + struct m_ext2fs *fs; + struct ext4_extent_header *ehp; + struct ext4_extent_tail *etp; + + fs = ip->i_e2fs; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return; + + ehp = (struct ext4_extent_header *)data; + etp = (struct ext4_extent_tail *)(((char *)data) + + EXT4_EXTENT_TAIL_OFFSET(ehp)); + + etp->et_checksum = ext2_extent_blk_csum(ip, + (struct ext4_extent_header *)data); +} + +int +ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) +{ + uint32_t hi, provided, calculated; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return (0); + + provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum; + calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, + fs->e2fs->e2fs_ipg / 8); + if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) { + hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi; + provided |= (hi << 16); + } else + calculated &= 0xFFFF; + + if (provided != calculated) { + printf("WARNING: bad inode bitmap csum detected, " + "cg=%d - run fsck\n", cg); + return (EIO); + } + + return (0); +} + +void +ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) +{ + uint32_t csum; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return; + + csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, + fs->e2fs->e2fs_ipg / 8); + fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF; + if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) + fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16; +} + +int +ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) +{ + uint32_t hi, provided, calculated, size; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return (0); + + size = fs->e2fs_fpg / 8; + provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum; + calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); + if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) { + hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi; + provided |= (hi << 16); + } else + calculated &= 0xFFFF; + + if (provided != calculated) { + printf("WARNING: bad block bitmap csum detected, " + "cg=%d - run fsck\n", cg); + return (EIO); + } + + return (0); +} + +void +ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) +{ + uint32_t csum, size; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return; + + size = fs->e2fs_fpg / 8; + csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); + fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF; + if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) + fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16; +} + +static uint32_t +ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei) +{ + struct m_ext2fs *fs; + uint16_t old_lo, old_hi; + uint32_t inum, gen, crc; + + fs = ip->i_e2fs; + + old_lo = ei->e2di_chksum_lo; + ei->e2di_chksum_lo = 0; + if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE && + ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) { + old_hi = ei->e2di_chksum_hi; + ei->e2di_chksum_hi = 0; + } + + inum = ip->i_number; + gen = ip->i_gen; + + crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); + crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); + crc = calculate_crc32c(crc, (uint8_t *)ei, fs->e2fs->e2fs_inode_size); + + if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && + ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) + ei->e2di_chksum_hi = old_hi; + + return (crc); +} + +int +ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei) +{ + struct m_ext2fs *fs; + const static struct ext2fs_dinode ei_zero; + uint32_t hi, provided, calculated; + + fs = ip->i_e2fs; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return (0); + + /* Check case, when dinode was not initialized */ + if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode))) + return (0); + + provided = ei->e2di_chksum_lo; + calculated = ext2_ei_csum(ip, ei); + + if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && + ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) { + hi = ei->e2di_chksum_hi; + provided |= hi << 16; + } else + calculated &= 0xFFFF; + + if (provided != calculated) + return (EIO); + + return (0); +} + +void +ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei) +{ + struct m_ext2fs *fs; + uint32_t crc; + + fs = ip->i_e2fs; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + return; + + crc = ext2_ei_csum(ip, ei); + + ei->e2di_chksum_lo = crc & 0xFFFF; + if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && + ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) + ei->e2di_chksum_hi = crc >> 16; +} + static uint16_t ext2_crc16(uint16_t crc, const void *buffer, unsigned int len) { @@ -96,11 +663,26 @@ static uint16_t ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd) { size_t offset; - uint16_t crc; + uint32_t csum32; + uint16_t crc, dummy_csum; offset = offsetof(struct ext2_gd, ext4bgd_csum); - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { + csum32 = calculate_crc32c(fs->e2fs_csum_seed, + (uint8_t *)&block_group, sizeof(block_group)); + csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset); + dummy_csum = 0; + csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum, + sizeof(dummy_csum)); + offset += sizeof(dummy_csum); + if (offset < fs->e2fs->e3fs_desc_size) + csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset, + fs->e2fs->e3fs_desc_size - offset); + + crc = csum32 & 0xFFFF; + return (crc); + } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid, sizeof(fs->e2fs->e2fs_uuid)); crc = ext2_crc16(crc, (uint8_t *)&block_group, @@ -130,7 +712,7 @@ ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev) "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n", devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum, ext2_gd_csum(fs, i, &fs->e2fs_gd[i])); - error = EINVAL; + error = EIO; break; } } diff --git a/sys/fs/ext2fs/ext2_dir.h b/sys/fs/ext2fs/ext2_dir.h index 7723481bfc53..11688e19bd02 100644 --- a/sys/fs/ext2fs/ext2_dir.h +++ b/sys/fs/ext2fs/ext2_dir.h @@ -72,6 +72,20 @@ struct ext2fs_direct_2 { * length<=EXT2FS_MAXNAMLEN */ }; +struct ext2fs_direct_tail { + uint32_t e2dt_reserved_zero1; /* pretend to be unused */ + uint16_t e2dt_rec_len; /* 12 */ + uint8_t e2dt_reserved_zero2; /* zero name length */ + uint8_t e2dt_reserved_ft; /* 0xDE, fake file type */ + uint32_t e2dt_checksum; /* crc32c(uuid+inum+dirblock) */ +}; + +#define EXT2_FT_DIR_CSUM 0xDE + +#define EXT2_DIRENT_TAIL(data, blocksize) \ + ((struct ext2fs_direct_tail *)(((char *)(data)) + \ + (blocksize) - sizeof(struct ext2fs_direct_tail))) + /* * Maximal count of links to a file */ diff --git a/sys/fs/ext2fs/ext2_extattr.c b/sys/fs/ext2fs/ext2_extattr.c index 6bb99cec4ab2..943f862678d8 100644 --- a/sys/fs/ext2fs/ext2_extattr.c +++ b/sys/fs/ext2fs/ext2_extattr.c @@ -165,6 +165,22 @@ ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end) return (0); } +static int +ext2_extattr_block_check(struct inode *ip, struct buf *bp) +{ + struct ext2fs_extattr_header *header; + int error; + + header = (struct ext2fs_extattr_header *)bp->b_data; + + error = ext2_extattr_check(EXT2_IFIRST(header), + bp->b_data + bp->b_bufsize); + if (error) + return (error); + + return (ext2_extattr_blk_csum_verify(ip, bp)); +} + int ext2_extattr_inode_list(struct inode *ip, int attrnamespace, struct uio *uio, size_t *size) @@ -267,7 +283,7 @@ ext2_extattr_block_list(struct inode *ip, int attrnamespace, return (EINVAL); } - error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); + error = ext2_extattr_block_check(ip, bp); if (error) { brelse(bp); return (error); @@ -408,7 +424,7 @@ ext2_extattr_block_get(struct inode *ip, int attrnamespace, return (EINVAL); } - error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); + error = ext2_extattr_block_check(ip, bp); if (error) { brelse(bp); return (error); @@ -668,7 +684,7 @@ ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name) return (EINVAL); } - error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); + error = ext2_extattr_block_check(ip, bp); if (error) { brelse(bp); return (error); @@ -1061,8 +1077,7 @@ ext2_extattr_block_set(struct inode *ip, int attrnamespace, return (EINVAL); } - error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), - bp->b_data + bp->b_bufsize); + error = ext2_extattr_block_check(ip, bp); if (error) { brelse(bp); return (error); @@ -1130,6 +1145,7 @@ ext2_extattr_block_set(struct inode *ip, int attrnamespace, } ext2_extattr_rehash(header, entry); + ext2_extattr_blk_csum_set(ip, bp); return (bwrite(bp)); } @@ -1177,6 +1193,7 @@ ext2_extattr_block_set(struct inode *ip, int attrnamespace, } ext2_extattr_rehash(header, entry); + ext2_extattr_blk_csum_set(ip, bp); return (bwrite(bp)); } @@ -1207,7 +1224,8 @@ int ext2_extattr_free(struct inode *ip) return (EINVAL); } - error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); + error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), + bp->b_data + bp->b_bufsize); if (error) { brelse(bp); return (error); diff --git a/sys/fs/ext2fs/ext2_extattr.h b/sys/fs/ext2fs/ext2_extattr.h index 75632b7fd559..1047c8b607b6 100644 --- a/sys/fs/ext2fs/ext2_extattr.h +++ b/sys/fs/ext2fs/ext2_extattr.h @@ -59,7 +59,9 @@ struct ext2fs_extattr_header { int32_t h_refcount; /* reference count */ int32_t h_blocks; /* number of disk blocks used */ int32_t h_hash; /* hash value of all attributes */ - uint32_t h_reserved[4]; /* zero right now */ + int32_t h_checksum; /* crc32c(uuid+id+xattrblock) */ + /* id = inum if refcount=1, blknum otherwise */ + uint32_t h_reserved[3]; /* zero right now */ }; struct ext2fs_extattr_dinode_header { diff --git a/sys/fs/ext2fs/ext2_extents.c b/sys/fs/ext2fs/ext2_extents.c index 7e4ffe839c78..2291b2817bac 100644 --- a/sys/fs/ext2fs/ext2_extents.c +++ b/sys/fs/ext2fs/ext2_extents.c @@ -425,9 +425,11 @@ ext4_ext_find_extent(struct inode *ip, daddr_t block, bqrelse(bp); eh = ext4_ext_block_header(path[ppos].ep_data); - error = ext4_ext_check_header(ip, eh); - if (error) + if (ext4_ext_check_header(ip, eh) || + ext2_extent_blk_csum_verify(ip, path[ppos].ep_data)) { + error = EIO; goto error; + } path[ppos].ep_header = eh; @@ -622,6 +624,7 @@ ext4_ext_dirty(struct inode *ip, struct ext4_extent_path *path) if (!bp) return (EIO); ext4_ext_fill_path_buf(path, bp); + ext2_extent_blk_csum_set(ip, bp->b_data); error = bwrite(bp); } else { ip->i_flag |= IN_CHANGE | IN_UPDATE; @@ -791,6 +794,7 @@ ext4_ext_split(struct inode *ip, struct ext4_extent_path *path, neh->eh_ecount = neh->eh_ecount + m; } + ext2_extent_blk_csum_set(ip, bp->b_data); bwrite(bp); bp = NULL; @@ -838,6 +842,7 @@ ext4_ext_split(struct inode *ip, struct ext4_extent_path *path, neh->eh_ecount = neh->eh_ecount + m; } + ext2_extent_blk_csum_set(ip, bp->b_data); bwrite(bp); bp = NULL; @@ -905,6 +910,7 @@ ext4_ext_grow_indepth(struct inode *ip, struct ext4_extent_path *path, else neh->eh_max = ext4_ext_space_block(ip); + ext2_extent_blk_csum_set(ip, bp->b_data); error = bwrite(bp); if (error) goto out; diff --git a/sys/fs/ext2fs/ext2_extents.h b/sys/fs/ext2fs/ext2_extents.h index 6aa7de151d2e..8a985f286ab8 100644 --- a/sys/fs/ext2fs/ext2_extents.h +++ b/sys/fs/ext2fs/ext2_extents.h @@ -42,6 +42,13 @@ #define EXT4_EXT_CACHE_GAP 1 #define EXT4_EXT_CACHE_IN 2 +/* + * Ext4 extent tail with csum + */ +struct ext4_extent_tail { + uint32_t et_checksum; /* crc32c(uuid+inum+extent_block) */ +}; + /* * Ext4 file system extent on disk. */ diff --git a/sys/fs/ext2fs/ext2_extern.h b/sys/fs/ext2fs/ext2_extern.h index aed99bd1be47..c8a77b6431dc 100644 --- a/sys/fs/ext2fs/ext2_extern.h +++ b/sys/fs/ext2fs/ext2_extern.h @@ -66,7 +66,7 @@ int ext4_bmapext(struct vnode *, int32_t, int64_t *, int *, int *); void ext2_clusteracct(struct m_ext2fs *, char *, int, e4fs_daddr_t, int); void ext2_dirbad(struct inode *ip, doff_t offset, char *how); void ext2_fserr(struct m_ext2fs *, uid_t, char *); -void ext2_ei2i(struct ext2fs_dinode *, struct inode *); +int ext2_ei2i(struct ext2fs_dinode *, struct inode *); int ext2_getlbns(struct vnode *, daddr_t, struct indir *, int *); int ext2_i2ei(struct inode *, struct ext2fs_dinode *); void ext2_itimes(struct vnode *vp); @@ -103,9 +103,25 @@ int ext2_htree_lookup(struct inode *, const char *, int, struct buf **, int ext2_search_dirblock(struct inode *, void *, int *, const char *, int, int *, doff_t *, doff_t *, doff_t *, struct ext2fs_searchslot *); uint32_t e2fs_gd_get_ndirs(struct ext2_gd *gd); -uint64_t e2fs_gd_get_i_tables(struct ext2_gd *gd); -int ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev); -void ext2_gd_csum_set(struct m_ext2fs *fs); +uint64_t e2fs_gd_get_i_tables(struct ext2_gd *); +void ext2_sb_csum_set_seed(struct m_ext2fs *); +int ext2_sb_csum_verify(struct m_ext2fs *); +void ext2_sb_csum_set(struct m_ext2fs *); +int ext2_extattr_blk_csum_verify(struct inode *, struct buf *); +void ext2_extattr_blk_csum_set(struct inode *, struct buf *); +int ext2_dir_blk_csum_verify(struct inode *, struct buf *); +void ext2_dir_blk_csum_set(struct inode *, struct buf *); +void ext2_dir_blk_csum_set_mem(struct inode *, char *, int); +int ext2_extent_blk_csum_verify(struct inode *, void *); +void ext2_extent_blk_csum_set(struct inode *, void *); +int ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *, int, struct buf *); +void ext2_gd_i_bitmap_csum_set(struct m_ext2fs *, int, struct buf *); +int ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *, int, struct buf *); +void ext2_gd_b_bitmap_csum_set(struct m_ext2fs *, int, struct buf *); +int ext2_ei_csum_verify(struct inode *, struct ext2fs_dinode *); +void ext2_ei_csum_set(struct inode *, struct ext2fs_dinode *); +int ext2_gd_csum_verify(struct m_ext2fs *, struct cdev *); +void ext2_gd_csum_set(struct m_ext2fs *); /* Flags to low-level allocation routines. diff --git a/sys/fs/ext2fs/ext2_inode_cnv.c b/sys/fs/ext2fs/ext2_inode_cnv.c index 159d15217257..1951a5186e00 100644 --- a/sys/fs/ext2fs/ext2_inode_cnv.c +++ b/sys/fs/ext2fs/ext2_inode_cnv.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -86,9 +87,11 @@ ext2_print_inode(struct inode *in) /* * raw ext2 inode to inode */ -void +int ext2_ei2i(struct ext2fs_dinode *ei, struct inode *ip) { + const static struct ext2fs_dinode ei_zero; + ip->i_nlink = ei->e2di_nlink; /* * Godmar thinks - if the link count is zero, then the inode is @@ -131,6 +134,11 @@ ext2_ei2i(struct ext2fs_dinode *ei, struct inode *ip) ip->i_gid |= (uint32_t)ei->e2di_gid_high << 16; memcpy(ip->i_data, ei->e2di_blocks, sizeof(ei->e2di_blocks)); + + if (memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode))) + return (ext2_ei_csum_verify(ip, ei)); + + return (0); } /* @@ -191,5 +199,8 @@ ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei) memcpy(ei->e2di_blocks, ip->i_data, sizeof(ei->e2di_blocks)); + /* Set inode csum */ + ext2_ei_csum_set(ip, ei); + return (0); } diff --git a/sys/fs/ext2fs/ext2_lookup.c b/sys/fs/ext2fs/ext2_lookup.c index a62ac72e9ad4..6614fee6f6b1 100644 --- a/sys/fs/ext2fs/ext2_lookup.c +++ b/sys/fs/ext2fs/ext2_lookup.c @@ -63,6 +63,7 @@ #include #include #include +#include #ifdef INVARIANTS static int dirchk = 1; @@ -866,8 +867,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) { struct inode *dp; struct ext2fs_direct_2 newdir; - struct iovec aiov; - struct uio auio; + struct buf *bp; int error, newentrysize; int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize; @@ -917,26 +917,25 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) */ if (dp->i_offset & (DIRBLKSIZ - 1)) panic("ext2_direnter: newblk"); - auio.uio_offset = dp->i_offset; + newdir.e2d_reclen = DIRBLKSIZ; - auio.uio_resid = newentrysize; - aiov.iov_len = newentrysize; - aiov.iov_base = (caddr_t)&newdir; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - auio.uio_rw = UIO_WRITE; - auio.uio_segflg = UIO_SYSSPACE; - auio.uio_td = (struct thread *)0; - error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); - if (DIRBLKSIZ > - VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) - /* XXX should grow with balloc() */ - panic("ext2_direnter: frag size"); - else if (!error) { - dp->i_size = roundup2(dp->i_size, DIRBLKSIZ); - dp->i_flag |= IN_CHANGE; - } - return (error); + + bp = getblk(ip->i_devvp, lblkno(dp->i_e2fs, dp->i_offset), + DIRBLKSIZ, 0, 0, 0); + if (!bp) + return (EIO); + + memcpy(bp->b_data, &newdir, sizeof(struct ext2fs_direct_2)); + + ext2_dir_blk_csum_set(dp, bp); + error = bwrite(bp); + if (error) + return (error); + + dp->i_size = roundup2(dp->i_size, DIRBLKSIZ); + dp->i_flag |= IN_CHANGE; + + return (0); } error = ext2_add_entry(dvp, &newdir); @@ -1028,6 +1027,7 @@ ext2_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry) ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); } bcopy((caddr_t)entry, (caddr_t)ep, (u_int)newentrysize); + ext2_dir_blk_csum_set(dp, bp); if (DOINGASYNC(dvp)) { bdwrite(bp); error = 0; @@ -1085,6 +1085,7 @@ ext2_dirremove(struct vnode *dvp, struct componentname *cnp) else rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen); ep->e2d_reclen += rep->e2d_reclen; + ext2_dir_blk_csum_set(dp, bp); if (DOINGASYNC(dvp) && dp->i_count != 0) bdwrite(bp); else @@ -1115,6 +1116,7 @@ ext2_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp) ep->e2d_type = DTTOFT(IFTODT(ip->i_mode)); else ep->e2d_type = EXT2_FT_UNKNOWN; + ext2_dir_blk_csum_set(dp, bp); error = bwrite(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); diff --git a/sys/fs/ext2fs/ext2_subr.c b/sys/fs/ext2fs/ext2_subr.c index f592f53ace80..572af03fc922 100644 --- a/sys/fs/ext2fs/ext2_subr.c +++ b/sys/fs/ext2fs/ext2_subr.c @@ -79,6 +79,11 @@ ext2_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp) brelse(bp); return (error); } + error = ext2_dir_blk_csum_verify(ip, bp); + if (error != 0) { + brelse(bp); + return (error); + } if (res) *res = (char *)bp->b_data + blkoff(fs, offset); diff --git a/sys/fs/ext2fs/ext2_vfsops.c b/sys/fs/ext2fs/ext2_vfsops.c index c1a93940cd2e..c0bb1dfd2d06 100644 --- a/sys/fs/ext2fs/ext2_vfsops.c +++ b/sys/fs/ext2fs/ext2_vfsops.c @@ -372,6 +372,12 @@ compute_sb_data(struct vnode *devvp, struct ext2fs *es, printf("ext2fs: no space for extra inode timestamps\n"); return (EINVAL); } + /* Check checksum features */ + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) && + EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { + printf("ext2fs: incorrect checksum features combination\n"); + return (EINVAL); + } /* Check for group descriptor size */ if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) && (es->e3fs_desc_size != sizeof(struct ext2_gd))) { @@ -430,8 +436,11 @@ compute_sb_data(struct vnode *devvp, struct ext2fs *es, brelse(bp); bp = NULL; } - /* Verify cg csum */ - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { + /* Precompute checksum seed for all metadata */ + ext2_sb_csum_set_seed(fs); + /* Verfy cg csum */ + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) || + EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { error = ext2_gd_csum_verify(fs, devvp->v_rdev); if (error) return (error); @@ -439,7 +448,7 @@ compute_sb_data(struct vnode *devvp, struct ext2fs *es, /* Initialization for the ext2 Orlov allocator variant. */ fs->e2fs_total_dir = 0; for (i = 0; i < fs->e2fs_gcount; i++) - fs->e2fs_total_dir += fs->e2fs_gd[i].ext2bgd_ndirs; + fs->e2fs_total_dir += e2fs_gd_get_ndirs(&fs->e2fs_gd[i]); if (es->e2fs_rev == E2FS_REV0 || !EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_LARGEFILE)) @@ -459,8 +468,10 @@ compute_sb_data(struct vnode *devvp, struct ext2fs *es, es->e4fs_flags |= E2FS_SIGNED_HASH; #endif } + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + error = ext2_sb_csum_verify(fs); - return (0); + return (error); } /* @@ -993,8 +1004,16 @@ ext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) return (error); } /* convert ext2 inode to dinode */ - ext2_ei2i((struct ext2fs_dinode *)((char *)bp->b_data + EXT2_INODE_SIZE(fs) * - ino_to_fsbo(fs, ino)), ip); + error = ext2_ei2i((struct ext2fs_dinode *)((char *)bp->b_data + + EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ino)), ip); + if (error) { + printf("ext2fs: Bad inode %lu csum - run fsck\n", + (unsigned long)ino); + brelse(bp); + vput(vp); + *vpp = NULL; + return (error); + } ip->i_block_group = ino_to_cg(fs, ino); ip->i_next_alloc_block = 0; ip->i_next_alloc_goal = 0; @@ -1099,6 +1118,9 @@ ext2_sbupdate(struct ext2mount *mp, int waitfor) es->e4fs_fbcount_hi = fs->e2fs_fbcount >> 32; } + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) + ext2_sb_csum_set(fs); + bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0, 0); bcopy((caddr_t)es, bp->b_data, (u_int)sizeof(struct ext2fs)); if (waitfor == MNT_WAIT) @@ -1123,7 +1145,8 @@ ext2_cgupdate(struct ext2mount *mp, int waitfor) allerror = ext2_sbupdate(mp, waitfor); /* Update gd csums */ - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) || + EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) ext2_gd_csum_set(fs); for (i = 0; i < fs->e2fs_gdbcount; i++) { diff --git a/sys/fs/ext2fs/ext2_vnops.c b/sys/fs/ext2fs/ext2_vnops.c index 00607d1df236..07c25b68d4bb 100644 --- a/sys/fs/ext2fs/ext2_vnops.c +++ b/sys/fs/ext2fs/ext2_vnops.c @@ -784,7 +784,7 @@ ext2_rename(struct vop_rename_args *ap) struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; struct inode *ip, *xp, *dp; - struct dirtemplate dirbuf; + struct dirtemplate *dirbuf; int doingdirectory = 0, oldparent = 0, newparent = 0; int error = 0; u_char namlen; @@ -1071,23 +1071,31 @@ ext2_rename(struct vop_rename_args *ap) if (doingdirectory && newparent) { ext2_dec_nlink(dp); dp->i_flag |= IN_CHANGE; - error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf, - sizeof(struct dirtemplate), (off_t)0, + dirbuf = malloc(dp->i_e2fs->e2fs_bsize, M_TEMP, M_WAITOK | M_ZERO); + if (!dirbuf) { + error = ENOMEM; + goto bad; + } + error = vn_rdwr(UIO_READ, fvp, (caddr_t)dirbuf, + ip->i_e2fs->e2fs_bsize, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, tcnp->cn_cred, NOCRED, NULL, NULL); if (error == 0) { /* Like ufs little-endian: */ - namlen = dirbuf.dotdot_type; + namlen = dirbuf->dotdot_type; if (namlen != 2 || - dirbuf.dotdot_name[0] != '.' || - dirbuf.dotdot_name[1] != '.') { + dirbuf->dotdot_name[0] != '.' || + dirbuf->dotdot_name[1] != '.') { ext2_dirbad(xp, (doff_t)12, "rename: mangled dir"); } else { - dirbuf.dotdot_ino = newparent; + dirbuf->dotdot_ino = newparent; + ext2_dir_blk_csum_set_mem(ip, + (char *)dirbuf, + ip->i_e2fs->e2fs_bsize); (void)vn_rdwr(UIO_WRITE, fvp, - (caddr_t)&dirbuf, - sizeof(struct dirtemplate), + (caddr_t)dirbuf, + ip->i_e2fs->e2fs_bsize, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED | IO_SYNC | IO_NOMACCHECK, tcnp->cn_cred, @@ -1095,6 +1103,7 @@ ext2_rename(struct vop_rename_args *ap) cache_purge(fdvp); } } + free(dirbuf, M_TEMP); } error = ext2_dirremove(fdvp, fcnp); if (!error) { @@ -1274,18 +1283,28 @@ ext2_do_posix1e_acl_inheritance_file(struct vnode *dvp, struct vnode *tvp, #endif /* UFS_ACL */ +static void +ext2_init_dirent_tail(struct ext2fs_direct_tail *tp) +{ + memset(tp, 0, sizeof(struct ext2fs_direct_tail)); + tp->e2dt_rec_len = sizeof(struct ext2fs_direct_tail); + tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM; +} + /* * Mkdir system call */ static int ext2_mkdir(struct vop_mkdir_args *ap) { + struct m_ext2fs *fs; struct vnode *dvp = ap->a_dvp; struct vattr *vap = ap->a_vap; struct componentname *cnp = ap->a_cnp; struct inode *ip, *dp; struct vnode *tvp; struct dirtemplate dirtemplate, *dtp; + char *buf = NULL; int error, dmode; #ifdef INVARIANTS @@ -1309,6 +1328,7 @@ ext2_mkdir(struct vop_mkdir_args *ap) if (error) goto out; ip = VTOI(tvp); + fs = ip->i_e2fs; ip->i_gid = dp->i_gid; #ifdef SUIDDIR { @@ -1367,8 +1387,21 @@ ext2_mkdir(struct vop_mkdir_args *ap) #undef DIRBLKSIZ #define DIRBLKSIZ VTOI(dvp)->i_e2fs->e2fs_bsize dirtemplate.dotdot_reclen = DIRBLKSIZ - 12; - error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate, - sizeof(dirtemplate), (off_t)0, UIO_SYSSPACE, + buf = malloc(DIRBLKSIZ, M_TEMP, M_WAITOK | M_ZERO); + if (!buf) { + error = ENOMEM; + ext2_dec_nlink(dp); + dp->i_flag |= IN_CHANGE; + goto bad; + } + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { + dirtemplate.dotdot_reclen -= sizeof(struct ext2fs_direct_tail); + ext2_init_dirent_tail(EXT2_DIRENT_TAIL(buf, DIRBLKSIZ)); + } + memcpy(buf, &dirtemplate, sizeof(dirtemplate)); + ext2_dir_blk_csum_set_mem(ip, buf, DIRBLKSIZ); + error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)buf, + DIRBLKSIZ, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED | IO_SYNC | IO_NOMACCHECK, cnp->cn_cred, NOCRED, NULL, NULL); if (error) { @@ -1412,6 +1445,7 @@ ext2_mkdir(struct vop_mkdir_args *ap) } else *ap->a_vpp = tvp; out: + free(buf, M_TEMP); return (error); #undef DIRBLKSIZ #define DIRBLKSIZ DEV_BSIZE diff --git a/sys/fs/ext2fs/ext2fs.h b/sys/fs/ext2fs/ext2fs.h index d9b7b7f72e66..e73e0e803cea 100644 --- a/sys/fs/ext2fs/ext2fs.h +++ b/sys/fs/ext2fs/ext2fs.h @@ -182,6 +182,7 @@ struct m_ext2fs { int32_t *e2fs_maxcluster; /* max cluster in each cyl group */ struct csum *e2fs_clustersum; /* cluster summary in each cyl group */ int32_t e2fs_uhash; /* 3 if hash should be signed, 0 if not */ + uint32_t e2fs_csum_seed; /* sb checksum seed */ }; /* cluster summary information */ @@ -204,6 +205,11 @@ struct csum { #define E2FS_REV0_INODE_SIZE 128 +/* + * Metadata checksum algorithm codes + */ +#define EXT4_CRC32C_CHKSUM 1 + /* * compatible/incompatible features */ @@ -323,13 +329,15 @@ static const struct ext2_feature incompat[] = { #define EXT2F_ROCOMPAT_SUPP (EXT2F_ROCOMPAT_SPARSESUPER | \ EXT2F_ROCOMPAT_LARGEFILE | \ EXT2F_ROCOMPAT_GDT_CSUM | \ + EXT2F_ROCOMPAT_METADATA_CKSUM | \ EXT2F_ROCOMPAT_DIR_NLINK | \ EXT2F_ROCOMPAT_HUGE_FILE | \ EXT2F_ROCOMPAT_EXTRA_ISIZE) #define EXT2F_INCOMPAT_SUPP (EXT2F_INCOMPAT_FTYPE | \ - EXT2F_INCOMPAT_64BIT) -#define EXT4F_RO_INCOMPAT_SUPP (EXT2F_INCOMPAT_EXTENTS | \ - EXT2F_INCOMPAT_RECOVER | \ + EXT2F_INCOMPAT_EXTENTS | \ + EXT2F_INCOMPAT_64BIT | \ + EXT2F_INCOMPAT_CSUM_SEED) +#define EXT4F_RO_INCOMPAT_SUPP (EXT2F_INCOMPAT_RECOVER | \ EXT2F_INCOMPAT_FLEX_BG | \ EXT2F_INCOMPAT_META_BG ) diff --git a/sys/fs/ext2fs/htree.h b/sys/fs/ext2fs/htree.h index 5a4bc059cb27..e5220c09918f 100644 --- a/sys/fs/ext2fs/htree.h +++ b/sys/fs/ext2fs/htree.h @@ -60,6 +60,14 @@ struct ext2fs_htree_entry { uint32_t h_blk; }; +/* + * This goes at the end of each htree block. + */ +struct ext2fs_htree_tail { + uint32_t ht_reserved; + uint32_t ht_checksum; /* crc32c(uuid+inum+dirblock) */ +}; + struct ext2fs_htree_root_info { uint32_t h_reserved1; uint8_t h_hash_version;