static int used_resources;
static void *primary_keyring=NULL;
-struct keydb_handle
-{
- int locked;
- int found;
- int saved_found;
- unsigned long skipped_long_blobs;
- int no_caching;
- int current;
- int used; /* Number of items in ACTIVE. */
- struct resource_item active[MAX_KEYDB_RESOURCES];
-};
-
-
-/* This object is used to keep a list of keyids in a linked list. */
-typedef struct kid_list_s
-{
- struct kid_list_s *next;
- u32 kid[2];
- int state; /* True if found. */
-} *kid_list_t;
-
-/* To avoid looking up a key by keyid where we know that it does not
- yet exist, we keep a table of keyids with search results. This
- improves the --list-sigs and --check-sigs commands substantively.
- To avoid extra complexity we clear the entire table on any insert
- or update operation. The array is indexed by the LSB of the keyid.
- KID_FOUND_TABLE_COUNT gives the number of keys in the table. */
-static kid_list_t kid_found_table[256];
-static unsigned int kid_found_table_count;
-
/* This is a simple cache used to return the last result of a
successful fingerprint search. This works only for keybox resources
KEYBLOCK_CACHE_FILLED
};
-struct {
+struct keyblock_cache {
enum keyblock_cache_states state;
byte fpr[MAX_FINGERPRINT_LEN];
iobuf_t iobuf; /* Image of the keyblock. */
u32 *sigstatus;
int pk_no;
int uid_no;
-} keyblock_cache;
+};
+
+
+struct keydb_handle
+{
+ /* When we locked all of the resources in ACTIVE (using keyring_lock
+ / keybox_lock, as appropriate). */
+ int locked;
+
+ /* The index into ACTIVE of the resources in which the last search
+ result was found. Initially -1. */
+ int found;
+
+ /* Initially -1 (invalid). This is used to save a search result and
+ later restore it as the selected result. */
+ int saved_found;
+
+ /* The number of skipped long blobs since the last search
+ (keydb_search_reset). */
+ unsigned long skipped_long_blobs;
+
+ /* If set, this disables the use of the keyblock cache. */
+ int no_caching;
+
+ /* Whether the next search will be from the beginning of the
+ database (and thus consider all records). */
+ int is_reset;
+
+ /* The "file position." In our case, this is index of the current
+ resource in ACTIVE. */
+ int current;
+
+ /* The number of resources in ACTIVE. */
+ int used;
+
+ /* Cache of the last found and parsed key block (only used for
+ keyboxes, not keyrings). */
+ struct keyblock_cache keyblock_cache;
+
+ /* Copy of ALL_RESOURCES when keydb_new is called. */
+ struct resource_item active[MAX_KEYDB_RESOURCES];
+};
+
+/* Looking up keys is expensive. To hide the cost, we cache whether
+ keys exist in the key database. Then, if we know a key does not
+ exist, we don't have to spend time looking it up. This
+ particularly helps the --list-sigs and --check-sigs commands.
+
+ The cache stores the results in a hash using separate chaining.
+ Concretely: we use the LSB of the keyid to index the hash table and
+ each bucket consists of a linked list of entries. An entry
+ consists of the 64-bit key id. If a key id is not in the cache,
+ then we don't know whether it is in the DB or not.
+
+ To simplify the cache consistency protocol, we simply flush the
+ whole cache whenever a key is inserted or updated. */
+
+#define KID_NOT_FOUND_CACHE_BUCKETS 256
+static struct kid_not_found_cache_bucket *
+ kid_not_found_cache[KID_NOT_FOUND_CACHE_BUCKETS];
+
+/* The total number of entries in the hash table. */
+static unsigned int kid_not_found_cache_count;
+
+struct kid_not_found_cache_bucket
+{
+ struct kid_not_found_cache_bucket *next;
+ u32 kid[2];
+};
static int lock_all (KEYDB_HANDLE hd);
static void unlock_all (KEYDB_HANDLE hd);
-/* Checkwhether the keyid KID is in the table of found or not found
- keyids.
+/* Check whether the keyid KID is in key id is definately not in the
+ database.
Returns:
- 0 - Keyid not in table
- 1 - Keyid in table because not found in a previous search
- 2 - Keyid in table because found in a previous search
- */
+
+ 0 - Indeterminate: the key id is not in the cache; we don't know
+ whether the key is in the database or not. If you want a
+ definitive answer, you'll need to perform a lookup.
+
+ 1 - There is definitely no key with this key id in the database.
+ We searched for a key with this key id previously, but we
+ didn't find it in the database. */
static int
kid_not_found_p (u32 *kid)
{
- kid_list_t k;
+ struct kid_not_found_cache_bucket *k;
- for (k = kid_found_table[kid[0] % 256]; k; k = k->next)
+ for (k = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; k; k = k->next)
if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
{
if (DBG_CACHE)
- log_debug ("keydb: kid_not_found_p (%08lx%08lx) => %s\n",
- (ulong)kid[0], (ulong)kid[1],
- k->state? "false (found)": "true");
- return k->state? 2 : 1;
+ log_debug ("keydb: kid_not_found_p (%08lx%08lx) => not in DB\n",
+ (ulong)kid[0], (ulong)kid[1]);
+ return 1;
}
if (DBG_CACHE)
- log_debug ("keydb: kid_not_found_p (%08lx%08lx) => false\n",
+ log_debug ("keydb: kid_not_found_p (%08lx%08lx) => indeterminate\n",
(ulong)kid[0], (ulong)kid[1]);
return 0;
}
-/* Put the keyid KID into the table of keyids with their find states of
- previous searches. Note that there is no check whether the keyid
- is already in the table, thus kid_not_found_p() should be used prior. */
+/* Insert the keyid KID into the kid_not_found_cache. FOUND is whether
+ the key is in the key database or not.
+
+ Note this function does not check whether the key id is already in
+ the cache. As such, kid_not_found_p() should be called first. */
static void
-kid_not_found_insert (u32 *kid, int found)
+kid_not_found_insert (u32 *kid)
{
- kid_list_t k;
+ struct kid_not_found_cache_bucket *k;
if (DBG_CACHE)
- log_debug ("keydb: kid_not_found_insert (%08lx%08lx, %d)\n",
- (ulong)kid[0], (ulong)kid[1], found);
+ log_debug ("keydb: kid_not_found_insert (%08lx%08lx)\n",
+ (ulong)kid[0], (ulong)kid[1]);
k = xmalloc (sizeof *k);
k->kid[0] = kid[0];
k->kid[1] = kid[1];
- k->state = found;
- k->next = kid_found_table[kid[0]%256];
- kid_found_table[kid[0]%256] = k;
- kid_found_table_count++;
+ k->next = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS];
+ kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS] = k;
+ kid_not_found_cache_count++;
}
-/* Flush the entire table of keyids whche were not found in previous
- searches. */
+/* Flush the kid not found cache. */
static void
kid_not_found_flush (void)
{
- kid_list_t k, knext;
+ struct kid_not_found_cache_bucket *k, *knext;
int i;
if (DBG_CACHE)
log_debug ("keydb: kid_not_found_flush\n");
- if (!kid_found_table_count)
+ if (!kid_not_found_cache_count)
return;
- for (i=0; i < DIM(kid_found_table); i++)
+ for (i=0; i < DIM(kid_not_found_cache); i++)
{
- for (k = kid_found_table[i]; k; k = knext)
+ for (k = kid_not_found_cache[i]; k; k = knext)
{
knext = k->next;
xfree (k);
}
- kid_found_table[i] = NULL;
+ kid_not_found_cache[i] = NULL;
}
- kid_found_table_count = 0;
+ kid_not_found_cache_count = 0;
}
static void
-keyblock_cache_clear (void)
+keyblock_cache_clear (struct keydb_handle *hd)
{
- keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
- xfree (keyblock_cache.sigstatus);
- keyblock_cache.sigstatus = NULL;
- iobuf_close (keyblock_cache.iobuf);
- keyblock_cache.iobuf = NULL;
+ hd->keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
+ xfree (hd->keyblock_cache.sigstatus);
+ hd->keyblock_cache.sigstatus = NULL;
+ iobuf_close (hd->keyblock_cache.iobuf);
+ hd->keyblock_cache.iobuf = NULL;
}
/* Handle the creation of a keyring or a keybox if it does not yet
exist. Take into account that other processes might have the
keyring/keybox already locked. This lock check does not work if
- the directory itself is not yet available. If is IS_BOX is true
- the filename is expected to be a keybox. If FORCE_CREATE is true
- the keyring or keybox shall be created. */
+ the directory itself is not yet available. If IS_BOX is true the
+ filename is expected to refer to a keybox. If FORCE_CREATE is true
+ the keyring or keybox will be created.
+
+ Return 0 if it is okay to access the specified file. */
static int
maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
{
}
-/* Helper for keydb_add_resource. Opens FILENAME to figures out the
- resource type. Returns the resource type and a flag at R_NOTFOUND
- indicating whether FILENAME could be opened at all. If the openpgp
- flag is set in a keybox header, R_OPENPGP will be set to true. */
+/* Helper for keydb_add_resource. Opens FILENAME to figure out the
+ resource type.
+
+ Returns the specified file's likely type. If the file does not
+ exist, returns KEYDB_RESOURCE_TYPE_NONE and sets *R_FOUND to 0.
+ Otherwise, tries to figure out the file's type. This is either
+ KEYDB_RESOURCE_TYPE_KEYBOX, KEYDB_RESOURCE_TYPE_KEYRING or
+ KEYDB_RESOURCE_TYPE_KEYNONE. If the file is a keybox and it has
+ the OpenPGP flag set, then R_OPENPGP is also set. */
static KeydbResourceType
rt_from_file (const char *filename, int *r_found, int *r_openpgp)
{
}
-/*
- * Register a resource (keyring or aeybox). The first keyring or
- * keybox which is added by this function is created if it does not
- * exist. FLAGS are a combination of the KEYDB_RESOURCE_FLAG_
- * constants as defined in keydb.h.
- */
gpg_error_t
keydb_add_resource (const char *url, unsigned int flags)
{
+ /* Whether we have successfully registered a resource. */
static int any_registered;
+ /* The file named by the URL (i.e., without the prototype). */
const char *resname = url;
+
char *filename = NULL;
int create;
int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY);
/* Create the resource if it is the first registered one. */
create = (!read_only && !any_registered);
- /* Do we have an URL?
- * gnupg-ring:filename := this is a plain keyring.
- * gnupg-kbx:filename := this is a keybox file.
- * filename := See what is is, but create as plain keyring.
- */
if (strlen (resname) > 11 && !strncmp( resname, "gnupg-ring:", 11) )
{
rt = KEYDB_RESOURCE_TYPE_KEYRING;
void
keydb_dump_stats (void)
{
- if (kid_found_table_count)
- log_info ("keydb: kid_not_found_table: total: %u\n", kid_found_table_count);
+ if (kid_not_found_cache_count)
+ log_info ("keydb: kid_not_found_cache: total: %u\n",
+ kid_not_found_cache_count);
}
{
KEYDB_HANDLE hd;
int i, j;
+ int die = 0;
if (DBG_CLOCK)
log_clock ("keydb_new");
hd = xmalloc_clear (sizeof *hd);
hd->found = -1;
hd->saved_found = -1;
+ hd->is_reset = 1;
assert (used_resources <= MAX_KEYDB_RESOURCES);
- for (i=j=0; i < used_resources; i++)
+ for (i=j=0; ! die && i < used_resources; i++)
{
switch (all_resources[i].type)
{
hd->active[j].type = all_resources[i].type;
hd->active[j].token = all_resources[i].token;
hd->active[j].u.kr = keyring_new (all_resources[i].token);
- if (!hd->active[j].u.kr) {
- xfree (hd);
- return NULL; /* fixme: release all previously allocated handles*/
- }
+ if (!hd->active[j].u.kr)
+ die = 1;
j++;
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
hd->active[j].token = all_resources[i].token;
hd->active[j].u.kb = keybox_new_openpgp (all_resources[i].token, 0);
if (!hd->active[j].u.kb)
- {
- xfree (hd);
- return NULL; /* fixme: release all previously allocated handles*/
- }
+ die = 1;
j++;
break;
}
hd->used = j;
active_handles++;
+
+ if (die)
+ {
+ keydb_release (hd);
+ hd = NULL;
+ }
+
return hd;
}
}
-/* Set a flag on handle to not use cached results. This is required
- for updating a keyring and for key listins. Fixme: Using a new
- parameter for keydb_new might be a better solution. */
void
keydb_disable_caching (KEYDB_HANDLE hd)
{
}
-/*
- * Return the name of the current resource. This is function first
- * looks for the last found found, then for the current search
- * position, and last returns the first available resource. The
- * returned string is only valid as long as the handle exists. This
- * function does only return NULL if no handle is specified, in all
- * other error cases an empty string is returned.
- */
const char *
keydb_get_resource_name (KEYDB_HANDLE hd)
{
\f
-/* Push the last found state if any. */
void
keydb_push_found_state (KEYDB_HANDLE hd)
{
}
-/* Pop the last found state. */
void
keydb_pop_found_state (KEYDB_HANDLE hd)
{
}
-/*
- * Return the last found keyring. Caller must free it.
- * The returned keyblock has the kbode flag bit 0 set for the node with
- * the public key used to locate the keyblock or flag bit 1 set for
- * the user ID node.
- */
gpg_error_t
keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
{
if (DBG_CLOCK)
log_clock ("keydb_get_keybock enter");
- if (keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
+ if (hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
{
- iobuf_seek (keyblock_cache.iobuf, 0);
- err = parse_keyblock_image (keyblock_cache.iobuf,
- keyblock_cache.pk_no,
- keyblock_cache.uid_no,
- keyblock_cache.sigstatus,
- ret_kb);
+ err = iobuf_seek (hd->keyblock_cache.iobuf, 0);
if (err)
- keyblock_cache_clear ();
- if (DBG_CLOCK)
- log_clock (err? "keydb_get_keyblock leave (cached, failed)"
- : "keydb_get_keyblock leave (cached)");
- return err;
+ {
+ log_error ("keydb_get_keyblock: failed to rewind iobuf for cache\n");
+ keyblock_cache_clear (hd);
+ }
+ else
+ {
+ err = parse_keyblock_image (hd->keyblock_cache.iobuf,
+ hd->keyblock_cache.pk_no,
+ hd->keyblock_cache.uid_no,
+ hd->keyblock_cache.sigstatus,
+ ret_kb);
+ if (err)
+ keyblock_cache_clear (hd);
+ if (DBG_CLOCK)
+ log_clock (err? "keydb_get_keyblock leave (cached, failed)"
+ : "keydb_get_keyblock leave (cached)");
+ return err;
+ }
}
if (hd->found < 0 || hd->found >= hd->used)
{
err = parse_keyblock_image (iobuf, pk_no, uid_no, sigstatus,
ret_kb);
- if (!err && keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
+ if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
{
- keyblock_cache.state = KEYBLOCK_CACHE_FILLED;
- keyblock_cache.sigstatus = sigstatus;
- keyblock_cache.iobuf = iobuf;
- keyblock_cache.pk_no = pk_no;
- keyblock_cache.uid_no = uid_no;
+ hd->keyblock_cache.state = KEYBLOCK_CACHE_FILLED;
+ hd->keyblock_cache.sigstatus = sigstatus;
+ hd->keyblock_cache.iobuf = iobuf;
+ hd->keyblock_cache.pk_no = pk_no;
+ hd->keyblock_cache.uid_no = uid_no;
}
else
{
break;
}
- if (keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
- keyblock_cache_clear ();
+ if (hd->keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
+ keyblock_cache_clear (hd);
if (DBG_CLOCK)
log_clock (err? "keydb_get_keyblock leave (failed)"
}
-/*
- * Update the current keyblock with the keyblock KB
- */
gpg_error_t
keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
{
return gpg_error (GPG_ERR_INV_ARG);
kid_not_found_flush ();
- keyblock_cache_clear ();
+ keyblock_cache_clear (hd);
if (hd->found < 0 || hd->found >= hd->used)
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
}
-/*
- * Insert a new KB into one of the resources.
- */
gpg_error_t
keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
{
return gpg_error (GPG_ERR_INV_ARG);
kid_not_found_flush ();
- keyblock_cache_clear ();
+ keyblock_cache_clear (hd);
if (opt.dry_run)
return 0;
}
-/*
- * Delete the current keyblock.
- */
gpg_error_t
keydb_delete_keyblock (KEYDB_HANDLE hd)
{
return gpg_error (GPG_ERR_INV_ARG);
kid_not_found_flush ();
- keyblock_cache_clear ();
+ keyblock_cache_clear (hd);
if (hd->found < 0 || hd->found >= hd->used)
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
\f
-/*
- * Locate the default writable key resource, so that the next
- * operation (which is only relevant for inserts) will be done on this
- * resource.
- */
gpg_error_t
-keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
+keydb_locate_writable (KEYDB_HANDLE hd)
{
gpg_error_t rc;
- (void)reserved;
-
if (!hd)
return GPG_ERR_INV_ARG;
return gpg_error (GPG_ERR_NOT_FOUND);
}
-/*
- * Rebuild the caches of all key resources.
- */
void
keydb_rebuild_caches (int noisy)
{
int i, rc;
- keyblock_cache_clear ();
-
for (i=0; i < used_resources; i++)
{
if (!keyring_is_writable (all_resources[i].token))
}
-/* Return the number of skipped blocks since the last search reset. */
unsigned long
keydb_get_skipped_counter (KEYDB_HANDLE hd)
{
}
-/*
- * Start the next search on this handle right at the beginning
- */
gpg_error_t
keydb_search_reset (KEYDB_HANDLE hd)
{
if (!hd)
return gpg_error (GPG_ERR_INV_ARG);
- keyblock_cache_clear ();
+ keyblock_cache_clear (hd);
if (DBG_CLOCK)
log_clock ("keydb_search_reset");
break;
}
}
+ hd->is_reset = 1;
return rc;
}
}
-/*
- * Search through all keydb resources, starting at the current
- * position, for a keyblock which contains one of the keys described
- * in the DESC array. Returns GPG_ERR_NOT_FOUND if no matching
- * keyring was found.
- */
gpg_error_t
keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
size_t ndesc, size_t *descindex)
{
gpg_error_t rc;
- int once_found = 0;
+ int was_reset = hd->is_reset;
+ /* If an entry is already in the cache, then don't add it again. */
+ int already_in_cache = 0;
if (descindex)
*descindex = 0; /* Make sure it is always set on return. */
dump_search_desc (hd, "keydb_search", desc, ndesc);
- /* Note that we track the found state in the table to cope with the
- case that a initial search found the key and the next search
- (without a reset) did not found the key. Without keeping the
- found state we would falsely claim that the key has not been
- found. Actually this is quite common because we need to check
- for ambgious keyids. */
if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
- && (once_found = kid_not_found_p (desc[0].u.kid)) == 1 )
+ && (already_in_cache = kid_not_found_p (desc[0].u.kid)) == 1 )
{
if (DBG_CLOCK)
log_clock ("keydb_search leave (not found, cached)");
&& ndesc == 1
&& (desc[0].mode == KEYDB_SEARCH_MODE_FPR20
|| desc[0].mode == KEYDB_SEARCH_MODE_FPR)
- && keyblock_cache.state == KEYBLOCK_CACHE_FILLED
- && !memcmp (keyblock_cache.fpr, desc[0].u.fpr, 20))
+ && hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED
+ && !memcmp (hd->keyblock_cache.fpr, desc[0].u.fpr, 20))
{
/* (DESCINDEX is already set). */
if (DBG_CLOCK)
else if (!rc)
hd->found = hd->current;
}
+ hd->is_reset = 0;
rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
? gpg_error (GPG_ERR_NOT_FOUND)
: rc);
- keyblock_cache_clear ();
+ keyblock_cache_clear (hd);
if (!hd->no_caching
&& !rc
&& ndesc == 1 && (desc[0].mode == KEYDB_SEARCH_MODE_FPR20
|| desc[0].mode == KEYDB_SEARCH_MODE_FPR))
{
- keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
- memcpy (keyblock_cache.fpr, desc[0].u.fpr, 20);
+ hd->keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
+ memcpy (hd->keyblock_cache.fpr, desc[0].u.fpr, 20);
}
- if ((!rc || gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
- && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
- && !once_found)
- {
- kid_not_found_insert (desc[0].u.kid, !rc);
- }
+ if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND
+ && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID && was_reset
+ && !already_in_cache)
+ kid_not_found_insert (desc[0].u.kid);
if (DBG_CLOCK)
log_clock (rc? "keydb_search leave (not found)"
}
-/* Note that in contrast to using keydb_search in search first mode,
- this function skips legacy keys. */
gpg_error_t
keydb_search_first (KEYDB_HANDLE hd)
{
gpg_error_t err;
KEYDB_SEARCH_DESC desc;
+ err = keydb_search_reset (hd);
+ if (err)
+ return err;
+
memset (&desc, 0, sizeof desc);
desc.mode = KEYDB_SEARCH_MODE_FIRST;
err = keydb_search (hd, &desc, 1, NULL);
}
-/* Note that in contrast to using keydb_search in search next mode,
- this fucntion skips legacy keys. */
gpg_error_t
keydb_search_next (KEYDB_HANDLE hd)
{