
Some filesystems can zero files if crash occur in special places. This can
lead to transaction file corruption if the data (but not the header) is zeroed
out, so we need some method for verification.

This patch implements a simple but effective checksum on the transaction
files, that is appended at their end.

The algorithm is quite small, based on RFC 1071, and it's the one used for IP.



---

 cur-root/Makefile   |    2 +-
 cur-root/check.c    |   13 +++++++++++--
 cur-root/checksum.c |   47 +++++++++++++++++++++++++++++++++++++++++++++++
 cur-root/common.h   |    8 ++++++++
 cur-root/jiofsck.c  |    1 +
 cur-root/libjio.h   |    1 +
 cur-root/trans.c    |   10 ++++++++++
 7 files changed, 79 insertions(+), 3 deletions(-)

diff -puN /dev/null checksum.c
--- /dev/null	2004-04-13 23:59:22.000000000 -0300
+++ cur-root/checksum.c	2004-07-13 18:16:46.036953960 -0300
@@ -0,0 +1,47 @@
+
+/*
+ * libjio - A library for Journaled I/O
+ * Alberto Bertogli (albertogli@telpin.com.ar)
+ *
+ * Checksum functions
+ * Based on RFC 1071, "Computing the Internet Checksum"
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include "common.h"
+
+
+int checksum(int fd, size_t len, uint32_t *csum)
+{
+	uint8_t *map;
+
+	map = (uint8_t *) mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+	if (map == MAP_FAILED)
+		return 0;
+
+	*csum = checksum_map(map, len);
+
+	munmap(map, len);
+	return 1;
+}
+
+uint32_t checksum_map(uint8_t *map, size_t count)
+{
+	uint32_t sum = 0;
+
+	while( count > 1 )  {
+		sum += * (uint16_t *) map++;
+		count -= 2;
+	}
+
+	if( count > 0 )
+		sum += * (uint8_t *) map;
+
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return ~sum;
+}
+
diff -puN common.h~checksum common.h
--- cur/common.h~checksum	2004-07-13 18:16:46.028955176 -0300
+++ cur-root/common.h	2004-07-13 18:16:46.037953808 -0300
@@ -9,6 +9,10 @@
 #ifndef _COMMON_H
 #define _COMMON_H
 
+#include <sys/types.h>	/* for ssize_t and off_t */
+#include <stdint.h>	/* for uint*_t */
+
+
 #define _F_READ		0x00001
 #define _F_WRITE	0x00010
 #define _F_LOCK		0x00100
@@ -21,11 +25,15 @@
 #define F_TLOCKW	(_F_TLOCK | _F_WRITE)
 #define F_UNLOCK	(_F_ULOCK)
 
+
 off_t plockf(int fd, int cmd, off_t offset, off_t len);
 ssize_t spread(int fd, void *buf, size_t count, off_t offset);
 ssize_t spwrite(int fd, const void *buf, size_t count, off_t offset);
 int get_jdir(const char *filename, char *jdir);
 int get_jtfile(const char *filename, int tid, char *jtfile);
 
+int checksum(int fd, size_t len, uint32_t *csum);
+uint32_t checksum_map(uint8_t *map, size_t count);
+
 #endif
 
