[freenet-cvs] r13166 - trunk/freenet/src/freenet/store

juiceman at freenetproject.org juiceman at freenetproject.org
Mon May 7 19:55:36 UTC 2007


Author: juiceman
Date: 2007-05-07 19:55:36 +0000 (Mon, 07 May 2007)
New Revision: 13166

Modified:
   trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
Log:
Spaces -> tabs

Modified: trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
===================================================================
--- trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java	2007-05-07 19:44:38 UTC (rev 13165)
+++ trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java	2007-05-07 19:55:36 UTC (rev 13166)
@@ -46,21 +46,21 @@
 import freenet.support.Logger;
 import freenet.support.SortedLongSet;
 
-/** 
- * Freenet datastore based on BerkelyDB Java Edition by sleepycat software
- * More info at http://www.sleepycat.com/products/bdbje.html
- * 
- * @author tubbie
- * 
- * TODO: Fix ugly Exception handling
- * 
- */
+/**
+* Freenet datastore based on BerkelyDB Java Edition by sleepycat software
+* More info at http://www.sleepycat.com/products/bdbje.html
+*
+* @author tubbie
+*
+* TODO: Fix ugly Exception handling
+*
+*/
 public class BerkeleyDBFreenetStore implements FreenetStore {
 
 	private static boolean logMINOR;
 	
-    final int dataBlockSize;
-    final int headerBlockSize;
+	final int dataBlockSize;
+	final int headerBlockSize;
 	
 	private final Environment environment;
 	private final TupleBinding storeBlockTupleBinding;
@@ -85,25 +85,25 @@
 	private boolean reallyClosed;
 	private final static byte[] dummy = new byte[0];
 	
-	public static BerkeleyDBFreenetStore construct(int lastVersion, File baseStoreDir, boolean isStore, 
+	public static BerkeleyDBFreenetStore construct(int lastVersion, File baseStoreDir, boolean isStore,
 			String suffix, long maxStoreKeys, int blockSize, int headerSize, boolean throwOnTooFewKeys, short type, Environment storeEnvironment, RandomSource random, SemiOrderedShutdownHook storeShutdownHook) throws DatabaseException, IOException {
 
 		/**
-		 * Migration strategy:
-		 * 
-		 * If nothing exists, create a new database in the storeEnvironment and store files of new names.
-		 * Else
-		 * If the old store directories exist:
-		 *      If the old store file does not exist, delete the old store directories, and create a new database in the storeEnvironment and store files of new names.
-		 *      Try to load the old database.
-		 *      If successful
-		 *             Migrate to the new database.
-		 *             Move the files.
-		 *      If not successful
-		 *             Reconstruct the new database from the old file.
-		 *             Move the old file to the new location.
-		 * 
-		 */
+		* Migration strategy:
+		*
+		* If nothing exists, create a new database in the storeEnvironment and store files of new names.
+		* Else
+		* If the old store directories exist:
+		*      If the old store file does not exist, delete the old store directories, and create a new database in the storeEnvironment and store files of new names.
+		*      Try to load the old database.
+		*      If successful
+		*             Migrate to the new database.
+		*             Move the files.
+		*      If not successful
+		*             Reconstruct the new database from the old file.
+		*             Move the old file to the new location.
+		*
+		*/
 		
 		// Location of old directory.
 		String oldDirName = oldTypeName(type) + (isStore ? "store" : "cache") + suffix;
@@ -251,9 +251,9 @@
 
 		System.err.println("Migrating data from old Environment to new Environment");
 		/** Reads from old database */
-    	Cursor c = null;
-    	/** Writes to new store */
-    	Transaction t = null;
+		Cursor c = null;
+		/** Writes to new store */
+		Transaction t = null;
 		try {
 			// Read from old database
 			t = newStore.environment.beginTransaction(null, null);
@@ -328,12 +328,12 @@
 		
 	}
 
-	private static BerkeleyDBFreenetStore openStore(Environment storeEnvironment, String newDBPrefix, File newStoreFile, 
-			File newFixSecondaryFile, long maxStoreKeys, int blockSize, int headerSize, boolean throwOnTooFewKeys, 
+	private static BerkeleyDBFreenetStore openStore(Environment storeEnvironment, String newDBPrefix, File newStoreFile,
+			File newFixSecondaryFile, long maxStoreKeys, int blockSize, int headerSize, boolean throwOnTooFewKeys,
 			boolean noCheck, int lastVersion, short type, boolean wipe, SemiOrderedShutdownHook storeShutdownHook) throws DatabaseException, IOException {
 		
 		try {
-			return new BerkeleyDBFreenetStore(storeEnvironment, newDBPrefix, newStoreFile, newFixSecondaryFile, 
+			return new BerkeleyDBFreenetStore(storeEnvironment, newDBPrefix, newStoreFile, newFixSecondaryFile,
 					maxStoreKeys, blockSize, headerSize, throwOnTooFewKeys, noCheck, wipe, storeShutdownHook);
 		} catch (DatabaseException e) {
 			
@@ -420,14 +420,14 @@
 	}
 	
 	/**
-     * Initializes database
-	 * @param noCheck If true, don't check for holes etc.
-	 * @param wipe If true, wipe the database first.
-     * @param the directory where the store is located
-	 * @throws IOException 
-	 * @throws DatabaseException 
-     * @throws FileNotFoundException if the dir does not exist and could not be created
-     */
+	* Initializes database
+	* @param noCheck If true, don't check for holes etc.
+	* @param wipe If true, wipe the database first.
+	* @param the directory where the store is located
+	* @throws IOException
+	* @throws DatabaseException
+	* @throws FileNotFoundException if the dir does not exist and could not be created
+	*/
 	public BerkeleyDBFreenetStore(Environment env, String prefix, File storeFile, File fixSecondaryFile, long maxChkBlocks, int blockSize, int headerSize, boolean throwOnTooFewKeys, boolean noCheck, boolean wipe, SemiOrderedShutdownHook storeShutdownHook) throws IOException, DatabaseException {
 		logMINOR = Logger.shouldLog(Logger.MINOR, this);
 		this.dataBlockSize = blockSize;
@@ -458,7 +458,7 @@
 			Logger.error(this, "This may take some time...");
 			System.err.println("Recreating secondary databases");
 			System.err.println("This may take some time...");
-			WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000+chkDB.count()*100))); 
+			WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000+chkDB.count()*100)));
 			// Of course it's not a solution but a quick fix
 			// Integer.MAX_VALUE seems to trigger an overflow or whatever ...
 			// Either we find out what the maximum value is and we do a static method somewhere ensuring
@@ -489,7 +489,7 @@
 		secDbConfig.setTransactional(true);
 		secDbConfig.setAllowPopulate(false);
 		storeBlockTupleBinding = new StoreBlockTupleBinding();
-		AccessTimeKeyCreator accessTimeKeyCreator = 
+		AccessTimeKeyCreator accessTimeKeyCreator =
 			new AccessTimeKeyCreator(storeBlockTupleBinding);
 		secDbConfig.setKeyCreator(accessTimeKeyCreator);
 		try {
@@ -505,7 +505,7 @@
 //				throw new DatabaseException("Needs repopulation");
 //			}
 		} catch (DatabaseException e) {
-			WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000+chkDB.count()*100))); 
+			WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000+chkDB.count()*100)));
 			// Of course it's not a solution but a quick fix
 			// Integer.MAX_VALUE seems to trigger an overflow or whatever ...
 			// Either we find out what the maximum value is and we do a static method somewhere ensuring
@@ -545,7 +545,7 @@
 		blockNoDbConfig.setAllowPopulate(false);
 		blockNoDbConfig.setTransactional(true);
 		
-		BlockNumberKeyCreator bnkc = 
+		BlockNumberKeyCreator bnkc =
 			new BlockNumberKeyCreator(storeBlockTupleBinding);
 		blockNoDbConfig.setKeyCreator(bnkc);
 		SecondaryDatabase blockNums = null;
@@ -562,7 +562,7 @@
 //				throw new DatabaseException("Needs repopulation");
 //			}
 		} catch (DatabaseException e) {
-			WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000+chkDB.count()*100))); 
+			WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000+chkDB.count()*100)));
 			// Of course it's not a solution but a quick fix
 			// Integer.MAX_VALUE seems to trigger an overflow or whatever ...
 			// Either we find out what the maximum value is and we do a static method somewhere ensuring
@@ -674,7 +674,7 @@
 			DatabaseEntry found = new DatabaseEntry();
 			LongBinding.longToEntry(i, blockNumEntry);
 			
