Author: toad
Date: 2008-01-05 19:35:42 +0000 (Sat, 05 Jan 2008)
New Revision: 16909
Added:
trunk/freenet/src/freenet/keys/PubkeyVerifyException.java
trunk/freenet/src/freenet/store/CHKStore.java
trunk/freenet/src/freenet/store/PubkeyStore.java
trunk/freenet/src/freenet/store/SSKStore.java
trunk/freenet/src/freenet/store/StorableBlock.java
trunk/freenet/src/freenet/store/StoreCallback.java
Modified:
trunk/freenet/src/freenet/crypt/DSAPublicKey.java
trunk/freenet/src/freenet/keys/CHKBlock.java
trunk/freenet/src/freenet/keys/KeyBlock.java
trunk/freenet/src/freenet/keys/NodeCHK.java
trunk/freenet/src/freenet/keys/NodeSSK.java
trunk/freenet/src/freenet/keys/SSKBlock.java
trunk/freenet/src/freenet/node/Node.java
trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
trunk/freenet/src/freenet/store/FreenetStore.java
Log:
Factor out CHK/SSK/pubkey construction/serialisation logic, maximum block size
etc to StoreCallback variants SSK/CHK/PubkeyStore.
Modified: trunk/freenet/src/freenet/crypt/DSAPublicKey.java
===================================================================
--- trunk/freenet/src/freenet/crypt/DSAPublicKey.java 2008-01-05 11:14:06 UTC
(rev 16908)
+++ trunk/freenet/src/freenet/crypt/DSAPublicKey.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -7,18 +7,21 @@
import java.math.BigInteger;
import net.i2p.util.NativeBigInteger;
+import freenet.store.StorableBlock;
import freenet.support.Base64;
import freenet.support.HexUtil;
import freenet.support.IllegalBase64Exception;
import freenet.support.SimpleFieldSet;
-public class DSAPublicKey extends CryptoKey {
+public class DSAPublicKey extends CryptoKey implements StorableBlock {
private static final long serialVersionUID = -1;
private final BigInteger y;
public static final int PADDED_SIZE = 1024;
+ public static final int HASH_LENGTH = 32;
+
private final DSAGroup group;
private byte[] fingerprint = null;
@@ -188,4 +191,12 @@
new NativeBigInteger(1, Base64.decode(set.get("y")));
return new DSAPublicKey(group, x);
}
+
+ public byte[] getFullKey() {
+ return asBytesHash();
+ }
+
+ public byte[] getRoutingKey() {
+ return asBytesHash();
+ }
}
Modified: trunk/freenet/src/freenet/keys/CHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/CHKBlock.java 2008-01-05 11:14:06 UTC
(rev 16908)
+++ trunk/freenet/src/freenet/keys/CHKBlock.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -104,4 +104,12 @@
public byte[] getPubkeyBytes() {
return null;
}
+
+ public byte[] getFullKey() {
+ return getKey().getFullKey();
+ }
+
+ public byte[] getRoutingKey() {
+ return getKey().getRoutingKey();
+ }
}
Modified: trunk/freenet/src/freenet/keys/KeyBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/KeyBlock.java 2008-01-05 11:14:06 UTC
(rev 16908)
+++ trunk/freenet/src/freenet/keys/KeyBlock.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -3,10 +3,12 @@
* http://www.gnu.org/ for further details of the GPL. */
package freenet.keys;
+import freenet.store.StorableBlock;
+
/**
* Interface for fetched blocks. Can be decoded with a key.
*/
-public interface KeyBlock {
+public interface KeyBlock extends StorableBlock {
final static int HASH_SHA256 = 1;
Modified: trunk/freenet/src/freenet/keys/NodeCHK.java
===================================================================
--- trunk/freenet/src/freenet/keys/NodeCHK.java 2008-01-05 11:14:06 UTC (rev
16908)
+++ trunk/freenet/src/freenet/keys/NodeCHK.java 2008-01-05 19:35:42 UTC (rev
16909)
@@ -27,7 +27,7 @@
this.cryptoAlgorithm = cryptoAlgorithm;
}
- static final int KEY_LENGTH = 32;
+ public static final int KEY_LENGTH = 32;
/** Crypto algorithm */
final byte cryptoAlgorithm;
Modified: trunk/freenet/src/freenet/keys/NodeSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/NodeSSK.java 2008-01-05 11:14:06 UTC (rev
16908)
+++ trunk/freenet/src/freenet/keys/NodeSSK.java 2008-01-05 19:35:42 UTC (rev
16909)
@@ -44,6 +44,7 @@
static final int E_H_DOCNAME_SIZE = 32;
static final byte BASE_TYPE = 2;
public static final int FULL_KEY_LENGTH = 66;
+ public static final int ROUTING_KEY_LENGTH = 32;
public String toString() {
return
super.toString()+":pkh="+HexUtil.bytesToHex(pubKeyHash)+":ehd="+HexUtil.bytesToHex(encryptedHashedDocname);
Added: trunk/freenet/src/freenet/keys/PubkeyVerifyException.java
===================================================================
--- trunk/freenet/src/freenet/keys/PubkeyVerifyException.java
(rev 0)
+++ trunk/freenet/src/freenet/keys/PubkeyVerifyException.java 2008-01-05
19:35:42 UTC (rev 16909)
@@ -0,0 +1,11 @@
+package freenet.keys;
+
+import freenet.crypt.CryptFormatException;
+
+public class PubkeyVerifyException extends KeyVerifyException {
+
+ public PubkeyVerifyException(CryptFormatException e) {
+ super(e);
+ }
+
+}
Modified: trunk/freenet/src/freenet/keys/SSKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/SSKBlock.java 2008-01-05 11:14:06 UTC
(rev 16908)
+++ trunk/freenet/src/freenet/keys/SSKBlock.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -167,4 +167,12 @@
return pubKey.asBytes();
}
+ public byte[] getFullKey() {
+ return getKey().getFullKey();
+ }
+
+ public byte[] getRoutingKey() {
+ return getKey().getRoutingKey();
+ }
+
}
Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java 2008-01-05 11:14:06 UTC (rev
16908)
+++ trunk/freenet/src/freenet/node/Node.java 2008-01-05 19:35:42 UTC (rev
16909)
@@ -87,8 +87,11 @@
import freenet.pluginmanager.ForwardPort;
import freenet.pluginmanager.PluginManager;
import freenet.store.BerkeleyDBFreenetStore;
+import freenet.store.CHKStore;
import freenet.store.FreenetStore;
import freenet.store.KeyCollisionException;
+import freenet.store.PubkeyStore;
+import freenet.store.SSKStore;
import freenet.support.DoubleTokenBucket;
import freenet.support.Executor;
import freenet.support.Fields;
@@ -1315,28 +1318,28 @@
Logger.normal(this, "Initializing CHK Datastore");
System.out.println("Initializing CHK Datastore
("+maxStoreKeys+" keys)");
chkDatastore =
BerkeleyDBFreenetStore.construct(lastVersion, storeDir, true, suffix,
maxStoreKeys,
- CHKBlock.DATA_LENGTH,
CHKBlock.TOTAL_HEADERS_LENGTH, true, BerkeleyDBFreenetStore.TYPE_CHK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this);
+ true, BerkeleyDBFreenetStore.TYPE_CHK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this,
new CHKStore());
Logger.normal(this, "Initializing CHK Datacache");
System.out.println("Initializing CHK Datacache
("+maxCacheKeys+ ':' +maxCacheKeys+" keys)");
chkDatacache =
BerkeleyDBFreenetStore.construct(lastVersion, storeDir, false, suffix,
maxCacheKeys,
- CHKBlock.DATA_LENGTH,
CHKBlock.TOTAL_HEADERS_LENGTH, true, BerkeleyDBFreenetStore.TYPE_CHK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this);
+ true, BerkeleyDBFreenetStore.TYPE_CHK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this,
new CHKStore());
Logger.normal(this, "Initializing pubKey Datastore");
System.out.println("Initializing pubKey Datastore");
pubKeyDatastore =
BerkeleyDBFreenetStore.construct(lastVersion, storeDir, true, suffix,
maxStoreKeys,
- DSAPublicKey.PADDED_SIZE, 0, true,
BerkeleyDBFreenetStore.TYPE_PUBKEY, storeEnvironment, random,
storeShutdownHook, tryDbLoad, reconstructFile, this);
+ true,
BerkeleyDBFreenetStore.TYPE_PUBKEY, storeEnvironment, random,
storeShutdownHook, tryDbLoad, reconstructFile, this, new PubkeyStore());
Logger.normal(this, "Initializing pubKey Datacache");
System.out.println("Initializing pubKey Datacache
("+maxCacheKeys+" keys)");
pubKeyDatacache =
BerkeleyDBFreenetStore.construct(lastVersion, storeDir, false, suffix,
maxCacheKeys,
- DSAPublicKey.PADDED_SIZE, 0, true,
BerkeleyDBFreenetStore.TYPE_PUBKEY, storeEnvironment, random,
storeShutdownHook, tryDbLoad, reconstructFile, this);
+ true,
BerkeleyDBFreenetStore.TYPE_PUBKEY, storeEnvironment, random,
storeShutdownHook, tryDbLoad, reconstructFile, this, new PubkeyStore());
// FIXME can't auto-fix SSK stores.
Logger.normal(this, "Initializing SSK Datastore");
System.out.println("Initializing SSK Datastore");
sskDatastore =
BerkeleyDBFreenetStore.construct(lastVersion, storeDir, true, suffix,
maxStoreKeys,
- SSKBlock.DATA_LENGTH,
SSKBlock.TOTAL_HEADERS_LENGTH, false, BerkeleyDBFreenetStore.TYPE_SSK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this);
+ false, BerkeleyDBFreenetStore.TYPE_SSK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this,
new SSKStore(this));
Logger.normal(this, "Initializing SSK Datacache");
System.out.println("Initializing SSK Datacache
("+maxCacheKeys+" keys)");
sskDatacache =
BerkeleyDBFreenetStore.construct(lastVersion, storeDir, false, suffix,
maxStoreKeys,
- SSKBlock.DATA_LENGTH,
SSKBlock.TOTAL_HEADERS_LENGTH, false, BerkeleyDBFreenetStore.TYPE_SSK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this);
+ false, BerkeleyDBFreenetStore.TYPE_SSK,
storeEnvironment, random, storeShutdownHook, tryDbLoad, reconstructFile, this,
new SSKStore(this));
} catch (FileNotFoundException e1) {
String msg = "Could not open datastore: "+e1;
Logger.error(this, msg, e1);
Modified: trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
===================================================================
--- trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2008-01-05
11:14:06 UTC (rev 16908)
+++ trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2008-01-05
19:35:42 UTC (rev 16909)
@@ -89,6 +89,9 @@
private final RandomAccessFile lruRAF;
private final SortedLongSet freeBlocks;
private final String name;
+ /** Callback which translates records to blocks and back, specifies the
size of blocks etc. */
+ private final StoreCallback callback;
+ private final boolean collidable;
private long lastRecentlyUsed;
private final Object lastRecentlyUsedSync = new Object();
@@ -108,9 +111,9 @@
}
public static BerkeleyDBFreenetStore construct(int lastVersion, File
baseStoreDir, boolean isStore,
- String suffix, long maxStoreKeys, int blockSize, int
headerSize, boolean throwOnTooFewKeys,
+ String suffix, long maxStoreKeys, boolean
throwOnTooFewKeys,
short type, Environment storeEnvironment, RandomSource
random,
- SemiOrderedShutdownHook storeShutdownHook, boolean
tryDbLoad, File reconstructFile, GetPubkey pubkeyCache) throws
DatabaseException, IOException {
+ SemiOrderedShutdownHook storeShutdownHook, boolean
tryDbLoad, File reconstructFile, GetPubkey pubkeyCache, StoreCallback callback)
throws DatabaseException, IOException {
/**
* Migration strategy:
@@ -134,13 +137,10 @@
File newStoreFile = new File(baseStoreDir, newStoreFileName);
File lruFile = new File(baseStoreDir, newStoreFileName+".lru");
- int keyLength;
File keysFile;
- if(type == TYPE_SSK) {
- keyLength = NodeSSK.FULL_KEY_LENGTH;
+ if(callback.storeFullKeys()) {
keysFile = new File(baseStoreDir,
newStoreFileName+".keys");
} else {
- keyLength = 0;
keysFile = null;
}
@@ -158,7 +158,7 @@
// Don't need to create a new Environment, since we can
use the old one.
tmp = openStore(storeEnvironment, baseStoreDir,
newDBPrefix, newStoreFile, lruFile, keysFile, newFixSecondaryFile, maxStoreKeys,
- blockSize, headerSize,
throwOnTooFewKeys, false, lastVersion, type, false, storeShutdownHook,
tryDbLoad, reconstructFile, keyLength, pubkeyCache);
+ throwOnTooFewKeys, false, lastVersion,
type, false, storeShutdownHook, tryDbLoad, reconstructFile, pubkeyCache,
callback);
} else {
@@ -166,8 +166,8 @@
// Start from scratch, with new store.
tmp = openStore(storeEnvironment, baseStoreDir,
newDBPrefix, newStoreFile, lruFile, keysFile, newFixSecondaryFile,
- maxStoreKeys, blockSize, headerSize,
throwOnTooFewKeys, false, lastVersion, type,
- false, storeShutdownHook, tryDbLoad,
reconstructFile, keyLength, pubkeyCache);
+ maxStoreKeys, throwOnTooFewKeys, false,
lastVersion, type,
+ false, storeShutdownHook, tryDbLoad,
reconstructFile, pubkeyCache, callback);
}
@@ -175,9 +175,9 @@
}
private static BerkeleyDBFreenetStore openStore(Environment
storeEnvironment, File baseDir, String newDBPrefix, File newStoreFile,
- File lruFile, File keysFile, File newFixSecondaryFile,
long maxStoreKeys, int blockSize, int headerSize, boolean throwOnTooFewKeys,
+ File lruFile, File keysFile, File newFixSecondaryFile,
long maxStoreKeys, boolean throwOnTooFewKeys,
boolean noCheck, int lastVersion, short type, boolean
wipe, SemiOrderedShutdownHook storeShutdownHook,
- boolean tryDbLoad, File reconstructFile, int keyLength,
GetPubkey pubkeyCache) throws DatabaseException, IOException {
+ boolean tryDbLoad, File reconstructFile, GetPubkey
pubkeyCache, StoreCallback callback) throws DatabaseException, IOException {
if(tryDbLoad) {
String dbName = newDBPrefix+"CHK";
@@ -207,8 +207,8 @@
try {
// First try just opening it.
return new BerkeleyDBFreenetStore(type,
storeEnvironment, newDBPrefix, newStoreFile, lruFile, keysFile,
newFixSecondaryFile,
- maxStoreKeys, blockSize, headerSize,
throwOnTooFewKeys, noCheck, wipe, storeShutdownHook,
- reconstructFile, keyLength);
+ maxStoreKeys, throwOnTooFewKeys,
noCheck, wipe, storeShutdownHook,
+ reconstructFile, callback);
} catch (DatabaseException e) {
// Try a reconstruct
@@ -221,8 +221,8 @@
BerkeleyDBFreenetStore.wipeOldDatabases(storeEnvironment, newDBPrefix);
newStoreFile.delete();
return new BerkeleyDBFreenetStore(type,
storeEnvironment, newDBPrefix, newStoreFile, lruFile, keysFile,
newFixSecondaryFile,
- maxStoreKeys, blockSize,
headerSize, throwOnTooFewKeys, noCheck, wipe, storeShutdownHook,
- reconstructFile, keyLength);
+ maxStoreKeys,
throwOnTooFewKeys, noCheck, wipe, storeShutdownHook,
+ reconstructFile, callback);
}
System.err.println("Attempting to reconstruct
index...");
@@ -231,7 +231,7 @@
// Reconstruct
return new BerkeleyDBFreenetStore(storeEnvironment,
newDBPrefix, newStoreFile, lruFile, keysFile, newFixSecondaryFile,
- maxStoreKeys, blockSize, headerSize,
type, noCheck, storeShutdownHook, reconstructFile, keyLength, pubkeyCache);
+ maxStoreKeys, type, noCheck,
storeShutdownHook, reconstructFile, pubkeyCache, callback);
}
}
@@ -255,12 +255,15 @@
* @throws DatabaseException
* @throws FileNotFoundException if the dir does not exist and could not
be created
*/
- private BerkeleyDBFreenetStore(short type, Environment env, String
prefix, File storeFile, File lruFile, File keysFile, File fixSecondaryFile,
long maxChkBlocks, int blockSize, int headerSize, boolean throwOnTooFewKeys,
boolean noCheck, boolean wipe, SemiOrderedShutdownHook storeShutdownHook, File
reconstructFile, int keyLength) throws IOException, DatabaseException {
+ private BerkeleyDBFreenetStore(short type, Environment env, String
prefix, File storeFile, File lruFile, File keysFile, File fixSecondaryFile,
long maxChkBlocks, boolean throwOnTooFewKeys, boolean noCheck, boolean wipe,
SemiOrderedShutdownHook storeShutdownHook, File reconstructFile, StoreCallback
callback) throws IOException, DatabaseException {
logMINOR = Logger.shouldLog(Logger.MINOR, this);
+ this.callback = callback;
+ this.collidable = callback.collisionPossible();
+ this.dataBlockSize = callback.dataLength();
+ this.headerBlockSize = callback.headerLength();
+ this.keyLength = callback.fullKeyLength();
+ callback.setStore(this);
this.storeType = type;
- this.keyLength = keyLength;
- this.dataBlockSize = blockSize;
- this.headerBlockSize = headerSize;
this.freeBlocks = new SortedLongSet();
name = prefix;
@@ -1022,12 +1025,15 @@
* @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
*/
- private BerkeleyDBFreenetStore(Environment env, String prefix, File
storeFile, File lruFile, File keysFile, File fixSecondaryFile, long
maxChkBlocks, int blockSize, int headerSize, short type, boolean noCheck,
SemiOrderedShutdownHook storeShutdownHook, File reconstructFile, int keyLength,
GetPubkey pubkeyCache) throws DatabaseException, IOException {
+ private BerkeleyDBFreenetStore(Environment env, String prefix, File
storeFile, File lruFile, File keysFile, File fixSecondaryFile, long
maxChkBlocks, short type, boolean noCheck, SemiOrderedShutdownHook
storeShutdownHook, File reconstructFile, GetPubkey pubkeyCache, StoreCallback
callback) throws DatabaseException, IOException {
logMINOR = Logger.shouldLog(Logger.MINOR, this);
this.storeType = type;
- this.keyLength = keyLength;
- this.dataBlockSize = blockSize;
- this.headerBlockSize = headerSize;
+ this.callback = callback;
+ this.keyLength = callback.fullKeyLength();
+ this.dataBlockSize = callback.dataLength();
+ this.headerBlockSize = callback.headerLength();
+ this.collidable = callback.collisionPossible();
+ callback.setStore(this);
this.freeBlocks = new SortedLongSet();
this.maxBlocksInStore=maxChkBlocks;
this.environment = env;
@@ -1410,6 +1416,123 @@
}
/**
+ * Retrieve a block.
+ * @param dontPromote If true, don't promote data to the top of the LRU
if we fetch it.
+ * @return null if there is no such block stored, otherwise the block.
+ */
+ public StorableBlock fetch(byte[] routingkey, byte[] fullKey, boolean
dontPromote) throws IOException {
+ synchronized(this) {
+ if(closed)
+ return null;
+ }
+
+ DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+ DatabaseEntry blockDBE = new DatabaseEntry();
+ Cursor c = null;
+ Transaction t = null;
+ try {
+ t = environment.beginTransaction(null,null);
+ c = keysDB.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
"+HexUtil.bytesToHex(routingkey)+" 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);
+
+ StorableBlock block = null;
+
+ try {
+ byte[] header = new byte[headerBlockSize];
+ byte[] data = new byte[dataBlockSize];
+ try {
+ synchronized(storeRAF) {
+
storeRAF.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
+ storeRAF.readFully(header);
+ storeRAF.readFully(data);
+ }
+ } catch (EOFException e) {
+ Logger.error(this, "No block");
+ c.close();
+ c = null;
+ keysDB.delete(t, routingkeyDBE);
+ t.commit();
+ t = null;
+ addFreeBlock(storeBlock.offset, true,
"Data off end of store file");
+ return null;
+ }
+
+ block = callback.construct(data, header,
routingkey, fullKey);
+
+ if(!dontPromote) {
+ storeBlock.updateRecentlyUsed();
+ DatabaseEntry updateDBE = new
DatabaseEntry();
+
storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
+ c.putCurrent(updateDBE);
+ c.close();
+ c = null;
+ t.commit();
+ t = null;
+ synchronized(storeRAF) {
+ lruRAF.seek(storeBlock.offset *
8);
+
lruRAF.writeLong(storeBlock.recentlyUsed);
+ }
+ } 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 " +
HexUtil.bytesToHex(routingkey));
+ }
+
+ } catch(SSKVerifyException ex) {
+ Logger.normal(this, "SSKBlock: Does not verify
("+ex+"), setting accessTime to 0 for : "+HexUtil.bytesToHex(routingkey), ex);
+ keysDB.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());
+ }
+ }
+
+ /**
* 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.
@@ -1718,6 +1841,23 @@
innerPut(b);
}
+ public void put(StorableBlock block, byte[] routingkey, byte[] fullKey,
byte[] data, byte[] header,
+ boolean overwrite) throws KeyCollisionException,
IOException {
+ StorableBlock oldBlock = fetch(routingkey, fullKey, false);
+ if(oldBlock != null) {
+ if(collidable) return;
+ if(!block.equals(oldBlock)) {
+ if(!overwrite)
+ throw new KeyCollisionException();
+ else {
+ overwrite(block, routingkey, fullKey,
data, header);
+ }
+ } else {
+ innerPut(block, routingkey, fullKey, data,
header);
+ }
+ }
+ }
+
public void put(SSKBlock b, boolean overwrite) throws IOException,
KeyCollisionException {
assert(storeType == TYPE_SSK);
NodeSSK ssk = (NodeSSK) b.getKey();
@@ -1735,6 +1875,67 @@
}
}
+ private boolean overwrite(StorableBlock block, byte[] routingkey,
byte[] fullKey, byte[] data, byte[] header) throws IOException {
+ synchronized(this) {
+ if(closed)
+ return false;
+ }
+
+ DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+ DatabaseEntry blockDBE = new DatabaseEntry();
+ Cursor c = null;
+ Transaction t = null;
+ try {
+ t = environment.beginTransaction(null,null);
+ c = keysDB.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;
+ }
+
+ StoreBlock storeBlock = (StoreBlock)
storeBlockTupleBinding.entryToObject(blockDBE);
+
+ synchronized(storeRAF) {
+
storeRAF.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
+ storeRAF.write(header);
+ storeRAF.write(data);
+ if(keysRAF != null) {
+ keysRAF.seek(storeBlock.offset *
keyLength);
+ keysRAF.write(fullKey);
+ }
+ }
+
+ // 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;
+ }
+
/**
* Overwrite an SSK with a new SSK of the same key.
*/
@@ -1804,6 +2005,74 @@
return true;
}
+ private void innerPut(StorableBlock block, byte[] routingkey, byte[]
fullKey, byte[] data, byte[] header) throws IOException {
+ synchronized(this) {
+ if(closed)
+ return;
+ }
+
+ 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 = keysDB.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(fetch(routingkey, fullKey, 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, routingkey, fullKey, data,
header);
+ 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, fullKey);
+
+ 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 "+HexUtil.bytesToHex(routingkey));
+ }
+
+ } 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());
+ }
+ }
+
/**
* Store a block.
*/
Added: trunk/freenet/src/freenet/store/CHKStore.java
===================================================================
--- trunk/freenet/src/freenet/store/CHKStore.java
(rev 0)
+++ trunk/freenet/src/freenet/store/CHKStore.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -0,0 +1,38 @@
+package freenet.store;
+
+import freenet.keys.CHKBlock;
+import freenet.keys.KeyVerifyException;
+import freenet.keys.NodeCHK;
+
+public class CHKStore extends StoreCallback {
+
+ public boolean collisionPossible() {
+ return false;
+ }
+
+ public StorableBlock construct(byte[] data, byte[] headers,
+ byte[] routingKey, byte[] fullKey) throws
KeyVerifyException {
+ return CHKBlock.construct(data, headers);
+ }
+
+ public int dataLength() {
+ return CHKBlock.DATA_LENGTH;
+ }
+
+ public int fullKeyLength() {
+ return NodeCHK.FULL_KEY_LENGTH;
+ }
+
+ public int headerLength() {
+ return CHKBlock.TOTAL_HEADERS_LENGTH;
+ }
+
+ public int routingKeyLength() {
+ return NodeCHK.KEY_LENGTH;
+ }
+
+ public boolean storeFullKeys() {
+ return false;
+ }
+
+}
Modified: trunk/freenet/src/freenet/store/FreenetStore.java
===================================================================
--- trunk/freenet/src/freenet/store/FreenetStore.java 2008-01-05 11:14:06 UTC
(rev 16908)
+++ trunk/freenet/src/freenet/store/FreenetStore.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -15,6 +15,15 @@
*/
public interface FreenetStore {
+ /**
+ * Retrieve a block. Use the StoreCallback to convert it to the
appropriate type of block.
+ * @param routingKey The routing key i.e. the database key under which
the block is stored.
+ * @param dontPromote If true, don't promote the block to the top of
the LRU.
+ * @return A StorableBlock, or null if the key cannot be found.
+ * @throws IOException If a disk I/O error occurs.
+ */
+ StorableBlock fetch(byte[] routingKey, byte[] fullKey, boolean
dontPromote) throws IOException;
+
/**
* Retrieve a block.
* @param dontPromote If true, don't promote data if fetched.
@@ -34,6 +43,13 @@
*/
public DSAPublicKey fetchPubKey(byte[] hash, boolean dontPromote) throws
IOException;
+ /** Store a block.
+ * @throws KeyCollisionException If the key already exists but has
different contents.
+ * @param ignoreAndOverwrite If true, overwrite old content rather than
throwing a KeyCollisionException.
+ */
+ public void put(StorableBlock block, byte[] routingkey, byte[] fullKey,
byte[] data, byte[] header,
+ boolean overwrite) throws IOException, KeyCollisionException;
+
/**
* Store a block.
* @throws KeyCollisionException If the key already exists but has
different contents.
Added: trunk/freenet/src/freenet/store/PubkeyStore.java
===================================================================
--- trunk/freenet/src/freenet/store/PubkeyStore.java
(rev 0)
+++ trunk/freenet/src/freenet/store/PubkeyStore.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -0,0 +1,43 @@
+package freenet.store;
+
+import freenet.crypt.CryptFormatException;
+import freenet.crypt.DSAPublicKey;
+import freenet.keys.KeyVerifyException;
+import freenet.keys.PubkeyVerifyException;
+
+public class PubkeyStore extends StoreCallback {
+
+ public boolean collisionPossible() {
+ return false;
+ }
+
+ StorableBlock construct(byte[] data, byte[] headers, byte[] routingKey,
+ byte[] fullKey) throws KeyVerifyException {
+ try {
+ return DSAPublicKey.create(data);
+ } catch (CryptFormatException e) {
+ throw new PubkeyVerifyException(e);
+ }
+ }
+
+ public int dataLength() {
+ return DSAPublicKey.PADDED_SIZE;
+ }
+
+ public int fullKeyLength() {
+ return DSAPublicKey.HASH_LENGTH;
+ }
+
+ public int headerLength() {
+ return 0;
+ }
+
+ public int routingKeyLength() {
+ return DSAPublicKey.HASH_LENGTH;
+ }
+
+ public boolean storeFullKeys() {
+ return false;
+ }
+
+}
Added: trunk/freenet/src/freenet/store/SSKStore.java
===================================================================
--- trunk/freenet/src/freenet/store/SSKStore.java
(rev 0)
+++ trunk/freenet/src/freenet/store/SSKStore.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -0,0 +1,60 @@
+package freenet.store;
+
+import java.io.IOException;
+
+import freenet.keys.NodeSSK;
+import freenet.keys.SSKBlock;
+import freenet.keys.SSKVerifyException;
+import freenet.node.GetPubkey;
+
+public class SSKStore extends StoreCallback {
+
+ private final GetPubkey pubkeyCache;
+
+ public SSKStore(GetPubkey pubkeyCache) {
+ this.pubkeyCache = pubkeyCache;
+ }
+
+ public StorableBlock construct(byte[] data, byte[] headers,
+ byte[] routingKey, byte[] fullKey) throws
SSKVerifyException {
+ NodeSSK key;
+ key = NodeSSK.construct(fullKey);
+ key.grabPubkey(pubkeyCache);
+ SSKBlock block = new SSKBlock(data, headers, key, false);
+ return block;
+ }
+
+ public SSKBlock fetch(NodeSSK chk, boolean dontPromote) throws
IOException {
+ return (SSKBlock) store.fetch(chk.getRoutingKey(),
chk.getFullKey(), dontPromote);
+ }
+
+ public void put(SSKBlock b, boolean overwrite) throws IOException,
KeyCollisionException {
+ NodeSSK key = (NodeSSK) b.getKey();
+ store.put(b, key.getRoutingKey(), key.getFullKey(),
b.getRawData(), b.getRawHeaders(), overwrite);
+ }
+
+ public int dataLength() {
+ return SSKBlock.DATA_LENGTH;
+ }
+
+ public int fullKeyLength() {
+ return NodeSSK.FULL_KEY_LENGTH;
+ }
+
+ public int headerLength() {
+ return SSKBlock.TOTAL_HEADERS_LENGTH;
+ }
+
+ public int routingKeyLength() {
+ return NodeSSK.ROUTING_KEY_LENGTH;
+ }
+
+ public boolean storeFullKeys() {
+ return true;
+ }
+
+ public boolean collisionPossible() {
+ return true;
+ }
+
+}
Added: trunk/freenet/src/freenet/store/StorableBlock.java
===================================================================
--- trunk/freenet/src/freenet/store/StorableBlock.java
(rev 0)
+++ trunk/freenet/src/freenet/store/StorableBlock.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -0,0 +1,9 @@
+package freenet.store;
+
+public interface StorableBlock {
+
+ public byte[] getRoutingKey();
+
+ public byte[] getFullKey();
+
+}
Added: trunk/freenet/src/freenet/store/StoreCallback.java
===================================================================
--- trunk/freenet/src/freenet/store/StoreCallback.java
(rev 0)
+++ trunk/freenet/src/freenet/store/StoreCallback.java 2008-01-05 19:35:42 UTC
(rev 16909)
@@ -0,0 +1,45 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.store;
+
+import freenet.keys.KeyVerifyException;
+
+/**
+ * @author toad
+ */
+public abstract class StoreCallback {
+
+ /** Length of a data block. Fixed for lifetime of the store. */
+ public abstract int dataLength();
+
+ /** Length of a header block. Fixed for the lifetime of the store. */
+ public abstract int headerLength();
+
+ /** Length of a routing key. Routing key is what we use to search for a
block. Also fixed. */
+ public abstract int routingKeyLength();
+
+ /** Whether we should create a .keys file to keep full keys in in order
to reconstruct. */
+ public abstract boolean storeFullKeys();
+
+ /** Length of a full key. Full keys are stored in the .keys file. Also
fixed. */
+ public abstract int fullKeyLength();
+
+ /** Can the same key be valid for two different StorableBlocks? */
+ public abstract boolean collisionPossible();
+
+ protected FreenetStore store;
+
+ /** Called once when first connecting to a FreenetStore */
+ void setStore(FreenetStore store) {
+ if(this.store != null)
+ throw new IllegalArgumentException("Calling setStore
twice!");
+ this.store = store;
+ }
+
+ // Reconstruction
+
+ /** Construct a StorableBlock from the data, headers, and optionally
routing key or full key
+ * @throws KeyVerifyException */
+ abstract StorableBlock construct(byte[] data, byte[] headers, byte[]
routingKey, byte[] fullKey) throws KeyVerifyException;
+}