From 6fcdf43b866c8199065bdf5bfc05942ed79bde16 Mon Sep 17 00:00:00 2001
From: Jonathan McDowell <noodles@pot>
Date: Wed, 16 Jan 2008 22:05:58 +0000
Subject: [PATCH] (Hopefully) really fix the DB4 backend issues.

---
 keydb.c     |   3 +-
 keydb_db4.c | 394 ++++++++++++++++++++++++++--------------------------
 2 files changed, 199 insertions(+), 198 deletions(-)

diff --git a/keydb.c b/keydb.c
index 339450b..0039057 100644
--- a/keydb.c
+++ b/keydb.c
@@ -205,7 +205,8 @@ int generic_update_keys(struct openpgp_publickey **keys, bool sendsync)
 				prev = curkey;
 				logthing(LOGTHING_INFO,
 					"Merged key; storing updated key.");
-				store_key(oldkey, intrans, true);
+				config.dbbackend->store_key(oldkey, intrans,
+					true);
 			}
 			free_publickey(oldkey);
 			oldkey = NULL;
diff --git a/keydb_db4.c b/keydb_db4.c
index 986c49a..b443942 100644
--- a/keydb_db4.c
+++ b/keydb_db4.c
@@ -319,7 +319,7 @@ static void db4_initdb(bool readonly)
 	}
 
 	if (txn != NULL) {
-		endtrans();
+		db4_endtrans();
 	}
 
 	if (ret != 0) {
@@ -393,7 +393,7 @@ static int db4_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
 	}
 
 	if (!intrans) {
-		endtrans();
+		db4_endtrans();
 	}
 
 	return (numkeys);
@@ -481,14 +481,14 @@ static int db4_fetch_key_text(const char *search,
 		}
 		ret = cursor->c_close(cursor);
 		cursor = NULL;