-			OperationStatus success = 
+			OperationStatus success =
 				chkDB_blockNum.get(null, blockNumEntry, found, LockMode.DEFAULT);
 			
 			if(success.equals(OperationStatus.NOTFOUND)) {
@@ -709,28 +709,28 @@
 	private boolean shrinking = false;
 	
 	/**
-	 * Do an offline shrink, if necessary. Will not return until completed.
-	 * @param dontCheckForHoles If true, don't check for holes.
-	 * @throws DatabaseException
-	 * @throws IOException
-	 */
+	* Do an offline shrink, if necessary. Will not return until completed.
+	* @param dontCheckForHoles If true, don't check for holes.
+	* @throws DatabaseException
+	* @throws IOException
+	*/
 	private void maybeOfflineShrink(boolean dontCheckForHoles) throws DatabaseException, IOException {
 		if(chkBlocksInStore <= maxChkBlocks) return;
 		maybeSlowShrink(dontCheckForHoles, true);
 	}
 
 	/**
-	 * Do an online shrink, if necessary. Non-blocking i.e. it will do the shrink on another thread.
-	 * @param forceBigOnlineShrinks If true, force the node to shrink the store immediately even if
-	 * it is a major (more than 10%) shrink. Normally this is not allowed because online shrinks do not
-	 * preserve the most recently used data; the best thing to do is to restart the node and let it do
-	 * an offline shrink.
-	 * @throws DatabaseException If a database error occurs.
-	 * @throws IOException If an I/O error occurs.
-	 * @return True if the database will be shrunk in the background (or the database is already small 
-	 * enough), false if it is not possible to shrink it because a large shrink was requested and we 
-	 * don't want to do a large online shrink.
-	 */
+	* Do an online shrink, if necessary. Non-blocking i.e. it will do the shrink on another thread.
+	* @param forceBigOnlineShrinks If true, force the node to shrink the store immediately even if
+	* it is a major (more than 10%) shrink. Normally this is not allowed because online shrinks do not
+	* preserve the most recently used data; the best thing to do is to restart the node and let it do
+	* an offline shrink.
+	* @throws DatabaseException If a database error occurs.
+	* @throws IOException If an I/O error occurs.
+	* @return True if the database will be shrunk in the background (or the database is already small
+	* enough), false if it is not possible to shrink it because a large shrink was requested and we
+	* don't want to do a large online shrink.
+	*/
 	private boolean maybeOnlineShrink(boolean forceBigOnlineShrinks) throws DatabaseException, IOException {
 		synchronized(this) {
 			if(chkBlocksInStore <= maxChkBlocks) return true;
@@ -764,24 +764,24 @@
 		Vector unwantedMove = new Vector(); // content is not wanted, but is in the part of the store we will keep
 		Vector alreadyDropped = new Vector(); // any blocks past the end which have already been truncated, but which there are still database blocks pointing to
 		
-    	Cursor c = null;
-    	Transaction t = null;
+		Cursor c = null;
+		Transaction t = null;
 
-    	long newSize = maxChkBlocks;
-    	if(chkBlocksInStore < maxChkBlocks) return;
-    	
-    	System.err.println("Shrinking from "+chkBlocksInStore+" to "+maxChkBlocks+" (from db "+chkDB.count()+" from file "+countCHKBlocksFromFile()+ ')');
-    	
-    	if(!dontCheckForHoles)
-    		checkForHoles(maxChkBlocks, true);
-    	
-    	WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000 + chkBlocksInStore * 100))); // 10 per second
-    	
-    	long realSize = countCHKBlocksFromFile();
-    	
-    	long highestBlock = 0;
-    	
-    	try {
+		long newSize = maxChkBlocks;
+		if(chkBlocksInStore < maxChkBlocks) return;
+		
+		System.err.println("Shrinking from "+chkBlocksInStore+" to "+maxChkBlocks+" (from db "+chkDB.count()+" from file "+countCHKBlocksFromFile()+ ')');
+		
+		if(!dontCheckForHoles)
+			checkForHoles(maxChkBlocks, true);
+		
+		WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 5*60*1000 + chkBlocksInStore * 100))); // 10 per second
+		
+		long realSize = countCHKBlocksFromFile();
+		
+		long highestBlock = 0;
+		
+		try {
 			c = chkDB_accessTime.openCursor(null,null);
 			
 			DatabaseEntry keyDBE = new DatabaseEntry();
@@ -799,7 +799,7 @@
 			//Logger.minor(this, "Found first key");
 			int x = 0;
 			while(true) {
-		    	StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
+				StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
 				long block = storeBlock.offset;
 				if(block > highestBlock) highestBlock = block;
 				if(storeBlock.offset > Integer.MAX_VALUE) {
@@ -855,148 +855,148 @@
 				}
 			}
 			
-    	} finally {
-    		if(c != null)
-    			c.close();
-    	}
-    	
-    	Integer[] wantedKeepNums = (Integer[]) wantedKeep.toArray(new Integer[wantedKeep.size()]);
-    	Integer[] unwantedIgnoreNums = (Integer[]) unwantedIgnore.toArray(new Integer[unwantedIgnore.size()]);
-    	Integer[] wantedMoveNums = (Integer[]) wantedMove.toArray(new Integer[wantedMove.size()]);
-    	Integer[] unwantedMoveNums = (Integer[]) unwantedMove.toArray(new Integer[unwantedMove.size()]);
-    	long[] freeEarlySlots = freeBlocks.toArray();
-    	Arrays.sort(wantedKeepNums);
-    	Arrays.sort(unwantedIgnoreNums);
-    	Arrays.sort(wantedMoveNums);
-    	Arrays.sort(unwantedMoveNums);
-    	
-    	for(int i=0;i<newSize;i++) {
-    		Integer ii = new Integer(i);
-    		if(Arrays.binarySearch(wantedKeepNums, ii) >= 0) continue;
-    		if(Arrays.binarySearch(unwantedIgnoreNums, ii) >= 0) continue;
-    		if(Arrays.binarySearch(wantedMoveNums, ii) >= 0) continue;
-    		if(Arrays.binarySearch(unwantedMoveNums, ii) >= 0) continue;
-    		unwantedMove.add(ii);
-    	}
-    	unwantedMoveNums = (Integer[]) unwantedMove.toArray(new Integer[unwantedMove.size()]);
-    	
-    	System.err.println("Keys to keep where they are:     "+wantedKeepNums.length);
-    	System.err.println("Keys which will be wiped anyway: "+unwantedIgnoreNums.length);
-    	System.err.println("Keys to move:                    "+wantedMoveNums.length);
-    	System.err.println("Keys to be moved over:           "+unwantedMoveNums.length);
-    	System.err.println("Free slots to be moved over:     "+freeEarlySlots.length);
-    	
-    	// Now move all the wantedMove blocks onto the corresponding unwantedMove's.
-    	
-    	WrapperManager.signalStarting((int)Math.min(Integer.MAX_VALUE, (5*60*1000 + wantedMoveNums.length*1000L + alreadyDropped.size() * 100L))); // 1 per second
-    	
-    	byte[] buf = new byte[headerBlockSize + dataBlockSize];
-    	t = null;
-    	try {
-    		t = environment.beginTransaction(null,null);
-    		if(alreadyDropped.size() > 0) {
-    			System.err.println("Deleting "+alreadyDropped.size()+" blocks beyond the length of the file");
-    			for(int i=0;i<alreadyDropped.size();i++) {
-    				int unwantedBlock = ((Integer) alreadyDropped.get(i)).intValue();
-    				DatabaseEntry unwantedBlockEntry = new DatabaseEntry();
-    				LongBinding.longToEntry(unwantedBlock, unwantedBlockEntry);
-    				chkDB_blockNum.delete(t, unwantedBlockEntry);
-    				if(i % 1024 == 0) {
-    					t.commit();
-    					t = environment.beginTransaction(null,null);
-    				}
-    			}
-    			if(alreadyDropped.size() % 1024 != 0) {
-    				t.commit();
-    				t = environment.beginTransaction(null,null);
-    			}
-    		}
-    		for(int i=0;i<wantedMoveNums.length;i++) {
-    			Integer wantedBlock = wantedMoveNums[i];
-    			
-    			Integer unwantedBlock;
-    			
-    			// Can we move over an empty slot?
-    			if(i < freeEarlySlots.length) {
-    				// Don't need to delete old block
-    				unwantedBlock = new Integer((int) freeEarlySlots[i]); // will fit in an int
-    			} else if(unwantedMoveNums.length + freeEarlySlots.length > i) {
-    				unwantedBlock = unwantedMoveNums[i-freeEarlySlots.length];
-    				// Delete unwantedBlock from the store
-    				DatabaseEntry unwantedBlockEntry = new DatabaseEntry();
-    				LongBinding.longToEntry(unwantedBlock.longValue(), unwantedBlockEntry);
-    				// Delete the old block from the database.
-    				chkDB_blockNum.delete(t, unwantedBlockEntry);
-    			} else {
-    				System.err.println("Keys to move but no keys to move over! Moved "+i);
-    				t.commit();
-    				t = null;
-    				return;
-    			}
-    			// Move old data to new location
-    			
-    			DatabaseEntry wantedBlockEntry = new DatabaseEntry();
-    			LongBinding.longToEntry(wantedBlock.longValue(), wantedBlockEntry);
-    			long seekTo = wantedBlock.longValue() * (headerBlockSize + dataBlockSize);
-    			try {
-    				chkStore.seek(seekTo);
-    				chkStore.readFully(buf);
-    			} catch (EOFException e) {
-    				System.err.println("Was reading "+wantedBlock+" to write to "+unwantedBlock);
-    				System.err.println(e);
-    				e.printStackTrace();
-    				throw e;
-    			}
-    			seekTo = unwantedBlock.longValue() * (headerBlockSize + dataBlockSize);
-    			chkStore.seek(seekTo);
-    			chkStore.write(buf);
-    			
-    			// Update the database w.r.t. the old block.
-    			
-    			DatabaseEntry routingKeyDBE = new DatabaseEntry();
-    			DatabaseEntry blockDBE = new DatabaseEntry();
-    			chkDB_blockNum.get(t, wantedBlockEntry, routingKeyDBE, blockDBE, LockMode.RMW);
-    			StoreBlock block = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
-    			block.offset = unwantedBlock.longValue();
-    			storeBlockTupleBinding.objectToEntry(block, blockDBE);
-    			chkDB.put(t, routingKeyDBE, blockDBE);
-    			
-    			// Think about committing the transaction.
-    			
-    			if((i+1) % 2048 == 0) {
-    				t.commit();
-    				t = environment.beginTransaction(null,null);
-    				System.out.println("Moving blocks: "+(i*100/wantedMove.size())+ "% ( "+i+ '/' +wantedMove.size()+ ')');
-    			}
-    			//System.err.println("Moved "+wantedBlock+" to "+unwantedBlock);
-    		}
-    		System.out.println("Moved all "+wantedMove.size()+" blocks");
-    		if(t != null) {
-    			t.commit();
-    			t = null;
-    		}
-    	} finally {
-    		if(t != null)
-    			t.abort();
-    		t = null;
-    	}
-    	System.out.println("Completing shrink"); // FIXME remove
-    	
-    	int totalUnwantedBlocks = unwantedMoveNums.length+freeEarlySlots.length;
-    	WrapperManager.signalStarting((int)Math.min(Integer.MAX_VALUE, 5*60*1000 + (totalUnwantedBlocks-wantedMoveNums.length) * 100));
-    	// If there are any slots left over, they must be free.
-    	freeBlocks.clear();
+		} finally {
+			if(c != null)
+				c.close();
+		}
+		
+		Integer[] wantedKeepNums = (Integer[]) wantedKeep.toArray(new Integer[wantedKeep.size()]);
+		Integer[] unwantedIgnoreNums = (Integer[]) unwantedIgnore.toArray(new Integer[unwantedIgnore.size()]);
+		Integer[] wantedMoveNums = (Integer[]) wantedMove.toArray(new Integer[wantedMove.size()]);
+		Integer[] unwantedMoveNums = (Integer[]) unwantedMove.toArray(new Integer[unwantedMove.size()]);
+		long[] freeEarlySlots = freeBlocks.toArray();
+		Arrays.sort(wantedKeepNums);
+		Arrays.sort(unwantedIgnoreNums);
+		Arrays.sort(wantedMoveNums);
+		Arrays.sort(unwantedMoveNums);
+		
+		for(int i=0;i<newSize;i++) {
+			Integer ii = new Integer(i);
+			if(Arrays.binarySearch(wantedKeepNums, ii) >= 0) continue;
+			if(Arrays.binarySearch(unwantedIgnoreNums, ii) >= 0) continue;
+			if(Arrays.binarySearch(wantedMoveNums, ii) >= 0) continue;
+			if(Arrays.binarySearch(unwantedMoveNums, ii) >= 0) continue;
+			unwantedMove.add(ii);
+		}
+		unwantedMoveNums = (Integer[]) unwantedMove.toArray(new Integer[unwantedMove.size()]);
+		
+		System.err.println("Keys to keep where they are:     "+wantedKeepNums.length);
+		System.err.println("Keys which will be wiped anyway: "+unwantedIgnoreNums.length);
+		System.err.println("Keys to move:                    "+wantedMoveNums.length);
+		System.err.println("Keys to be moved over:           "+unwantedMoveNums.length);
+		System.err.println("Free slots to be moved over:     "+freeEarlySlots.length);
+		
+		// Now move all the wantedMove blocks onto the corresponding unwantedMove's.
+		
+		WrapperManager.signalStarting((int)Math.min(Integer.MAX_VALUE, (5*60*1000 + wantedMoveNums.length*1000L + alreadyDropped.size() * 100L))); // 1 per second
+		
+		byte[] buf = new byte[headerBlockSize + dataBlockSize];
+		t = null;
+		try {
+			t = environment.beginTransaction(null,null);
+			if(alreadyDropped.size() > 0) {
+				System.err.println("Deleting "+alreadyDropped.size()+" blocks beyond the length of the file");
+				for(int i=0;i<alreadyDropped.size();i++) {
+					int unwantedBlock = ((Integer) alreadyDropped.get(i)).intValue();
+					DatabaseEntry unwantedBlockEntry = new DatabaseEntry();
+					LongBinding.longToEntry(unwantedBlock, unwantedBlockEntry);
+					chkDB_blockNum.delete(t, unwantedBlockEntry);
+					if(i % 1024 == 0) {
+						t.commit();
+						t = environment.beginTransaction(null,null);
+					}
+				}
+				if(alreadyDropped.size() % 1024 != 0) {
+					t.commit();
+					t = environment.beginTransaction(null,null);
+				}
+			}
+			for(int i=0;i<wantedMoveNums.length;i++) {
+				Integer wantedBlock = wantedMoveNums[i];
+				
+				Integer unwantedBlock;
+				
+				// Can we move over an empty slot?
+				if(i < freeEarlySlots.length) {
+					// Don't need to delete old block
+					unwantedBlock = new Integer((int) freeEarlySlots[i]); // will fit in an int
+				} else if(unwantedMoveNums.length + freeEarlySlots.length > i) {
+					unwantedBlock = unwantedMoveNums[i-freeEarlySlots.length];
+					// Delete unwantedBlock from the store
+					DatabaseEntry unwantedBlockEntry = new DatabaseEntry();
+					LongBinding.longToEntry(unwantedBlock.longValue(), unwantedBlockEntry);
+					// Delete the old block from the database.
+					chkDB_blockNum.delete(t, unwantedBlockEntry);
+				} else {
+					System.err.println("Keys to move but no keys to move over! Moved "+i);
+					t.commit();
+					t = null;
+					return;
+				}
+				// Move old data to new location
+				
+				DatabaseEntry wantedBlockEntry = new DatabaseEntry();
+				LongBinding.longToEntry(wantedBlock.longValue(), wantedBlockEntry);
+				long seekTo = wantedBlock.longValue() * (headerBlockSize + dataBlockSize);
+				try {
+					chkStore.seek(seekTo);
+					chkStore.readFully(buf);
+				} catch (EOFException e) {
+					System.err.println("Was reading "+wantedBlock+" to write to "+unwantedBlock);
+					System.err.println(e);
+					e.printStackTrace();
+					throw e;
+				}
+				seekTo = unwantedBlock.longValue() * (headerBlockSize + dataBlockSize);
+				chkStore.seek(seekTo);
+				chkStore.write(buf);
+				
+				// Update the database w.r.t. the old block.
+				
+				DatabaseEntry routingKeyDBE = new DatabaseEntry();
+				DatabaseEntry blockDBE = new DatabaseEntry();
+				chkDB_blockNum.get(t, wantedBlockEntry, routingKeyDBE, blockDBE, LockMode.RMW);
+				StoreBlock block = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
+				block.offset = unwantedBlock.longValue();
+				storeBlockTupleBinding.objectToEntry(block, blockDBE);
+				chkDB.put(t, routingKeyDBE, blockDBE);
+				
+				// Think about committing the transaction.
+				
+				if((i+1) % 2048 == 0) {
+					t.commit();
+					t = environment.beginTransaction(null,null);
+					System.out.println("Moving blocks: "+(i*100/wantedMove.size())+ "% ( "+i+ '/' +wantedMove.size()+ ')');
+				}
+				//System.err.println("Moved "+wantedBlock+" to "+unwantedBlock);
+			}
+			System.out.println("Moved all "+wantedMove.size()+" blocks");
+			if(t != null) {
+				t.commit();
+				t = null;
+			}
+		} finally {
+			if(t != null)
+				t.abort();
+			t = null;
+		}
+		System.out.println("Completing shrink"); // FIXME remove
+		
+		int totalUnwantedBlocks = unwantedMoveNums.length+freeEarlySlots.length;
+		WrapperManager.signalStarting((int)Math.min(Integer.MAX_VALUE, 5*60*1000 + (totalUnwantedBlocks-wantedMoveNums.length) * 100));
+		// If there are any slots left over, they must be free.
+		freeBlocks.clear();
 		t = environment.beginTransaction(null,null);