diff -puN trans.c~checksum trans.c
--- cur/trans.c~checksum	2004-07-13 18:16:46.030954872 -0300
+++ cur-root/trans.c	2004-07-13 18:16:46.037953808 -0300
@@ -179,6 +179,7 @@ int jtrans_add(struct jtrans *ts, const 
 int jtrans_commit(struct jtrans *ts)
 {
 	int id, rv, fd = -1;
+	uint32_t csum;
 	char *name;
 	unsigned char *buf_init, *bufp;
 	struct joper *op;
@@ -307,6 +308,15 @@ int jtrans_commit(struct jtrans *ts)
 		curpos += op->len;
 	}
 
+	/* compute and save the checksum */
+	if (!checksum(fd, curpos, &csum))
+		goto exit;
+
+	rv = spwrite(fd, &csum, sizeof(uint32_t), curpos);
+	if (rv != sizeof(uint32_t))
+		goto exit;
+	curpos += sizeof(uint32_t);
+
 	/* this is a simple but efficient optimization: instead of doing
 	 * everything O_SYNC, we sync at this point only, this way we avoid
 	 * doing a lot of very small writes; in case of a crash the
diff -puN Makefile~checksum Makefile
--- cur/Makefile~checksum	2004-07-13 18:16:46.031954720 -0300
+++ cur-root/Makefile	2004-07-13 18:16:46.037953808 -0300
@@ -3,7 +3,7 @@ include Make.conf
 
 
 # objects to build
-OBJS = common.o trans.o check.o unix.o ansi.o
+OBJS = checksum.o common.o trans.o check.o unix.o ansi.o
 
 # rules
 default: all
diff -puN check.c~checksum check.c
--- cur/check.c~checksum	2004-07-13 18:16:46.032954568 -0300
+++ cur-root/check.c	2004-07-13 18:16:46.037953808 -0300
@@ -95,13 +95,14 @@ int jfsck(const char *name, struct jfsck
 {
 	int fd, tfd, rv, i;
 	unsigned int maxtid;
+	uint32_t csum1, csum2;
 	char jdir[PATH_MAX], jlockfile[PATH_MAX], tname[PATH_MAX];
 	struct stat sinfo;
 	struct jfs fs;
 	struct jtrans *curts;
 	DIR *dir;
 	struct dirent *dent;
-	void *map;
+	unsigned char *map;
 	off_t filelen;
 
 
@@ -192,12 +193,20 @@ int jfsck(const char *name, struct jfsck
 
 		filelen = lseek(tfd, 0, SEEK_END);
 		map = mmap(0, filelen, PROT_READ, MAP_SHARED, tfd, 0);
-		rv = fill_trans((unsigned char *) map, filelen, curts);
+		rv = fill_trans(map, filelen, curts);
 		if (rv != 1) {
 			res->broken++;
 			goto loop;
 		}
 
+		/* verify the checksum */
+		csum1 = checksum_map(map, filelen - (sizeof(uint32_t)));
+		csum2 = * (uint32_t *) (map + filelen - (sizeof(uint32_t)));
+		if (csum1 != csum2) {
+			res->corrupt++;
+			goto loop;
+		}
+
 		/* remove flags from the transaction */
 		curts->flags = 0;
 
diff -puN libjio.h~checksum libjio.h
--- cur/libjio.h~checksum	2004-07-13 18:16:46.033954416 -0300
+++ cur-root/libjio.h	2004-07-13 18:16:46.038953656 -0300
@@ -67,6 +67,7 @@ struct jfsck_result {
 	int invalid;		/* invalid files in the journal directory */
 	int in_progress;	/* transactions in progress */
 	int broken;		/* transactions broken */
+	int corrupt;		/* corrupt transactions */
 	int apply_error;	/* errors applying the transaction */
 	int reapplied;		/* transactions that were reapplied */
 };
diff -puN jiofsck.c~checksum jiofsck.c
--- cur/jiofsck.c~checksum	2004-07-13 18:16:46.035954112 -0300
+++ cur-root/jiofsck.c	2004-07-13 18:16:46.038953656 -0300
@@ -75,6 +75,7 @@ int main(int argc, char **argv)
 	printf("Invalid:\t %d\n", res.invalid);
 	printf("In progress:\t %d\n", res.in_progress);
 	printf("Broken:\t\t %d\n", res.broken);
+	printf("Corrupt:\t %d\n", res.corrupt);
 	printf("Apply error:\t %d\n", res.apply_error);
 	printf("Reapplied:\t %d\n", res.reapplied);
 	printf("\n");

_
