git » libjio » commit b0bed7b

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.

author Alberto Bertogli
2004-07-13 21:16:46 UTC
committer Alberto Bertogli
2007-07-15 13:12:27 UTC
parent 8c967b60786a87a2562e2f99911222bcffbccb45

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.

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.

Makefile +1 -1
check.c +11 -2
checksum.c +47 -0
common.h +8 -0
jiofsck.c +1 -0
libjio.h +1 -0
trans.c +10 -0

diff --git a/Makefile b/Makefile
index a032e05..31942ad 100644
--- a/Makefile
+++ b/Makefile
@@ -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 --git a/check.c b/check.c
index ba7cd4c..a6ff36a 100644
--- a/check.c
+++ b/check.c
@@ -95,13 +95,14 @@ int jfsck(const char *name, struct jfsck_result *res)
 {
 	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_result *res)
 
 		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 --git a/checksum.c b/checksum.c
new file mode 100644
index 0000000..60e9758
--- /dev/null
+++ b/checksum.c
@@ -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 --git a/common.h b/common.h
index 3e8f703..1b7bada 100644
--- a/common.h
+++ b/common.h
@@ -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 --git a/jiofsck.c b/jiofsck.c
index f9ac3ea..b7c8ad4 100644
--- a/jiofsck.c
+++ b/jiofsck.c
@@ -77,6 +77,7 @@ int main(int argc, char **argv)
 	printf("Broken head:\t %d\n", res.broken_head);
 	printf("Broken body:\t %d\n", res.broken_body);
 	printf("Load error:\t %d\n", res.load_error);
+	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");
diff --git a/libjio.h b/libjio.h
index 90ef862..101a42e 100644
--- a/libjio.h
+++ b/libjio.h
@@ -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 --git a/trans.c b/trans.c
index 3a25de2..6bc3904 100644
--- a/trans.c
+++ b/trans.c
@@ -179,6 +179,7 @@ int jtrans_add(struct jtrans *ts, const void *buf, size_t count, off_t offset)
 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