X-Git-Url: http://the.earth.li/gitweb/?a=blobdiff_plain;f=keydb_db4.c;h=2ffb256c4c350ea4b77e941ba4b162b3e77e9f87;hb=23f086c85c5d2db35e9ce76cf0bbf72200b4dc42;hp=4e35bff934264bb8467bed73866a19d79b269f89;hpb=5633e41c3ef6f5eb99308dd21ea0b4a3205be797;p=onak.git diff --git a/keydb_db4.c b/keydb_db4.c index 4e35bff..2ffb256 100644 --- a/keydb_db4.c +++ b/keydb_db4.c @@ -44,48 +44,24 @@ #define DB4_UPGRADE_FILE "db_upgrade.lck" -/** - * dbenv - our database environment. - */ -static DB_ENV *dbenv = NULL; - -/** - * numdb - The number of database files we have. - */ -static int numdbs = 16; - -/** - * dbconn - our connections to the key database files. - */ -static DB **dbconns = NULL; - -/** - * worddb - our connection to the word database. - */ -static DB *worddb = NULL; - -/** - * id32db - our connection to the 32bit ID database. - */ -static DB *id32db = NULL; - -/** - * skshashdb - our connection to the SKS hash database. - */ -static DB *skshashdb = NULL; - -/** - * txn - our current transaction id. - */ -static DB_TXN *txn = NULL; +struct onak_db4_dbctx { + DB_ENV *dbenv; /* The database environment context */ + int numdbs; /* Number of data databases in use */ + DB **dbconns; /* Connections to the key data databases */ + DB *worddb; /* Connection to the word lookup database */ + DB *id32db; /* Connection to the 32 bit ID lookup database */ + DB *skshashdb; /* Connection to the SKS hash database */ + DB *subkeydb; /* Connection to the subkey ID lookup database */ + DB_TXN *txn; /* Our current transaction ID */ +}; -DB *keydb(uint64_t keyid) +DB *keydb(struct onak_db4_dbctx *privctx, uint64_t keyid) { uint64_t keytrun; keytrun = keyid >> 8; - return(dbconns[keytrun % numdbs]); + return(privctx->dbconns[keytrun % privctx->numdbs]); } /** @@ -117,16 +93,17 @@ static void db4_errfunc(const DB_ENV *edbenv, const char *errpfx, * operations on the database to help speed it all up, or if we want * something to only succeed if all relevant operations are successful. */ -static bool db4_starttrans(void) +static bool db4_starttrans(struct onak_dbctx *dbctx) { + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; int ret; - log_assert(dbenv != NULL); - log_assert(txn == NULL); + log_assert(privctx->dbenv != NULL); + log_assert(privctx->txn == NULL); - ret = dbenv->txn_begin(dbenv, + ret = privctx->dbenv->txn_begin(privctx->dbenv, NULL, /* No parent transaction */ - &txn, + &privctx->txn, 0); if (ret != 0) { logthing(LOGTHING_CRITICAL, @@ -143,14 +120,15 @@ static bool db4_starttrans(void) * * Ends a transaction. */ -static void db4_endtrans(void) +static void db4_endtrans(struct onak_dbctx *dbctx) { + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; int ret; - log_assert(dbenv != NULL); - log_assert(txn != NULL); + log_assert(privctx->dbenv != NULL); + log_assert(privctx->txn != NULL); - ret = txn->commit(txn, + ret = privctx->txn->commit(privctx->txn, 0); if (ret != 0) { logthing(LOGTHING_CRITICAL, @@ -158,48 +136,11 @@ static void db4_endtrans(void) db_strerror(ret)); exit(1); } - txn = NULL; + privctx->txn = NULL; return; } -/** - * cleanupdb - De-initialize the key database. - * - * This function should be called upon program exit to allow the DB to - * cleanup after itself. - */ -static void db4_cleanupdb(void) -{ - int i = 0; - - if (dbenv != NULL) { - dbenv->txn_checkpoint(dbenv, 0, 0, 0); - if (skshashdb != NULL) { - skshashdb->close(skshashdb, 0); - skshashdb = NULL; - } - if (id32db != NULL) { - id32db->close(id32db, 0); - id32db = NULL; - } - if (worddb != NULL) { - worddb->close(worddb, 0); - worddb = NULL; - } - for (i = 0; i < numdbs; i++) { - if (dbconns[i] != NULL) { - dbconns[i]->close(dbconns[i], 0); - dbconns[i] = NULL; - } - } - free(dbconns); - dbconns = NULL; - dbenv->close(dbenv, 0); - dbenv = NULL; - } -} - /** * db4_upgradedb - Upgrade a DB4 database * @@ -207,7 +148,7 @@ static void db4_cleanupdb(void) * we're running with a newer version of db4 than the database was * created with. */ -static int db4_upgradedb(int numdb) +static int db4_upgradedb(struct onak_db4_dbctx *privctx) { DB *curdb = NULL; int ret; @@ -234,17 +175,19 @@ static int db4_upgradedb(int numdb) close(lockfile_fd); logthing(LOGTHING_NOTICE, "Upgrading DB4 database"); - ret = db_env_create(&dbenv, 0); - dbenv->set_errcall(dbenv, &db4_errfunc); - dbenv->remove(dbenv, config.db_dir, 0); - dbenv = NULL; - for (i = 0; i < numdb; i++) { + ret = db_env_create(&privctx->dbenv, 0); + if (ret == 0) { + privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc); + privctx->dbenv->remove(privctx->dbenv, config.db_dir, 0); + privctx->dbenv = NULL; + } + for (i = 0; i < privctx->numdbs; i++) { ret = db_create(&curdb, NULL, 0); if (ret == 0) { snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db", config.db_dir, i); logthing(LOGTHING_DEBUG, "Upgrading %s", buf); - ret = curdb->upgrade(curdb, buf, 0); + curdb->upgrade(curdb, buf, 0); curdb->close(curdb, 0); } else { logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s", @@ -257,7 +200,7 @@ static int db4_upgradedb(int numdb) if (ret == 0) { snprintf(buf, sizeof(buf) - 1, "%s/worddb", config.db_dir); logthing(LOGTHING_DEBUG, "Upgrading %s", buf); - ret = curdb->upgrade(curdb, buf, 0); + curdb->upgrade(curdb, buf, 0); curdb->close(curdb, 0); } else { logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s", @@ -269,7 +212,7 @@ static int db4_upgradedb(int numdb) if (ret == 0) { snprintf(buf, sizeof(buf) - 1, "%s/id32db", config.db_dir); logthing(LOGTHING_DEBUG, "Upgrading %s", buf); - ret = curdb->upgrade(curdb, buf, 0); + curdb->upgrade(curdb, buf, 0); curdb->close(curdb, 0); } else { logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s", @@ -281,7 +224,19 @@ static int db4_upgradedb(int numdb) if (ret == 0) { snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", config.db_dir); logthing(LOGTHING_DEBUG, "Upgrading %s", buf); - ret = curdb->upgrade(curdb, buf, 0); + curdb->upgrade(curdb, buf, 0); + curdb->close(curdb, 0); + } else { + logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s", + buf, + db_strerror(ret)); + } + + ret = db_create(&curdb, NULL, 0); + if (ret == 0) { + snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", config.db_dir); + logthing(LOGTHING_DEBUG, "Upgrading %s", buf); + curdb->upgrade(curdb, buf, 0); curdb->close(curdb, 0); } else { logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s", @@ -297,351 +252,139 @@ static int db4_upgradedb(int numdb) } /** - * initdb - Initialize the key database. + * getfullkeyid - Maps a 32bit key id to a 64bit one. + * @keyid: The 32bit keyid. * - * This function should be called before any of the other functions in - * this file are called in order to allow the DB to be initialized ready - * for access. + * This function maps a 32bit key id to the full 64bit one. It returns the + * full keyid. If the key isn't found a keyid of 0 is returned. */ -static void db4_initdb(bool readonly) +static uint64_t db4_getfullkeyid(struct onak_dbctx *dbctx, uint64_t keyid) { - char buf[1024]; - FILE *numdb = NULL; - int ret = 0; - int i = 0; - uint32_t flags = 0; - struct stat statbuf; - int maxlocks; - - snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir, - DB4_UPGRADE_FILE); - ret = stat(buf, &statbuf); - while ((ret == 0) || (errno != ENOENT)) { - if (ret != 0) { - logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade " - "lock file: %s (%d)", strerror(errno), ret); - exit(1); - } - logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting."); - sleep(5); - ret = stat(buf, &statbuf); - } - ret = 0; - - snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir); - numdb = fopen(buf, "r"); - if (numdb != NULL) { - if (fgets(buf, sizeof(buf), numdb) != NULL) { - numdbs = atoi(buf); - } - fclose(numdb); - } else if (!readonly) { - logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s", - strerror(errno)); - numdb = fopen(buf, "w"); - if (numdb != NULL) { - fprintf(numdb, "%d", numdbs); - fclose(numdb); - } else { - logthing(LOGTHING_ERROR, - "Couldn't write num_keydb: %s", - strerror(errno)); - } - } + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; + DBT key, data; + DBC *cursor = NULL; + uint32_t shortkeyid = 0; + int ret = 0; - dbconns = calloc(numdbs, sizeof (DB *)); - if (dbconns == NULL) { - logthing(LOGTHING_CRITICAL, - "Couldn't allocate memory for dbconns"); - ret = 1; - } + if (keyid < 0x100000000LL) { + ret = privctx->id32db->cursor(privctx->id32db, + privctx->txn, + &cursor, + 0); /* flags */ - if (ret == 0) { - ret = db_env_create(&dbenv, 0); if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "db_env_create: %s", db_strerror(ret)); + return 0; } - } - /* - * Up the number of locks we're allowed at once. We base this on - * the maximum number of keys we're going to return. - */ - maxlocks = config.maxkeys * 16; - if (maxlocks < 1000) { - maxlocks = 1000; - } - dbenv->set_lk_max_locks(dbenv, maxlocks); - dbenv->set_lk_max_objects(dbenv, maxlocks); + shortkeyid = keyid & 0xFFFFFFFF; - /* - * Enable deadlock detection so that we don't block indefinitely on - * anything. What we really want is simple 2 state locks, but I'm not - * sure how to make the standard DB functions do that yet. - */ - if (ret == 0) { - dbenv->set_errcall(dbenv, &db4_errfunc); - ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "db_env_create: %s", db_strerror(ret)); - } - } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = &shortkeyid; + key.size = sizeof(shortkeyid); + data.flags = DB_DBT_MALLOC; - if (ret == 0) { - ret = dbenv->open(dbenv, config.db_dir, - DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | - DB_INIT_TXN | - DB_CREATE, - 0); -#ifdef DB_VERSION_MISMATCH - if (ret == DB_VERSION_MISMATCH) { - dbenv->close(dbenv, 0); - dbenv = NULL; - ret = db4_upgradedb(numdbs); - if (ret == 0) { - ret = db_env_create(&dbenv, 0); - } - if (ret == 0) { - dbenv->set_errcall(dbenv, &db4_errfunc); - dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT); - ret = dbenv->open(dbenv, config.db_dir, - DB_INIT_LOG | DB_INIT_MPOOL | - DB_INIT_LOCK | DB_INIT_TXN | - DB_CREATE | DB_RECOVER, - 0); + ret = cursor->c_get(cursor, + &key, + &data, + DB_SET); - if (ret == 0) { - dbenv->txn_checkpoint(dbenv, - 0, - 0, - DB_FORCE); - } + if (ret == 0) { + keyid = *(uint64_t *) data.data; + + if (data.data != NULL) { + free(data.data); + data.data = NULL; } } -#endif - if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "Error opening db environment: %s (%s)", - config.db_dir, - db_strerror(ret)); - dbenv->close(dbenv, 0); - dbenv = NULL; - } - } - if (ret == 0) { - db4_starttrans(); + cursor->c_close(cursor); + cursor = NULL; + } - for (i = 0; !ret && i < numdbs; i++) { - ret = db_create(&dbconns[i], dbenv, 0); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "db_create: %s", db_strerror(ret)); - } + return keyid; +} - if (ret == 0) { - snprintf(buf, 1023, "keydb.%d.db", i); - flags = DB_CREATE; - if (readonly) { - flags = DB_RDONLY; - } - ret = dbconns[i]->open(dbconns[i], - txn, - buf, - "keydb", - DB_HASH, - flags, - 0664); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "Error opening key database:" - " %s (%s)", - buf, - db_strerror(ret)); - } - } - } +/** + * fetch_key_id - Given a keyid fetch the key from storage. + * @keyid: The keyid to fetch. + * @publickey: A pointer to a structure to return the key in. + * @intrans: If we're already in a transaction. + * + * We use the hex representation of the keyid as the filename to fetch the + * key from. The key is stored in the file as a binary OpenPGP stream of + * packets, so we can just use read_openpgp_stream() to read the packets + * in and then parse_keys() to parse the packets into a publickey + * structure. + */ +static int db4_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid, + struct openpgp_publickey **publickey, + bool intrans) +{ + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; + struct openpgp_packet_list *packets = NULL; + DBT key, data; + int ret = 0; + int numkeys = 0; + struct buffer_ctx fetchbuf; + if (keyid < 0x100000000LL) { + keyid = db4_getfullkeyid(dbctx, keyid); } - if (ret == 0) { - ret = db_create(&worddb, dbenv, 0); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, "db_create: %s", - db_strerror(ret)); - } - } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); - if (ret == 0) { - ret = worddb->set_flags(worddb, DB_DUP); - } + data.size = 0; + data.data = NULL; - if (ret == 0) { - ret = worddb->open(worddb, txn, "worddb", "worddb", DB_BTREE, - flags, - 0664); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "Error opening word database: %s (%s)", - "worddb", - db_strerror(ret)); - } - } + key.size = sizeof(keyid); + key.data = &keyid; - if (ret == 0) { - ret = db_create(&id32db, dbenv, 0); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, "db_create: %s", - db_strerror(ret)); - } + if (!intrans) { + db4_starttrans(dbctx); } - if (ret == 0) { - ret = id32db->set_flags(id32db, DB_DUP); - } - - if (ret == 0) { - ret = id32db->open(id32db, txn, "id32db", "id32db", DB_HASH, - flags, - 0664); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "Error opening id32 database: %s (%s)", - "id32db", - db_strerror(ret)); - } - } - - if (ret == 0) { - ret = db_create(&skshashdb, dbenv, 0); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, "db_create: %s", - db_strerror(ret)); - } - } - - if (ret == 0) { - ret = skshashdb->open(skshashdb, txn, "skshashdb", - "skshashdb", DB_HASH, - flags, - 0664); - if (ret != 0) { - logthing(LOGTHING_CRITICAL, - "Error opening skshash database: %s (%s)", - "skshashdb", - db_strerror(ret)); - } - } - - if (txn != NULL) { - db4_endtrans(); - } - - if (ret != 0) { - db4_cleanupdb(); - logthing(LOGTHING_CRITICAL, - "Error opening database; exiting"); - exit(EXIT_FAILURE); - } - - return; -} - -/** - * getfullkeyid - Maps a 32bit key id to a 64bit one. - * @keyid: The 32bit keyid. - * - * This function maps a 32bit key id to the full 64bit one. It returns the - * full keyid. If the key isn't found a keyid of 0 is returned. - */ -static uint64_t db4_getfullkeyid(uint64_t keyid) -{ - DBT key, data; - DBC *cursor = NULL; - uint32_t shortkeyid = 0; - int ret = 0; - - if (keyid < 0x100000000LL) { - ret = id32db->cursor(id32db, - txn, - &cursor, - 0); /* flags */ - - shortkeyid = keyid & 0xFFFFFFFF; + ret = keydb(privctx, keyid)->get(keydb(privctx, keyid), + privctx->txn, + &key, + &data, + 0); /* flags*/ + if (ret == DB_NOTFOUND) { + /* If we didn't find the key ID see if it's a subkey ID */ memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); - key.data = &shortkeyid; - key.size = sizeof(shortkeyid); - data.flags = DB_DBT_MALLOC; + data.size = 0; + data.data = NULL; + key.size = sizeof(keyid); + key.data = &keyid; - ret = cursor->c_get(cursor, + ret = privctx->subkeydb->get(privctx->subkeydb, + privctx->txn, &key, &data, - DB_SET); + 0); /* flags*/ if (ret == 0) { + /* We got a subkey match; retrieve the actual key */ keyid = *(uint64_t *) data.data; - if (data.data != NULL) { - free(data.data); - data.data = NULL; - } - } - - ret = cursor->c_close(cursor); - cursor = NULL; - } - - return keyid; -} - -/** - * fetch_key - Given a keyid fetch the key from storage. - * @keyid: The keyid to fetch. - * @publickey: A pointer to a structure to return the key in. - * @intrans: If we're already in a transaction. - * - * We use the hex representation of the keyid as the filename to fetch the - * key from. The key is stored in the file as a binary OpenPGP stream of - * packets, so we can just use read_openpgp_stream() to read the packets - * in and then parse_keys() to parse the packets into a publickey - * structure. - */ -static int db4_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey, - bool intrans) -{ - struct openpgp_packet_list *packets = NULL; - DBT key, data; - int ret = 0; - int numkeys = 0; - struct buffer_ctx fetchbuf; - - if (keyid < 0x100000000LL) { - keyid = db4_getfullkeyid(keyid); - } - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - - data.size = 0; - data.data = NULL; - - key.size = sizeof(keyid); - key.data = &keyid; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + data.size = 0; + data.data = NULL; + key.size = sizeof(keyid); + key.data = &keyid; - if (!intrans) { - db4_starttrans(); + ret = keydb(privctx, keyid)->get(keydb(privctx, keyid), + privctx->txn, + &key, + &data, + 0); /* flags*/ + } } - ret = keydb(keyid)->get(keydb(keyid), - txn, - &key, - &data, - 0); /* flags*/ - if (ret == 0) { fetchbuf.buffer = data.data; fetchbuf.offset = 0; @@ -659,7 +402,7 @@ static int db4_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey, } if (!intrans) { - db4_endtrans(); + db4_endtrans(dbctx); } return (numkeys); @@ -678,9 +421,10 @@ int worddb_cmp(const void *d1, const void *d2) * This function searches for the supplied text and returns the keys that * contain it. */ -static int db4_fetch_key_text(const char *search, +static int db4_fetch_key_text(struct onak_dbctx *dbctx, const char *search, struct openpgp_publickey **publickey) { + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; DBC *cursor = NULL; DBT key, data; int ret; @@ -699,13 +443,18 @@ static int db4_fetch_key_text(const char *search, wordlist = makewordlist(wordlist, searchtext); for (curword = wordlist; curword != NULL; curword = curword->next) { - db4_starttrans(); + db4_starttrans(dbctx); - ret = worddb->cursor(worddb, - txn, + ret = privctx->worddb->cursor(privctx->worddb, + privctx->txn, &cursor, 0); /* flags */ + if (ret != 0) { + db4_endtrans(dbctx); + break; + } + memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); key.data = curword->object; @@ -750,10 +499,10 @@ static int db4_fetch_key_text(const char *search, free(data.data); data.data = NULL; } - ret = cursor->c_close(cursor); + cursor->c_close(cursor); cursor = NULL; firstpass = 0; - db4_endtrans(); + db4_endtrans(dbctx); } llfree(wordlist, NULL); wordlist = NULL; @@ -761,10 +510,10 @@ static int db4_fetch_key_text(const char *search, if (keylist.count > config.maxkeys) { keylist.count = config.maxkeys; } - - db4_starttrans(); + + db4_starttrans(dbctx); for (i = 0; i < keylist.count; i++) { - numkeys += db4_fetch_key(keylist.keys[i], + numkeys += db4_fetch_key_id(dbctx, keylist.keys[i], publickey, true); } @@ -772,24 +521,30 @@ static int db4_fetch_key_text(const char *search, free(searchtext); searchtext = NULL; - db4_endtrans(); - + db4_endtrans(dbctx); + return (numkeys); } -static int db4_fetch_key_skshash(const struct skshash *hash, +static int db4_fetch_key_skshash(struct onak_dbctx *dbctx, + const struct skshash *hash, struct openpgp_publickey **publickey) { + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; DBT key, data; DBC *cursor = NULL; uint64_t keyid = 0; int ret = 0; - ret = skshashdb->cursor(skshashdb, - txn, + ret = privctx->skshashdb->cursor(privctx->skshashdb, + privctx->txn, &cursor, 0); /* flags */ + if (ret != 0) { + return 0; + } + memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); key.data = (void *) hash->hash; @@ -810,10 +565,10 @@ static int db4_fetch_key_skshash(const struct skshash *hash, } } - ret = cursor->c_close(cursor); + cursor->c_close(cursor); cursor = NULL; - return db4_fetch_key(keyid, publickey, false); + return db4_fetch_key_id(dbctx, keyid, publickey, false); } /** @@ -824,13 +579,15 @@ static int db4_fetch_key_skshash(const struct skshash *hash, * This function deletes a public key from whatever storage mechanism we * are using. Returns 0 if the key existed. */ -static int db4_delete_key(uint64_t keyid, bool intrans) +static int db4_delete_key(struct onak_dbctx *dbctx, + uint64_t keyid, bool intrans) { + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; struct openpgp_publickey *publickey = NULL; DBT key, data; DBC *cursor = NULL; uint32_t shortkeyid = 0; - uint64_t *subkeyids = NULL; + struct openpgp_fingerprint *subkeyids = NULL; int ret = 0; int i; char **uids = NULL; @@ -840,12 +597,13 @@ static int db4_delete_key(uint64_t keyid, bool intrans) struct ll *curword = NULL; bool deadlock = false; struct skshash hash; + uint64_t subkeyid; if (!intrans) { - db4_starttrans(); + db4_starttrans(dbctx); } - db4_fetch_key(keyid, &publickey, true); + db4_fetch_key_id(dbctx, keyid, &publickey, true); /* * Walk through the uids removing the words from the worddb. @@ -857,9 +615,9 @@ static int db4_delete_key(uint64_t keyid, bool intrans) for (i = 0; ret == 0 && uids[i] != NULL; i++) { wordlist = makewordlist(wordlist, uids[i]); } - - ret = worddb->cursor(worddb, - txn, + + privctx->worddb->cursor(privctx->worddb, + privctx->txn, &cursor, 0); /* flags */ @@ -909,9 +667,47 @@ static int db4_delete_key(uint64_t keyid, bool intrans) } } } - ret = cursor->c_close(cursor); + cursor->c_close(cursor); cursor = NULL; + ret = privctx->skshashdb->cursor(privctx->skshashdb, + privctx->txn, + &cursor, + 0); /* flags */ + if (ret == 0) { + get_skshash(publickey, &hash); + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = hash.hash; + key.size = sizeof(hash.hash); + data.data = &keyid; + data.size = sizeof(keyid); + + ret = cursor->c_get(cursor, + &key, + &data, + DB_GET_BOTH); + + if (ret == 0) { + ret = cursor->c_del(cursor, 0); + } + + if (ret != 0) { + logthing(LOGTHING_ERROR, + "Problem deleting skshash: %s " + "(0x%016" PRIX64 ")", + db_strerror(ret), + keyid); + if (ret == DB_LOCK_DEADLOCK) { + deadlock = true; + } + } + + cursor->c_close(cursor); + cursor = NULL; + } + /* * Free our UID and word lists. */ @@ -927,8 +723,8 @@ static int db4_delete_key(uint64_t keyid, bool intrans) } if (!deadlock) { - ret = id32db->cursor(id32db, - txn, + privctx->id32db->cursor(privctx->id32db, + privctx->txn, &cursor, 0); /* flags */ @@ -952,7 +748,7 @@ static int db4_delete_key(uint64_t keyid, bool intrans) if (ret != 0) { logthing(LOGTHING_ERROR, - "Problem deleting short keyid: %s ", + "Problem deleting short keyid: %s " "(0x%016" PRIX64 ")", db_strerror(ret), keyid); @@ -963,8 +759,25 @@ static int db4_delete_key(uint64_t keyid, bool intrans) subkeyids = keysubkeys(publickey); i = 0; - while (subkeyids != NULL && subkeyids[i] != 0) { - shortkeyid = subkeyids[i++] & 0xFFFFFFFF; + while (subkeyids != NULL && subkeyids[i].length != 0) { + subkeyid = fingerprint2keyid(&subkeyids[i]); + memset(&key, 0, sizeof(key)); + key.data = &subkeyid; + key.size = sizeof(subkeyid); + privctx->subkeydb->del(privctx->subkeydb, + privctx->txn, &key, 0); + if (ret != 0) { + logthing(LOGTHING_ERROR, + "Problem deleting subkey id: %s " + "(0x%016" PRIX64 ")", + db_strerror(ret), + keyid); + if (ret == DB_LOCK_DEADLOCK) { + deadlock = true; + } + } + + shortkeyid = subkeyid & 0xFFFFFFFF; memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); @@ -984,7 +797,7 @@ static int db4_delete_key(uint64_t keyid, bool intrans) if (ret != 0) { logthing(LOGTHING_ERROR, - "Problem deleting short keyid: %s ", + "Problem deleting short keyid: %s " "(0x%016" PRIX64 ")", db_strerror(ret), keyid); @@ -992,51 +805,13 @@ static int db4_delete_key(uint64_t keyid, bool intrans) deadlock = true; } } + i++; } if (subkeyids != NULL) { free(subkeyids); subkeyids = NULL; } - ret = cursor->c_close(cursor); - cursor = NULL; - - } - - if (!deadlock) { - ret = skshashdb->cursor(skshashdb, - txn, - &cursor, - 0); /* flags */ - get_skshash(publickey, &hash); - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - key.data = hash.hash; - key.size = sizeof(hash.hash); - data.data = &keyid; - data.size = sizeof(keyid); - - ret = cursor->c_get(cursor, - &key, - &data, - DB_GET_BOTH); - - if (ret == 0) { - ret = cursor->c_del(cursor, 0); - } - - if (ret != 0) { - logthing(LOGTHING_ERROR, - "Problem deleting skshash: %s ", - "(0x%016" PRIX64 ")", - db_strerror(ret), - keyid); - if (ret == DB_LOCK_DEADLOCK) { - deadlock = true; - } - } - - ret = cursor->c_close(cursor); + cursor->c_close(cursor); cursor = NULL; } @@ -1044,14 +819,14 @@ static int db4_delete_key(uint64_t keyid, bool intrans) key.data = &keyid; key.size = sizeof(keyid); - keydb(keyid)->del(keydb(keyid), - txn, + keydb(privctx, keyid)->del(keydb(privctx, keyid), + privctx->txn, &key, 0); /* flags */ } if (!intrans) { - db4_endtrans(); + db4_endtrans(dbctx); } return deadlock ? (-1) : (ret == DB_NOTFOUND); @@ -1069,9 +844,11 @@ static int db4_delete_key(uint64_t keyid, bool intrans) * the file. If update is true then we delete the old key first, otherwise * we trust that it doesn't exist. */ -static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, +static int db4_store_key(struct onak_dbctx *dbctx, + struct openpgp_publickey *publickey, bool intrans, bool update) { + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; struct openpgp_packet_list *packets = NULL; struct openpgp_packet_list *list_end = NULL; struct openpgp_publickey *next = NULL; @@ -1082,7 +859,7 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, DBT data; uint64_t keyid = 0; uint32_t shortkeyid = 0; - uint64_t *subkeyids = NULL; + struct openpgp_fingerprint *subkeyids = NULL; char **uids = NULL; char *primary = NULL; unsigned char worddb_data[12]; @@ -1090,11 +867,15 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, struct ll *curword = NULL; bool deadlock = false; struct skshash hash; + uint64_t subkeyid; - keyid = get_keyid(publickey); + if (get_keyid(publickey, &keyid) != ONAK_E_OK) { + logthing(LOGTHING_ERROR, "Couldn't find key ID for key."); + return 0; + } if (!intrans) { - db4_starttrans(); + db4_starttrans(dbctx); } /* @@ -1106,7 +887,7 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, * it definitely needs updated. */ if (update) { - deadlock = (db4_delete_key(keyid, true) == -1); + deadlock = (db4_delete_key(dbctx, keyid, true) == -1); } /* @@ -1118,10 +899,10 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, flatten_publickey(publickey, &packets, &list_end); publickey->next = next; - storebuf.offset = 0; + storebuf.offset = 0; storebuf.size = 8192; storebuf.buffer = malloc(8192); - + write_openpgp_stream(buffer_putchar, &storebuf, packets); /* @@ -1135,8 +916,8 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, data.size = storebuf.offset; data.data = storebuf.buffer; - ret = keydb(keyid)->put(keydb(keyid), - txn, + ret = keydb(privctx, keyid)->put(keydb(privctx, keyid), + privctx->txn, &key, &data, 0); /* flags*/ @@ -1152,8 +933,8 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, free(storebuf.buffer); storebuf.buffer = NULL; storebuf.size = 0; - storebuf.offset = 0; - + storebuf.offset = 0; + free_packet_list(packets); packets = NULL; } @@ -1193,9 +974,9 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, worddb_data[ 8] = (keyid >> 24) & 0xFF; worddb_data[ 9] = (keyid >> 16) & 0xFF; worddb_data[10] = (keyid >> 8) & 0xFF; - worddb_data[11] = keyid & 0xFF; - ret = worddb->put(worddb, - txn, + worddb_data[11] = keyid & 0xFF; + ret = privctx->worddb->put(privctx->worddb, + privctx->txn, &key, &data, 0); @@ -1235,8 +1016,8 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, data.data = &keyid; data.size = sizeof(keyid); - ret = id32db->put(id32db, - txn, + ret = privctx->id32db->put(privctx->id32db, + privctx->txn, &key, &data, 0); @@ -1253,8 +1034,32 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, if (!deadlock) { subkeyids = keysubkeys(publickey); i = 0; - while (subkeyids != NULL && subkeyids[i] != 0) { - shortkeyid = subkeyids[i++] & 0xFFFFFFFF; + while (subkeyids != NULL && subkeyids[i].length != 0) { + subkeyid = fingerprint2keyid(&subkeyids[i]); + /* Store the subkey ID -> main key ID mapping */ + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = &subkeyid; + key.size = sizeof(subkeyid); + data.data = &keyid; + data.size = sizeof(keyid); + + ret = privctx->subkeydb->put(privctx->subkeydb, + privctx->txn, + &key, + &data, + 0); + if (ret != 0) { + logthing(LOGTHING_ERROR, + "Problem storing subkey keyid: %s", + db_strerror(ret)); + if (ret == DB_LOCK_DEADLOCK) { + deadlock = true; + } + } + + /* Store the short subkey ID -> main key ID mapping */ + shortkeyid = subkeyid & 0xFFFFFFFF; memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); @@ -1263,8 +1068,8 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, data.data = &keyid; data.size = sizeof(keyid); - ret = id32db->put(id32db, - txn, + ret = privctx->id32db->put(privctx->id32db, + privctx->txn, &key, &data, 0); @@ -1276,6 +1081,7 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, deadlock = true; } } + i++; } if (subkeyids != NULL) { free(subkeyids); @@ -1292,8 +1098,8 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, data.data = &keyid; data.size = sizeof(keyid); - ret = skshashdb->put(skshashdb, - txn, + ret = privctx->skshashdb->put(privctx->skshashdb, + privctx->txn, &key, &data, 0); @@ -1308,7 +1114,7 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, } if (!intrans) { - db4_endtrans(); + db4_endtrans(dbctx); } return deadlock ? -1 : 0 ; @@ -1325,9 +1131,11 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, * * Returns the number of keys we iterated over. */ -static int db4_iterate_keys(void (*iterfunc)(void *ctx, - struct openpgp_publickey *key), void *ctx) +static int db4_iterate_keys(struct onak_dbctx *dbctx, + void (*iterfunc)(void *ctx, struct openpgp_publickey *key), + void *ctx) { + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; DBT dbkey, data; DBC *cursor = NULL; int ret = 0; @@ -1337,12 +1145,16 @@ static int db4_iterate_keys(void (*iterfunc)(void *ctx, struct openpgp_packet_list *packets = NULL; struct openpgp_publickey *key = NULL; - for (i = 0; i < numdbs; i++) { - ret = dbconns[i]->cursor(dbconns[i], + for (i = 0; i < privctx->numdbs; i++) { + ret = privctx->dbconns[i]->cursor(privctx->dbconns[i], NULL, &cursor, 0); /* flags */ + if (ret != 0) { + continue; + } + memset(&dbkey, 0, sizeof(dbkey)); memset(&data, 0, sizeof(data)); ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT); @@ -1355,12 +1167,12 @@ static int db4_iterate_keys(void (*iterfunc)(void *ctx, parse_keys(packets, &key); iterfunc(ctx, key); - + free_publickey(key); key = NULL; free_packet_list(packets); packets = NULL; - + memset(&dbkey, 0, sizeof(dbkey)); memset(&data, 0, sizeof(data)); ret = cursor->c_get(cursor, &dbkey, &data, @@ -1373,10 +1185,10 @@ static int db4_iterate_keys(void (*iterfunc)(void *ctx, db_strerror(ret)); } - ret = cursor->c_close(cursor); + cursor->c_close(cursor); cursor = NULL; } - + return numkeys; } @@ -1386,22 +1198,368 @@ static int db4_iterate_keys(void (*iterfunc)(void *ctx, #define NEED_GETKEYSIGS 1 #define NEED_KEYID2UID 1 #define NEED_UPDATEKEYS 1 +#define NEED_GET_FP 1 #include "keydb.c" -struct dbfuncs keydb_db4_funcs = { - .initdb = db4_initdb, - .cleanupdb = db4_cleanupdb, - .starttrans = db4_starttrans, - .endtrans = db4_endtrans, - .fetch_key = db4_fetch_key, - .fetch_key_text = db4_fetch_key_text, - .fetch_key_skshash = db4_fetch_key_skshash, - .store_key = db4_store_key, - .update_keys = generic_update_keys, - .delete_key = db4_delete_key, - .getkeysigs = generic_getkeysigs, - .cached_getkeysigs = generic_cached_getkeysigs, - .keyid2uid = generic_keyid2uid, - .getfullkeyid = db4_getfullkeyid, - .iterate_keys = db4_iterate_keys, -}; +/** + * cleanupdb - De-initialize the key database. + * + * This function should be called upon program exit to allow the DB to + * cleanup after itself. + */ +static void db4_cleanupdb(struct onak_dbctx *dbctx) +{ + struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv; + int i = 0; + + if (privctx->dbenv != NULL) { + privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0); + if (privctx->subkeydb != NULL) { + privctx->subkeydb->close(privctx->subkeydb, 0); + privctx->subkeydb = NULL; + } + if (privctx->skshashdb != NULL) { + privctx->skshashdb->close(privctx->skshashdb, 0); + privctx->skshashdb = NULL; + } + if (privctx->id32db != NULL) { + privctx->id32db->close(privctx->id32db, 0); + privctx->id32db = NULL; + } + if (privctx->worddb != NULL) { + privctx->worddb->close(privctx->worddb, 0); + privctx->worddb = NULL; + } + for (i = 0; i < privctx->numdbs; i++) { + if (privctx->dbconns[i] != NULL) { + privctx->dbconns[i]->close(privctx->dbconns[i], + 0); + privctx->dbconns[i] = NULL; + } + } + free(privctx->dbconns); + privctx->dbconns = NULL; + privctx->dbenv->close(privctx->dbenv, 0); + privctx->dbenv = NULL; + } + + free(privctx); + dbctx->priv = NULL; + free(dbctx); +} + +/** + * initdb - Initialize the key database. + * + * This function should be called before any of the other functions in + * this file are called in order to allow the DB to be initialized ready + * for access. + */ +struct onak_dbctx *keydb_db4_init(bool readonly) +{ + char buf[1024]; + FILE *numdb = NULL; + int ret = 0; + int i = 0; + uint32_t flags = 0; + struct stat statbuf; + int maxlocks; + struct onak_dbctx *dbctx; + struct onak_db4_dbctx *privctx; + + dbctx = malloc(sizeof(*dbctx)); + if (dbctx == NULL) { + return NULL; + } + dbctx->priv = privctx = calloc(1, sizeof(*privctx)); + if (privctx == NULL) { + free(dbctx); + return NULL; + } + + /* Default to 16 key data DBs */ + privctx->numdbs = 16; + + snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir, + DB4_UPGRADE_FILE); + ret = stat(buf, &statbuf); + while ((ret == 0) || (errno != ENOENT)) { + if (ret != 0) { + logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade " + "lock file: %s (%d)", strerror(errno), ret); + exit(1); + } + logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting."); + sleep(5); + ret = stat(buf, &statbuf); + } + ret = 0; + + snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir); + numdb = fopen(buf, "r"); + if (numdb != NULL) { + if (fgets(buf, sizeof(buf), numdb) != NULL) { + privctx->numdbs = atoi(buf); + } + fclose(numdb); + } else if (!readonly) { + logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s", + strerror(errno)); + numdb = fopen(buf, "w"); + if (numdb != NULL) { + fprintf(numdb, "%d", privctx->numdbs); + fclose(numdb); + } else { + logthing(LOGTHING_ERROR, + "Couldn't write num_keydb: %s", + strerror(errno)); + } + } + + privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *)); + if (privctx->dbconns == NULL) { + logthing(LOGTHING_CRITICAL, + "Couldn't allocate memory for dbconns"); + ret = 1; + } + + if (ret == 0) { + ret = db_env_create(&privctx->dbenv, 0); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "db_env_create: %s", db_strerror(ret)); + } + } + + /* + * Up the number of locks we're allowed at once. We base this on + * the maximum number of keys we're going to return. + */ + maxlocks = config.maxkeys * 16; + if (maxlocks < 1000) { + maxlocks = 1000; + } + privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks); + privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks); + + /* + * Enable deadlock detection so that we don't block indefinitely on + * anything. What we really want is simple 2 state locks, but I'm not + * sure how to make the standard DB functions do that yet. + */ + if (ret == 0) { + privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc); + ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "db_env_create: %s", db_strerror(ret)); + } + } + + if (ret == 0) { + ret = privctx->dbenv->open(privctx->dbenv, config.db_dir, + DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | + DB_INIT_TXN | + DB_CREATE, + 0); +#ifdef DB_VERSION_MISMATCH + if (ret == DB_VERSION_MISMATCH) { + privctx->dbenv->close(privctx->dbenv, 0); + privctx->dbenv = NULL; + ret = db4_upgradedb(privctx); + if (ret == 0) { + ret = db_env_create(&privctx->dbenv, 0); + } + if (ret == 0) { + privctx->dbenv->set_errcall(privctx->dbenv, + &db4_errfunc); + privctx->dbenv->set_lk_detect(privctx->dbenv, + DB_LOCK_DEFAULT); + ret = privctx->dbenv->open(privctx->dbenv, + config.db_dir, + DB_INIT_LOG | DB_INIT_MPOOL | + DB_INIT_LOCK | DB_INIT_TXN | + DB_CREATE | DB_RECOVER, + 0); + + if (ret == 0) { + privctx->dbenv->txn_checkpoint( + privctx->dbenv, + 0, + 0, + DB_FORCE); + } + } + } +#endif + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "Error opening db environment: %s (%s)", + config.db_dir, + db_strerror(ret)); + if (privctx->dbenv != NULL) { + privctx->dbenv->close(privctx->dbenv, 0); + privctx->dbenv = NULL; + } + } + } + + if (ret == 0) { + db4_starttrans(dbctx); + + for (i = 0; !ret && i < privctx->numdbs; i++) { + ret = db_create(&privctx->dbconns[i], + privctx->dbenv, 0); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "db_create: %s", db_strerror(ret)); + } + + if (ret == 0) { + snprintf(buf, 1023, "keydb.%d.db", i); + flags = DB_CREATE; + if (readonly) { + flags = DB_RDONLY; + } + ret = privctx->dbconns[i]->open( + privctx->dbconns[i], + privctx->txn, + buf, + "keydb", + DB_HASH, + flags, + 0664); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "Error opening key database:" + " %s (%s)", + buf, + db_strerror(ret)); + } + } + } + } + + if (ret == 0) { + ret = db_create(&privctx->worddb, privctx->dbenv, 0); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, "db_create: %s", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP); + } + + if (ret == 0) { + ret = privctx->worddb->open(privctx->worddb, privctx->txn, + "worddb", "worddb", DB_BTREE, + flags, + 0664); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "Error opening word database: %s (%s)", + "worddb", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = db_create(&privctx->id32db, privctx->dbenv, 0); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, "db_create: %s", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP); + } + + if (ret == 0) { + ret = privctx->id32db->open(privctx->id32db, privctx->txn, + "id32db", "id32db", DB_HASH, + flags, + 0664); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "Error opening id32 database: %s (%s)", + "id32db", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = db_create(&privctx->skshashdb, privctx->dbenv, 0); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, "db_create: %s", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn, + "skshashdb", + "skshashdb", DB_HASH, + flags, + 0664); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "Error opening skshash database: %s (%s)", + "skshashdb", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = db_create(&privctx->subkeydb, privctx->dbenv, 0); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, "db_create: %s", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn, + "subkeydb", "subkeydb", + DB_HASH, + flags, + 0664); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "Error opening subkey database: %s (%s)", + "subkeydb", + db_strerror(ret)); + } + } + + if (privctx->txn != NULL) { + db4_endtrans(dbctx); + } + + if (ret != 0) { + db4_cleanupdb(dbctx); + logthing(LOGTHING_CRITICAL, + "Error opening database; exiting"); + exit(EXIT_FAILURE); + } + + dbctx->cleanupdb = db4_cleanupdb; + dbctx->starttrans = db4_starttrans; + dbctx->endtrans = db4_endtrans; + dbctx->fetch_key_id = db4_fetch_key_id; + dbctx->fetch_key_fp = generic_fetch_key_fp; + dbctx->fetch_key_text = db4_fetch_key_text; + dbctx->fetch_key_skshash = db4_fetch_key_skshash; + dbctx->store_key = db4_store_key; + dbctx->update_keys = generic_update_keys; + dbctx->delete_key = db4_delete_key; + dbctx->getkeysigs = generic_getkeysigs; + dbctx->cached_getkeysigs = generic_cached_getkeysigs; + dbctx->keyid2uid = generic_keyid2uid; + dbctx->getfullkeyid = db4_getfullkeyid; + dbctx->iterate_keys = db4_iterate_keys; + + return dbctx; +}