-    	for(int i=wantedMoveNums.length;i<totalUnwantedBlocks;i++) {
-    		long blockNo;
-    		String reason;
-    		if(i < freeEarlySlots.length) {
-    			blockNo = freeEarlySlots[i];
-    			reason = "early slot "+i;
-    		} else {
-    			blockNo = unwantedMoveNums[i-freeEarlySlots.length].longValue();
-    			reason = "unwanted "+(i-freeEarlySlots.length);
-    		}
+		for(int i=wantedMoveNums.length;i<totalUnwantedBlocks;i++) {
+			long blockNo;
+			String reason;
+			if(i < freeEarlySlots.length) {
+				blockNo = freeEarlySlots[i];
+				reason = "early slot "+i;
+			} else {
+				blockNo = unwantedMoveNums[i-freeEarlySlots.length].longValue();
+				reason = "unwanted "+(i-freeEarlySlots.length);
+			}
 			DatabaseEntry unwantedBlockEntry = new DatabaseEntry();
 			LongBinding.longToEntry(blockNo, unwantedBlockEntry);
 			chkDB_blockNum.delete(t, unwantedBlockEntry);
@@ -1009,26 +1009,26 @@
 					t = environment.beginTransaction(null,null);
 			}
 			addFreeBlock(blockNo, true, reason);
-    	}
-    	if(t != null) t.commit();
+		}
+		if(t != null) t.commit();
 		t = null;
