shithub: lwext4

Download patch

ref: 7e64817a1cf30c6ce31a9a23ab3301dc5fa3056b
parent: fbfb8e6fbaf7833a851b713efa31f0085ea0cc60
author: ngkaho1234 <ngkaho1234@gmail.com>
date: Fri Jan 1 08:48:10 EST 2016

ext4_journal: initial support of journal checksumming is added.

--- a/lwext4/ext4_journal.c
+++ b/lwext4/ext4_journal.c
@@ -118,6 +118,209 @@
 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
 #define jbd_free_revoke_entry(addr) free(addr)
 
+static int jbd_has_csum(struct jbd_sb *jbd_sb)
+{
+	if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V2))
+		return 2;
+
+	if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V3))
+		return 3;
+
+	return 0;
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t jbd_sb_csum(struct jbd_sb *jbd_sb)
+{
+	uint32_t checksum = 0;
+
+	if (jbd_has_csum(jbd_sb)) {
+		uint32_t orig_checksum = jbd_sb->checksum;
+		jbd_set32(jbd_sb, checksum, 0);
+		/* Calculate crc32c checksum against tho whole superblock */
+		checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_sb,
+				JBD_SUPERBLOCK_SIZE);
+		jbd_sb->checksum = orig_checksum;
+	}
+	return checksum;
+}
+#else
+#define jbd_sb_csum(...) 0
+#endif
+
+static void jbd_sb_csum_set(struct jbd_sb *jbd_sb)
+{
+	if (!jbd_has_csum(jbd_sb))
+		return;
+
+	jbd_set32(jbd_sb, checksum, jbd_sb_csum(jbd_sb));
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool
+jbd_verify_sb_csum(struct jbd_sb *jbd_sb)
+{
+	if (!jbd_has_csum(jbd_sb))
+		return true;
+
+	return jbd_sb_csum(jbd_sb) == jbd_get32(jbd_sb, checksum);
+}
+#else
+#define jbd_verify_sb_csum(...) true
+#endif
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t jbd_meta_csum(struct jbd_fs *jbd_fs,
+			      struct jbd_bhdr *bhdr)
+{
+	uint32_t checksum = 0;
+
+	if (jbd_has_csum(&jbd_fs->sb)) {
+		uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+		struct jbd_block_tail *tail =
+			(struct jbd_block_tail *)((char *)bhdr + block_size -
+				sizeof(struct jbd_block_tail));
+		uint32_t orig_checksum = tail->checksum;
+		tail->checksum = 0;
+
+		/* First calculate crc32c checksum against fs uuid */
+		checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
+				       sizeof(jbd_fs->sb.uuid));
+		/* Calculate crc32c checksum against tho whole block */
+		checksum = ext4_crc32(checksum, bhdr,
+				block_size);
+		tail->checksum = orig_checksum;
+	}
+	return checksum;
+}
+#else
+#define jbd_meta_csum(...) 0
+#endif
+
+static void jbd_meta_csum_set(struct jbd_fs *jbd_fs,
+			      struct jbd_bhdr *bhdr)
+{
+	uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+	struct jbd_block_tail *tail = (struct jbd_block_tail *)
+				((char *)bhdr + block_size -
+				sizeof(struct jbd_block_tail));
+	if (!jbd_has_csum(&jbd_fs->sb))
+		return;
+
+	tail->checksum = to_be32(jbd_meta_csum(jbd_fs, bhdr));
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool
+jbd_verify_meta_csum(struct jbd_fs *jbd_fs,
+		     struct jbd_bhdr *bhdr)
+{
+	uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+	struct jbd_block_tail *tail = (struct jbd_block_tail *)
+				((char *)bhdr + block_size -
+				sizeof(struct jbd_block_tail));
+	if (!jbd_has_csum(&jbd_fs->sb))
+		return true;
+
+	return jbd_meta_csum(jbd_fs, bhdr) == to_be32(tail->checksum);
+}
+#else
+#define jbd_verify_meta_csum(...) true
+#endif
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t jbd_commit_csum(struct jbd_fs *jbd_fs,
+			      struct jbd_commit_header *header)
+{
+	uint32_t checksum = 0;
+
+	if (jbd_has_csum(&jbd_fs->sb)) {
+		uint32_t orig_checksum_type = header->chksum_type,
+			 orig_checksum_size = header->chksum_size,
+			 orig_checksum = header->chksum[0];
+		uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+		header->chksum_type = 0;
+		header->chksum_size = 0;
+		header->chksum[0] = 0;
+
+		/* First calculate crc32c checksum against fs uuid */
+		checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
+				       sizeof(jbd_fs->sb.uuid));
+		/* Calculate crc32c checksum against tho whole block */
+		checksum = ext4_crc32(checksum, header,
+				block_size);
+
+		header->chksum_type = orig_checksum_type;
+		header->chksum_size = orig_checksum_size;
+		header->chksum[0] = orig_checksum;
+	}
+	return checksum;
+}
+#else
+#define jbd_commit_csum(...) 0
+#endif
+
+static void jbd_commit_csum_set(struct jbd_fs *jbd_fs,
+			      struct jbd_commit_header *header)
+{
+	if (!jbd_has_csum(&jbd_fs->sb))
+		return;
+
+	header->chksum_type = 0;
+	header->chksum_size = 0;
+	header->chksum[0] = jbd_commit_csum(jbd_fs, header);
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool jbd_verify_commit_csum(struct jbd_fs *jbd_fs,
+				   struct jbd_commit_header *header)
+{
+	if (!jbd_has_csum(&jbd_fs->sb))
+		return true;
+
+	return header->chksum[0] == to_be32(jbd_commit_csum(jbd_fs,
+					    header));
+}
+#else
+#define jbd_verify_commit_csum(...) true
+#endif
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t jbd_block_csum(struct jbd_fs *jbd_fs, const void *buf)
+{
+	uint32_t checksum = 0;
+
+	if (jbd_has_csum(&jbd_fs->sb)) {
+		uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+		/* First calculate crc32c checksum against fs uuid */
+		checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
+				       sizeof(jbd_fs->sb.uuid));
+		/* Calculate crc32c checksum against tho whole block */
+		checksum = ext4_crc32(checksum, buf,
+				block_size);
+	}
+	return checksum;
+}
+#else
+#define jbd_block_csum(...) 0
+#endif
+
+static void jbd_block_tag_csum_set(struct jbd_fs *jbd_fs, void *__tag,
+				   uint32_t checksum)
+{
+	int ver = jbd_has_csum(&jbd_fs->sb);
+	if (!ver)
+		return;
+
+	if (ver == 2) {
+		struct jbd_block_tag *tag = __tag;
+		tag->checksum = (uint16_t)to_be32(checksum);
+	} else {
+		struct jbd_block_tag3 *tag = __tag;
+		tag->checksum = to_be32(checksum);
+	}
+}
+
 /**@brief  Write jbd superblock to disk.
  * @param  jbd_fs jbd filesystem
  * @param  s jbd superblock
@@ -132,6 +335,7 @@
 	if (rc != EOK)
 		return rc;
 
+	jbd_sb_csum_set(s);
 	offset = fblock * ext4_sb_get_block_size(&fs->sb);
 	return ext4_block_writebytes(fs->bdev, offset, s,
 				     EXT4_SUPERBLOCK_SIZE);
@@ -169,7 +373,7 @@
 	    jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
 		return false;
 
-	return true;
+	return jbd_verify_sb_csum(sb);
 }
 
 /**@brief  Write back dirty jbd superblock to disk.
@@ -373,6 +577,9 @@
 
 	/**@brief  Is this the last tag? */
 	bool last_tag;
