Index: pks/NEWS
diff -c pks/NEWS:1.3 pks/NEWS:1.5
*** pks/NEWS:1.3	Wed Jun  2 02:03:48 1999
--- pks/NEWS	Wed Sep 22 23:54:01 1999
***************
*** 4,9 ****
--- 4,20 ----
  For general information about the keyserver, visit
  <http://www.mit.edu/people/marc/pks/>.
  
+ * Changes since version 0.9.4
+ 
+ ** Add missing transaction hooks
+ 
+ ** Make checkpoint code retry if necessary
+ 
+ ** Change the way memory is allocated in some places to reduce the
+ number of extra calls to malloc and free
+ 
+ ** Fix a bug in db 2.7.5 which causes db_recover to fail under some
+ circumstances.
  
  * Changes in version 0.9.4
  
Index: pks/globals.h
diff -c pks/globals.h:1.14 pks/globals.h:1.15
*** pks/globals.h:1.14	Wed Jun  2 02:09:33 1999
--- pks/globals.h	Tue Jul 20 01:02:45 1999
***************
*** 2,8 ****
  #define _GLOBALS_H_
  
  /*
!  * $Id: globals.h,v 1.14 1999/06/02 06:09:33 marc Exp $
   * 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
   * See the LICENSE file in the release for redistribution information.
--- 2,8 ----
  #define _GLOBALS_H_
  
  /*
!  * $Id: globals.h,v 1.15 1999/07/20 05:02:45 marc Exp $
   * 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
   * See the LICENSE file in the release for redistribution information.
***************
*** 13,19 ****
     "42.17.2+magicfeature", or "42.17.3+joe".  Don't remove any
     existing modifiers. */
  
! #define PKS_VERSION "0.9.4"
  
  #ifdef DEBUG
  extern int debug;
--- 13,19 ----
     "42.17.2+magicfeature", or "42.17.3+joe".  Don't remove any
     existing modifiers. */
  
! #define PKS_VERSION "0.9.4+patch2"
  
  #ifdef DEBUG
  extern int debug;
Index: pks/kd_add.c
diff -c pks/kd_add.c:1.41 pks/kd_add.c:1.42
*** pks/kd_add.c:1.41	Mon May 31 16:21:07 1999
--- pks/kd_add.c	Wed Jun  9 01:50:00 1999
***************
*** 1,4 ****
! const char rcsid_kd_add_c[] = "$Id: kd_add.c,v 1.41 1999/05/31 20:21:07 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_add_c[] = "$Id: kd_add.c,v 1.42 1999/06/09 05:50:00 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 56,61 ****
--- 56,66 ----
     error *err;
  } dkm_state;
  
+ typedef struct _dkm1_state {
+    kd_txn tid;
+    dkm_state *dkms;
+ } dkm1_state;
+ 
  int words_elem_merge(llist *lout, void *e1, void *e2, void *c)
  {
     int o;
***************
*** 784,789 ****
--- 789,795 ----
  }
  
  typedef struct _aetd_state {
+    kd_txn tid;
     unsigned char entry[12];
     int writing;
     int pks_errno;
***************
*** 798,804 ****
     memset(&data, 0, sizeof(data));
     memset(&newdata, 0, sizeof(newdata));
  
!    ret = (*(db->get))(db, NULL, key, &data, 0);
  
     if (ret == DB_NOTFOUND) {
        /* word not in file, create DBT for insert */
--- 804,810 ----
     memset(&data, 0, sizeof(data));
     memset(&newdata, 0, sizeof(newdata));
  
!    ret = (*(db->get))(db, s->tid, key, &data, 0);
  
     if (ret == DB_NOTFOUND) {
        /* word not in file, create DBT for insert */
***************
*** 870,876 ****
        }
     }
  
!    if ((ret = (*(db->put))(db, NULL, key, &newdata, 0))) {
        s->writing = 1;
        s->pks_errno = ret;
        if (needfree)
--- 876,882 ----
        }
     }
  