-    	
-    	System.out.println("Finishing shrink"); // FIXME remove
-    	
-    	chkStore.setLength(newSize * (dataBlockSize + headerBlockSize));
-    	
-    	synchronized(this) {
-    		chkBlocksInStore = newSize;
-    	}
-    	System.err.println("Shrunk store, now have "+chkBlocksInStore+" of "+maxChkBlocks);
+		
+		System.out.println("Finishing shrink"); // FIXME remove
+		
+		chkStore.setLength(newSize * (dataBlockSize + headerBlockSize));
+		
+		synchronized(this) {
+			chkBlocksInStore = newSize;
+		}
+		System.err.println("Shrunk store, now have "+chkBlocksInStore+" of "+maxChkBlocks);
 	}
 	
 	/**
-	 * Shrink the store, on the fly/quickly.
-	 * @param offline If false, keep going until the store has shrunk enough.
-	 * @throws DatabaseException
-	 * @throws IOException
-	 */
+	* Shrink the store, on the fly/quickly.
+	* @param offline If false, keep going until the store has shrunk enough.
+	* @throws DatabaseException
+	* @throws IOException
+	*/
 	private void maybeQuickShrink(boolean offline) throws DatabaseException, IOException {
 		// long's are not atomic.
 		long maxBlocks;
@@ -1045,15 +1045,15 @@
 	}
 
 	/**
-	 * @param curBlocks The current number of blocks in the file. (From the file length).
-	 * @param maxBlocks The target number of blocks in the file. (The file will be truncated to this length in blocks).
-	 * @param offline If true, innerQuickShrink will run once. If false, after the first run, if
-	 * the store is still over its required size, it will shrink it again, and so on until the store 
-	 * is within its required size. 
-	 * If false, innerQuickShrink will repeat itself until it deletes no more blocks, This is to handle  
-	 * @throws DatabaseException If a database error occurs.
-	 * @throws IOException If an I/O error occurs.
-	 */
+	* @param curBlocks The current number of blocks in the file. (From the file length).
+	* @param maxBlocks The target number of blocks in the file. (The file will be truncated to this length in blocks).
+	* @param offline If true, innerQuickShrink will run once. If false, after the first run, if
+	* the store is still over its required size, it will shrink it again, and so on until the store
+	* is within its required size.
+	* If false, innerQuickShrink will repeat itself until it deletes no more blocks, This is to handle
+	* @throws DatabaseException If a database error occurs.
+	* @throws IOException If an I/O error occurs.
+	*/
 	private void innerQuickShrink(long curBlocks, long maxBlocks, boolean offline) throws DatabaseException, IOException {
 		long oldCurBlocks = curBlocks;
 		try {
@@ -1079,14 +1079,14 @@
 					DatabaseEntry blockNumEntry = new DatabaseEntry();
 					LongBinding.longToEntry(i, blockNumEntry);
 					
-					OperationStatus result = 
+					OperationStatus result =
 						chkDB_blockNum.delete(t, blockNumEntry);
 					if(result.equals(OperationStatus.SUCCESS))
 						deleted++;
 					
 					if((curBlocks-i) % 2048 == 0) {
 						t.commit();
-						t = null; 
+						t = null;
 					}
 
 					freeBlocks.remove(i);
@@ -1130,12 +1130,12 @@
 	public static final short TYPE_SSK = 2;
 	
 	/**
-     * Recreate the index from the data file. Call this when the index has been corrupted.
-     * @param the directory where the store is located
-	 * @throws DatabaseException If the store cannot be opened because of a database problem.
-	 * @throws IOException If the store cannot be opened because of a filesystem problem.
-     * @throws FileNotFoundException if the dir does not exist and could not be created
-     */
+	* Recreate the index from the data file. Call this when the index has been corrupted.
+	* @param the directory where the store is located
+	* @throws DatabaseException If the store cannot be opened because of a database problem.
+	* @throws IOException If the store cannot be opened because of a filesystem problem.
+	* @throws FileNotFoundException if the dir does not exist and could not be created
+	*/
 	public BerkeleyDBFreenetStore(Environment env, String prefix, File storeFile, File fixSecondaryFile, long maxChkBlocks, int blockSize, int headerSize, short type, boolean noCheck, SemiOrderedShutdownHook storeShutdownHook) throws DatabaseException, IOException {
 		logMINOR = Logger.shouldLog(Logger.MINOR, this);
 		this.dataBlockSize = blockSize;
@@ -1169,7 +1169,7 @@
 		secDbConfig.setTransactional(true);
 		secDbConfig.setAllowPopulate(true);
 		storeBlockTupleBinding = new StoreBlockTupleBinding();
-		AccessTimeKeyCreator accessTimeKeyCreator = 
+		AccessTimeKeyCreator accessTimeKeyCreator =
 			new AccessTimeKeyCreator(storeBlockTupleBinding);
 		secDbConfig.setKeyCreator(accessTimeKeyCreator);
 		chkDB_accessTime = environment.openSecondaryDatabase
@@ -1182,7 +1182,7 @@
 		blockNoDbConfig.setAllowPopulate(true);
 		blockNoDbConfig.setTransactional(true);
 		
-		BlockNumberKeyCreator bnkc = 
+		BlockNumberKeyCreator bnkc =
 			new BlockNumberKeyCreator(storeBlockTupleBinding);
 		blockNoDbConfig.setKeyCreator(bnkc);
 		System.err.println("Creating block db index");
@@ -1314,388 +1314,388 @@
 	}
 
 	/**
-     * Retrieve a block.
-     * @param dontPromote If true, don't promote data if fetched.
-     * @return null if there is no such block stored, otherwise the block.
-     */
-    public CHKBlock fetch(NodeCHK chk, boolean dontPromote) throws IOException {
-    	synchronized(this) {
-    		if(closed)
-    			return null;
-    	}
-    	
-    	byte[] routingkey = chk.getRoutingKey();
-    	DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
-    	DatabaseEntry blockDBE = new DatabaseEntry();
-    	Cursor c = null;
-    	Transaction t = null;
-    	try{
-    		t = environment.beginTransaction(null,null);
-    		c = chkDB.openCursor(t,null);
+	* Retrieve a block.
+	* @param dontPromote If true, don't promote data if fetched.
+	* @return null if there is no such block stored, otherwise the block.
+	*/
+	public CHKBlock fetch(NodeCHK chk, boolean dontPromote) throws IOException {
+		synchronized(this) {
+			if(closed)
+				return null;
+		}
+		
+		byte[] routingkey = chk.getRoutingKey();
+		DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+		DatabaseEntry blockDBE = new DatabaseEntry();
+		Cursor c = null;
+		Transaction t = null;
+		try{
+			t = environment.beginTransaction(null,null);
+			c = chkDB.openCursor(t,null);
 
-    		if(logMINOR) Logger.minor(this, "Fetching "+chk+" dontPromote="+dontPromote);
-    		/**
-    		 * We will have to write, unless both dontPromote and the key is valid.
-    		 * The lock only applies to this record, so it's not a big problem for our use.
-    		 * What *IS* a big problem is that if we take a LockMode.DEFAULT, and two threads
-    		 * access the same key, they will both take the read lock, and then both try to
-    		 * take the write lock. Neither can relinquish the read in order for the other to
-    		 * take the write, so we're screwed.
-    		 */
-    		if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
-    				!=OperationStatus.SUCCESS) {
-    			c.close();
-    			c = null;
-    			t.abort();
-    			t = null;
-    			synchronized(this) {
-    				misses++;
-    			}
-    			return null;
-    		}
+			if(logMINOR) Logger.minor(this, "Fetching "+chk+" dontPromote="+dontPromote);
+			/**
+			* We will have to write, unless both dontPromote and the key is valid.
+			* The lock only applies to this record, so it's not a big problem for our use.
+			* What *IS* a big problem is that if we take a LockMode.DEFAULT, and two threads
+			* access the same key, they will both take the read lock, and then both try to
+			* take the write lock. Neither can relinquish the read in order for the other to
+			* take the write, so we're screwed.
+			*/
+			if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
+					!=OperationStatus.SUCCESS) {
+				c.close();
+				c = null;
+				t.abort();
+				t = null;
+				synchronized(this) {
+					misses++;
+				}
+				return null;
+			}
 
-	    	StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
-	    		    	
-	    	CHKBlock block = null;
-	    	try{
-	    		byte[] header = new byte[headerBlockSize];
-	    		byte[] data = new byte[dataBlockSize];
-    			try {
-    				synchronized(chkStore) {
-    					long seekTarget = storeBlock.offset*(long)(dataBlockSize+headerBlockSize);
-    					try {
-    						chkStore.seek(seekTarget);
-    					} catch (IOException ioe) {
-    						if(seekTarget > (2l*1024*1024*1024)) {
-    							Logger.error(this, "Environment does not support files bigger than 2 GB?");
-    							System.out.println("Environment does not support files bigger than 2 GB? (exception to follow)");
-    						}
-    						Logger.error(this, "Caught IOException on chkStore.seek("+seekTarget+ ')');
-    						throw ioe;
-    					}
-    					chkStore.readFully(header);
-    					chkStore.readFully(data);
-    				}
-    			} catch (EOFException e) {
-    				Logger.error(this, "No block");
-    	    		c.close();
-    	    		c = null;
-    	    		chkDB.delete(t, routingkeyDBE);
-    	    		t.commit();
-    	    		t = null;
-    	    		addFreeBlock(storeBlock.offset, true, "Data off end of store file");
-    	    		return null;
-    			}
-	    		
-	    		
-	    		block = new CHKBlock(data,header,chk);
-	    		
-	    		if(!dontPromote)
-	    		{
-	    			storeBlock.updateRecentlyUsed();
-	    			DatabaseEntry updateDBE = new DatabaseEntry();
-	    			storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
-	    			c.putCurrent(updateDBE);
-		    		c.close();
-		    		c = null;
-		    		t.commit();
-		    		t = null;
-	    		}else{
-	    			c.close();
-	    			c = null;
-	    			t.abort();
-	    			t = null;
-	    		}
-	    		
-	    		if(logMINOR) {
-	    			Logger.minor(this, "Get key: " + chk);
-	    			Logger.minor(this, "Headers: " + header.length+" bytes, hash " + Fields.hashCode(header));
-	    			Logger.minor(this, "Data: " + data.length + " bytes, hash " + Fields.hashCode(data) + " fetching " + chk);
-	    		}
-	    		
-	    	}catch(CHKVerifyException ex){
-	    		Logger.error(this, "CHKBlock: Does not verify ("+ex+"), setting accessTime to 0 for : "+chk);
-	    		System.err.println("Does not verify (CHK block "+storeBlock.offset+ ')');
-	    		c.close();
-	    		c = null;
-	    		chkDB.delete(t, routingkeyDBE);
-	    		t.commit();
-	    		t = null;
-	    		addFreeBlock(storeBlock.offset, true, "CHK does not verify");
-	    		synchronized(this) {
-	    			misses++;
-	    		}
-	            return null;
-	    	}
-	    	synchronized(this) {
-	    		hits++;
-	    	}
-	    	return block;
-    	}catch(Throwable ex) {  // FIXME: ugly  
-    		if(c!=null) {
-    			try{c.close();}catch(DatabaseException ex2){}
-    		}
-    		if(t!=null)
-    			try{t.abort();}catch(DatabaseException ex2){}
-    		Logger.error(this, "Caught "+ex, ex);
-    		ex.printStackTrace();
-           	checkSecondaryDatabaseError(ex);
-        	IOException e = new IOException(ex.getMessage());
-        	e.initCause(ex);
-        	throw e;
-        }
-    	
+			StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
+						
+			CHKBlock block = null;
+			try{
+				byte[] header = new byte[headerBlockSize];
+				byte[] data = new byte[dataBlockSize];
+				try {
+					synchronized(chkStore) {
+						long seekTarget = storeBlock.offset*(long)(dataBlockSize+headerBlockSize);
+						try {
+							chkStore.seek(seekTarget);
+						} catch (IOException ioe) {
+							if(seekTarget > (2l*1024*1024*1024)) {
+								Logger.error(this, "Environment does not support files bigger than 2 GB?");
+								System.out.println("Environment does not support files bigger than 2 GB? (exception to follow)");
+							}
+							Logger.error(this, "Caught IOException on chkStore.seek("+seekTarget+ ')');
+							throw ioe;
+						}
+						chkStore.readFully(header);
+						chkStore.readFully(data);
+					}
+				} catch (EOFException e) {
+					Logger.error(this, "No block");
+					c.close();
+					c = null;
+					chkDB.delete(t, routingkeyDBE);
+					t.commit();
+					t = null;
+					addFreeBlock(storeBlock.offset, true, "Data off end of store file");
+					return null;
+				}
+				
+				
+				block = new CHKBlock(data,header,chk);
+				
+				if(!dontPromote)
+				{
+					storeBlock.updateRecentlyUsed();
+					DatabaseEntry updateDBE = new DatabaseEntry();
+					storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
+					c.putCurrent(updateDBE);
+					c.close();
+					c = null;
+					t.commit();
+					t = null;
+				}else{
+					c.close();
+					c = null;
+					t.abort();
+					t = null;
+				}
+				
+				if(logMINOR) {
+					Logger.minor(this, "Get key: " + chk);
+					Logger.minor(this, "Headers: " + header.length+" bytes, hash " + Fields.hashCode(header));
+					Logger.minor(this, "Data: " + data.length + " bytes, hash " + Fields.hashCode(data) + " fetching " + chk);
+				}
+				
+			}catch(CHKVerifyException ex){
+				Logger.error(this, "CHKBlock: Does not verify ("+ex+"), setting accessTime to 0 for : "+chk);
+				System.err.println("Does not verify (CHK block "+storeBlock.offset+ ')');
+				c.close();
+				c = null;
+				chkDB.delete(t, routingkeyDBE);
+				t.commit();
+				t = null;
+				addFreeBlock(storeBlock.offset, true, "CHK does not verify");
+				synchronized(this) {
+					misses++;
+				}
+				return null;
+			}
+			synchronized(this) {
+				hits++;
+			}
+			return block;
+		}catch(Throwable ex) {  // FIXME: ugly
+			if(c!=null) {
+				try{c.close();}catch(DatabaseException ex2){}
+			}
+			if(t!=null)
+				try{t.abort();}catch(DatabaseException ex2){}
+			Logger.error(this, "Caught "+ex, ex);
+			ex.printStackTrace();
+			checkSecondaryDatabaseError(ex);
+			IOException e = new IOException(ex.getMessage());
+			e.initCause(ex);
+			throw e;
+		}
+		
 //    	return null;
-    }
+	}
 
 	/**
-     * Retrieve a block.
-     * @param dontPromote If true, don't promote data if fetched.
-     * @return null if there is no such block stored, otherwise the block.
-     */
-    public SSKBlock fetch(NodeSSK chk, boolean dontPromote) throws IOException {
-    	synchronized(this) {
-    		if(closed)
-    			return null;
-    	}
-    	
-    	byte[] routingkey = chk.getRoutingKey();
-    	DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
-    	DatabaseEntry blockDBE = new DatabaseEntry();
-    	Cursor c = null;
-    	Transaction t = null;
-    	try{
-    		t = environment.beginTransaction(null,null);
-    		c = chkDB.openCursor(t,null);
-    		
-    		// Explanation of locking is in fetchPubKey.
-    		// Basically, locking the whole element saves us all sorts of trouble, especially
-    		// since we will usually be writing here if only to promote it.
-    		if(logMINOR) Logger.minor(this, "Fetching "+chk+" dontPromote="+dontPromote);
-    		if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
-    				!=OperationStatus.SUCCESS) {
-    			c.close();
-    			c = null;
-    			t.abort();
-    			t = null;
-    			synchronized(this) {
-    				misses++;
-    			}
-    			return null;
-    		}
+	* Retrieve a block.
+	* @param dontPromote If true, don't promote data if fetched.
+	* @return null if there is no such block stored, otherwise the block.
+	*/
+	public SSKBlock fetch(NodeSSK chk, boolean dontPromote) throws IOException {
+		synchronized(this) {
+			if(closed)
+				return null;
+		}
+		
+		byte[] routingkey = chk.getRoutingKey();
+		DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+		DatabaseEntry blockDBE = new DatabaseEntry();
+		Cursor c = null;
+		Transaction t = null;
+		try{
+			t = environment.beginTransaction(null,null);
+			c = chkDB.openCursor(t,null);
+			
+			// Explanation of locking is in fetchPubKey.
+			// Basically, locking the whole element saves us all sorts of trouble, especially
+			// since we will usually be writing here if only to promote it.
+			if(logMINOR) Logger.minor(this, "Fetching "+chk+" dontPromote="+dontPromote);
+			if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
+					!=OperationStatus.SUCCESS) {
+				c.close();
+				c = null;
+				t.abort();
+				t = null;
+				synchronized(this) {
+					misses++;
+				}
+				return null;
+			}
 
-	    	StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
-	    		    	
-	    	SSKBlock block = null;
-	    	try{
-	    		byte[] header = new byte[headerBlockSize];
-	    		byte[] data = new byte[dataBlockSize];
-	    		try {
-	    			synchronized(chkStore) {
-	    				chkStore.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
-	    				chkStore.readFully(header);
-	    				chkStore.readFully(data);
-	    			}
-	    		} catch (EOFException e) {
-	    			Logger.error(this, "No block");
-	    			c.close();
-	    			c = null;
-	    			chkDB.delete(t, routingkeyDBE);
-	    			t.commit();
-	    			t = null;
-	    			addFreeBlock(storeBlock.offset, true, "Data off end of store file");
-    	    		return null;
-	    		}
-	    		
-	    		
-	    		block = new SSKBlock(data,header,chk, true);
-	    		
-	    		if(!dontPromote) {
-	    			storeBlock.updateRecentlyUsed();
-	    			DatabaseEntry updateDBE = new DatabaseEntry();
-	    			storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
-	    			c.putCurrent(updateDBE);
-		    		c.close();
-	    			c = null;
-		    		t.commit();
-		    		t = null;
-	    		}else{
-	    			c.close();
-	    			c = null;
-	    			t.abort();
-	    			t = null;
-	    		}
-	    		
-	    		if(logMINOR) {
-	    			Logger.minor(this, "Headers: " + header.length+" bytes, hash " + Fields.hashCode(header));
-	    			Logger.minor(this, "Data: " + data.length + " bytes, hash " + Fields.hashCode(data) + " fetching " + chk);
-	    		}
-	    		
-	    	}catch(SSKVerifyException ex){
-	    		Logger.normal(this, "SSKBlock: Does not verify ("+ex+"), setting accessTime to 0 for : "+chk, ex);
-	    		chkDB.delete(t, routingkeyDBE);
-	    		c.close();
-	    		c = null;
-	    		t.commit();
-	    		t = null;
-	    		addFreeBlock(storeBlock.offset, true, "SSK does not verify");
-	    		synchronized(this) {
-	    			misses++;
-	    		}
-	            return null;
-	    	}
-	    	synchronized(this) {
-	    		hits++;
-	    	}
-	    	return block;
-    	}catch(Throwable ex) {  // FIXME: ugly  
-    		if(c!=null) {
-    			try{c.close();}catch(DatabaseException ex2){}
-    		}
-    		if(t!=null) {
-    			try{t.abort();}catch(DatabaseException ex2){}
-    		}
-        	checkSecondaryDatabaseError(ex);
-    		Logger.error(this, "Caught "+ex, ex);
-    		ex.printStackTrace();
-        	throw new IOException(ex.getMessage());
-        }
-    	
+			StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
+						
+			SSKBlock block = null;
+			try{
+				byte[] header = new byte[headerBlockSize];
+				byte[] data = new byte[dataBlockSize];
+				try {
+					synchronized(chkStore) {
+						chkStore.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
+						chkStore.readFully(header);
+						chkStore.readFully(data);
+					}
+				} catch (EOFException e) {
+					Logger.error(this, "No block");
+					c.close();
+					c = null;
+					chkDB.delete(t, routingkeyDBE);
+					t.commit();
+					t = null;
+					addFreeBlock(storeBlock.offset, true, "Data off end of store file");
+					return null;
+				}
+				
+				
+				block = new SSKBlock(data,header,chk, true);
+				
+				if(!dontPromote) {
+					storeBlock.updateRecentlyUsed();
+					DatabaseEntry updateDBE = new DatabaseEntry();
+					storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
+					c.putCurrent(updateDBE);
+					c.close();
+					c = null;
+					t.commit();
+					t = null;
+				}else{
+					c.close();
+					c = null;
+					t.abort();
+					t = null;
+				}
+				
+				if(logMINOR) {
+					Logger.minor(this, "Headers: " + header.length+" bytes, hash " + Fields.hashCode(header));
+					Logger.minor(this, "Data: " + data.length + " bytes, hash " + Fields.hashCode(data) + " fetching " + chk);
+				}
+				
+			}catch(SSKVerifyException ex){
+				Logger.normal(this, "SSKBlock: Does not verify ("+ex+"), setting accessTime to 0 for : "+chk, ex);
+				chkDB.delete(t, routingkeyDBE);
+				c.close();
+				c = null;
+				t.commit();
+				t = null;
+				addFreeBlock(storeBlock.offset, true, "SSK does not verify");
+				synchronized(this) {
+					misses++;
+				}
+				return null;
+			}
+			synchronized(this) {
+				hits++;
+			}
+			return block;
+		}catch(Throwable ex) {  // FIXME: ugly
+			if(c!=null) {
+				try{c.close();}catch(DatabaseException ex2){}
+			}
+			if(t!=null) {
+				try{t.abort();}catch(DatabaseException ex2){}
+			}
+			checkSecondaryDatabaseError(ex);
+			Logger.error(this, "Caught "+ex, ex);
+			ex.printStackTrace();
+			throw new IOException(ex.getMessage());
+		}
+		
 //    	return null;
