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;
+}


Reply via email to