From 312534b635e5df2f5ac728aa5bdf24b4465eb318 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Sat, 26 Mar 2011 05:27:34 +0000
Subject: [PATCH] - Change disk format to add magic number to all pages -
 Change blockfile magic number to reflect new format - Cleanups and javadocs

---
 .../net/metanotion/io/block/BlockFile.java    | 49 ++++++++++++++-----
 .../io/block/index/BSkipLevels.java           |  7 +++
 .../metanotion/io/block/index/BSkipList.java  | 14 ++++++
 .../metanotion/io/block/index/BSkipSpan.java  | 46 +++++++++++++++--
 .../metanotion/io/block/index/IBSkipSpan.java | 14 ++++--
 5 files changed, 109 insertions(+), 21 deletions(-)

diff --git a/core/java/src/net/metanotion/io/block/BlockFile.java b/core/java/src/net/metanotion/io/block/BlockFile.java
index 867617bdb8..2d08ec0015 100644
--- a/core/java/src/net/metanotion/io/block/BlockFile.java
+++ b/core/java/src/net/metanotion/io/block/BlockFile.java
@@ -51,10 +51,20 @@ import net.metanotion.util.skiplist.SkipList;
 import net.i2p.I2PAppContext;
 import net.i2p.util.Log;
 
-class CorruptFileException extends IOException { }
-class BadFileFormatException extends IOException { }
-class BadVersionException extends IOException { }
-
+/**
+ * On-disk format:
+ *    Magic number (6 bytes)
+ *    Version major/minor (2 bytes)
+ *    file length (long)
+ *    free list start (unsigned int)
+ *    is mounted (unsigned short) 0 = no, 1 = yes
+ *    span size (unsigned short)
+ *
+ * Metaindex skiplist is on page 2
+ *
+ * Pages are 1 KB and are numbered starting from 1.
+ * e.g. the Metaindex skiplist is at offset 1024 bytes
+ */
 public class BlockFile {
 	public static final long PAGESIZE = 1024;
 	public static final long OFFSET_MOUNTED = 20;
@@ -62,7 +72,13 @@ public class BlockFile {
 
 	public RandomAccessInterface file;
 
-	private long magicBytes = 0x3141deadbeef0100L;
+	private static final int MAJOR = 0x01;
+	private static final int MINOR = 0x01;
+	// I2P changed magic number, format changed, magic numbers now on all pages
+	private static final long MAGIC_BASE = 0x3141de4932500000L;   // 0x3141de I 2 P 00 00
+	private static final long MAGIC = MAGIC_BASE | (MAJOR << 8) | MINOR;
+	private long magicBytes = MAGIC;
+	public static final int MAGIC_CONT = 0x434f4e54;   // "CONT"
 	private long fileLen = PAGESIZE * 2;
 	private int freeListStart = 0;
 	private int mounted = 0;
@@ -123,14 +139,17 @@ public class BlockFile {
 				if(curNextPage==0) {
 					curNextPage = this.allocPage();
 					BlockFile.pageSeek(this.file, curNextPage);
+					this.file.writeInt(MAGIC_CONT);
 					this.file.writeInt(0);
 					BlockFile.pageSeek(this.file, curPage);
+					this.file.skipBytes(4);   // skip magic
 					this.file.writeInt(curNextPage);
 				}
 				BlockFile.pageSeek(this.file, curNextPage);
 				curPage = curNextPage;
+				this.file.skipBytes(4);   // skip magic
 				curNextPage = this.file.readUnsignedInt();
-				pageCounter = 4;
+				pageCounter = 8;
 				len = ((int) BlockFile.PAGESIZE) - pageCounter;
 			}
 			this.file.write(data, dct, Math.min(len, data.length - dct));
@@ -152,9 +171,12 @@ public class BlockFile {
 			int len = ((int) BlockFile.PAGESIZE) - pageCounter;
 			if(len <= 0) {
 				BlockFile.pageSeek(this.file, curNextPage);
+				int magic = this.file.readInt();
+				if (magic != MAGIC_CONT)
+					throw new IOException("Bad SkipSpan continuation magic number 0x" + Integer.toHexString(magic) + " on page " + curNextPage);
 				curPage = curNextPage;
 				curNextPage = this.file.readUnsignedInt();
-				pageCounter = 4;
+				pageCounter = 8;
 				len = ((int) BlockFile.PAGESIZE) - pageCounter;
 			}
 			res = this.file.read(arr, dct, Math.min(len, arr.length - dct));
@@ -184,16 +206,19 @@ public class BlockFile {
 		}
 
 		readSuperBlock();
-		if(magicBytes != 0x3141deadbeef0100L) {
-			if((magicBytes & 0x3141deadbeef0000L) == 0x3141deadbeef0000L) {
-				throw new BadVersionException();
+		if(magicBytes != MAGIC) {
+			if((magicBytes & MAGIC_BASE) == MAGIC_BASE) {
+				throw new IOException("Expected " + MAJOR + '.' + MINOR +
+				                      " but got " + (magicBytes >> 8 & 0xff) + '.' + (magicBytes & 0xff));
 			} else {
-				throw new BadFileFormatException();
+				throw new IOException("Bad magic number");
 			}
 		}
 		if (mounted != 0)
 			log.warn("Warning - file was not previously closed");
-		if(fileLen != file.length()) { throw new CorruptFileException(); }
+		if(fileLen != file.length())
+			throw new IOException("Expected file length " + fileLen +
+		                              " but actually " + file.length());
 		mount();
 
 		metaIndex = new BSkipList(spanSize, this, 2, new StringBytes(), new IntBytes());
diff --git a/core/java/src/net/metanotion/io/block/index/BSkipLevels.java b/core/java/src/net/metanotion/io/block/index/BSkipLevels.java
index 7450422134..5f17929d65 100644
--- a/core/java/src/net/metanotion/io/block/index/BSkipLevels.java
+++ b/core/java/src/net/metanotion/io/block/index/BSkipLevels.java
@@ -38,12 +38,14 @@ import net.metanotion.util.skiplist.SkipSpan;
 
 /**
  * On-disk format:
+ *    Magic number (long)
  *    max height (unsigned short)
  *    non-null height (unsigned short)
  *    span page (unsigned int)
  *    height number of level pages (unsigned ints)
  */
 public class BSkipLevels extends SkipLevels {
+	private static final long MAGIC = 0x42534c6576656c73l;  // "BSLevels"
 	public final int levelPage;
 	public final int spanPage;
 	public final BlockFile bf;
@@ -53,6 +55,9 @@ public class BSkipLevels extends SkipLevels {
 		this.bf = bf;
 
 		BlockFile.pageSeek(bf.file, levelPage);
+		long magic = bf.file.readLong();
+		if (magic != MAGIC)
+			throw new IOException("Bad SkipLevels magic number 0x" + Long.toHexString(magic) + " on page " + levelPage);
 
 		bsl.levelHash.put(new Integer(this.levelPage), this);
 
@@ -87,6 +92,7 @@ public class BSkipLevels extends SkipLevels {
 
 	public static void init(BlockFile bf, int page, int spanPage, int maxHeight) throws IOException {
 		BlockFile.pageSeek(bf.file, page);
+		bf.file.writeLong(MAGIC);
 		bf.file.writeShort((short) maxHeight);
 		bf.file.writeShort(0);
 		bf.file.writeInt(spanPage);
@@ -95,6 +101,7 @@ public class BSkipLevels extends SkipLevels {
 	public void flush() {
 		try {
 			BlockFile.pageSeek(bf.file, levelPage);
+			bf.file.writeLong(MAGIC);
 			bf.file.writeShort((short) levels.length);
 			int i = 0;
 			for( ; i < levels.length; i++) {
diff --git a/core/java/src/net/metanotion/io/block/index/BSkipList.java b/core/java/src/net/metanotion/io/block/index/BSkipList.java
index e28165a9d0..8a8a409508 100644
--- a/core/java/src/net/metanotion/io/block/index/BSkipList.java
+++ b/core/java/src/net/metanotion/io/block/index/BSkipList.java
@@ -37,7 +37,16 @@ import net.metanotion.io.Serializer;
 import net.metanotion.io.block.BlockFile;
 import net.metanotion.util.skiplist.*;
 
+/**
+ * On-disk format:
+ *    Magic number (long)
+ *    first span page (unsigned int)
+ *    first level page (unsigned int)
+ *    size (unsigned int)
+ *    spans (unsigned int)
+ */
 public class BSkipList extends SkipList {
+	private static final long MAGIC = 0x536b69704c697374l;  // "SkipList"
 	public int firstSpanPage = 0;
 	public int firstLevelPage = 0;
 	public int skipPage = 0;
@@ -59,6 +68,9 @@ public class BSkipList extends SkipList {
 		this.bf = bf;
 
 		BlockFile.pageSeek(bf.file, skipPage);
+		long magic = bf.file.readLong();
+		if (magic != MAGIC)
+			throw new IOException("Bad SkipList magic number 0x" + Long.toHexString(magic) + " on page " + skipPage);
 		firstSpanPage = bf.file.readUnsignedInt();
 		firstLevelPage = bf.file.readUnsignedInt();
 		size = bf.file.readUnsignedInt();
@@ -84,6 +96,7 @@ public class BSkipList extends SkipList {
 	public void flush() {
 		try {
 			BlockFile.pageSeek(bf.file, skipPage);
+			bf.file.writeLong(MAGIC);
 			bf.file.writeInt(firstSpanPage);
 			bf.file.writeInt(firstLevelPage);
 			bf.file.writeInt(size);
@@ -114,6 +127,7 @@ public class BSkipList extends SkipList {
 		int firstSpan = bf.allocPage();
 		int firstLevel = bf.allocPage();
 		BlockFile.pageSeek(bf.file, page);
+		bf.file.writeLong(MAGIC);
 		bf.file.writeInt(firstSpan);
 		bf.file.writeInt(firstLevel);
 		bf.file.writeInt(0);
diff --git a/core/java/src/net/metanotion/io/block/index/BSkipSpan.java b/core/java/src/net/metanotion/io/block/index/BSkipSpan.java
index 8e9dc19d12..2188a6db30 100644
--- a/core/java/src/net/metanotion/io/block/index/BSkipSpan.java
+++ b/core/java/src/net/metanotion/io/block/index/BSkipSpan.java
@@ -36,8 +36,30 @@ import net.metanotion.io.block.BlockFile;
 import net.metanotion.util.skiplist.SkipList;
 import net.metanotion.util.skiplist.SkipSpan;
 
+/**
+ * On-disk format:
+ *
+ *   First Page:
+ *     Magic number (int)
+ *     overflow page (unsigned int)
+ *     previous page (unsigned int)
+ *     next page (unsigned int)
+ *     max keys (unsigned short)
+ *     number of keys (unsigned short)
+ *     for each key:
+ *         key length (unsigned short)
+ *         value length (unsigned short)
+ *         key data
+ *         value data
+ *
+ *   Overflow pages:
+ *     Magic number (int)
+ *     next overflow page (unsigned int)
+ */
 public class BSkipSpan extends SkipSpan {
-
+	protected static final int MAGIC = 0x5370616e;  // "Span"
+	protected static final int HEADER_LEN = 20;
+	protected static final int CONT_HEADER_LEN = 8;
 	protected BlockFile bf;
 	protected int page;
 	protected int overflowPage;
@@ -52,6 +74,7 @@ public class BSkipSpan extends SkipSpan {
 
 	public static void init(BlockFile bf, int page, int spanSize) throws IOException {
 		BlockFile.pageSeek(bf.file, page);
+		bf.file.writeInt(MAGIC);
 		bf.file.writeInt(0);
 		bf.file.writeInt(0);
 		bf.file.writeInt(0);
@@ -73,6 +96,7 @@ public class BSkipSpan extends SkipSpan {
 			int next;
 			while(curPage != 0) {
 				BlockFile.pageSeek(bf.file, curPage);
+				bf.file.skipBytes(4);   // skip magic
 				next = bf.file.readUnsignedInt();
 				bf.freePage(curPage);
 				curPage = next;
@@ -91,6 +115,7 @@ public class BSkipSpan extends SkipSpan {
 	private void fflush() {
 		try {
 			BlockFile.pageSeek(bf.file, page);
+			bf.file.writeInt(MAGIC);
 			bf.file.writeInt(overflowPage);
 			bf.file.writeInt((prev != null) ? ((BSkipSpan) prev).page : 0);
 			bf.file.writeInt((next != null) ? ((BSkipSpan) next).page : 0);
@@ -102,7 +127,7 @@ public class BSkipSpan extends SkipSpan {
 			int[] curNextPage = new int[1];
 			curNextPage[0] = this.overflowPage;
 			int[] pageCounter = new int[1];
-			pageCounter[0] = 16;
+			pageCounter[0] = HEADER_LEN;
 			byte[] keyData;
 			byte[] valData;
 
@@ -111,14 +136,17 @@ public class BSkipSpan extends SkipSpan {
 					if(curNextPage[0] == 0) {
 						curNextPage[0] = bf.allocPage();
 						BlockFile.pageSeek(bf.file, curNextPage[0]);
+						bf.file.writeInt(BlockFile.MAGIC_CONT);
 						bf.file.writeInt(0);
 						BlockFile.pageSeek(bf.file, curPage);
+						bf.file.skipBytes(4);  // skip magic
 						bf.file.writeInt(curNextPage[0]);
 					}
 					BlockFile.pageSeek(bf.file, curNextPage[0]);
 					curPage = curNextPage[0];
+					bf.file.skipBytes(4);  // skip magic
 					curNextPage[0] = bf.file.readUnsignedInt();
-					pageCounter[0] = 4;
+					pageCounter[0] = CONT_HEADER_LEN;
 				}
 				// Drop bad entry without throwing exception
 				if (keys[i] == null || vals[i] == null) {
@@ -144,7 +172,9 @@ public class BSkipSpan extends SkipSpan {
 				curPage = bf.writeMultiPageData(keyData, curPage, pageCounter, curNextPage);
 				curPage = bf.writeMultiPageData(valData, curPage, pageCounter, curNextPage);
 			}
+			// FIXME why seek and rescan the overflow page?
 			BlockFile.pageSeek(bf.file, this.page);
+			bf.file.skipBytes(4);  // skip magic
 			this.overflowPage = bf.file.readUnsignedInt();
 		} catch (IOException ioe) { throw new RuntimeException("Error writing to database", ioe); }
 		// FIXME can't get there from here
@@ -171,6 +201,9 @@ public class BSkipSpan extends SkipSpan {
 
 		BlockFile.pageSeek(bf.file, spanPage);
 
+		int magic = bf.file.readInt();
+		if (magic != MAGIC)
+			throw new IOException("Bad SkipSpan magic number 0x" + Integer.toHexString(magic) + " on page " + spanPage);
 		bss.overflowPage = bf.file.readUnsignedInt();
 		bss.prevPage = bf.file.readUnsignedInt();
 		bss.nextPage = bf.file.readUnsignedInt();
@@ -200,15 +233,18 @@ public class BSkipSpan extends SkipSpan {
 		int[] curNextPage = new int[1];
 		curNextPage[0] = this.overflowPage;
 		int[] pageCounter = new int[1];
-		pageCounter[0] = 16;
+		pageCounter[0] = HEADER_LEN;
 //		System.out.println("Span Load " + sz + " nKeys " + nKeys + " page " + curPage);
 		int fail = 0;
 		for(int i=0;i<this.nKeys;i++) {
 			if((pageCounter[0] + 4) > BlockFile.PAGESIZE) {
 				BlockFile.pageSeek(this.bf.file, curNextPage[0]);
 				curPage = curNextPage[0];
+				int magic = bf.file.readInt();
+				if (magic != BlockFile.MAGIC_CONT)
+					throw new IOException("Bad SkipSpan magic number 0x" + Integer.toHexString(magic) + " on page " + curPage);
 				curNextPage[0] = this.bf.file.readUnsignedInt();
-				pageCounter[0] = 4;
+				pageCounter[0] = CONT_HEADER_LEN;
 			}
 			ksz = this.bf.file.readUnsignedShort();
 			vsz = this.bf.file.readUnsignedShort();
diff --git a/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java b/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java
index 47e8775dab..4591b4b6bf 100644
--- a/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java
+++ b/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java
@@ -114,7 +114,7 @@ public class IBSkipSpan extends BSkipSpan {
 		int[] curNextPage = new int[1];
 		curNextPage[0] = this.overflowPage;
 		int[] pageCounter = new int[1];
-		pageCounter[0] = 16;
+		pageCounter[0] = HEADER_LEN;
 		ksz = this.bf.file.readUnsignedShort();
 		this.bf.file.skipBytes(2);  //vsz
 		pageCounter[0] +=4;
@@ -134,8 +134,11 @@ public class IBSkipSpan extends BSkipSpan {
 	 */
 	private void seekData() throws IOException {
 		BlockFile.pageSeek(this.bf.file, this.page);
+		int magic = bf.file.readInt();
+		if (magic != MAGIC)
+			throw new IOException("Bad SkipSpan magic number 0x" + Integer.toHexString(magic) + " on page " + this.page);
 		// 3 ints and 2 shorts
-		this.bf.file.skipBytes(16);
+		this.bf.file.skipBytes(HEADER_LEN - 4);
 	}
 
 	/**
@@ -157,15 +160,18 @@ public class IBSkipSpan extends BSkipSpan {
 		int[] curNextPage = new int[1];
 		curNextPage[0] = this.overflowPage;
 		int[] pageCounter = new int[1];
-		pageCounter[0] = 16;
+		pageCounter[0] = HEADER_LEN;
 		int fail = 0;
 		//System.out.println("Span Load " + sz + " nKeys " + nKeys + " page " + curPage);
 		for(int i=0;i<this.nKeys;i++) {
 			if((pageCounter[0] + 4) > BlockFile.PAGESIZE) {
 				BlockFile.pageSeek(this.bf.file, curNextPage[0]);
 				curPage = curNextPage[0];
+				int magic = bf.file.readInt();
+				if (magic != BlockFile.MAGIC_CONT)
+					throw new IOException("Bad SkipSpan continuation magic number 0x" + Integer.toHexString(magic) + " on page " + curPage);
 				curNextPage[0] = this.bf.file.readUnsignedInt();
-				pageCounter[0] = 4;
+				pageCounter[0] = CONT_HEADER_LEN;
 			}
 			ksz = this.bf.file.readUnsignedShort();
 			vsz = this.bf.file.readUnsignedShort();
-- 
GitLab