-		endtrans();
+		db4_endtrans();
 	}
 	llfree(wordlist, NULL);
 	wordlist = NULL;
 	
 	db4_starttrans();
 	for (i = 0; i < keylist.count; i++) {
-		numkeys += fetch_key(keylist.keys[i],
+		numkeys += db4_fetch_key(keylist.keys[i],
 			publickey,
 			true);
 	}
@@ -496,121 +496,56 @@ static int db4_fetch_key_text(const char *search,
 	free(searchtext);
 	searchtext = NULL;
 
-	endtrans();
+	db4_endtrans();
 	
 	return (numkeys);
 }
 
 /**
- *	store_key - Takes a key and stores it.
- *	@publickey: A pointer to the public key to store.
+ *	delete_key - Given a keyid delete the key from storage.
+ *	@keyid: The keyid to delete.
  *	@intrans: If we're already in a transaction.
- *	@update: If true the key exists and should be updated.
  *
- *	Again we just use the hex representation of the keyid as the filename
- *	to store the key to. We flatten the public key to a list of OpenPGP
- *	packets and then use write_openpgp_stream() to write the stream out to
- *	the file. If update is true then we delete the old key first, otherwise
- *	we trust that it doesn't exist.
+ *	This function deletes a public key from whatever storage mechanism we
+ *	are using. Returns 0 if the key existed.
  */
-static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
-		bool update)
+static int db4_delete_key(uint64_t keyid, bool intrans)
 {
-	struct     openpgp_packet_list *packets = NULL;
-	struct     openpgp_packet_list *list_end = NULL;
-	struct     openpgp_publickey *next = NULL;
-	int        ret = 0;
-	int        i = 0;
-	struct     buffer_ctx storebuf;
-	DBT        key;
-	DBT        data;
-	uint64_t   keyid = 0;
+	struct openpgp_publickey *publickey = NULL;
+	DBT key, data;
+	DBC *cursor = NULL;
 	uint32_t   shortkeyid = 0;
 	uint64_t  *subkeyids = NULL;
-	char     **uids = NULL;
-	char      *primary = NULL;
+	int ret = 0;
+	int i;
+	char **uids = NULL;
+	char *primary = NULL;
 	unsigned char worddb_data[12];
 	struct ll *wordlist = NULL;
 	struct ll *curword  = NULL;
-	bool       deadlock = false;
-
-	keyid = get_keyid(publickey);
+	bool deadlock = false;
 
 	if (!intrans) {
 		db4_starttrans();
 	}
 
-	/*
-	 * Delete the key if we already have it.
-	 *
-	 * TODO: Can we optimize this perhaps? Possibly when other data is
-	 * involved as well? I suspect this is easiest and doesn't make a lot
-	 * of difference though - the largest chunk of data is the keydata and
-	 * it definitely needs updated.
-	 */
-	if (update) {
-		deadlock = (delete_key(keyid, true) == -1);
-	}
-
-	/*
-	 * Convert the key to a flat set of binary data.
-	 */
-	if (!deadlock) {
-		next = publickey->next;
-		publickey->next = NULL;
-		flatten_publickey(publickey, &packets, &list_end);
-		publickey->next = next;
-
-		storebuf.offset = 0; 
-		storebuf.size = 8192;
-		storebuf.buffer = malloc(8192);
-	
-		write_openpgp_stream(buffer_putchar, &storebuf, packets);
-
-		/*
-		 * Now we have the key data store it in the DB; the keyid is
-		 * the key.
-		 */
-		memset(&key, 0, sizeof(key));
-		memset(&data, 0, sizeof(data));
-		key.data = &keyid;
-		key.size = sizeof(keyid);
-		data.size = storebuf.offset;
-		data.data = storebuf.buffer;
-
-		ret = keydb(keyid)->put(keydb(keyid),
-				txn,
-				&key,
-				&data,
-				0); /* flags*/
-		if (ret != 0) {
-			logthing(LOGTHING_ERROR,
-					"Problem storing key: %s",
-					db_strerror(ret));
-			if (ret == DB_LOCK_DEADLOCK) {
-				deadlock = true;
-			}
-		}
-
-		free(storebuf.buffer);
-		storebuf.buffer = NULL;
-		storebuf.size = 0;
-		storebuf.offset = 0; 
-	
-		free_packet_list(packets);
-		packets = NULL;
-	}
+	db4_fetch_key(keyid, &publickey, true);
 
 	/*
-	 * Walk through our uids storing the words into the db with the keyid.
+	 * Walk through the uids removing the words from the worddb.
 	 */
-	if (!deadlock) {
+	if (publickey != NULL) {
 		uids = keyuids(publickey, &primary);
 	}
 	if (uids != NULL) {
 		for (i = 0; ret == 0 && uids[i] != NULL; i++) {
 			wordlist = makewordlist(wordlist, uids[i]);
 		}
+				
+		ret = worddb->cursor(worddb,
+			txn,
+			&cursor,
+			0);   /* flags */
 
 		for (curword = wordlist; curword != NULL && !deadlock;
 				curword = curword->next) {
@@ -637,20 +572,32 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
 			worddb_data[ 9] = (keyid >> 16) & 0xFF;
 			worddb_data[10] = (keyid >>  8) & 0xFF;
 			worddb_data[11] = keyid & 0xFF; 
-			ret = worddb->put(worddb,
-				txn,
+
+			ret = cursor->c_get(cursor,
 				&key,
 				&data,
-				0);
+				DB_GET_BOTH);
+
+			if (ret == 0) {
+				ret = cursor->c_del(cursor, 0);
+				if (ret != 0) {
+					logthing(LOGTHING_ERROR,
+						"Problem deleting word: %s",
+						db_strerror(ret));
+				}
+			}
+
 			if (ret != 0) {
 				logthing(LOGTHING_ERROR,
-					"Problem storing word: %s",
+					"Problem deleting word: %s",
 					db_strerror(ret));
 				if (ret == DB_LOCK_DEADLOCK) {
 					deadlock = true;
 				}
 			}
 		}
+		ret = cursor->c_close(cursor);
+		cursor = NULL;
 
 		/*
 		 * Free our UID and word lists.
@@ -662,13 +609,16 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
 		}
 		free(uids);
 		uids = NULL;
+		free_publickey(publickey);
+		publickey = NULL;
 	}
 
-	/*
-	 * Write the truncated 32 bit keyid so we can lookup the full id for
-	 * queries.
-	 */
 	if (!deadlock) {
+		ret = id32db->cursor(id32db,
+			txn,
+			&cursor,
+			0);   /* flags */
+
 		shortkeyid = keyid & 0xFFFFFFFF;
 
 		memset(&key, 0, sizeof(key));
@@ -678,22 +628,29 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
 		data.data = &keyid;
 		data.size = sizeof(keyid);
 
-		ret = id32db->put(id32db,
-			txn,
+		ret = cursor->c_get(cursor,
 			&key,
 			&data,
-			0);
+			DB_GET_BOTH);
+
+		if (ret == 0) {
+			ret = cursor->c_del(cursor, 0);
+			if (ret != 0) {
+				logthing(LOGTHING_ERROR,
+					"Problem deleting short keyid: %s",
+					db_strerror(ret));
+			}
+		}
+
 		if (ret != 0) {
 			logthing(LOGTHING_ERROR,
-				"Problem storing short keyid: %s",
+				"Problem deleting short keyid: %s",
 				db_strerror(ret));
 			if (ret == DB_LOCK_DEADLOCK) {
 				deadlock = true;
 			}
 		}
-	}
 
-	if (!deadlock) {
 		subkeyids = keysubkeys(publickey);
 		i = 0;
 		while (subkeyids != NULL && subkeyids[i] != 0) {
@@ -706,14 +663,24 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
 			data.data = &keyid;
 			data.size = sizeof(keyid);
 
-			ret = id32db->put(id32db,
-				txn,
+			ret = cursor->c_get(cursor,
 				&key,
 				&data,
-				0);
+				DB_GET_BOTH);
+
+			if (ret == 0) {
+				ret = cursor->c_del(cursor, 0);
+				if (ret != 0) {
+					logthing(LOGTHING_ERROR,
+						"Problem deleting short"
+						" keyid: %s",
+						db_strerror(ret));
+				}
+			}
+
 			if (ret != 0) {
 				logthing(LOGTHING_ERROR,
-					"Problem storing short keyid: %s",
+					"Problem deleting short keyid: %s",
 					db_strerror(ret));
 				if (ret == DB_LOCK_DEADLOCK) {
 					deadlock = true;
@@ -724,60 +691,138 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
 			free(subkeyids);
 			subkeyids = NULL;
 		}
+
+		ret = cursor->c_close(cursor);
+		cursor = NULL;
+	}
+
+	if (!deadlock) {
+		key.data = &keyid;
+		key.size = sizeof(keyid);
+
+		keydb(keyid)->del(keydb(keyid),
+				txn,
+				&key,
+				0); /* flags */
 	}
 
 	if (!intrans) {
-		endtrans();
+		db4_endtrans();
 	}
 
-	return deadlock ? -1 : 0 ;
+	return deadlock ? (-1) : (ret == DB_NOTFOUND);
 }
 
 /**
- *	delete_key - Given a keyid delete the key from storage.
- *	@keyid: The keyid to delete.
+ *	store_key - Takes a key and stores it.
+ *	@publickey: A pointer to the public key to store.
  *	@intrans: If we're already in a transaction.
+ *	@update: If true the key exists and should be updated.
  *
- *	This function deletes a public key from whatever storage mechanism we
- *	are using. Returns 0 if the key existed.
+ *	Again we just use the hex representation of the keyid as the filename
+ *	to store the key to. We flatten the public key to a list of OpenPGP
+ *	packets and then use write_openpgp_stream() to write the stream out to
+ *	the file. If update is true then we delete the old key first, otherwise
+ *	we trust that it doesn't exist.
  */
-static int db4_delete_key(uint64_t keyid, bool intrans)
+static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
+		bool update)
 {
-	struct openpgp_publickey *publickey = NULL;
-	DBT key, data;
-	DBC *cursor = NULL;
+	struct     openpgp_packet_list *packets = NULL;
+	struct     openpgp_packet_list *list_end = NULL;
+	struct     openpgp_publickey *next = NULL;
+	int        ret = 0;
+	int        i = 0;
+	struct     buffer_ctx storebuf;
+	DBT        key;
+	DBT        data;
+	uint64_t   keyid = 0;
 	uint32_t   shortkeyid = 0;
 	uint64_t  *subkeyids = NULL;
-	int ret = 0;
-	int i;
-	char **uids = NULL;
-	char *primary = NULL;
+	char     **uids = NULL;
+	char      *primary = NULL;
 	unsigned char worddb_data[12];
 	struct ll *wordlist = NULL;
 	struct ll *curword  = NULL;
-	bool deadlock = false;
+	bool       deadlock = false;
+
+	keyid = get_keyid(publickey);
 
 	if (!intrans) {
 		db4_starttrans();
 	}
 
-	fetch_key(keyid, &publickey, true);
+	/*
+	 * Delete the key if we already have it.
+	 *
+	 * TODO: Can we optimize this perhaps? Possibly when other data is
+	 * involved as well? I suspect this is easiest and doesn't make a lot
+	 * of difference though - the largest chunk of data is the keydata and
+	 * it definitely needs updated.
+	 */
+	if (update) {
+		deadlock = (db4_delete_key(keyid, true) == -1);
+	}
 
 	/*
-	 * Walk through the uids removing the words from the worddb.
+	 * Convert the key to a flat set of binary data.
 	 */
-	if (publickey != NULL) {
+	if (!deadlock) {
+		next = publickey->next;
+		publickey->next = NULL;
+		flatten_publickey(publickey, &packets, &list_end);
+		publickey->next = next;
+
+		storebuf.offset = 0; 
+		storebuf.size = 8192;
+		storebuf.buffer = malloc(8192);
+	
+		write_openpgp_stream(buffer_putchar, &storebuf, packets);
+
+		/*
+		 * Now we have the key data store it in the DB; the keyid is
+		 * the key.
+		 */
+		memset(&key, 0, sizeof(key));
+		memset(&data, 0, sizeof(data));
+		key.data = &keyid;
+		key.size = sizeof(keyid);
+		data.size = storebuf.offset;
+		data.data = storebuf.buffer;
+
+		ret = keydb(keyid)->put(keydb(keyid),
+				txn,
+				&key,
+				&data,
+				0); /* flags*/
+		if (ret != 0) {
+			logthing(LOGTHING_ERROR,
+					"Problem storing key: %s",
+					db_strerror(ret));
+			if (ret == DB_LOCK_DEADLOCK) {
+				deadlock = true;
+			}
+		}
+
+		free(storebuf.buffer);
+		storebuf.buffer = NULL;
+		storebuf.size = 0;
+		storebuf.offset = 0; 
+	
+		free_packet_list(packets);
+		packets = NULL;
+	}
+
+	/*
+	 * Walk through our uids storing the words into the db with the keyid.
+	 */
+	if (!deadlock) {
 		uids = keyuids(publickey, &primary);
 	}
 	if (uids != NULL) {
 		for (i = 0; ret == 0 && uids[i] != NULL; i++) {
 			wordlist = makewordlist(wordlist, uids[i]);
 		}
-				
-		ret = worddb->cursor(worddb,
-			txn,
-			&cursor,
-			0);   /* flags */
 
 		for (curword = wordlist; curword != NULL && !deadlock;
 				curword = curword->next) {
@@ -804,32 +849,20 @@ static int db4_delete_key(uint64_t keyid, bool intrans)
 			worddb_data[ 9] = (keyid >> 16) & 0xFF;
 			worddb_data[10] = (keyid >>  8) & 0xFF;
 			worddb_data[11] = keyid & 0xFF; 
-
-			ret = cursor->c_get(cursor,
+			ret = worddb->put(worddb,
+				txn,
 				&key,
 				&data,
-				DB_GET_BOTH);
-
-			if (ret == 0) {
-				ret = cursor->c_del(cursor, 0);
-				if (ret != 0) {
-					logthing(LOGTHING_ERROR,
-						"Problem deleting word: %s",
-						db_strerror(ret));
-				}
-			}
-
+				0);
 			if (ret != 0) {
 				logthing(LOGTHING_ERROR,
-					"Problem deleting word: %s",
+					"Problem storing word: %s",
 					db_strerror(ret));
 				if (ret == DB_LOCK_DEADLOCK) {
 					deadlock = true;
 				}
 			}
 		}
-		ret = cursor->c_close(cursor);
-		cursor = NULL;
 
 		/*
 		 * Free our UID and word lists.
@@ -841,16 +874,13 @@ static int db4_delete_key(uint64_t keyid, bool intrans)
 		}
 		free(uids);
 		uids = NULL;
-		free_publickey(publickey);
-		publickey = NULL;
 	}
 
+	/*
+	 * Write the truncated 32 bit keyid so we can lookup the full id for
+	 * queries.
+	 */
 	if (!deadlock) {
-		ret = id32db->cursor(id32db,
-			txn,
-			&cursor,
-			0);   /* flags */
-
 		shortkeyid = keyid & 0xFFFFFFFF;
 
 		memset(&key, 0, sizeof(key));
@@ -860,29 +890,22 @@ static int db4_delete_key(uint64_t keyid, bool intrans)
 		data.data = &keyid;
 		data.size = sizeof(keyid);
 
-		ret = cursor->c_get(cursor,
+		ret = id32db->put(id32db,
+			txn,
 			&key,
 			&data,
-			DB_GET_BOTH);
-
-		if (ret == 0) {
-			ret = cursor->c_del(cursor, 0);
-			if (ret != 0) {
-				logthing(LOGTHING_ERROR,
-					"Problem deleting short keyid: %s",
-					db_strerror(ret));
-			}
-		}
-
+			0);
 		if (ret != 0) {
 			logthing(LOGTHING_ERROR,
-				"Problem deleting short keyid: %s",
+				"Problem storing short keyid: %s",
 				db_strerror(ret));
 			if (ret == DB_LOCK_DEADLOCK) {
 				deadlock = true;
 			}
 		}
+	}
 
+	if (!deadlock) {
 		subkeyids = keysubkeys(publickey);
 		i = 0;
 		while (subkeyids != NULL && subkeyids[i] != 0) {
@@ -895,24 +918,14 @@ static int db4_delete_key(uint64_t keyid, bool intrans)
 			data.data = &keyid;
 			data.size = sizeof(keyid);
 
-			ret = cursor->c_get(cursor,
+			ret = id32db->put(id32db,
+				txn,
 				&key,
 				&data,
-				DB_GET_BOTH);
-
-			if (ret == 0) {
-				ret = cursor->c_del(cursor, 0);
-				if (ret != 0) {
-					logthing(LOGTHING_ERROR,
-						"Problem deleting short"
-						" keyid: %s",
-						db_strerror(ret));
-				}
-			}
-
+				0);
 			if (ret != 0) {
 				logthing(LOGTHING_ERROR,
-					"Problem deleting short keyid: %s",
+					"Problem storing short keyid: %s",
 					db_strerror(ret));
 				if (ret == DB_LOCK_DEADLOCK) {
 					deadlock = true;
@@ -923,26 +936,13 @@ static int db4_delete_key(uint64_t keyid, bool intrans)
 			free(subkeyids);
 			subkeyids = NULL;
 		}
-
-		ret = cursor->c_close(cursor);
-		cursor = NULL;
-	}
-
-	if (!deadlock) {
-		key.data = &keyid;
-		key.size = sizeof(keyid);
-
-		keydb(keyid)->del(keydb(keyid),
-				txn,
-				&key,
-				0); /* flags */
 	}
 
 	if (!intrans) {
-		endtrans();
+		db4_endtrans();
 	}
 
-	return deadlock ? (-1) : (ret == DB_NOTFOUND);
+	return deadlock ? -1 : 0 ;
 }
 
 /**
-- 
2.39.5