svn commit: r327977 - head/sys/fs/ext2fs

Fedor Uporov fsu at FreeBSD.org
Sun Jan 14 20:46:42 UTC 2018


Author: fsu
Date: Sun Jan 14 20:46:39 2018
New Revision: 327977
URL: https://svnweb.freebsd.org/changeset/base/327977

Log:
  Add metadata_csum feature support.
  
  Reviewed by:   pfg (mentor)
  Approved by:   pfg (mentor)
  MFC after:     6 months
  
  Differential Revision:    https://reviews.freebsd.org/D13810

Modified:
  head/sys/fs/ext2fs/ext2_alloc.c
  head/sys/fs/ext2fs/ext2_csum.c
  head/sys/fs/ext2fs/ext2_dir.h
  head/sys/fs/ext2fs/ext2_extattr.c
  head/sys/fs/ext2fs/ext2_extattr.h
  head/sys/fs/ext2fs/ext2_extents.c
  head/sys/fs/ext2fs/ext2_extents.h
  head/sys/fs/ext2fs/ext2_extern.h
  head/sys/fs/ext2fs/ext2_inode_cnv.c
  head/sys/fs/ext2fs/ext2_lookup.c
  head/sys/fs/ext2fs/ext2_subr.c
  head/sys/fs/ext2fs/ext2_vfsops.c
  head/sys/fs/ext2fs/ext2_vnops.c
  head/sys/fs/ext2fs/ext2fs.h
  head/sys/fs/ext2fs/htree.h

Modified: head/sys/fs/ext2fs/ext2_alloc.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_alloc.c	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_alloc.c	Sun Jan 14 20:46:39 2018	(r327977)
@@ -898,14 +898,22 @@ ext2_alloccg(struct inode *ip, int cg, daddr_t bpref, 
 		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 @@ gotit:
 	    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 ipr
 		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 ipr
 			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 @@ gotit:
 	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 @@ gotit:
 		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 
 	    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);
 }

Modified: head/sys/fs/ext2fs/ext2_csum.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_csum.c	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_csum.c	Sun Jan 14 20:46:39 2018	(r327977)
@@ -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 @@ 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 *
 "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;
 		}
 	}

Modified: head/sys/fs/ext2fs/ext2_dir.h
==============================================================================
--- head/sys/fs/ext2fs/ext2_dir.h	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_dir.h	Sun Jan 14 20:46:39 2018	(r327977)
@@ -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
  */

Modified: head/sys/fs/ext2fs/ext2_extattr.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_extattr.c	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_extattr.c	Sun Jan 14 20:46:39 2018	(r327977)
@@ -165,6 +165,22 @@ ext2_extattr_check(struct ext2fs_extattr_entry *entry,
 	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 attrname
 		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 attrnames
 		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 attrna
 		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 attrnames
 			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 attrnames
 		}
 
 		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 attrnames
 	}
 
 	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);

Modified: head/sys/fs/ext2fs/ext2_extattr.h
==============================================================================
--- head/sys/fs/ext2fs/ext2_extattr.h	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_extattr.h	Sun Jan 14 20:46:39 2018	(r327977)
@@ -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 {

Modified: head/sys/fs/ext2fs/ext2_extents.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_extents.c	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_extents.c	Sun Jan 14 20:46:39 2018	(r327977)
@@ -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_pa
 		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_pa
 		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_pa
 			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_ex
 	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;

Modified: head/sys/fs/ext2fs/ext2_extents.h
==============================================================================
--- head/sys/fs/ext2fs/ext2_extents.h	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_extents.h	Sun Jan 14 20:46:39 2018	(r327977)
@@ -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 {

Modified: head/sys/fs/ext2fs/ext2_extern.h
==============================================================================
--- head/sys/fs/ext2fs/ext2_extern.h	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_extern.h	Sun Jan 14 20:46:39 2018	(r327977)
@@ -66,7 +66,7 @@ int	ext4_bmapext(struct vnode *, int32_t, int64_t *, i
 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 *, in
 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.

Modified: head/sys/fs/ext2fs/ext2_inode_cnv.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_inode_cnv.c	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_inode_cnv.c	Sun Jan 14 20:46:39 2018	(r327977)
@@ -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 @@ 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);
 }
 
 /*
@@ -190,6 +198,9 @@ ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei)
 	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);
 }

Modified: head/sys/fs/ext2fs/ext2_lookup.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_lookup.c	Sun Jan 14 20:36:21 2018	(r327976)
+++ head/sys/fs/ext2fs/ext2_lookup.c	Sun Jan 14 20:46:39 2018	(r327977)
@@ -63,6 +63,7 @@
 #include <fs/ext2fs/ext2_dinode.h>

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list