!    if ((ret = (*(db->put))(db, s->tid, key, &newdata, 0))) {
        s->writing = 1;
        s->pks_errno = ret;
        if (needfree)
***************
*** 907,913 ****
     data.data = s->entry;
     data.size = 12;
  
!    if ((ret = (*(worddb->put))(worddb, NULL, &key, &data, 0))) {
        s->writing = 1;
        s->pks_errno = ret;
        fail();
--- 913,919 ----
     data.data = s->entry;
     data.size = 12;
  
!    if ((ret = (*(worddb->put))(worddb, s->tid, &key, &data, 0))) {
        s->writing = 1;
        s->pks_errno = ret;
        fail();
***************
*** 929,938 ****
     return(1);
  }
  
! int db_store_wordlist(llist *keys, error *err)
  {
     aetd_state aetds;
  
     aetds.pks_errno = 0;
  
     if (!llist_iterate(keys, add_key_to_worddb, &aetds)) {
--- 935,945 ----
     return(1);
  }
  
! int db_store_wordlist(kd_txn tid, llist *keys, error *err)
  {
     aetd_state aetds;
  
+    aetds.tid = tid;
     aetds.pks_errno = 0;
  
     if (!llist_iterate(keys, add_key_to_worddb, &aetds)) {
***************
*** 978,987 ****
     return(add_entry_to_db(timedb, &key, s));
  }
  
! int db_store_timestamp(llist *keys, error *err)
  {
     aetd_state aetds;
  
     aetds.pks_errno = 0;
  
     /* all the keyid's are the same, only need to deal with the first,
--- 985,995 ----
     return(add_entry_to_db(timedb, &key, s));
  }
  
! int db_store_timestamp(kd_txn tid, llist *keys, error *err)
  {
     aetd_state aetds;
  
+    aetds.tid = tid;
     aetds.pks_errno = 0;
  
     /* all the keyid's are the same, only need to deal with the first,
***************
*** 1008,1017 ****
  "unprocessable key is %02X%02X%02X%02X.\n";
  const char revocation_sig[] = "revocation signature";
  
! int db_key_merge(void *e, void *c)
  {
     keys_elem *ke = (keys_elem *) e;
!    dkm_state *s = (dkm_state *) c;
  
     llist db_keys;
  
--- 1016,1025 ----
  "unprocessable key is %02X%02X%02X%02X.\n";
  const char revocation_sig[] = "revocation signature";
  
! int db_key_merge_1(void *e, void *c)
  {
     keys_elem *ke = (keys_elem *) e;
!    dkm1_state *s = (dkm1_state *) c;
  
     llist db_keys;
  
***************
*** 1019,1033 ****
  
     /* first, clear the disabled bit in files from a user */
  
!    if (s->no_strip_disabled && ke->disabled)
        ke->disabled = -1;
     else
        ke->disabled = 0;
  
!    ke->disabled = s->no_strip_disabled?(ke->disabled?-1:0):0;
  
!    if (!kd_get_keys_by_keyid(ke->keyidbits.buf /* 8 byte keyid */,
! 			     &db_keys, s->err))
        return(0);
  
     if (llist_count(&db_keys) == 0) {
--- 1027,1041 ----
  
     /* first, clear the disabled bit in files from a user */
  
!    if (s->dkms->no_strip_disabled && ke->disabled)
        ke->disabled = -1;
     else
        ke->disabled = 0;
  
!    ke->disabled = s->dkms->no_strip_disabled?(ke->disabled?-1:0):0;
  
!    if (!kd_get_keys_by_keyid(s->tid, ke->keyidbits.buf /* 8 byte keyid */,
! 			     &db_keys, s->dkms->err))
        return(0);
  
     if (llist_count(&db_keys) == 0) {
***************
*** 1037,1058 ****
        if (!llist_add(&db_keys, e)) {
  	 llist_free(&db_keys);
  
! 	 s->err->fatal = 1;
! 	 s->err->str = "adding request key to db_keys failed";
  	 fail();
        }
  
!       s->ms.new_pubkeys++;
!       if (s->ms.verbose)
! 	 display_new_key(&(s->ms), &(ke->keyidbits));
  
        /* this is definitely a new key.  marshall it into the add
  	 xbuffer for later */
  
!       if (s->ms.save_add) {
! 	 if (!kd_keys_elem_marshall(ke, s->ms.add_xb)) {
! 	    s->err->fatal = 1;
! 	    s->err->str = "failed marshalling new key";
  	    fail();
  	 }
        }
--- 1045,1066 ----
        if (!llist_add(&db_keys, e)) {
  	 llist_free(&db_keys);
  
! 	 s->dkms->err->fatal = 1;
! 	 s->dkms->err->str = "adding request key to db_keys failed";
  	 fail();
        }
  
!       s->dkms->ms.new_pubkeys++;
!       if (s->dkms->ms.verbose)
! 	 display_new_key(&(s->dkms->ms), &(ke->keyidbits));
  
        /* this is definitely a new key.  marshall it into the add
  	 xbuffer for later */
  
!       if (s->dkms->ms.save_add) {
! 	 if (!kd_keys_elem_marshall(ke, s->dkms->ms.add_xb)) {
! 	    s->dkms->err->fatal = 1;
! 	    s->dkms->err->str = "failed marshalling new key";
  	    fail();
  	 }
        }
***************
*** 1071,1078 ****
  	 llist_iterate(&db_keys, keys_elem_free, NULL);
  	 llist_free(&db_keys);
  
! 	 s->err->fatal = 1;
! 	 s->err->str = "adding request key to request_key failed";
  	 fail();
        }
  
--- 1079,1086 ----
  	 llist_iterate(&db_keys, keys_elem_free, NULL);
  	 llist_free(&db_keys);
  
! 	 s->dkms->err->fatal = 1;
! 	 s->dkms->err->str = "adding request key to request_key failed";
  	 fail();
        }
  
***************
*** 1081,1087 ****
        llist_alloc(&merged_keys);
     
        if (!llist_merge(&merged_keys, &db_keys, &request_key,
! 		       kd_keys_elem_merge, (void *) &(s->ms),
  		       keys_elem_free, NULL)) {
  	 /* if this ever becomes non-fatal, remember this:
  	    // merge will destroy the input lists
--- 1089,1095 ----
        llist_alloc(&merged_keys);
     
        if (!llist_merge(&merged_keys, &db_keys, &request_key,
! 		       kd_keys_elem_merge, (void *) &(s->dkms->ms),
  		       keys_elem_free, NULL)) {
  	 /* if this ever becomes non-fatal, remember this:
  	    // merge will destroy the input lists
***************
*** 1091,1128 ****
  	    keys_elem_alloc(ke);
  	    */
  
! 	 s->err->fatal = 1;
! 	 s->err->str = "merging database key with request key failed";
  	 fail();
        }
  
        db_keys = merged_keys;
  
!       if (s->ms.save_add) {
  	 /* check to see if anything was added, and if so, marshall it
  	    into the add xbuffer for later */
  
! 	 if (!llist_iterate(&(s->ms.add_keys), kd_keys_elem_marshall,
! 			    s->ms.add_xb) ||
! 	     !llist_iterate(&(s->ms.add_keys), add_keys_elem_free, NULL)) {
! 	    s->err->fatal = 1;
! 	    s->err->str = "failed marshalling added keys";
  	    fail();
  	 }
  
! 	 llist_free(&(s->ms.add_keys));
        }
     }
  
     /* check to see if the key actually changed.  Don't bother writing
        if nothing did */
  
!    if (!s->ms.new_sigs &&
!        !s->ms.repl_sigs &&
!        !s->ms.new_userids &&
!        !s->ms.changed_primary_userids &&
!        !s->ms.new_revocations &&
!        !s->ms.new_pubkeys) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
--- 1099,1137 ----
  	    keys_elem_alloc(ke);
  	    */
  
! 	 s->dkms->err->fatal = 1;
! 	 s->dkms->err->str = "merging database key with request key failed";
  	 fail();
        }
  
        db_keys = merged_keys;
  
!       if (s->dkms->ms.save_add) {
  	 /* check to see if anything was added, and if so, marshall it
  	    into the add xbuffer for later */
  
! 	 if (!llist_iterate(&(s->dkms->ms.add_keys), kd_keys_elem_marshall,
! 			    s->dkms->ms.add_xb) ||
! 	     !llist_iterate(&(s->dkms->ms.add_keys), add_keys_elem_free,
! 			    NULL)) {
! 	    s->dkms->err->fatal = 1;
! 	    s->dkms->err->str = "failed marshalling added keys";
  	    fail();
  	 }
  
! 	 llist_free(&(s->dkms->ms.add_keys));
        }
     }
  
     /* check to see if the key actually changed.  Don't bother writing
        if nothing did */
  
!    if (!s->dkms->ms.new_sigs &&
!        !s->dkms->ms.repl_sigs &&
!        !s->dkms->ms.new_userids &&
!        !s->dkms->ms.changed_primary_userids &&
!        !s->dkms->ms.new_revocations &&
!        !s->dkms->ms.new_pubkeys) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
***************
*** 1140,1160 ****
  
     /* any errors past this point are fatal */
  
!    if (!kd_db_store_keyblock(&db_keys, s->err)) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
        fail();
     }
  
!    if (!db_store_wordlist(&db_keys, s->err)) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
        fail();
     }
  
!    if (!db_store_timestamp(&db_keys, s->err)) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
--- 1149,1169 ----
  
     /* any errors past this point are fatal */
  
!    if (!kd_db_store_keyblock(s->tid, &db_keys, s->dkms->err)) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
        fail();
     }
  
!    if (!db_store_wordlist(s->tid, &db_keys, s->dkms->err)) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
        fail();
     }
  
!    if (!db_store_timestamp(s->tid, &db_keys, s->dkms->err)) {
        llist_iterate(&db_keys, keys_elem_free, NULL);
        llist_free(&db_keys);
  
***************
*** 1167,1172 ****
--- 1176,1198 ----
     return(1);
  }
  