+
+	/**@brief  crc32c checksum. */
+	uint32_t checksum;
 };
 
 /**@brief  Extract information from a block tag.
@@ -493,6 +700,8 @@
 			jbd_set32(tag, flags,
 				  jbd_get32(tag, flags) | JBD_FLAG_SAME_UUID);
 
+		jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum);
+
 		if (tag_info->last_tag)
 			jbd_set32(tag, flags,
 				  jbd_get32(tag, flags) | JBD_FLAG_LAST_TAG);
@@ -517,6 +726,8 @@
 			jbd_set16(tag, flags,
 				  jbd_get16(tag, flags) | JBD_FLAG_SAME_UUID);
 
+		jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum);
+
 		if (tag_info->last_tag)
 			jbd_set16(tag, flags,
 				  jbd_get16(tag, flags) | JBD_FLAG_LAST_TAG);
@@ -845,6 +1056,14 @@
 
 		switch (jbd_get32(header, blocktype)) {
 		case JBD_DESCRIPTOR_BLOCK:
+			if (!jbd_verify_meta_csum(jbd_fs, header)) {
+				ext4_dbg(DEBUG_JBD,
+					DBG_WARN "Descriptor block checksum failed."
+						"Journal block: %" PRIu32"\n",
+						this_block);
+				log_end = true;
+				break;
+			}
 			ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
 					    "trans_id: %" PRIu32"\n",
 					    this_block, this_trans_id);
@@ -862,6 +1081,15 @@
 
 			break;
 		case JBD_COMMIT_BLOCK:
+			if (!jbd_verify_commit_csum(jbd_fs,
+					(struct jbd_commit_header *)header)) {
+				ext4_dbg(DEBUG_JBD,
+					DBG_WARN "Commit block checksum failed."
+						"Journal block: %" PRIu32"\n",
+						this_block);
+				log_end = true;
+				break;
+			}
 			ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
 					    "trans_id: %" PRIu32"\n",
 					    this_block, this_trans_id);
@@ -871,6 +1099,14 @@
 			this_trans_id++;
 			break;
 		case JBD_REVOKE_BLOCK:
+			if (!jbd_verify_meta_csum(jbd_fs, header)) {
+				ext4_dbg(DEBUG_JBD,
+					DBG_WARN "Revoke block checksum failed."
+						"Journal block: %" PRIu32"\n",
+						this_block);
+				log_end = true;
+				break;
+			}
 			ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
 					    "trans_id: %" PRIu32"\n",
 					    this_block, this_trans_id);
@@ -1375,6 +1611,7 @@
 	jbd_set32(&header->header, blocktype, JBD_COMMIT_BLOCK);
 	jbd_set32(&header->header, sequence, trans->trans_id);
 
+	jbd_commit_csum_set(journal->jbd_fs, header);
 	ext4_bcache_set_dirty(commit_block.buf);
 	rc = jbd_block_set(journal->jbd_fs, &commit_block);
 	if (rc != EOK)
@@ -1424,6 +1661,7 @@
 	TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node, tmp) {
 		struct tag_info tag_info;
 		bool uuid_exist = false;
+		uint32_t checksum;
 		if (!ext4_bcache_test_flag(jbd_buf->block.buf,
 					   BC_DIRTY)) {
 			/* The buffer has not been modified, just release
@@ -1439,6 +1677,8 @@
 			free(jbd_buf);
 			continue;
 		}
+		checksum = jbd_block_csum(journal->jbd_fs,
+					  jbd_buf->block.data);
 again:
 		if (!desc_iblock) {
 			struct jbd_bhdr *bhdr;
@@ -1461,6 +1701,9 @@
 			tag_tbl_size = journal->block_size -
 				sizeof(struct jbd_bhdr);
 
+			if (jbd_has_csum(&journal->jbd_fs->sb))
+				tag_tbl_size -= sizeof(struct jbd_block_tail);
+
 			if (!trans->start_iblock)
 				trans->start_iblock = desc_iblock;
 
@@ -1471,6 +1714,7 @@
 			tag_info.last_tag = true;
 		else
 			tag_info.last_tag = false;
+		tag_info.checksum = checksum;
 
 		if (uuid_exist)
 			memcpy(tag_info.uuid, journal->jbd_fs->sb.uuid,
@@ -1481,6 +1725,8 @@
 				tag_tbl_size,
 				&tag_info);
 		if (rc != EOK) {
+			jbd_meta_csum_set(journal->jbd_fs,
+					(struct jbd_bhdr *)desc_block.data);
 			jbd_block_set(journal->jbd_fs, &desc_block);
 			desc_iblock = 0;
 			goto again;
@@ -1506,8 +1752,11 @@
 
 		i++;
 	}
-	if (rc == EOK && desc_iblock)
+	if (rc == EOK && desc_iblock) {
+		jbd_meta_csum_set(journal->jbd_fs,
+				(struct jbd_bhdr *)desc_block.data);
 		jbd_block_set(journal->jbd_fs, &desc_block);
+	}
 
 	return rc;
 }
@@ -1557,6 +1806,9 @@
 			tag_tbl_size = journal->block_size -
 				sizeof(struct jbd_revoke_header);
 
+			if (jbd_has_csum(&journal->jbd_fs->sb))
+				tag_tbl_size -= sizeof(struct jbd_block_tail);
+
 			if (!trans->start_iblock)
 				trans->start_iblock = desc_iblock;
 
@@ -1565,6 +1817,8 @@
 		if (tag_tbl_size < record_len) {
 			jbd_set32(header, count,
 				  journal->block_size - tag_tbl_size);
+			jbd_meta_csum_set(journal->jbd_fs,
+					(struct jbd_bhdr *)desc_block.data);
 			jbd_block_set(journal->jbd_fs, &desc_block);
 			desc_iblock = 0;
 			header = NULL;
@@ -1589,6 +1843,8 @@
 			jbd_set32(header, count,
 				  journal->block_size - tag_tbl_size);
 
+		jbd_meta_csum_set(journal->jbd_fs,
+				(struct jbd_bhdr *)desc_block.data);
 		jbd_block_set(journal->jbd_fs, &desc_block);
 	}