Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F153985662
D13810.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
38 KB
Referenced Files
None
Subscribers
None
D13810.diff
View Options
Index: head/sys/fs/ext2fs/ext2_alloc.c
===================================================================
--- head/sys/fs/ext2fs/ext2_alloc.c
+++ head/sys/fs/ext2fs/ext2_alloc.c
@@ -898,14 +898,22 @@
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) {
/*
* Another thread allocated the last block in this
@@ -1008,6 +1016,7 @@
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_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 @@
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_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 @@
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 @@
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 @@
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 @@
}
fs->e2fs_fmod = 1;
EXT2_UNLOCK(ump);
+ ext2_gd_i_bitmap_csum_set(fs, cg, bp);
bdwrite(bp);
return (0);
}
Index: head/sys/fs/ext2fs/ext2_csum.c
===================================================================
--- head/sys/fs/ext2fs/ext2_csum.c
+++ head/sys/fs/ext2fs/ext2_csum.c
@@ -43,9 +43,576 @@
#include <fs/ext2fs/fs.h>
#include <fs/ext2fs/ext2fs.h>
+#include <fs/ext2fs/ext2_dinode.h>
#include <fs/ext2fs/inode.h>
+#include <fs/ext2fs/ext2_dir.h>
+#include <fs/ext2fs/htree.h>
+#include <fs/ext2fs/ext2_extattr.h>
#include <fs/ext2fs/ext2_extern.h>
+#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 @@
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 @@
"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;
}
}
Index: head/sys/fs/ext2fs/ext2_dir.h
===================================================================
--- head/sys/fs/ext2fs/ext2_dir.h
+++ head/sys/fs/ext2fs/ext2_dir.h
@@ -72,6 +72,20 @@
* 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
*/
Index: head/sys/fs/ext2fs/ext2_extattr.h
===================================================================
--- head/sys/fs/ext2fs/ext2_extattr.h
+++ head/sys/fs/ext2fs/ext2_extattr.h
@@ -59,7 +59,9 @@
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 {
Index: head/sys/fs/ext2fs/ext2_extattr.c
===================================================================
--- head/sys/fs/ext2fs/ext2_extattr.c
+++ head/sys/fs/ext2fs/ext2_extattr.c
@@ -165,6 +165,22 @@
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 @@
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 @@
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 @@
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 @@
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_rehash(header, entry);
+ ext2_extattr_blk_csum_set(ip, bp);
return (bwrite(bp));
}
@@ -1177,6 +1193,7 @@
}
ext2_extattr_rehash(header, entry);
+ ext2_extattr_blk_csum_set(ip, bp);
return (bwrite(bp));
}
@@ -1207,7 +1224,8 @@
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);
Index: head/sys/fs/ext2fs/ext2_extents.h
===================================================================
--- head/sys/fs/ext2fs/ext2_extents.h
+++ head/sys/fs/ext2fs/ext2_extents.h
@@ -43,6 +43,13 @@
#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.
*/
struct ext4_extent {
Index: head/sys/fs/ext2fs/ext2_extents.c
===================================================================
--- head/sys/fs/ext2fs/ext2_extents.c
+++ head/sys/fs/ext2fs/ext2_extents.c
@@ -425,9 +425,11 @@
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 @@
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 @@
neh->eh_ecount = neh->eh_ecount + m;
}
+ ext2_extent_blk_csum_set(ip, bp->b_data);
bwrite(bp);
bp = NULL;
@@ -838,6 +842,7 @@
neh->eh_ecount = neh->eh_ecount + m;
}
+ ext2_extent_blk_csum_set(ip, bp->b_data);
bwrite(bp);
bp = NULL;
@@ -905,6 +910,7 @@
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;
Index: head/sys/fs/ext2fs/ext2_extern.h
===================================================================
--- head/sys/fs/ext2fs/ext2_extern.h
+++ head/sys/fs/ext2fs/ext2_extern.h
@@ -66,7 +66,7 @@
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_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.
Index: head/sys/fs/ext2fs/ext2_inode_cnv.c
===================================================================
--- head/sys/fs/ext2fs/ext2_inode_cnv.c
+++ head/sys/fs/ext2fs/ext2_inode_cnv.c
@@ -34,6 +34,7 @@
#include <sys/stat.h>
#include <sys/vnode.h>
+#include <fs/ext2fs/ext2fs.h>
#include <fs/ext2fs/fs.h>
#include <fs/ext2fs/inode.h>
#include <fs/ext2fs/ext2fs.h>
@@ -86,9 +87,11 @@
/*
* 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 @@
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);
}
/*
@@ -190,6 +198,9 @@
ei->e2di_gid_high = ip->i_gid >> 16 & 0xffff;
memcpy(ei->e2di_blocks, ip->i_data, sizeof(ei->e2di_blocks));
+
+ /* Set inode csum */
+ ext2_ei_csum_set(ip, ei);
return (0);
}
Index: head/sys/fs/ext2fs/ext2_lookup.c
===================================================================
--- head/sys/fs/ext2fs/ext2_lookup.c
+++ head/sys/fs/ext2fs/ext2_lookup.c
@@ -63,6 +63,7 @@
#include <fs/ext2fs/ext2_dinode.h>
#include <fs/ext2fs/ext2_dir.h>
#include <fs/ext2fs/ext2_extern.h>
+#include <fs/ext2fs/fs.h>
#ifdef INVARIANTS
static int dirchk = 1;
@@ -866,8 +867,7 @@
{
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 @@
*/
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 @@
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 @@
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 @@
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);
Index: head/sys/fs/ext2fs/ext2_subr.c
===================================================================
--- head/sys/fs/ext2fs/ext2_subr.c
+++ head/sys/fs/ext2fs/ext2_subr.c
@@ -79,6 +79,11 @@
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);
Index: head/sys/fs/ext2fs/ext2_vfsops.c
===================================================================
--- head/sys/fs/ext2fs/ext2_vfsops.c
+++ head/sys/fs/ext2fs/ext2_vfsops.c
@@ -372,6 +372,12 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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++) {
Index: head/sys/fs/ext2fs/ext2_vnops.c
===================================================================
--- head/sys/fs/ext2fs/ext2_vnops.c
+++ head/sys/fs/ext2fs/ext2_vnops.c
@@ -784,7 +784,7 @@
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 @@
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 @@
cache_purge(fdvp);
}
}
+ free(dirbuf, M_TEMP);
}
error = ext2_dirremove(fdvp, fcnp);
if (!error) {
@@ -1274,18 +1283,28 @@
#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 @@
if (error)
goto out;
ip = VTOI(tvp);
+ fs = ip->i_e2fs;
ip->i_gid = dp->i_gid;
#ifdef SUIDDIR
{
@@ -1367,8 +1387,21 @@
#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 @@
} else
*ap->a_vpp = tvp;
out:
+ free(buf, M_TEMP);
return (error);
#undef DIRBLKSIZ
#define DIRBLKSIZ DEV_BSIZE
Index: head/sys/fs/ext2fs/ext2fs.h
===================================================================
--- head/sys/fs/ext2fs/ext2fs.h
+++ head/sys/fs/ext2fs/ext2fs.h
@@ -182,6 +182,7 @@
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 */
@@ -205,6 +206,11 @@
#define E2FS_REV0_INODE_SIZE 128
/*
+ * Metadata checksum algorithm codes
+ */
+#define EXT4_CRC32C_CHKSUM 1
+
+/*
* compatible/incompatible features
*/
#define EXT2F_COMPAT_PREALLOC 0x0001
@@ -323,13 +329,15 @@
#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 )
Index: head/sys/fs/ext2fs/htree.h
===================================================================
--- head/sys/fs/ext2fs/htree.h
+++ head/sys/fs/ext2fs/htree.h
@@ -60,6 +60,14 @@
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;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Apr 26, 6:38 AM (18 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32170050
Default Alt Text
D13810.diff (38 KB)
Attached To
Mode
D13810: Add metadata_csum feature support.
Attached
Detach File
Event Timeline
Log In to Comment