+ int db_key_merge(void *e, void *c)
+ {
+    dkm1_state dkm1s;
+    dkm_state *s = (dkm_state *) c;
+ 
+    dkm1s.dkms = s;
+ 
+    if (kd_txn_begin(&(dkm1s.tid), s->err) &&
+        db_key_merge_1(e, &dkm1s) &&
+        kd_txn_commit(dkm1s.tid, s->err))
+       return(1);
+ 
+    kd_txn_abort(dkm1s.tid, s->err);
+ 
+    return(0);
+ }
+ 
  int kd_add_1(unsigned char *keys, long len, int flags,
  	     xbuffer *win_msg, xbuffer *newkeys_xb, error *err)
  {
***************
*** 1435,1441 ****
  {
     error err;
     xbuffer msg, addkeys;
-    kd_txn tid;
  
     err.str = err.buf;
     xbuffer_alloc(&msg);
--- 1461,1466 ----
***************
*** 1445,1453 ****
  
     kd_log_start("kd_add", NULL, 0, flags);
  
!    if (kd_txn_begin(&tid, &err) &&
!        kd_add_1(keys, len, flags, &msg, newkeys?&addkeys:NULL, &err) &&
!        kd_txn_commit(tid, &err)) {
        *ret = msg.buf;
        *retlen = msg.len;
        if (newkeys) {
--- 1470,1476 ----
  
     kd_log_start("kd_add", NULL, 0, flags);
  
!    if (kd_add_1(keys, len, flags, &msg, newkeys?&addkeys:NULL, &err)) {
        *ret = msg.buf;
        *retlen = msg.len;
        if (newkeys) {
***************
*** 1459,1466 ****
  
        return(1);
     }
- 
-    kd_txn_abort(tid, NULL);
  
     if (!err.fatal) {
        if (!(*ret = (unsigned char *) my_strdup(err.str))) {
--- 1482,1487 ----
Index: pks/kd_delete.c
diff -c pks/kd_delete.c:1.22 pks/kd_delete.c:1.23
*** pks/kd_delete.c:1.22	Mon May 31 16:21:10 1999
--- pks/kd_delete.c	Wed Jun  9 01:43:58 1999
***************
*** 1,4 ****
! const char rcsid_kd_delete_c[] = "$Id: kd_delete.c,v 1.22 1999/05/31 20:21:10 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_delete_c[] = "$Id: kd_delete.c,v 1.23 1999/06/09 05:43:58 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 21,30 ****
--- 21,36 ----
  #include "util.h"
  
  typedef struct _dwfw_state {
+    kd_txn tid;
     unsigned char entry[12];
     error *err;
  } dwfw_state;
  
+ typedef struct _dkfw_state {
+    kd_txn tid;
+    xbuffer *deleted;
+ } dkfw_state;
+ 
  /* this isn't particularly efficient, but delete's aren't all that
     common. */
  
***************
*** 34,42 ****
     dwfw_state *s = (dwfw_state *) c;
  
     unsigned char word[256];
     DBT key, data;
!    unsigned char *here, *last;
!    int o, i, ret;
  
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
--- 40,48 ----
     dwfw_state *s = (dwfw_state *) c;
  
     unsigned char word[256];
+    DBC *cursor;
     DBT key, data;
!    int i, ret;
  
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
***************
*** 47,141 ****
     key.data = (void *) word;
     key.size = (size_t) we->len;
  
!    ret = (*(worddb->get))(worddb, NULL, &key, &data, 0);
  
!    if (ret == DB_NOTFOUND) {
!       s->err->fatal = 1;
!       sprintf(s->err->buf,
! 	      "consistency error reading worddb for delete: %.*s not found ",
! 	      (int) we->len, we->ptr);
!       fail();
!    } else if (ret) {
        s->err->fatal = 1;
!       sprintf(s->err->buf,
! 	      "error reading worddb entry for delete (errno = %d)", ret);
        fail();
     }
  
!    if (data.size % 12) {
!       /*
!        * unknown error, db is broken??
!        */
        s->err->fatal = 1;
        sprintf(s->err->buf,
  	      "consistency error reading worddb for delete: %.*s not found ",
  	      (int) we->len, we->ptr);
        fail();
-       return(1);
     }
- 
-    here = NULL;
  
!    /* find the matching entry */
  
!    for (last = (unsigned char *) data.data;
! 	((last < (((unsigned char *) data.data) + data.size)) &&
! 	 (memcmp(last, zeros, 12) != 0));
! 	last += 12) {
!       o = memcmp(last, s->entry, 12);
! 
!       if (o == 0) {
! 	 here = last;
  	 break;
-       }
  
!       if (o > 0)
  	 break;
     }
- 
-    /* bail if there isn't one */
- 
-    if (here == NULL) {
-       char buf[1024];
- 
-       sprintf(buf,
- 	      "consistency error: word \"%.*s\" in key id %02X%02X%02X%02X\n"
- 	      "does not refer back to key", (int) key.size, (char *) key.data,
- 	      s->entry[8], s->entry[9], s->entry[10], s->entry[11]);
-       log_error("delete_word_from_worddb", buf);
- 
-       return(1);
-    }
- 
-    /* if there is only one useful entry, then remove the whole db
-       entry */
- 
-    if ((here == data.data) &&
-        ((data.size == 12) ||
- 	(memcmp(here+12, zeros, 12)))) {
-       if ((*(worddb->del))(worddb, NULL, &key, 0)) {
- 	 s->err->fatal = 1;
- 	 s->err->str = "failed deleting worddb entry from database";
- 	 fail();
-       }
- 
-       return(1);
-    }
  
!    /* find the end of the real entries */
  
!    for (;
! 	((last < (((unsigned char *) data.data) + data.size)) &&
! 	 (memcmp(last, zeros, 12) != 0));
! 	last += 12)
!       ;
! 
!    memmove(here+12, here, last-(here+12));
!    memset(here, 0, 12);
! 
!    if ((*(worddb->put))(worddb, NULL, &key, &data, 0)) {
        s->err->fatal = 1;
!       s->err->str = "failed replacing worddb entry in database";
        fail();
     }
  
--- 53,97 ----
     key.data = (void *) word;
     key.size = (size_t) we->len;
  
!    data.data = s->entry;
!    data.size = 12;
  
!    ret = (*(worddb->cursor))(worddb, s->tid, &cursor, 0);
! 
!    if (ret && (ret != DB_NOTFOUND)) {
        s->err->fatal = 1;
!       s->err->str = "database read error creating worddb cursor for delete";
        fail();
     }
  
!    if ((ret = (*(cursor->c_get))(cursor, &key, &data, DB_GET_BOTH))) {
        s->err->fatal = 1;
        sprintf(s->err->buf,
  	      "consistency error reading worddb for delete: %.*s not found ",
  	      (int) we->len, we->ptr);
        fail();
     }
  
!    /* loop over the matching entries, deleting as we go.  Normally, there
!       can be only one matching entry, but there was once a bug which
!       would leave extra matches around */
  
!    for (;
! 	ret == 0;
! 	ret = (*(cursor->c_get))(cursor, &key, &data, DB_NEXT_DUP)) {
!       if (memcmp(data.data, s->entry, 12) != 0)
  	 break;
  
!       if ((ret = (*(cursor->c_del))(cursor, 0)))
  	 break;
     }
  
!    (*(cursor->c_close))(cursor);
  
!    if (ret && (ret != DB_NOTFOUND)) {
        s->err->fatal = 1;
!       sprintf(s->err->buf,
! 	      "error reading worddb entry for delete (errno = %d)", ret);
        fail();
     }
  
***************
*** 145,154 ****
  int delete_key_from_worddb(void *e, llist *new_list, void *c, error *err)
  {
     keys_elem *ke = (keys_elem *) e;
!    xbuffer *xb = (xbuffer *) c;
     dwfw_state dwfws;
     char buf[128];
  
     kd_make_worddb_entry(ke, dwfws.entry);
     dwfws.err = err;
  
--- 101,111 ----
  int delete_key_from_worddb(void *e, llist *new_list, void *c, error *err)
  {
     keys_elem *ke = (keys_elem *) e;
!    dkfw_state *s = (dkfw_state *) c;
     dwfw_state dwfws;
     char buf[128];
  
+    dwfws.tid = s->tid;
     kd_make_worddb_entry(ke, dwfws.entry);
     dwfws.err = err;
  
***************
*** 161,167 ****
  	   ke->keyidbits.buf[6],
  	   ke->keyidbits.buf[7]);
  
!    if (!xbuffer_append_str(xb, buf))
        return(0);
  
     keys_elem_free(e, NULL);
--- 118,124 ----
  	   ke->keyidbits.buf[6],
  	   ke->keyidbits.buf[7]);
  
!    if (!xbuffer_append_str(s->deleted, buf))
        return(0);
  
     keys_elem_free(e, NULL);
***************
*** 169,181 ****
     return(1);
  }
  
! int kd_delete_1(unsigned char *userid, long len, int flags,
  		xbuffer *deleted, error *err)
  {
     int ret;
  
!    ret = kd_search_1(userid, len, KD_SEARCH_EXACT, -1,
! 		     NULL, delete_key_from_worddb, deleted, err);
  
     kd_sync();
  
--- 126,146 ----
     return(1);
  }
  
! int kd_delete_1(kd_txn tid, unsigned char *userid, long len, int flags,
  		xbuffer *deleted, error *err)
  {
+    dkfw_state dkfws;
     int ret;
+ 
+    /* the NULL here as the append function means that any matching key
+       will just disappear.  delete_key_from_worddb will remove the
+       words as a side effect. */
+ 
+    dkfws.tid = tid;
+    dkfws.deleted = deleted;
  
!    ret = kd_search_1(tid, userid, len, KD_SEARCH_EXACT, -1,
! 		     NULL, delete_key_from_worddb, &dkfws, err);
  
     kd_sync();
  
***************
*** 207,213 ****
     kd_log_start("kd_delete", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_delete_1(userid, len, flags, &deleted, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = deleted.buf;
        *retlen = deleted.len;
--- 172,178 ----
     kd_log_start("kd_delete", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_delete_1(tid, userid, len, flags, &deleted, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = deleted.buf;
        *retlen = deleted.len;
Index: pks/kd_disable.c
diff -c pks/kd_disable.c:1.7 pks/kd_disable.c:1.8
*** pks/kd_disable.c:1.7	Mon May 31 16:21:11 1999
--- pks/kd_disable.c	Wed Jun  9 01:50:00 1999
***************
*** 1,4 ****
! const char rcsid_kd_disable_c[] = "$Id: kd_disable.c,v 1.7 1999/05/31 20:21:11 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_disable_c[] = "$Id: kd_disable.c,v 1.8 1999/06/09 05:50:00 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 54,60 ****
     return(1);
  }
  
! int kd_disable_1(unsigned char *userid, long len, int flags,
  		 xbuffer *disabled, error *err)
  {
     int ret;
--- 54,60 ----
     return(1);
  }
  
! int kd_disable_1(kd_txn tid, unsigned char *userid, long len, int flags,
  		 xbuffer *disabled, error *err)
  {
     int ret;
***************
*** 63,69 ****
     cds.xb = disabled;
     cds.set = (flags & KD_DISABLE_CLEAR)?0:1;
  
!    ret = kd_search_1(userid, len, KD_SEARCH_EXACT, -1,
  		     NULL, change_disable, &cds, err);
  
     kd_sync();
--- 63,69 ----
     cds.xb = disabled;
     cds.set = (flags & KD_DISABLE_CLEAR)?0:1;
  
!    ret = kd_search_1(tid, userid, len, KD_SEARCH_EXACT, -1,
  		     NULL, change_disable, &cds, err);
  
     kd_sync();
***************
*** 96,102 ****
     kd_log_start("kd_disable", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_disable_1(userid, len, flags, &disabled, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = disabled.buf;
        *retlen = disabled.len;
--- 96,102 ----
     kd_log_start("kd_disable", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_disable_1(tid, userid, len, flags, &disabled, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = disabled.buf;
        *retlen = disabled.len;
Index: pks/kd_generic.c
diff -c pks/kd_generic.c:1.19 pks/kd_generic.c:1.20
*** pks/kd_generic.c:1.19	Mon May 31 16:21:11 1999
--- pks/kd_generic.c	Wed Jun  9 01:45:19 1999
***************
*** 1,4 ****
! const char rcsid_kd_generic_c[] = "$Id: kd_generic.c,v 1.19 1999/05/31 20:21:11 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_generic_c[] = "$Id: kd_generic.c,v 1.20 1999/06/09 05:45:19 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 149,155 ****
    return 1;
  }
  
! int kd_db_store_keyblock(llist *keys, error *err)
  {
     DBT key, newdata;
     xbuffer newxb;
--- 149,155 ----
    return 1;
  }
  
! int kd_db_store_keyblock(kd_txn tid, llist *keys, error *err)
  {
     DBT key, newdata;
     xbuffer newxb;
***************
*** 176,182 ****
     newdata.data = (void *) newxb.buf;
     newdata.size = (size_t) newxb.len;
  
!    if ((*(keydb(&key)->put))(keydb(&key), NULL, &key, &newdata, 0) < 0) {
        xbuffer_free(&newxb);
        err->fatal = 1;
        sprintf(err->buf, "error %s keydb, errno = %d", "writing to", errno);
--- 176,182 ----
     newdata.data = (void *) newxb.buf;
     newdata.size = (size_t) newxb.len;
  
!    if ((*(keydb(&key)->put))(keydb(&key), tid, &key, &newdata, 0) < 0) {
        xbuffer_free(&newxb);
        err->fatal = 1;
        sprintf(err->buf, "error %s keydb, errno = %d", "writing to", errno);
***************
*** 665,671 ****
  
  void kd_close_1()
  {
!    int i, ret;
     char buf[MAXPATHLEN+100];
     char **loglist, **logfileptr;
  
--- 665,671 ----
  
  void kd_close_1()
  {
!    int i, ret, count;
     char buf[MAXPATHLEN+100];
     char **loglist, **logfileptr;
  
***************
*** 678,715 ****
  	 (*(keydb_files[i]->close))(keydb_files[i], 0);
  
     if (dbenv.tx_info) {
!        /* I don't know why I need to do this twice, but if I don't,
! 	  log_archive() returns no files */
  
!        if ((ret = txn_checkpoint(dbenv.tx_info, 0, 0))) {
! 	   sprintf(buf, "failed db checkpointing: errno = %d", ret);
! 	   log_error("kd_close_1", buf);
! 	   return;
!        }
!        if ((ret = txn_checkpoint(dbenv.tx_info, 0, 0))) {
! 	   sprintf(buf, "failed db checkpointing: errno = %d", ret);
! 	   log_error("kd_close_1", buf);
! 	   return;
!        }
! 
!        if ((ret = log_archive(dbenv.lg_info, &loglist, DB_ARCH_ABS, NULL))) {
! 	   sprintf(buf, "failed listing unneeded log files: errno = %d", ret);
! 	   log_error("kd_close_1", buf);
! 	   return;
!        }
! 
!        if (loglist) {
! 	   for (logfileptr = loglist; *logfileptr; logfileptr++) {
! 	       if ((ret = unlink(*logfileptr))) {
! 		   sprintf(buf, "failed removing log file %s: errno = %d",
! 			   *logfileptr, ret);
! 		   log_error("kd_close_1", buf);
! 		   return;
! 	       }
! 	   }
  
! 	   free(loglist);
!        }
     }
  
     if ((ret = db_appexit(&dbenv))) {
--- 678,735 ----
  	 (*(keydb_files[i]->close))(keydb_files[i], 0);
  
     if (dbenv.tx_info) {
!       /* I don't know why I need to do this twice, but if I don't,
! 	 log_archive() returns no files */
  
!       for (count=0; count<10; count++) {
! 	 if ((ret = txn_checkpoint(dbenv.tx_info, 0, 0)) == DB_INCOMPLETE) {
! 	    sleep(1);
! 	    continue;
! 	 }
! 
! 	 break;
!       }
! 
!       if (ret) {
! 	 sprintf(buf, "failed db checkpointing: errno = %d", ret);
! 	 log_error("kd_close_1", buf);
! 	 return;
!       }
! 
!       for (count=0; count<10; count++) {
! 	 if ((ret = txn_checkpoint(dbenv.tx_info, 0, 0)) == DB_INCOMPLETE) {
! 	    sleep(1);
! 	    continue;
! 	 }
! 
! 	 break;
!       }
! 
!       if (ret) {
! 	 sprintf(buf, "failed db checkpointing: errno = %d", ret);
! 	 log_error("kd_close_1", buf);
! 	 return;
!       }
! 
!       if ((ret = log_archive(dbenv.lg_info, &loglist, DB_ARCH_ABS, NULL))) {
! 	 sprintf(buf, "failed listing unneeded log files: errno = %d", ret);
! 	 log_error("kd_close_1", buf);
! 	 return;
!       }
! 
  
!       if (loglist) {
! 	 for (logfileptr = loglist; *logfileptr; logfileptr++) {
! 	    if ((ret = unlink(*logfileptr))) {
! 	       sprintf(buf, "failed removing log file %s: errno = %d",
! 		       *logfileptr, ret);
! 	       log_error("kd_close_1", buf);
! 	       return;
! 	    }
! 	 }
! 
! 	 free(loglist);
!       }
     }
  
     if ((ret = db_appexit(&dbenv))) {
Index: pks/kd_get.c
diff -c pks/kd_get.c:1.15 pks/kd_get.c:1.17
*** pks/kd_get.c:1.15	Mon May 31 16:21:14 1999
--- pks/kd_get.c	Wed Sep 22 23:18:21 1999
***************
*** 1,4 ****
! const char rcsid_kd_get_c[] = "$Id: kd_get.c,v 1.15 1999/05/31 20:21:14 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_get_c[] = "$Id: kd_get.c,v 1.17 1999/09/23 03:18:21 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 20,46 ****
  
  typedef struct _kkec_state {
     FILE *out;
  } kkec_state;
  
  int kd_keys_elem_cout(void *e, void *c)
  {
     kkec_state *s = (kkec_state *) c;
-    xbuffer keys;
- 
-    xbuffer_alloc(&keys);
  
!    if (!kd_keys_elem_marshall(e, &keys))
        fail();
  
!    fwrite(keys.buf, keys.len, 1, s->out);
  
!    xbuffer_free(&keys);
  
     return(1);
  }
  
! int kd_get_1(unsigned char *userid, long len, int flags, int maxkeys,
! 	     ddesc *armored, error *err)
  {
     xbuffer keys;
     ddesc binary;
--- 20,46 ----
  
  typedef struct _kkec_state {
     FILE *out;
+    xbuffer keys;
  } kkec_state;
  
  int kd_keys_elem_cout(void *e, void *c)
  {
     kkec_state *s = (kkec_state *) c;
  
!    if (!kd_keys_elem_marshall(e, &s->keys))
        fail();
  
!    fwrite(s->keys.buf, s->keys.len, 1, s->out);
  
!    /* "remove" the data from the buffer, but don't free it.
!       this will save a lot of malloc/free calls */
!    s->keys.len = 0;
  
     return(1);
  }
  
! int kd_get_1(kd_txn tid, unsigned char *userid, long len, int flags,
! 	     int maxkeys, ddesc *armored, error *err)
  {
     xbuffer keys;
     ddesc binary;
***************
*** 52,65 ****
  
     if (flags & KD_GET_STDOUT) {
        kkec_state kkecs;
  
        kkecs.out = stdout;
  
!       if (!kd_search_1(userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
! 		       kd_keys_elem_cout, NULL, &kkecs, err))
! 	 return(0);
  
!       xbuffer_alloc(&keys);
  
        armored->data = keys.buf;
        armored->size = keys.len;
--- 52,69 ----
  
     if (flags & KD_GET_STDOUT) {
        kkec_state kkecs;
+       int ret;
  
        kkecs.out = stdout;
+       xbuffer_alloc(&kkecs.keys);
  
!       ret = kd_search_1(tid, userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
! 			kd_keys_elem_cout, NULL, &kkecs, err);
! 
!       xbuffer_free(&kkecs.keys);
  
!       if (!ret)
! 	 return(0);
  
        armored->data = keys.buf;
        armored->size = keys.len;
***************
*** 68,74 ****
        return(1);
     }
  
!    if (!kd_search_1(userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
  		    kd_keys_elem_marshall, NULL, &keys, err))
        return(0);
  
--- 72,78 ----
        return(1);
     }
  
!    if (!kd_search_1(tid, userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
  		    kd_keys_elem_marshall, NULL, &keys, err))
        return(0);
  
***************
*** 127,133 ****
     kd_log_start("kd_get", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_get_1(userid, len, flags, maxkeys, &armored, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = armored.data;
        *retlen = armored.offset;
--- 131,137 ----
     kd_log_start("kd_get", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_get_1(tid, userid, len, flags, maxkeys, &armored, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = armored.data;
        *retlen = armored.offset;
Index: pks/kd_index.c
diff -c pks/kd_index.c:1.14 pks/kd_index.c:1.16
*** pks/kd_index.c:1.14	Mon May 31 16:21:14 1999
--- pks/kd_index.c	Wed Sep 22 23:19:36 1999
***************
*** 1,4 ****
! const char rcsid_kd_index_c[] = "$Id: kd_index.c,v 1.14 1999/05/31 20:21:14 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_index_c[] = "$Id: kd_index.c,v 1.16 1999/09/23 03:19:36 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 41,46 ****
--- 41,47 ----
  }
  
  typedef struct _gi_state {
+    kd_txn tid;
     int verbose;
     xbuffer *xb;
  } gi_state;
***************
*** 57,63 ****
     llist_alloc(&keys);
     err.str = err.buf;
  
!    if (!kd_get_keys_by_keyid(se->keyid.buf, &keys, &err)) {
        if (err.fatal) {
  	 llist_free(&keys);
  	 return(0);
--- 58,64 ----
     llist_alloc(&keys);
     err.str = err.buf;
  
!    if (!kd_get_keys_by_keyid(s->tid, se->keyid.buf, &keys, &err)) {
        if (err.fatal) {
  	 llist_free(&keys);
  	 return(0);
***************
*** 114,119 ****
--- 115,121 ----
  }
  
  typedef struct _keg_state {
+    kd_txn tid;
     int flags;
     xbuffer *xb;
  } keg_state;
***************
*** 186,191 ****
--- 188,194 ----
        }
     }
  
+    gis.tid = s->tid;
     gis.verbose = (s->flags & KD_INDEX_VERBOSE);
     gis.xb = s->xb;
  
***************
*** 208,255 ****
  int keys_elem_genindex_cout(void *e, void *c)
  {
     kegc_state *s = (kegc_state *) c;
-    xbuffer buf;
- 
-    xbuffer_alloc(&buf);
- 
-    s->keg.xb = &buf;
  
     if (!keys_elem_genindex(e, &s->keg))
        fail();
  
!    fwrite(buf.buf, buf.len, 1, s->out);
  
!    xbuffer_free(&buf);
  
     return(1);
  }
  
! int kd_index_1(unsigned char *userid, long len, int flags, int maxkeys,
! 	       xbuffer *index, error *err)
  {
     keg_state kegs;
  
     /* This is called violating abstractions in the interest of
        efficiency.  Whee. */
  
     if (flags & KD_INDEX_STDOUT) {
        kegc_state kegcs;
  
        kegcs.out = stdout;
        kegcs.keg.flags = flags;
!       /* kegcs.keg.xb is filled in by keys_elem_genindex_cout */
  
!       if (!kd_search_1(userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
! 		       keys_elem_genindex_cout, NULL, &kegcs, err))
! 	 return(0);
  
!       return(1);
     }
  
     kegs.flags = flags;
     kegs.xb = index;
  
!    if (!kd_search_1(userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
  		    keys_elem_genindex, NULL, &kegs, err))
        return(0);
  
--- 211,262 ----
  int keys_elem_genindex_cout(void *e, void *c)
  {
     kegc_state *s = (kegc_state *) c;
  
     if (!keys_elem_genindex(e, &s->keg))
        fail();
  
!    fwrite(s->keg.xb->buf, s->keg.xb->len, 1, s->out);
  
!    /* "remove" the data from the buffer, but don't free it.
!       this will save a lot of malloc/free calls */
!    s->keg.xb->len = 0;
  
     return(1);
  }
  
! int kd_index_1(kd_txn tid, unsigned char *userid, long len, int flags,
! 	       int maxkeys, xbuffer *index, error *err)
  {
     keg_state kegs;
+    int ret;
  
     /* This is called violating abstractions in the interest of
        efficiency.  Whee. */
  
     if (flags & KD_INDEX_STDOUT) {
        kegc_state kegcs;
+       xbuffer buf;
  
+       xbuffer_alloc(&buf);
+ 
        kegcs.out = stdout;
+       kegcs.keg.tid = tid;
        kegcs.keg.flags = flags;
!       kegcs.keg.xb = &buf;
  
!       ret = kd_search_1(tid, userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
! 			keys_elem_genindex_cout, NULL, &kegcs, err);
! 
!       xbuffer_free(&buf);
  
!       return(ret);
     }
  
+    kegs.tid = tid;
     kegs.flags = flags;
     kegs.xb = index;
  
!    if (!kd_search_1(tid, userid, len, flags & KD_SEARCH_FLAGS, maxkeys,
  		    keys_elem_genindex, NULL, &kegs, err))
        return(0);
  
***************
*** 277,283 ****
     kd_log_start("kd_index", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_index_1(userid, len, flags, maxkeys, &index, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = index.buf;
        *retlen = index.len;
--- 284,290 ----
     kd_log_start("kd_index", userid, len, flags);
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_index_1(tid, userid, len, flags, maxkeys, &index, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = index.buf;
        *retlen = index.len;
Index: pks/kd_internal.h
diff -c pks/kd_internal.h:1.8 pks/kd_internal.h:1.9
*** pks/kd_internal.h:1.8	Mon May 31 16:21:15 1999
--- pks/kd_internal.h	Wed Jun  9 01:50:01 1999
***************
*** 2,8 ****
  #define _KD_INTERNAL_H_
  
  /*
!  * $Id: kd_internal.h,v 1.8 1999/05/31 20:21:15 marc Exp $
   * 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
   * See the LICENSE file in the release for redistribution information.
--- 2,8 ----
  #define _KD_INTERNAL_H_
  
  /*
!  * $Id: kd_internal.h,v 1.9 1999/06/09 05:50:01 marc Exp $
   * 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
   * See the LICENSE file in the release for redistribution information.
***************
*** 28,33 ****
--- 28,35 ----
     char buf[1024];
  } error;
  
+ typedef DB_TXN *kd_txn;
+ 
  extern int num_keydb;
  extern DB **keydb_files;
  extern DB *keydb(DBT *key), *worddb, *timedb;
***************
*** 41,49 ****
  int kd_add_userid_to_wordlist(llist *wl,
  			      unsigned char *userid, long userid_len);
  int kd_keys_elem_marshall(void *e, void *c);
! int kd_db_store_keyblock(llist *keys, error *err);
! 
! typedef DB_TXN *kd_txn;
  
  int kd_txn_begin(kd_txn *tid, error *err);
  int kd_txn_commit(kd_txn tid, error *err);
--- 43,49 ----
  int kd_add_userid_to_wordlist(llist *wl,
  			      unsigned char *userid, long userid_len);
  int kd_keys_elem_marshall(void *e, void *c);
! int kd_db_store_keyblock(kd_txn tid, llist *keys, error *err);
  
  int kd_txn_begin(kd_txn *tid, error *err);
  int kd_txn_commit(kd_txn tid, error *err);
Index: pks/kd_search.c
diff -c pks/kd_search.c:1.41 pks/kd_search.c:1.42
*** pks/kd_search.c:1.41	Mon May 31 16:21:15 1999
--- pks/kd_search.c	Wed Jun  9 01:50:02 1999
***************
*** 1,4 ****
! const char rcsid_kd_search_c[] = "$Id: kd_search.c,v 1.41 1999/05/31 20:21:15 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_search_c[] = "$Id: kd_search.c,v 1.42 1999/06/09 05:50:02 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 638,644 ****
     return(1);
  }
  
! int kd_get_keys_by_keyid(unsigned char *keyid, llist *keys, error *err)
  {
     DBT key, data;
     int ret;
--- 638,645 ----
     return(1);
  }
  
! int kd_get_keys_by_keyid(kd_txn tid, unsigned char *keyid,
! 			 llist *keys, error *err)
  {
     DBT key, data;
     int ret;
***************
*** 650,656 ****
     key.data = keyid+(8-KEYDB_KEYID_BYTES);
     key.size = KEYDB_KEYID_BYTES;
  
!    ret = (*(keydb(&key)->get))(keydb(&key), NULL, &key, &data, 0);
  
     if ((ret == DB_NOTFOUND) ||
         ((ret == 0) && (data.size == 0))) {
--- 651,657 ----
     key.data = keyid+(8-KEYDB_KEYID_BYTES);
     key.size = KEYDB_KEYID_BYTES;
  
!    ret = (*(keydb(&key)->get))(keydb(&key), tid, &key, &data, 0);
  
     if ((ret == DB_NOTFOUND) ||
         ((ret == 0) && (data.size == 0))) {
***************
*** 822,827 ****
--- 823,829 ----
  }
  
  typedef struct _wki_state {
+    kd_txn tid;
     int first;
     llist wdes;
     error *err;
***************
*** 862,868 ****
     key.data = (void *) word;
     key.size = (size_t) we->len;
  
!    ret = (*(worddb->cursor))(worddb, NULL, &cursor, 0);
  
     if (ret && ret != DB_NOTFOUND) {
        s->err->fatal = 1;
--- 864,870 ----
     key.data = (void *) word;
     key.size = (size_t) we->len;
  
!    ret = (*(worddb->cursor))(worddb, s->tid, &cursor, 0);
  
     if (ret && ret != DB_NOTFOUND) {
        s->err->fatal = 1;
***************
*** 947,953 ****
  
     llist_alloc(&db_keys);
  
!    if (!kd_get_keys_by_keyid(wde+4, &db_keys, s->err))
        return(0);
  
     create_time = ((wde[0]<<24)+(wde[1]<<16)+(wde[2]<<8)+(wde[3]));
--- 949,955 ----
  
     llist_alloc(&db_keys);
  
!    if (!kd_get_keys_by_keyid(s->tid, wde+4, &db_keys, s->err))
        return(0);
  
     create_time = ((wde[0]<<24)+(wde[1]<<16)+(wde[2]<<8)+(wde[3]));
***************
*** 982,988 ****
  
     if (s->filter) {
        if (llist_count(&(ow1s.new_keys_elem))) {
! 	 if (!kd_db_store_keyblock(&(ow1s.new_keys_elem), s->err))
  	    return(0);
  
  	 llist_iterate(&(ow1s.new_keys_elem), keys_elem_free, NULL);
--- 984,990 ----
  
     if (s->filter) {
        if (llist_count(&(ow1s.new_keys_elem))) {
! 	 if (!kd_db_store_keyblock(s->tid, &(ow1s.new_keys_elem), s->err))
  	    return(0);
  
  	 llist_iterate(&(ow1s.new_keys_elem), keys_elem_free, NULL);
***************
*** 995,1001 ****
  	 key.data = wde+4 + (8-KEYDB_KEYID_BYTES);
  	 key.size = KEYDB_KEYID_BYTES;
  
! 	 if ((*(keydb(&key)->del))(keydb(&key), NULL, &key, 0)) {
  	    s->err->fatal = 1;
  	    s->err->str = "failed deleting keydb entry from database";
  	    fail();
--- 997,1003 ----
  	 key.data = wde+4 + (8-KEYDB_KEYID_BYTES);
  	 key.size = KEYDB_KEYID_BYTES;
  
! 	 if ((*(keydb(&key)->del))(keydb(&key), s->tid, &key, 0)) {
  	    s->err->fatal = 1;
  	    s->err->str = "failed deleting keydb entry from database";
  	    fail();
***************
*** 1048,1054 ****
     return(1);
  }
  
! int do_all_keys(int flags, llist_iter func, void *c, error *err)
  {
     akte_state aktes;
     int ret, i;
--- 1050,1056 ----
     return(1);
  }
  
! int do_all_keys(kd_txn tid, int flags, llist_iter func, void *c, error *err)
  {
     akte_state aktes;
     int ret, i;
***************
*** 1067,1073 ****
     ftfs.state = c;
  
     for (i=0; i<num_keydb; i++) {
!       if ((ret = (*(keydb_files[i]->cursor))(keydb_files[i], NULL, &cursor,
  					     0))) {
  	 err->fatal = 1;
  	 sprintf(err->buf, "error creating keydb[%d] cursor: error = %d",
--- 1069,1075 ----
     ftfs.state = c;
  
     for (i=0; i<num_keydb; i++) {
!       if ((ret = (*(keydb_files[i]->cursor))(keydb_files[i], tid, &cursor,
  					     0))) {
  	 err->fatal = 1;
  	 sprintf(err->buf, "error creating keydb[%d] cursor: error = %d",
***************
*** 1138,1143 ****
--- 1140,1146 ----
        qsort(aktes.entries.buf, (size_t) (aktes.entries.len/12), 12, 
  	    sort_twelvebytes);
  
+       ows.tid = tid;
        ows.userid = NULL;
        ows.userid_len = 0;
        ows.filter = NULL;
***************
*** 1166,1173 ****
  
     */
  
! int do_by_userid(unsigned char *userid, long len, int flags, int maxkeys,
! 		 llist_iter func, search_llist_filter filt,
  		 void *c, error *err)
  {
     llist words;
--- 1169,1176 ----
  
     */
  
! int do_by_userid(kd_txn tid, unsigned char *userid, long len, int flags,
! 		 int maxkeys, llist_iter func, search_llist_filter filt,
  		 void *c, error *err)
  {
     llist words;
***************
*** 1193,1198 ****
--- 1196,1202 ----
        return(0);
     }
  
+    wkis.tid = tid;
     wkis.first = 1;
     llist_alloc(&(wkis.wdes));
     wkis.err = err;
***************
*** 1243,1248 ****
--- 1247,1253 ----
     err->fatal = 1;
     strcpy(err->buf, "internal error generating key list");
  
+    ows.tid = tid;
     if (flags & KD_SEARCH_EXACT) {
        ows.userid = userid;
        ows.userid_len = len;
***************
*** 1311,1322 ****
     return(1);
  }
  
! int kd_search_1(unsigned char *userid, long len, int flags, int maxkeys,
! 		llist_iter func, search_llist_filter filt,
  		void *c, error *err)
  {
     if (flags & KD_SEARCH_ALL)
!       return(do_all_keys(flags, func, c, err));
  
     /* skip over initial whitespace */
  
--- 1316,1327 ----
     return(1);
  }
  
! int kd_search_1(kd_txn tid, unsigned char *userid, long len, int flags,
! 		int maxkeys, llist_iter func, search_llist_filter filt,
  		void *c, error *err)
  {
     if (flags & KD_SEARCH_ALL)
!       return(do_all_keys(tid, flags, func, c, err));
  
     /* skip over initial whitespace */
  
***************
*** 1348,1353 ****
--- 1353,1359 ----
        err->fatal = 1;
        strcpy(err->buf, "internal error generating key list");
  
+       ows.tid = tid;
        ows.userid = NULL;
        ows.userid_len = 0;
        ows.filter = filt;
***************
*** 1361,1367 ****
  
        return(1);
     } else {
!       if (!do_by_userid(userid, len, flags, maxkeys, func, filt, c, err))
  	 return(0);
     }
  
--- 1367,1373 ----
  
        return(1);
     } else {
!       if (!do_by_userid(tid, userid, len, flags, maxkeys, func, filt, c, err))
  	 return(0);
     }
  
Index: pks/kd_search.h
diff -c pks/kd_search.h:1.9 pks/kd_search.h:1.10
*** pks/kd_search.h:1.9	Wed May 19 23:39:48 1999
--- pks/kd_search.h	Wed Jun  9 01:50:03 1999
***************
*** 2,8 ****
  #define _KD_SEARCH_H_
  
  /*
!  * $Id: kd_search.h,v 1.9 1999/05/20 03:39:48 marc Exp $
   * 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
   * See the LICENSE file in the release for redistribution information.
--- 2,8 ----
  #define _KD_SEARCH_H_
  
  /*
!  * $Id: kd_search.h,v 1.10 1999/06/09 05:50:03 marc Exp $
   * 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
   * See the LICENSE file in the release for redistribution information.
***************
*** 16,21 ****
--- 16,22 ----
  				   void *c, error *err);
  
  typedef struct _ow_state {
+    kd_txn tid;
     unsigned char *userid;
     long userid_len;
     void *c;
***************
*** 35,45 ****
  int kd_keyblock_iterate(unsigned char *block, long blocklen,
  			llist_iter iter, void *c, error *err,
  			ki_softerr *softerr);
! int kd_get_keys_by_keyid(unsigned char *keyid, llist *keys, error *err);
  void kd_make_worddb_entry(keys_elem *ke, unsigned char entry[]);
  int kd_output_wde(void *e, void *c);
! int kd_search_1(unsigned char *userid, long len, int flags, int maxkeys,
! 		llist_iter func, search_llist_filter filt,
  		void *c, error *err);
  
  #endif
--- 36,47 ----
  int kd_keyblock_iterate(unsigned char *block, long blocklen,
  			llist_iter iter, void *c, error *err,
  			ki_softerr *softerr);
! int kd_get_keys_by_keyid(kd_txn tid, unsigned char *keyid,
! 			 llist *keys, error *err);
  void kd_make_worddb_entry(keys_elem *ke, unsigned char entry[]);
  int kd_output_wde(void *e, void *c);
! int kd_search_1(kd_txn tid, unsigned char *userid, long len, int flags,
! 		int maxkeys, llist_iter func, search_llist_filter filt,
  		void *c, error *err);
  
  #endif
Index: pks/kd_since.c
diff -c pks/kd_since.c:1.18 pks/kd_since.c:1.19
*** pks/kd_since.c:1.18	Mon May 31 16:21:18 1999
--- pks/kd_since.c	Wed Jun  9 01:50:04 1999
***************
*** 1,4 ****
! const char rcsid_kd_since_c[] = "$Id: kd_since.c,v 1.18 1999/05/31 20:21:18 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_kd_since_c[] = "$Id: kd_since.c,v 1.19 1999/06/09 05:50:04 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 25,31 ****
     return(memcmp(b, a, 12));
  }
  
! int kd_since_1(time_t since, int flags, int maxkeys,
  	       time_t *last, ddesc *armored, error *err)
  {
     xbuffer entries, keys;
--- 25,31 ----
     return(memcmp(b, a, 12));
  }
  
! int kd_since_1(kd_txn tid, time_t since, int flags, int maxkeys,
  	       time_t *last, ddesc *armored, error *err)
  {
     xbuffer entries, keys;
***************
*** 51,57 ****
     key.data = firstkey;
     key.size = sizeof(firstkey);
  
!    if ((ret = (*(timedb->cursor))(timedb, NULL, &cursor, 0))) {
        err->fatal = 1;
        sprintf(err->buf, "error creating timedb cursor: error = %d", ret);
        fail();
--- 51,57 ----
     key.data = firstkey;
     key.size = sizeof(firstkey);
  
!    if ((ret = (*(timedb->cursor))(timedb, tid, &cursor, 0))) {
        err->fatal = 1;
        sprintf(err->buf, "error creating timedb cursor: error = %d", ret);
        fail();
***************
*** 114,119 ****
--- 114,120 ----
  
     xbuffer_alloc(&keys);
  
+    ows.tid = tid;
     ows.userid = NULL;
     ows.userid_len = 0;
     ows.filter = NULL;
***************
*** 188,194 ****
     }
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_since_1(since, flags, maxkeys, last, &armored, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = armored.data;
        *retlen = armored.offset;
--- 189,195 ----
     }
  
     if (kd_txn_begin(&tid, &err) &&
!        kd_since_1(tid, since, flags, maxkeys, last, &armored, &err) &&
         kd_txn_commit(tid, &err)) {
        *ret = armored.data;
        *retlen = armored.offset;
Index: pks/mail_req.c
diff -c pks/mail_req.c:1.24 pks/mail_req.c:1.25
*** pks/mail_req.c:1.24	Wed May 19 23:39:49 1999
--- pks/mail_req.c	Wed Jun  9 01:48:05 1999
***************
*** 1,4 ****
! const char rcsid_mail_req_c[] = "$Id: mail_req.c,v 1.24 1999/05/20 03:39:49 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_mail_req_c[] = "$Id: mail_req.c,v 1.25 1999/06/09 05:48:05 marc Exp $";
  
  /* 
   * Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
***************
*** 420,427 ****
  	 xbuffer_free(&oldxsentto);
  	 return;
        } else {
! 	 ret = kd_get(msg+userid, userid_len, conf->max_reply_keys,
! 		      KD_GET_EXACT, &retstr, &ret_len);
  
  	 if (ret) {
  	    retheaders = pgpkeys_str;
--- 420,427 ----
  	 xbuffer_free(&oldxsentto);
  	 return;
        } else {
! 	 ret = kd_get(msg+userid, userid_len, KD_GET_EXACT, 
! 		      conf->max_reply_keys, &retstr, &ret_len);
  
  	 if (ret) {
  	    retheaders = pgpkeys_str;
Index: pks/pgpsplit.c
diff -c pks/pgpsplit.c:1.2 pks/pgpsplit.c:1.4
*** pks/pgpsplit.c:1.2	Wed May 19 23:36:01 1999
--- pks/pgpsplit.c	Sun Jun 13 16:03:52 1999
***************
*** 1,4 ****
! const char rcsid_pgpsplit_c[] = "$Id: pgpsplit.c,v 1.2 1999/05/20 03:36:01 marc Exp $";
  
  /* 
   * Copyright (c) 1999, Marc Horowitz.  All rights reserved.
--- 1,4 ----
! const char rcsid_pgpsplit_c[] = "$Id: pgpsplit.c,v 1.4 1999/06/13 20:03:52 marc Exp $";
  
  /* 
   * Copyright (c) 1999, Marc Horowitz.  All rights reserved.
***************
*** 91,100 ****
  
  	    i++;
  	 } else if (argv[i][1] == 'o') {
! 	    if (s.filenamebase || !argv[i+1])
  	       usage(argv[0]);
  
! 	    s.filenamebase = argv[i+1];
  
  	    i++;
  	 } else {
--- 91,100 ----
  
  	    i++;
  	 } else if (argv[i][1] == 'o') {
! 	    if (outfile || !argv[i+1])
  	       usage(argv[0]);
  
! 	    outfile = argv[i+1];
  
  	    i++;
  	 } else {
***************
*** 114,121 ****
     if (!s.maxbytes)
        s.maxbytes = 10*1024*1024;
  
!    if (!outfile)
!       outfile = infile;
  
     if (!infile) {
        infile = "<STDIN>";
--- 114,123 ----
     if (!s.maxbytes)
        s.maxbytes = 10*1024*1024;
  
!    if (outfile)
!       s.filenamebase = outfile;
!    else
!       s.filenamebase = infile;
  
     if (!infile) {
        infile = "<STDIN>";
***************
*** 136,141 ****
--- 138,146 ----
     data.size = xfc.len;
     data.offset = 0;
     
+    s.f = NULL;
+    s.filenum = 0;
+ 
     if (!decode_file(&data, split, (void *) &s)) {
        perror("reading file");
        xfilecontents_free(&xfc);
Index: pks/pks-intro.8
diff -c pks/pks-intro.8:1.7 pks/pks-intro.8:1.8
*** pks/pks-intro.8:1.7	Wed Jun  2 02:05:17 1999
--- pks/pks-intro.8	Wed Jun  9 01:49:20 1999
***************
*** 1,5 ****
  .\"
! .\" $Id: pks-intro.8,v 1.7 1999/06/02 06:05:17 marc Exp $
  .\"
  .\" Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
  .\" See the LICENSE file in the release for redistribution information.
--- 1,5 ----
  .\"
! .\" $Id: pks-intro.8,v 1.8 1999/06/09 05:49:20 marc Exp $
  .\"
  .\" Copyright (c) 1996, 1997, 1998, 1999, Marc Horowitz.  All rights reserved.
  .\" See the LICENSE file in the release for redistribution information.
***************
*** 99,103 ****
--- 99,105 ----
  .SH BUGS
  The timedb will grow without bound.  This database should be
  automatically pruned.
+ 
+ Not all operating systems can open 1000 files at once.
  .SH AUTHOR
  Marc Horowitz, Massachusetts Institute of Technology
Index: pks/db2-sleepycat/db/db_rec.c
diff -c pks/db2-sleepycat/db/db_rec.c:1.3 pks/db2-sleepycat/db/db_rec.c:1.4
*** pks/db2-sleepycat/db/db_rec.c:1.3	Sun May 16 01:09:34 1999
--- pks/db2-sleepycat/db/db_rec.c	Wed Sep 22 23:26:23 1999
***************
*** 416,427 ****
  			(void)__db_pgerr(file_dbp, argp->pgno);
  			goto out;
  		}
! 		goto next;
  	}
  	if (argp->opcode == DB_ADD_PAGE)
! 		goto next;
  
- 	modified = 0;
  	if (log_compare(&LSN(pagep), &argp->lsn) == 0 && redo) {
  		/* Redo the relink. */
  		pagep->lsn = *lsnp;
--- 416,427 ----
  			(void)__db_pgerr(file_dbp, argp->pgno);
  			goto out;
  		}
! 		goto next2;
  	}
+ 	modified = 0;
  	if (argp->opcode == DB_ADD_PAGE)
! 		goto next1;
  
  	if (log_compare(&LSN(pagep), &argp->lsn) == 0 && redo) {
  		/* Redo the relink. */
  		pagep->lsn = *lsnp;
***************
*** 434,443 ****
  		pagep->lsn = argp->lsn;
  		modified = 1;
  	}
! 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
  		goto out;
  
! next:	if ((ret = memp_fget(mpf, &argp->next, 0, &pagep)) != 0) {
  		if (redo) {
  			(void)__db_pgerr(file_dbp, argp->next);
  			goto out;
--- 434,443 ----
  		pagep->lsn = argp->lsn;
  		modified = 1;
  	}
! next1:	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
  		goto out;
  
! next2:	if ((ret = memp_fget(mpf, &argp->next, 0, &pagep)) != 0) {
  		if (redo) {
  			(void)__db_pgerr(file_dbp, argp->next);
  			goto out;