-    }
+	}
 
-    // FIXME do this with interfaces etc.
-    
-    public DSAPublicKey fetchPubKey(byte[] hash, boolean dontPromote) throws IOException {
-    	return fetchPubKey(hash, null, dontPromote);
-    }
-    
+	// FIXME do this with interfaces etc.
+	
+	public DSAPublicKey fetchPubKey(byte[] hash, boolean dontPromote) throws IOException {
+		return fetchPubKey(hash, null, dontPromote);
+	}
+	
 	/**
-     * Retrieve a block.
-     * @param dontPromote If true, don't promote data if fetched.
-     * @param replacement If non-null, and the data exists but is corrupt, replace it with this.
-     * @return null if there is no such block stored, otherwise the block.
-     */
-    public DSAPublicKey fetchPubKey(byte[] hash, DSAPublicKey replacement, boolean dontPromote) throws IOException {
-    	synchronized(this) {
-    		if(closed)
-    			return null;
-    	}
-    	
-    	DatabaseEntry routingkeyDBE = new DatabaseEntry(hash);
-    	DatabaseEntry blockDBE = new DatabaseEntry();
-    	Cursor c = null;
-    	Transaction t = null;
-    	try{
-    		if(logMINOR) Logger.minor(this, "Fetching pubkey: "+HexUtil.bytesToHex(hash));
-    		t = environment.beginTransaction(null,null);
-    		c = chkDB.openCursor(t,null);
+	* Retrieve a block.
+	* @param dontPromote If true, don't promote data if fetched.
+	* @param replacement If non-null, and the data exists but is corrupt, replace it with this.
+	* @return null if there is no such block stored, otherwise the block.
+	*/
+	public DSAPublicKey fetchPubKey(byte[] hash, DSAPublicKey replacement, boolean dontPromote) throws IOException {
+		synchronized(this) {
+			if(closed)
+				return null;
+		}
+		
+		DatabaseEntry routingkeyDBE = new DatabaseEntry(hash);
+		DatabaseEntry blockDBE = new DatabaseEntry();
+		Cursor c = null;
+		Transaction t = null;
+		try{
+			if(logMINOR) Logger.minor(this, "Fetching pubkey: "+HexUtil.bytesToHex(hash));
+			t = environment.beginTransaction(null,null);
+			c = chkDB.openCursor(t,null);
 
-    		// Lock the records as soon as we find them.
-    		// RMW - nobody else may access this key until we are finished.
-    		// This is advantageous as we will usually promote it and we may replace its content;
-    		// if two readers accessed it at once both might try to. Also IIRC we can deadlock
-    		// if we don't.
-    		if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
-    				!=OperationStatus.SUCCESS) {
-    			c.close();
-    			c = null;
-    			t.abort();
-    			t = null;
-    			synchronized(this) {
-    				misses++;
-    			}
-    			return null;
-    		}
+			// Lock the records as soon as we find them.
+			// RMW - nobody else may access this key until we are finished.
+			// This is advantageous as we will usually promote it and we may replace its content;
+			// if two readers accessed it at once both might try to. Also IIRC we can deadlock
+			// if we don't.
+			if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
+					!=OperationStatus.SUCCESS) {
+				c.close();
+				c = null;
+				t.abort();
+				t = null;
+				synchronized(this) {
+					misses++;
+				}
+				return null;
+			}
 
-	    	StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
-	    	
-	    	// Promote the key (we can always demote it later; promoting it here means it shouldn't be deallocated
-	    	// FIXME the locking/concurrency in this class is a bit dodgy!
-	    	
-    		if(!dontPromote) {
-    			storeBlock.updateRecentlyUsed();
-    			DatabaseEntry updateDBE = new DatabaseEntry();
-    			storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
-    			c.putCurrent(updateDBE);
-    		}
-    		
-	    	DSAPublicKey block = null;
-	    	
-    		byte[] data = new byte[dataBlockSize];
-    		if(logMINOR) Logger.minor(this, "Reading from store... "+storeBlock.offset+" ("+storeBlock.recentlyUsed+ ')');
-    		// When will java have pread/pwrite? :(
-    		try {
-    			synchronized(chkStore) {
-    				chkStore.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
-    				chkStore.readFully(data);
-    			}
+			StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
+			
+			// Promote the key (we can always demote it later; promoting it here means it shouldn't be deallocated
+			// FIXME the locking/concurrency in this class is a bit dodgy!
+			
+			if(!dontPromote) {
+				storeBlock.updateRecentlyUsed();
+				DatabaseEntry updateDBE = new DatabaseEntry();
+				storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
+				c.putCurrent(updateDBE);
+			}
+			
+			DSAPublicKey block = null;
+			
+			byte[] data = new byte[dataBlockSize];
+			if(logMINOR) Logger.minor(this, "Reading from store... "+storeBlock.offset+" ("+storeBlock.recentlyUsed+ ')');
+			// When will java have pread/pwrite? :(
+			try {
+				synchronized(chkStore) {
+					chkStore.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
+					chkStore.readFully(data);
+				}
 			} catch (EOFException e) {
 				Logger.error(this, "No block");
-	    		c.close();
-	    		c = null;
-	    		chkDB.delete(t, routingkeyDBE);
-	    		t.commit();
-	    		t = null;
-	    		addFreeBlock(storeBlock.offset, true, "Data off end of store file");
-	    		return null;
+				c.close();
+				c = null;
+				chkDB.delete(t, routingkeyDBE);
+				t.commit();
+				t = null;
+				addFreeBlock(storeBlock.offset, true, "Data off end of store file");
+				return null;
 			}
-    		if(logMINOR) Logger.minor(this, "Read");
-    		
-    		try {
-    			block = DSAPublicKey.create(data);
-    		} catch (CryptFormatException e) {
-    			Logger.error(this, "Could not read key: "+e, e);
-    			finishKey(storeBlock, c, t, routingkeyDBE, hash, replacement);
-    			return replacement;
-    		}
-    		
-    		if(!Arrays.equals(block.asBytesHash(), hash)) {
-    			finishKey(storeBlock, c, t, routingkeyDBE, hash, replacement);
-    			return replacement;
-    		}
-    		
-	    	// Finished, commit.
-	    	c.close();
-	    	c = null;
-	    	t.commit();
-	    	t = null;
-	    	
-	    	if(logMINOR) {
-	    		Logger.minor(this, "Data: " + data.length + " bytes, hash " + Fields.hashCode(data) + " fetching "+HexUtil.bytesToHex(hash));
-	    	}
-	    	
-	        synchronized(this) {
-	        	hits++;
-	        }
-	    	return block;
-    	} catch(Throwable ex) {  // FIXME: ugly
-    		// Clean up.
-    		// Reports of wierd NPEs when aborting a transaction, deal with it
-    		if(c!=null) {
-    			try {
-    				c.close();
-    			} catch(Throwable ex2) { 
-    				Logger.error(this, "Caught "+ex2+" closing in finally block", ex2);
-    			}
-    		}
-    		if(t!=null) {
-    			try {
-    				t.abort();
-    			} catch(Throwable ex2) {
-    				Logger.error(this, "Caught "+ex2+" aborting in finally block", ex2);
-    			}
-    		}
-        	checkSecondaryDatabaseError(ex);
-    		Logger.error(this, "Caught "+ex, ex);
-    		ex.printStackTrace();
-        	throw new IOException(ex.getMessage());
-        }
-    	
+			if(logMINOR) Logger.minor(this, "Read");
+			
+			try {
+				block = DSAPublicKey.create(data);
+			} catch (CryptFormatException e) {
+				Logger.error(this, "Could not read key: "+e, e);
+				finishKey(storeBlock, c, t, routingkeyDBE, hash, replacement);
+				return replacement;
+			}
+			
+			if(!Arrays.equals(block.asBytesHash(), hash)) {
+				finishKey(storeBlock, c, t, routingkeyDBE, hash, replacement);
+				return replacement;
+			}
+			
+			// Finished, commit.
+			c.close();
+			c = null;
+			t.commit();
+			t = null;
+			
+			if(logMINOR) {
+				Logger.minor(this, "Data: " + data.length + " bytes, hash " + Fields.hashCode(data) + " fetching "+HexUtil.bytesToHex(hash));
+			}
+			
+			synchronized(this) {
+				hits++;
+			}
+			return block;
+		} catch(Throwable ex) {  // FIXME: ugly
+			// Clean up.
+			// Reports of wierd NPEs when aborting a transaction, deal with it
+			if(c!=null) {
+				try {
+					c.close();
+				} catch(Throwable ex2) {
+					Logger.error(this, "Caught "+ex2+" closing in finally block", ex2);
+				}
+			}
+			if(t!=null) {
+				try {
+					t.abort();
+				} catch(Throwable ex2) {
+					Logger.error(this, "Caught "+ex2+" aborting in finally block", ex2);
+				}
+			}
+			checkSecondaryDatabaseError(ex);
+			Logger.error(this, "Caught "+ex, ex);
+			ex.printStackTrace();
+			throw new IOException(ex.getMessage());
+		}
+		
 //    	return null;
-    }
+	}
 
