* Jonathan McDowell <noodles@earth.li>
*
* Copyright 2002 Project Purple
+ *
+ * $Id: keydb_db3.c,v 1.22 2003/10/10 16:57:27 noodles Exp $
*/
+#include <assert.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <ctype.h>
#include <db.h>
+#include "charfuncs.h"
#include "keydb.h"
#include "keyid.h"
-#include "keyindex.h"
+#include "decodekey.h"
#include "keystructs.h"
#include "mem.h"
+#include "log.h"
#include "onak-conf.h"
#include "parsekey.h"
/**
- * dbconn - our connection to the key database.
+ * dbenv - our database environment.
*/
-static DB *dbconn = NULL;
+static DB_ENV *dbenv = NULL;
/**
- * worddb - our connection to the word database.
+ * numdb - The number of database files we have.
*/
-static DB *worddb = NULL;
+static int numdbs = 16;
/**
- * db3_get_ctx - Shared with CGI buffer stuff...
+ * dbconn - our connections to the key database files.
*/
-struct db3_get_ctx {
- char *buffer;
- int offset;
- int size;
-};
+static DB **dbconns = NULL;
/**
- * keydb_fetchchar - Fetches a char from a file.
+ * worddb - our connection to the word database.
*/
-static int keydb_fetchchar(void *ctx, size_t count, unsigned char *c)
-{
- struct db3_get_ctx *buf = NULL;
- int i;
-
- buf = (struct db3_get_ctx *) ctx;
- for (i = 0; i < count; i++) {
- c[i] = buf->buffer[buf->offset++];
- }
-
- return (((buf->offset) == (buf->size)) ? 1 : 0);
-}
+static DB *worddb = NULL;
/**
- * keydb_putchar - Puts a char to a file.
+ * txn - our current transaction id.
*/
-static int keydb_putchar(void *ctx, size_t count, unsigned char *c)
-{
- struct db3_get_ctx *buf = NULL;
- size_t newsize = 0;
- int i;
-
- buf = (struct db3_get_ctx *) ctx;
+static DB_TXN *txn = NULL;
- for (newsize = buf->size; newsize < (buf->offset + count);
- newsize *= 2) ;
+DB *keydb(uint64_t keyid)
+{
+ uint64_t keytrun;
- if (newsize != buf->size) {
- buf->buffer = realloc(buf->buffer, newsize);
- buf->size = newsize;
- }
-
- for (i = 0; i < count; i++) {
- buf->buffer[buf->offset++] = c[i];
- }
+ keytrun = keyid >> 8;
- return 1;
+ return(dbconns[keytrun % numdbs]);
}
/**
*/
void initdb(void)
{
- char buf[1024];
- int ret = 0;
+ char buf[1024];
+ FILE *numdb = NULL;
+ int ret = 0;
+ int i = 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 {
+ 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));
+ }
+ }
- strcpy(buf, config.db2_dbpath);
- strcat(buf, "/keydb.db");
-
- ret = db_create(&dbconn, NULL, 0);
+ dbconns = malloc(sizeof (DB *) * numdbs);
+ if (dbconns == NULL) {
+ logthing(LOGTHING_CRITICAL,
+ "Couldn't allocate memory for dbconns");
+ exit(1);
+ }
+
+ ret = db_env_create(&dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "db_env_create: %s", db_strerror(ret));
+ exit(1);
+ }
+
+ /*
+ * 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.
+ */
+ ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
if (ret != 0) {
- fprintf(stderr, "db_create: %s\n", db_strerror(ret));
+ logthing(LOGTHING_CRITICAL,
+ "db_env_create: %s", db_strerror(ret));
exit(1);
}
- ret = dbconn->open(dbconn, buf, NULL, DB_HASH,
+ ret = dbenv->open(dbenv, config.db_dir,
+ DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
+ DB_INIT_TXN |
DB_CREATE,
- 0664);
+ 0);
if (ret != 0) {
- dbconn->err(dbconn, ret, "%s", buf);
+ logthing(LOGTHING_CRITICAL,
+ "Error opening db environment: %s (%s)",
+ config.db_dir,
+ db_strerror(ret));
exit(1);
}
- strcpy(buf, config.db2_dbpath);
- strcat(buf, "/worddb");
-
- ret = db_create(&worddb, NULL, 0);
+ for (i = 0; i < numdbs; i++) {
+ ret = db_create(&dbconns[i], dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "db_create: %s", db_strerror(ret));
+ exit(1);
+ }
+
+ snprintf(buf, 1023, "keydb.%d.db", i);
+ ret = dbconns[i]->open(dbconns[i], buf,
+ NULL,
+ DB_HASH,
+ DB_CREATE,
+ 0664);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening key database: %s (%s)",
+ buf,
+ db_strerror(ret));
+ exit(1);
+ }
+ }
+
+ ret = db_create(&worddb, dbenv, 0);
if (ret != 0) {
- fprintf(stderr, "db_create: %s\n", db_strerror(ret));
+ logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
exit(1);
}
ret = worddb->set_flags(worddb, DB_DUP);
- ret = worddb->open(worddb, buf, NULL, DB_BTREE,
+ ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
DB_CREATE,
0664);
if (ret != 0) {
- worddb->err(worddb, ret, "%s", buf);
+ logthing(LOGTHING_CRITICAL,
+ "Error opening word database: %s (%s)",
+ "worddb",
+ db_strerror(ret));
exit(1);
}
*/
void cleanupdb(void)
{
+ int i = 0;
+
+ txn_checkpoint(dbenv, 0, 0, 0);
worddb->close(worddb, 0);
worddb = NULL;
- dbconn->close(dbconn, 0);
- dbconn = NULL;
+ for (i = 0; i < numdbs; i++) {
+ dbconns[i]->close(dbconns[i], 0);
+ dbconns[i] = NULL;
+ }
+ dbenv->close(dbenv, 0);
+ dbenv = NULL;
}
/**
*/
bool starttrans(void)
{
+ int ret;
+
+ assert(dbenv != NULL);
+ assert(txn == NULL);
+
+ ret = txn_begin(dbenv,
+ NULL, /* No parent transaction */
+ &txn,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error starting transaction: %s",
+ db_strerror(ret));
+ exit(1);
+ }
+
return true;
}
*/
void endtrans(void)
{
+ int ret;
+
+ assert(dbenv != NULL);
+ assert(txn != NULL);
+
+ ret = txn_commit(txn,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error ending transaction: %s",
+ db_strerror(ret));
+ exit(1);
+ }
+ txn = NULL;
+
return;
}
DBT key, data;
int ret = 0;
int numkeys = 0;
- struct db3_get_ctx fetchbuf;
+ struct buffer_ctx fetchbuf;
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.data = &keyid;
keyid &= 0xFFFFFFFF;
- ret = dbconn->get(dbconn,
- NULL, /* txn id */
+ if (!intrans) {
+ starttrans();
+ }
+
+ ret = keydb(keyid)->get(keydb(keyid),
+ txn,
&key,
&data,
0); /* flags*/
fetchbuf.buffer = data.data;
fetchbuf.offset = 0;
fetchbuf.size = data.size;
- read_openpgp_stream(keydb_fetchchar, &fetchbuf,
- &packets);
+ read_openpgp_stream(buffer_fetchchar, &fetchbuf,
+ &packets, 0);
parse_keys(packets, publickey);
+ free_packet_list(packets);
+ packets = NULL;
numkeys++;
} else if (ret != DB_NOTFOUND) {
- dbconn->err(dbconn, ret, "Problem retrieving key");
+ logthing(LOGTHING_ERROR,
+ "Problem retrieving key: %s",
+ db_strerror(ret));
+ }
+
+ if (!intrans) {
+ endtrans();
}
return (numkeys);
}
+int worddb_cmp(const char *d1, const char *d2)
+{
+ return memcmp(d1, d2, 12);
+}
+
/**
* fetch_key_text - Trys to find the keys that contain the supplied text.
* @search: The text to search for.
uint64_t keyid;
int i;
int numkeys;
+ char *searchtext = NULL;
+ struct ll *wordlist = NULL;
+ struct ll *curword = NULL;
+ struct ll *keylist = NULL;
+ struct ll *newkeylist = NULL;
numkeys = 0;
+ searchtext = strdup(search);
+ wordlist = makewordlist(wordlist, searchtext);
+
+ starttrans();
ret = worddb->cursor(worddb,
- NULL, /* txn */
+ txn,
&cursor,
0); /* flags */
- if (ret == 0) {
+
+ for (curword = wordlist; curword != NULL; curword = curword->next) {
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
- key.data = (void *) search;
- key.size = strlen(search);
+ key.data = curword->object;
+ key.size = strlen(curword->object);
+ data.flags = DB_DBT_MALLOC;
ret = cursor->c_get(cursor,
&key,
&data,
DB_SET);
- while (ret == 0 && strcmp(key.data, search) == 0) {
+ while (ret == 0 && strncmp(key.data, curword->object,
+ key.size) == 0 &&
+ ((char *) curword->object)[key.size] == 0) {
keyid = 0;
for (i = 4; i < 12; i++) {
keyid <<= 8;
- keyid += ((unsigned char *) data.data)[i];
+ keyid += ((unsigned char *)
+ data.data)[i];
+ }
+
+ if (keylist == NULL ||
+ llfind(keylist, data.data,
+ worddb_cmp) != NULL) {
+ newkeylist = lladd(newkeylist, data.data);
+ data.data = NULL;
+ } else {
+ free(data.data);
+ data.data = NULL;
}
- numkeys += fetch_key(keyid,
- publickey,
- false);
ret = cursor->c_get(cursor,
&key,
&data,
DB_NEXT);
}
- ret = cursor->c_close(cursor);
- cursor = NULL;
+ llfree(keylist, free);
+ keylist = newkeylist;
+ newkeylist = NULL;
+ if (data.data != NULL) {
+ free(data.data);
+ data.data = NULL;
+ }
+ }
+ llfree(wordlist, NULL);
+ wordlist = NULL;
+
+ for (newkeylist = keylist;
+ newkeylist != NULL && numkeys < config.maxkeys;
+ newkeylist = newkeylist->next) {
+
+ keyid = 0;
+ for (i = 4; i < 12; i++) {
+ keyid <<= 8;
+ keyid += ((unsigned char *)
+ newkeylist->object)[i];
+ }
+
+ numkeys += fetch_key(keyid,
+ publickey,
+ true);
}
+ llfree(keylist, free);
+ keylist = NULL;
+ free(searchtext);
+ searchtext = NULL;
+
+ ret = cursor->c_close(cursor);
+ cursor = NULL;
+
+ endtrans();
return (numkeys);
}
struct openpgp_publickey *next = NULL;
int ret = 0;
int i = 0;
- struct db3_get_ctx storebuf;
+ struct buffer_ctx storebuf;
DBT key;
DBT data;
uint64_t keyid = 0;
unsigned char worddb_data[12];
struct ll *wordlist = NULL;
struct ll *curword = NULL;
+ bool deadlock = false;
keyid = get_keyid(publickey);
+ if (!intrans) {
+ starttrans();
+ }
+
/*
* Delete the key if we already have it.
*
* it definitely needs updated.
*/
if (update) {
- delete_key(keyid, true);
+ deadlock = (delete_key(keyid, true) == -1);
}
/*
* Convert the key to a flat set of binary data.
*/
- 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);
+ 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(keydb_putchar, &storebuf, packets);
+ 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);
- keyid &= 0xFFFFFFFF;
- data.size = storebuf.offset;
- data.data = storebuf.buffer;
+ /*
+ * 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);
+ keyid &= 0xFFFFFFFF;
+ 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;
+ }
+ }
- ret = dbconn->put(dbconn,
- NULL, /* txn id */
- &key,
- &data,
- 0); /* flags*/
- if (ret != 0) {
- dbconn->err(dbconn, ret, "Problem storing key");
+ 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.
*/
- uids = keyuids(publickey, &primary);
+ if (!deadlock) {
+ uids = keyuids(publickey, &primary);
+ }
if (uids != NULL) {
for (i = 0; ret == 0 && uids[i] != NULL; i++) {
wordlist = makewordlist(wordlist, uids[i]);
}
- for (curword = wordlist; curword != NULL;
+ for (curword = wordlist; curword != NULL && !deadlock;
curword = curword->next) {
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
worddb_data[10] = (keyid >> 8) & 0xFF;
worddb_data[11] = keyid & 0xFF;
ret = worddb->put(worddb,
- 0,
+ txn,
&key,
&data,
0);
if (ret != 0) {
- worddb->err(worddb, ret,
- "Problem storing key");
+ logthing(LOGTHING_ERROR,
+ "Problem storing word: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
}
}
uids = NULL;
}
- return 0;
+ if (!intrans) {
+ endtrans();
+ }
+
+ return deadlock ? -1 : 0 ;
}
/**
unsigned char worddb_data[12];
struct ll *wordlist = NULL;
struct ll *curword = NULL;
+ bool deadlock = false;
keyid &= 0xFFFFFFFF;
- fetch_key(keyid, &publickey, intrans);
+ if (!intrans) {
+ starttrans();
+ }
+
+ fetch_key(keyid, &publickey, true);
/*
* Walk through the uids removing the words from the worddb.
}
ret = worddb->cursor(worddb,
- NULL, /* txn */
+ txn,
&cursor,
0); /* flags */
- for (curword = wordlist; curword != NULL;
+ for (curword = wordlist; curword != NULL && !deadlock;
curword = curword->next) {
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
&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));
+ }
}
if (ret != 0) {
- worddb->err(worddb, ret,
- "Problem deleting word.");
+ logthing(LOGTHING_ERROR,
+ "Problem deleting word: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
}
}
ret = cursor->c_close(cursor);
publickey = NULL;
}
- key.data = &keyid;
- key.size = sizeof(keyid);
+ if (!deadlock) {
+ key.data = &keyid;
+ key.size = sizeof(keyid);
- dbconn->del(dbconn,
- NULL, /* txn id */
- &key,
- 0); /* flags */
+ keydb(keyid)->del(keydb(keyid),
+ txn,
+ &key,
+ 0); /* flags */
+ }
+
+ if (!intrans) {
+ endtrans();
+ }
+
+ return deadlock ? (-1) : (ret == DB_NOTFOUND);
+}
- return (ret == DB_NOTFOUND);
+/**
+ * dumpdb - dump the key database
+ * @filenamebase: The base filename to use for the dump.
+ *
+ * Dumps the database into one or more files, which contain pure OpenPGP
+ * that can be reimported into onak or gpg. filenamebase provides a base
+ * file name for the dump; several files may be created, all of which will
+ * begin with this string and then have a unique number and a .pgp
+ * extension.
+ */
+int dumpdb(char *filenamebase)
+{
+ DBT key, data;
+ DBC *cursor = NULL;
+ int ret = 0;
+ int fd = -1;
+ int i = 0;
+ char filename[1024];
+
+ filename[1023] = 0;
+ for (i = 0; i < numdbs; i++) {
+ ret = dbconns[i]->cursor(dbconns[i],
+ NULL,
+ &cursor,
+ 0); /* flags */
+
+ snprintf(filename, 1023, "%s.%d.pgp", filenamebase, i);
+ fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
+ if (fd == -1) {
+ logthing(LOGTHING_ERROR,
+ "Error opening keydump file (%s): %s",
+ filename,
+ strerror(errno));
+ } else {
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
+ while (ret == 0) {
+ write(fd, data.data, data.size);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ ret = cursor->c_get(cursor, &key, &data,
+ DB_NEXT);
+ }
+ if (ret != DB_NOTFOUND) {
+ logthing(LOGTHING_ERROR,
+ "Problem reading key: %s",
+ db_strerror(ret));
+ }
+ close(fd);
+ }
+
+ ret = cursor->c_close(cursor);
+ cursor = NULL;
+ }
+
+ return 0;
}
/*