-    private boolean finishKey(StoreBlock storeBlock, Cursor c, Transaction t, DatabaseEntry routingkeyDBE, byte[] hash, DSAPublicKey replacement) throws IOException, DatabaseException {
+	private boolean finishKey(StoreBlock storeBlock, Cursor c, Transaction t, DatabaseEntry routingkeyDBE, byte[] hash, DSAPublicKey replacement) throws IOException, DatabaseException {
 		if(replacement != null) {
 			Logger.normal(this, "Replacing corrupt DSAPublicKey ("+HexUtil.bytesToHex(hash));
 			synchronized(chkStore) {
@@ -1722,16 +1722,16 @@
 	}
 
 	private void addFreeBlock(long offset, boolean loud, String reason) {
-   		if(freeBlocks.push(offset)) {
-   			if(loud) {
-   				System.err.println("Freed block "+offset+" ("+reason+ ')');
-   				Logger.normal(this, "Freed block "+offset+" ("+reason+ ')');
-   			} else {
-   				if(logMINOR) Logger.minor(this, "Freed block "+offset+" ("+reason+ ')');
-   			}
-   		} else {
-   			if(logMINOR) Logger.minor(this, "Already freed block "+offset+" ("+reason+ ')');
-   		}
+		if(freeBlocks.push(offset)) {
+			if(loud) {
+				System.err.println("Freed block "+offset+" ("+reason+ ')');
+				Logger.normal(this, "Freed block "+offset+" ("+reason+ ')');
+			} else {
+				if(logMINOR) Logger.minor(this, "Freed block "+offset+" ("+reason+ ')');
+			}
+		} else {
+			if(logMINOR) Logger.minor(this, "Already freed block "+offset+" ("+reason+ ')');
+		}
 	}
 
 	public void put(CHKBlock b) throws IOException {
@@ -1740,9 +1740,9 @@
 		if(oldBlock != null)
 			return;
 		innerPut(b);
-    }
-    
-    public void put(SSKBlock b, boolean overwrite) throws IOException, KeyCollisionException {
+	}
+	
+	public void put(SSKBlock b, boolean overwrite) throws IOException, KeyCollisionException {
 		NodeSSK ssk = (NodeSSK) b.getKey();
 		SSKBlock oldBlock = fetch(ssk, false);
 		if(oldBlock != null) {
@@ -1756,150 +1756,150 @@
 		} else {
 			innerPut(b);
 		}
-    }
-    
-    private boolean overwrite(SSKBlock b) throws IOException {
-    	synchronized(this) {
-    		if(closed)
-    			return false;
-    	}
-    	  	
-    	NodeSSK chk = (NodeSSK) b.getKey();
-    	byte[] routingkey = chk.getRoutingKey();
-    	DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
-    	DatabaseEntry blockDBE = new DatabaseEntry();
-    	Cursor c = null;
-    	Transaction t = null;
-    	try{
-    		t = environment.beginTransaction(null,null);
-    		c = chkDB.openCursor(t,null);
+	}
+	
+	private boolean overwrite(SSKBlock b) throws IOException {
+		synchronized(this) {
+			if(closed)
+				return false;
+		}
+			
+		NodeSSK chk = (NodeSSK) b.getKey();
+		byte[] routingkey = chk.getRoutingKey();
+		DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+		DatabaseEntry blockDBE = new DatabaseEntry();
+		Cursor c = null;
+		Transaction t = null;
+		try{
+			t = environment.beginTransaction(null,null);
+			c = chkDB.openCursor(t,null);
 
-    		// Lock the record.
-    		if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
-    				!=OperationStatus.SUCCESS) {
-    			c.close();
-    			c = null;
-    			t.abort();
-    			t = null;
-    			return false;
-    		}
+			// Lock the record.
+			if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
+					!=OperationStatus.SUCCESS) {
+				c.close();
+				c = null;
+				t.abort();
+				t = null;
+				return false;
+			}
 
-	    	StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
-	    		    	
-	    	byte[] header = b.getRawHeaders();
-	    	byte[] data = b.getRawData();
-	    	synchronized(chkStore) {
-		    	chkStore.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
-		    	chkStore.write(header);
-		    	chkStore.write(data);
-	    	}
-	    	
-	    	// Unlock record.
-	    	c.close();
-	    	c = null;
-	    	t.commit();
-	    	t = null;
-	    	
-	    } catch(Throwable ex) {  // FIXME: ugly  
-	    	checkSecondaryDatabaseError(ex);
-    		Logger.error(this, "Caught "+ex, ex);
-    		ex.printStackTrace();
-        	throw new IOException(ex.getMessage());
-        } finally {
-	    	if(c!=null) {
-	    		try{c.close();}catch(DatabaseException ex2){}
-	    	
-	    	}
-	    	if(t!=null) {
-	    		try{t.abort();}catch(DatabaseException ex2){}
-	    	}
-        	
-        }
-	    	
-    	return true;
+			StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(blockDBE);
+						
+			byte[] header = b.getRawHeaders();
+			byte[] data = b.getRawData();
+			synchronized(chkStore) {
+				chkStore.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
+				chkStore.write(header);
+				chkStore.write(data);
+			}
+			
+			// Unlock record.
+			c.close();
+			c = null;
+			t.commit();
+			t = null;
+			
+		} catch(Throwable ex) {  // FIXME: ugly
+			checkSecondaryDatabaseError(ex);
+			Logger.error(this, "Caught "+ex, ex);
+			ex.printStackTrace();
+			throw new IOException(ex.getMessage());
+		} finally {
+			if(c!=null) {
+				try{c.close();}catch(DatabaseException ex2){}
+			
+			}
+			if(t!=null) {
+				try{t.abort();}catch(DatabaseException ex2){}
+			}
+			
+		}
+			
+		return true;
 	}
 
 	/**
-     * Store a block.
-     */
-    private void innerPut(KeyBlock block) throws IOException {
-    	synchronized(this) {
-    		if(closed)
-    			return;
-    	}
-    	  	
-    	byte[] routingkey = block.getKey().getRoutingKey();
-        byte[] data = block.getRawData();
-        byte[] header = block.getRawHeaders();
-        
-        if(data.length!=dataBlockSize) {
-        	Logger.error(this, "This data is "+data.length+" bytes. Should be "+dataBlockSize);
-        	return;
-        }
-        if(header.length!=headerBlockSize) {
-        	Logger.error(this, "This header is "+data.length+" bytes. Should be "+headerBlockSize);
-        	return;
-        }
-        
-        Transaction t = null;
-        
-        try{
-        	t = environment.beginTransaction(null,null);
-        	DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
-        	
-        	DatabaseEntry blockDBE = new DatabaseEntry();
-        	
-        	// Check whether it already exists
-        	
-        	if(logMINOR) Logger.minor(this, "Putting key "+block+" - checking whether it exists first");
-        	OperationStatus result = chkDB.get(t, routingkeyDBE, blockDBE, LockMode.RMW);
-        	
-        	if(result == OperationStatus.SUCCESS || result == OperationStatus.KEYEXIST) {
-        		if(logMINOR) Logger.minor(this, "Key already exists");
-        		// Key already exists!
-        		// But is it valid?
-        		t.abort();
-        		if(fetchKey(block.getKey(), false) != null) return; // old key was valid, we are not overwriting
-        		// If we are here, it was corrupt, or it was just deleted, so we can replace it.
-        		if(logMINOR) Logger.minor(this, "Old key was invalid, adding anyway");
-        		innerPut(block);
-        		return;
-        	} else if(result == OperationStatus.KEYEMPTY) {
-        		Logger.error(this, "Got KEYEMPTY - record deleted? Shouldn't be possible with record locking...!");
-        		// Put it in anyway
-        	} else if(result == OperationStatus.NOTFOUND) {
-        		// Good
-        	} else
-        		throw new IllegalStateException("Unknown operation status: "+result);
-        	
-        	writeBlock(header, data, t, routingkeyDBE);
-        	
-    		t.commit();
-    		t = null;
-        	
-    		if(logMINOR) {
-    			Logger.minor(this, "Headers: "+header.length+" bytes, hash "+Fields.hashCode(header));
-    			Logger.minor(this, "Data: "+data.length+" bytes, hash "+Fields.hashCode(data)+" putting "+block.getKey());
-    		}
-                
-        }catch(Throwable ex) {  // FIXME: ugly  
-        	if(t!=null){
-        		try{t.abort();}catch(DatabaseException ex2){};
-        	}
-        	checkSecondaryDatabaseError(ex);
-        	Logger.error(this, "Caught "+ex, ex);
-        	ex.printStackTrace();
-        	if(ex instanceof IOException) throw (IOException) ex;
-        	else throw new IOException(ex.getMessage());
-        }
-    }
-    
-    private KeyBlock fetchKey(Key key, boolean b) throws IOException {
-    	if(key instanceof NodeCHK)
-    		return fetch((NodeCHK)key, b);
-    	else
-    		return fetch((NodeSSK)key, b);
+	* Store a block.
+	*/
+	private void innerPut(KeyBlock block) throws IOException {
+		synchronized(this) {
+			if(closed)
+				return;
+		}
+			
+		byte[] routingkey = block.getKey().getRoutingKey();
+		byte[] data = block.getRawData();
+		byte[] header = block.getRawHeaders();
+		
+		if(data.length!=dataBlockSize) {
+			Logger.error(this, "This data is "+data.length+" bytes. Should be "+dataBlockSize);
+			return;
+		}
+		if(header.length!=headerBlockSize) {
+			Logger.error(this, "This header is "+data.length+" bytes. Should be "+headerBlockSize);
+			return;
+		}
+		
+		Transaction t = null;
+		
+		try{
+			t = environment.beginTransaction(null,null);
+			DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+			
+			DatabaseEntry blockDBE = new DatabaseEntry();
+			
+			// Check whether it already exists
+			
+			if(logMINOR) Logger.minor(this, "Putting key "+block+" - checking whether it exists first");
+			OperationStatus result = chkDB.get(t, routingkeyDBE, blockDBE, LockMode.RMW);
+			
+			if(result == OperationStatus.SUCCESS || result == OperationStatus.KEYEXIST) {
+				if(logMINOR) Logger.minor(this, "Key already exists");
+				// Key already exists!
+				// But is it valid?
+				t.abort();
+				if(fetchKey(block.getKey(), false) != null) return; // old key was valid, we are not overwriting
+				// If we are here, it was corrupt, or it was just deleted, so we can replace it.
+				if(logMINOR) Logger.minor(this, "Old key was invalid, adding anyway");
+				innerPut(block);
+				return;
+			} else if(result == OperationStatus.KEYEMPTY) {
+				Logger.error(this, "Got KEYEMPTY - record deleted? Shouldn't be possible with record locking...!");
+				// Put it in anyway
+			} else if(result == OperationStatus.NOTFOUND) {
+				// Good
+			} else
+				throw new IllegalStateException("Unknown operation status: "+result);
+			
+			writeBlock(header, data, t, routingkeyDBE);
+			
+			t.commit();
+			t = null;
+			
+			if(logMINOR) {
+				Logger.minor(this, "Headers: "+header.length+" bytes, hash "+Fields.hashCode(header));
+				Logger.minor(this, "Data: "+data.length+" bytes, hash "+Fields.hashCode(data)+" putting "+block.getKey());
+			}
+				
+		}catch(Throwable ex) {  // FIXME: ugly
+			if(t!=null){
+				try{t.abort();}catch(DatabaseException ex2){};
+			}
+			checkSecondaryDatabaseError(ex);
+			Logger.error(this, "Caught "+ex, ex);
+			ex.printStackTrace();
+			if(ex instanceof IOException) throw (IOException) ex;
+			else throw new IOException(ex.getMessage());
+		}
 	}
+	
+	private KeyBlock fetchKey(Key key, boolean b) throws IOException {
+		if(key instanceof NodeCHK)
+			return fetch((NodeCHK)key, b);
+		else
+			return fetch((NodeSSK)key, b);
+	}
 
 	private void overwriteLRUBlock(byte[] header, byte[] data, Transaction t, DatabaseEntry routingkeyDBE) throws DatabaseException, IOException {
 		// Overwrite an other block
@@ -1935,7 +1935,7 @@
 			DatabaseEntry found = new DatabaseEntry();
 			LongBinding.longToEntry(blockNum, blockNumEntry);
 			
-			OperationStatus success = 
+			OperationStatus success =
 				chkDB_blockNum.get(t, blockNumEntry, found, LockMode.DEFAULT);
 
 			if(success == OperationStatus.KEYEXIST || success == OperationStatus.SUCCESS) {
@@ -1970,259 +1970,259 @@
 
 	private void checkSecondaryDatabaseError(Throwable ex) {
 		String msg = ex.getMessage();
-    	if((ex instanceof DatabaseException) && (msg != null && (msg.indexOf("missing key in the primary database") > -1) ||
-    			msg.indexOf("the primary record contains a key that is not present in the secondary") > -1)) {
-    		try {
+		if((ex instanceof DatabaseException) && (msg != null && (msg.indexOf("missing key in the primary database") > -1) ||
+				msg.indexOf("the primary record contains a key that is not present in the secondary") > -1)) {
+			try {
 				fixSecondaryFile.createNewFile();
 			} catch (IOException e) {
 				Logger.error(this, "Corrupt secondary database ("+getName()+") but could not create flag file "+fixSecondaryFile);
 				System.err.println("Corrupt secondary database ("+getName()+") but could not create flag file "+fixSecondaryFile);
 				return; // Not sure what else we can do
 			}
-    		Logger.error(this, "Corrupt secondary database ("+getName()+"). Should be cleaned up on restart.");
-    		System.err.println("Corrupt secondary database ("+getName()+"). Should be cleaned up on restart.");
-    		System.exit(freenet.node.Node.EXIT_DATABASE_REQUIRES_RESTART);
-    	}
+			Logger.error(this, "Corrupt secondary database ("+getName()+"). Should be cleaned up on restart.");
+			System.err.println("Corrupt secondary database ("+getName()+"). Should be cleaned up on restart.");
+			System.exit(freenet.node.Node.EXIT_DATABASE_REQUIRES_RESTART);
+		}
 	}
 
-    /**
-     * Store a pubkey.
-     */
-    public void put(byte[] hash, DSAPublicKey key) throws IOException {
-    	innerPut(hash, key);
-    }
+	/**
+	* Store a pubkey.
+	*/
+	public void put(byte[] hash, DSAPublicKey key) throws IOException {
+		innerPut(hash, key);
+	}
 
 	/**
-     * Store a block.
-     */
-    private void innerPut(byte[] hash, DSAPublicKey key) throws IOException {
-    	synchronized(this) {
-    		if(closed)
-    			return;
-    	}
-    	  	
-    	byte[] routingkey = hash;
-        byte[] data = key.asPaddedBytes();
-        
-        if(!(Arrays.equals(hash, key.asBytesHash()))) {
-        	Logger.error(this, "Invalid hash!: " + HexUtil.bytesToHex(hash) + " : " + HexUtil.bytesToHex(key.asBytesHash()));
-        }
-        
-        if(data.length!=dataBlockSize) {
-        	Logger.error(this, "This data is "+data.length+" bytes. Should be "+dataBlockSize);
-        	return;
-        }
-        
-        Transaction t = null;
-        
-        try{
-        	t = environment.beginTransaction(null,null);
-        	DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
-        	DatabaseEntry blockDBE = new DatabaseEntry();
-        	
-        	// Check whether it already exists
-        	
-        	if(logMINOR) Logger.minor(this, "Putting key: "+HexUtil.bytesToHex(hash)+" : "+key+" - checking whether it exists already...");
-        	OperationStatus result = chkDB.get(t, routingkeyDBE, blockDBE, LockMode.RMW);
-        	
-        	if(result == OperationStatus.SUCCESS || result == OperationStatus.KEYEXIST) {
-        		// Key already exists!
-        		// But is it valid?
-        		if(logMINOR)
-        			Logger.minor(this, "Putting "+HexUtil.bytesToHex(hash)+" : already exists - aborting transaction");
-        		t.abort();
-        		if(logMINOR)
-        			Logger.minor(this, "Fetching (replacing) key");
-        		if(fetchPubKey(hash, key, false) != null) {
-        			if(logMINOR) Logger.minor(this, "Fetch/replace succeeded");
-        			return; // replaced key
-        		}
-        		if(logMINOR) Logger.minor(this, "Fetch failed after key already exists");
-        		// If we are here, it was corrupt, and it got deleted before it could be replaced.
-        		innerPut(hash, key);
-        		return;
-        	} else if(result == OperationStatus.KEYEMPTY) {
-        		Logger.error(this, "Got KEYEMPTY - record deleted? Shouldn't be possible with record locking...!");
-        		// Put it in anyway
-        	} else if(result == OperationStatus.NOTFOUND) {
-        		// Good
-        	} else
-        		throw new IllegalStateException("Unknown operation status: "+result);
-        	
-        	writeBlock(dummy, data, t, routingkeyDBE);
-        	
-    		t.commit();
-    		t = null;
-        	
-    		if(logMINOR) {
-    			Logger.minor(this, "Data: "+data.length+" bytes, hash "+Fields.hashCode(data)+" putting "+HexUtil.bytesToHex(hash)+" : "+key);
-    		}
-                
-        } catch(Throwable ex) {  // FIXME: ugly  
-        	Logger.error(this, "Caught "+ex, ex);
-        	System.err.println("Caught: "+ex);
-        	ex.printStackTrace();
-        	if(t!=null){
-        		try{t.abort();}catch(DatabaseException ex2){};
-        	}
-        	checkSecondaryDatabaseError(ex);
-        	if(ex instanceof IOException) throw (IOException) ex;
-        	else throw new IOException(ex.getMessage());
-        }
-    }
-    
-    private void writeBlock(byte[] header, byte[] data, Transaction t, DatabaseEntry routingkeyDBE) throws DatabaseException, IOException {
-    	
-   		long blockNum;
-   		
-   		while(true) {
-   	       	if((blockNum = grabFreeBlock()) >= 0) {
-   	       		if(logMINOR)
-   	       			Logger.minor(this, "Overwriting free block: "+blockNum);
-   	       		if(writeNewBlock(blockNum, header, data, t, routingkeyDBE))
-   	       			return;
-   	       	} else if(chkBlocksInStore<maxChkBlocks) {
-   	       		// Expand the store file
-   	       		synchronized(chkBlocksInStoreLock) {
-   	       			blockNum = chkBlocksInStore;
-   	       			chkBlocksInStore++;
-   	       		}
-   	       		if(logMINOR)
-   	       			Logger.minor(this, "Expanding store and writing block "+blockNum);
-   	       		// Just in case
-   	       		freeBlocks.remove(blockNum);
-   	       		if(writeNewBlock(blockNum, header, data, t, routingkeyDBE))
-   	       			return;
-   	       	}else{
-   	       		if(logMINOR)
-   	       			Logger.minor(this, "Overwriting LRU block");
-   	       		overwriteLRUBlock(header, data, t, routingkeyDBE);
-   	       		return;
-   	       	}
-   			
-   		}
-   		
+	* Store a block.
+	*/
+	private void innerPut(byte[] hash, DSAPublicKey key) throws IOException {
+		synchronized(this) {
+			if(closed)
+				return;
+		}
+			
+		byte[] routingkey = hash;
+		byte[] data = key.asPaddedBytes();
+		
+		if(!(Arrays.equals(hash, key.asBytesHash()))) {
+			Logger.error(this, "Invalid hash!: " + HexUtil.bytesToHex(hash) + " : " + HexUtil.bytesToHex(key.asBytesHash()));
+		}
+		
+		if(data.length!=dataBlockSize) {
+			Logger.error(this, "This data is "+data.length+" bytes. Should be "+dataBlockSize);
+			return;
+		}
+		
+		Transaction t = null;
+		
+		try{
+			t = environment.beginTransaction(null,null);
+			DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+			DatabaseEntry blockDBE = new DatabaseEntry();
+			
+			// Check whether it already exists
+			
+			if(logMINOR) Logger.minor(this, "Putting key: "+HexUtil.bytesToHex(hash)+" : "+key+" - checking whether it exists already...");
+			OperationStatus result = chkDB.get(t, routingkeyDBE, blockDBE, LockMode.RMW);
+			
+			if(result == OperationStatus.SUCCESS || result == OperationStatus.KEYEXIST) {
+				// Key already exists!
+				// But is it valid?
+				if(logMINOR)
+					Logger.minor(this, "Putting "+HexUtil.bytesToHex(hash)+" : already exists - aborting transaction");
+				t.abort();
+				if(logMINOR)
+					Logger.minor(this, "Fetching (replacing) key");
+				if(fetchPubKey(hash, key, false) != null) {
+					if(logMINOR) Logger.minor(this, "Fetch/replace succeeded");
+					return; // replaced key
+				}
+				if(logMINOR) Logger.minor(this, "Fetch failed after key already exists");
+				// If we are here, it was corrupt, and it got deleted before it could be replaced.
+				innerPut(hash, key);
+				return;
+			} else if(result == OperationStatus.KEYEMPTY) {
+				Logger.error(this, "Got KEYEMPTY - record deleted? Shouldn't be possible with record locking...!");
+				// Put it in anyway
+			} else if(result == OperationStatus.NOTFOUND) {
+				// Good
+			} else
+				throw new IllegalStateException("Unknown operation status: "+result);
+			
+			writeBlock(dummy, data, t, routingkeyDBE);
+			
+			t.commit();
+			t = null;
+			
+			if(logMINOR) {
+				Logger.minor(this, "Data: "+data.length+" bytes, hash "+Fields.hashCode(data)+" putting "+HexUtil.bytesToHex(hash)+" : "+key);
+			}
+				
+		} catch(Throwable ex) {  // FIXME: ugly
+			Logger.error(this, "Caught "+ex, ex);
+			System.err.println("Caught: "+ex);
+			ex.printStackTrace();
+			if(t!=null){
+				try{t.abort();}catch(DatabaseException ex2){};
+			}
+			checkSecondaryDatabaseError(ex);
+			if(ex instanceof IOException) throw (IOException) ex;
+			else throw new IOException(ex.getMessage());
+		}
 	}
+	
+	private void writeBlock(byte[] header, byte[] data, Transaction t, DatabaseEntry routingkeyDBE) throws DatabaseException, IOException {
+		
+		long blockNum;
+		
+		while(true) {
+				if((blockNum = grabFreeBlock()) >= 0) {
+					if(logMINOR)
+						Logger.minor(this, "Overwriting free block: "+blockNum);
+					if(writeNewBlock(blockNum, header, data, t, routingkeyDBE))
+						return;
+				} else if(chkBlocksInStore<maxChkBlocks) {
+					// Expand the store file
+					synchronized(chkBlocksInStoreLock) {
+						blockNum = chkBlocksInStore;
+						chkBlocksInStore++;
+					}
+					if(logMINOR)
+						Logger.minor(this, "Expanding store and writing block "+blockNum);
+					// Just in case
+					freeBlocks.remove(blockNum);
+					if(writeNewBlock(blockNum, header, data, t, routingkeyDBE))
+						return;
+				}else{
+					if(logMINOR)
+						Logger.minor(this, "Overwriting LRU block");
+					overwriteLRUBlock(header, data, t, routingkeyDBE);
+					return;
+				}
+			
+		}
+		
+	}
 
 	private long grabFreeBlock() {
-    	while(!freeBlocks.isEmpty()) {
-    		long blockNum = freeBlocks.removeFirst();
-    		if(blockNum < maxChkBlocks) return blockNum;
-    	}
+		while(!freeBlocks.isEmpty()) {
+			long blockNum = freeBlocks.removeFirst();
+			if(blockNum < maxChkBlocks) return blockNum;
+		}
 		return -1;
 	}
 
 	private class StoreBlock {
-    	private long recentlyUsed;
-    	private long offset;
-    	
-    	public StoreBlock(final BerkeleyDBFreenetStore bdbfs, long offset) {
-    		this(offset, bdbfs.getNewRecentlyUsed());
-    	}
-    	
-    	public StoreBlock(long offset,long recentlyUsed) {
-    		this.offset = offset;
-    		this.recentlyUsed = recentlyUsed;
-    	}
-    	    	
-   	
-    	public long getRecentlyUsed() {
-    		return recentlyUsed;
-    	}
-    	
-    	public void setRecentlyUsedToZero() {
-    		recentlyUsed = 0;
-    	}
-    	
-    	public void updateRecentlyUsed() {
-    		recentlyUsed = getNewRecentlyUsed();
-    	}
-    	
-    	public long getOffset() {
-    		return offset;
-    	}
-    }
-    
-    /**
-     * Convert StoreBlock's to the format used by the database
-     */
-    private class StoreBlockTupleBinding extends TupleBinding {
+		private long recentlyUsed;
+		private long offset;
+		
+		public StoreBlock(final BerkeleyDBFreenetStore bdbfs, long offset) {
+			this(offset, bdbfs.getNewRecentlyUsed());
+		}
+		
+		public StoreBlock(long offset,long recentlyUsed) {
+			this.offset = offset;
+			this.recentlyUsed = recentlyUsed;
+		}
+				
+	
+		public long getRecentlyUsed() {
+			return recentlyUsed;
+		}
+		
+		public void setRecentlyUsedToZero() {
+			recentlyUsed = 0;
+		}
+		
+		public void updateRecentlyUsed() {
+			recentlyUsed = getNewRecentlyUsed();
+		}
+		
+		public long getOffset() {
+			return offset;
+		}
+	}
+	
+	/**
+	* Convert StoreBlock's to the format used by the database
+	*/
+	private class StoreBlockTupleBinding extends TupleBinding {
 
-    	public void objectToEntry(Object object, TupleOutput to)  {
-    		StoreBlock myData = (StoreBlock)object;
+		public void objectToEntry(Object object, TupleOutput to)  {
+			StoreBlock myData = (StoreBlock)object;
 
-    		to.writeLong(myData.getOffset());
-    		to.writeLong(myData.getRecentlyUsed());
-    	}
+			to.writeLong(myData.getOffset());
+			to.writeLong(myData.getRecentlyUsed());
+		}
 
-    	public Object entryToObject(TupleInput ti) {
-    		if(Logger.shouldLog(Logger.DEBUG, this))
-    			Logger.debug(this, "Available: "+ti.available());
-    		long offset = ti.readLong();
-	    	long lastAccessed = ti.readLong();
-	    	
-	    	StoreBlock storeBlock = new StoreBlock(offset,lastAccessed);
-	    	return storeBlock;
-    	}
-    }
-      
-    /**
-     * Used to create the secondary database sorted on accesstime
-     */
-    private class AccessTimeKeyCreator implements SecondaryKeyCreator {
-    	private TupleBinding theBinding;
-    	
-    	public AccessTimeKeyCreator(TupleBinding theBinding1) {
-    		theBinding = theBinding1;
-    	}
-    	
-    	public boolean createSecondaryKey(SecondaryDatabase secDb,
-    	DatabaseEntry keyEntry,
-    	DatabaseEntry dataEntry,
-    	DatabaseEntry resultEntry) {
+		public Object entryToObject(TupleInput ti) {
+			if(Logger.shouldLog(Logger.DEBUG, this))
+				Logger.debug(this, "Available: "+ti.available());
+			long offset = ti.readLong();
+			long lastAccessed = ti.readLong();
+			
+			StoreBlock storeBlock = new StoreBlock(offset,lastAccessed);
+			return storeBlock;
+		}
+	}
+	
+	/**
+	* Used to create the secondary database sorted on accesstime
+	*/
+	private class AccessTimeKeyCreator implements SecondaryKeyCreator {
+		private TupleBinding theBinding;
+		
+		public AccessTimeKeyCreator(TupleBinding theBinding1) {
+			theBinding = theBinding1;
+		}
+		
+		public boolean createSecondaryKey(SecondaryDatabase secDb,
+		DatabaseEntry keyEntry,
+		DatabaseEntry dataEntry,
+		DatabaseEntry resultEntry) {
 
-    		StoreBlock storeblock = (StoreBlock) theBinding.entryToObject(dataEntry);
-    		LongBinding.longToEntry(storeblock.getRecentlyUsed(), resultEntry);
-    		return true;
-    	}
-    }
+			StoreBlock storeblock = (StoreBlock) theBinding.entryToObject(dataEntry);
+			LongBinding.longToEntry(storeblock.getRecentlyUsed(), resultEntry);
+			return true;
+		}
+	}
 
-    private class BlockNumberKeyCreator implements SecondaryKeyCreator {
-    	private TupleBinding theBinding;
-    	
-    	public BlockNumberKeyCreator(TupleBinding theBinding1) {
-    		theBinding = theBinding1;
-    	}
-    	
-    	public boolean createSecondaryKey(SecondaryDatabase secDb,
-   	    	DatabaseEntry keyEntry,
-   	    	DatabaseEntry dataEntry,
-   	    	DatabaseEntry resultEntry) {
+	private class BlockNumberKeyCreator implements SecondaryKeyCreator {
+		private TupleBinding theBinding;
+		
+		public BlockNumberKeyCreator(TupleBinding theBinding1) {
+			theBinding = theBinding1;
+		}
+		
+		public boolean createSecondaryKey(SecondaryDatabase secDb,
+			DatabaseEntry keyEntry,
+			DatabaseEntry dataEntry,
+			DatabaseEntry resultEntry) {
 
-   	    		StoreBlock storeblock = (StoreBlock) theBinding.entryToObject(dataEntry);
-   	    		LongBinding.longToEntry(storeblock.offset, resultEntry);
-   	    		return true;
-   	    	}
-    	
-    }
-    
-    private class ShutdownHook extends Thread {
-    	public void run() {
-    		System.err.println("Closing database due to shutdown.");
-    		close(true);
-    	}
-    }
-    
-    private Object closeLock = new Object();
-    
-    private void close(boolean sleep) {
-    	try{
+				StoreBlock storeblock = (StoreBlock) theBinding.entryToObject(dataEntry);
+				LongBinding.longToEntry(storeblock.offset, resultEntry);
+				return true;
+			}
+		
+	}
+	
+	private class ShutdownHook extends Thread {
+		public void run() {
+			System.err.println("Closing database due to shutdown.");
+			close(true);
+		}
+	}
+	
+	private Object closeLock = new Object();
+	
+	private void close(boolean sleep) {
+		try{
 			// FIXME: 	we should be sure all access to the database has stopped
 			//			before we try to close it. Currently we just guess
-    		//			This is nothing too problematic however since the worst thing that should
-    		//			happen is that we miss the last few store()'s and get an exception.
-    		logMINOR = Logger.shouldLog(Logger.MINOR, this);
-    		if(logMINOR) Logger.minor(this, "Closing database "+this);
+			//			This is nothing too problematic however since the worst thing that should
+			//			happen is that we miss the last few store()'s and get an exception.
+			logMINOR = Logger.shouldLog(Logger.MINOR, this);
+			if(logMINOR) Logger.minor(this, "Closing database "+this);
 			closed=true;
 			if(reallyClosed) {
 				Logger.error(this, "Already closed "+this);
@@ -2276,44 +2276,44 @@
 				// Return anyway
 			}
 		}
-    }
-    
-    private long highestBlockNumberInDatabase() throws DatabaseException {
-    	Cursor c = null;
-    	try {
-    		c = chkDB_blockNum.openCursor(null,null);
-    		DatabaseEntry keyDBE = new DatabaseEntry();
-    		DatabaseEntry dataDBE = new DatabaseEntry();
-    		if(c.getLast(keyDBE,dataDBE,null)==OperationStatus.SUCCESS) {
-    			StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(dataDBE);
-    			return storeBlock.offset + 1;
-    		}
-    		c.close();
-    		c = null;
-    	} finally {
-    		if(c != null) {
-    			try {
-    				c.close();
-    			} catch (DatabaseException e) {
-    				Logger.error(this, "Caught "+e, e);
-    			}
-    		}
-    	}
+	}
+	
+	private long highestBlockNumberInDatabase() throws DatabaseException {
+		Cursor c = null;
+		try {
+			c = chkDB_blockNum.openCursor(null,null);
+			DatabaseEntry keyDBE = new DatabaseEntry();
+			DatabaseEntry dataDBE = new DatabaseEntry();
+			if(c.getLast(keyDBE,dataDBE,null)==OperationStatus.SUCCESS) {
+				StoreBlock storeBlock = (StoreBlock) storeBlockTupleBinding.entryToObject(dataDBE);
+				return storeBlock.offset + 1;
+			}
+			c.close();
+			c = null;
+		} finally {
+			if(c != null) {
+				try {
+					c.close();
+				} catch (DatabaseException e) {
+					Logger.error(this, "Caught "+e, e);
+				}
+			}
+		}
 		return 0;
-    }
-    
+	}
+	
 	private long countCHKBlocksFromFile() throws IOException {
 		int keySize = headerBlockSize + dataBlockSize;
 		long fileSize = chkStore.length();
 		return fileSize / keySize;
 	}
 
-    private long getMaxRecentlyUsed() {
-    	long maxRecentlyUsed = 0;
-    	
-    	Cursor c = null;
-    	try{
-	    	c = chkDB_accessTime.openCursor(null,null);
+	private long getMaxRecentlyUsed() {
+		long maxRecentlyUsed = 0;
+		
+		Cursor c = null;
+		try{
+			c = chkDB_accessTime.openCursor(null,null);
 			DatabaseEntry keyDBE = new DatabaseEntry();
 			DatabaseEntry dataDBE = new DatabaseEntry();
 			if(c.getLast(keyDBE,dataDBE,null)==OperationStatus.SUCCESS) {
@@ -2322,27 +2322,27 @@
 			}
 			c.close();
 			c = null;
-    	} catch(DatabaseException ex) {
-    		ex.printStackTrace();
-    	} finally {
-    		if(c != null) {
-    			try {
-    				c.close();
-    			} catch (DatabaseException e) {
-    				Logger.error(this, "Caught "+e, e);
-    			}
-    		}
-    	}
-    	
-    	return maxRecentlyUsed;
-    }
-    
-    private long getNewRecentlyUsed() {
-    	synchronized(lastRecentlyUsedSync) {
-    		lastRecentlyUsed++;
-    		return lastRecentlyUsed;
-    	}
-    }
+		} catch(DatabaseException ex) {
+			ex.printStackTrace();
+		} finally {
+			if(c != null) {
+				try {
+					c.close();
+				} catch (DatabaseException e) {
+					Logger.error(this, "Caught "+e, e);
+				}
+			}
+		}
+		
+		return maxRecentlyUsed;
+	}
+	
+	private long getNewRecentlyUsed() {
+		synchronized(lastRecentlyUsedSync) {
+			lastRecentlyUsed++;
+			return lastRecentlyUsed;
+		}
+	}
 
 	public void setMaxKeys(long maxStoreKeys, boolean forceBigShrink) throws DatabaseException, IOException {
 		synchronized(this) {
@@ -2350,10 +2350,10 @@
 		}
 		maybeOnlineShrink(false);
 	}
-    
-    public long getMaxKeys() {
-        return maxChkBlocks;
-    }
+	
+	public long getMaxKeys() {
+		return maxChkBlocks;
+	}
 
 	public long hits() {
 		return hits;




More information about the cvs mailing list