windoze version works again
[gnupg.git] / g10 / tdbio.c
1 /* tdbio.c
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * GNUPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GNUPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31
32 #include "errors.h"
33 #include "iobuf.h"
34 #include "memory.h"
35 #include "util.h"
36 #include "options.h"
37 #include "main.h"
38 #include "i18n.h"
39 #include "trustdb.h"
40 #include "tdbio.h"
41
42
43
44 static char *db_name;
45 static int  db_fd = -1;
46
47
48 static void open_db(void);
49
50
51 int
52 tdbio_set_dbname( const char *new_dbname, int create )
53 {
54     char *fname;
55
56     fname = new_dbname? m_strdup( new_dbname )
57                       : make_filename(opt.homedir, "trustdb.gpg", NULL );
58
59     if( access( fname, R_OK ) ) {
60         if( errno != ENOENT ) {
61             log_error_f( fname, _("can't access: %s\n"), strerror(errno) );
62             m_free(fname);
63             return G10ERR_TRUSTDB;
64         }
65         if( create ) {
66             FILE *fp;
67             TRUSTREC rec;
68             int rc;
69             char *p = strrchr( fname, '/' );
70
71             assert(p);
72             *p = 0;
73             if( access( fname, F_OK ) ) {
74                 if( strlen(fname) >= 7
75                     && !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) {
76                   #if __MINGW32__
77                     if( mkdir( fname ) )
78                   #else
79                     if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) )
80                   #endif
81                         log_fatal_f( fname, _("can't create directory: %s\n"),
82                                                             strerror(errno) );
83                 }
84                 else
85                     log_fatal_f(fname, _("directory does not exist!\n") );
86             }
87             *p = '/';
88
89             fp =fopen( fname, "wb" );
90             if( !fp )
91                 log_fatal_f( fname, _("can't create: %s\n"), strerror(errno) );
92             fclose(fp);
93             m_free(db_name);
94             db_name = fname;
95           #ifdef __MINGW32__
96             db_fd = open( db_name, O_RDWR | O_BINARY );
97           #else
98             db_fd = open( db_name, O_RDWR );
99           #endif
100             if( db_fd == -1 )
101                 log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) );
102
103             memset( &rec, 0, sizeof rec );
104             rec.r.ver.version = 2;
105             rec.r.ver.created = make_timestamp();
106             rec.rectype = RECTYPE_VER;
107             rec.recnum = 0;
108             rc = tdbio_write_record( &rec );
109             if( rc )
110                 log_fatal_f( fname, _("failed to create version record: %s"),
111                                                                g10_errstr(rc));
112             /* and read again to check that we are okay */
113             if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
114                 log_fatal_f( db_name, "invalid trust-db created\n" );
115             return 0;
116         }
117     }
118     m_free(db_name);
119     db_name = fname;
120     return 0;
121 }
122
123
124 const char *
125 tdbio_get_dbname()
126 {
127     return db_name;
128 }
129
130
131
132 static void
133 open_db()
134 {
135     TRUSTREC rec;
136     assert( db_fd == -1 );
137
138   #ifdef __MINGW32__
139     db_fd = open( db_name, O_RDWR | O_BINARY );
140   #else
141     db_fd = open( db_name, O_RDWR );
142   #endif
143     if( db_fd == -1 )
144         log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) );
145     if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
146         log_fatal_f( db_name, _("invalid trust-db\n") );
147     /* fixme: check ->locked and other stuff */
148 }
149
150
151 /****************
152  * Return the record number of the keyhash tbl or create a new one.
153  */
154 static ulong
155 get_keyhashrec()
156 {
157     static ulong keyhashtbl; /* record number of the key hashtable */
158     TRUSTREC vr;
159     int rc;
160
161     if( keyhashtbl )
162         return keyhashtbl;
163
164     rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
165     if( rc )
166         log_fatal_f( db_name, _("error reading version record: %s\n"),
167                                                         g10_errstr(rc) );
168     if( vr.r.ver.keyhashtbl )
169         keyhashtbl = vr.r.ver.keyhashtbl;
170     else {
171         TRUSTREC rec;
172         off_t offset;
173         ulong recnum;
174         int i, n;
175
176         offset = lseek( db_fd, 0, SEEK_END );
177         if( offset == -1 )
178             log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
179         recnum = offset / TRUST_RECORD_LEN;
180         assert(recnum); /* this is will never be the first record */
181
182         keyhashtbl = recnum;
183         /* Now write the records */
184         n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD;
185         for(i=0; i < n; i++, recnum++ ) {
186              memset( &rec, 0, sizeof rec );
187              rec.rectype = RECTYPE_HTBL; /* free record */
188              rec.recnum = recnum;
189              rc = tdbio_write_record( &rec );
190              if( rc )
191                  log_fatal_f(db_name,_("failed to create hashtable: %s\n"),
192                                                      g10_errstr(rc));
193         }
194         /* update the version record */
195         vr.r.ver.keyhashtbl = keyhashtbl;
196         rc = tdbio_write_record( &vr );
197         if( rc )
198             log_fatal_f( db_name, _("error updating version record: %s\n"),
199                                                              g10_errstr(rc));
200     }
201     return keyhashtbl;
202 }
203
204
205 /****************
206  * Update the key hashtbl or create the table if it does not exist
207  */
208 static int
209 update_keyhashtbl( TRUSTREC *kr )
210 {
211     TRUSTREC lastrec, rec;
212     ulong hashrec, item;
213     int msb;
214     int level=0;
215     int rc, i;
216
217     hashrec = get_keyhashrec();
218   next_level:
219     msb = kr->r.key.fingerprint[level];
220     hashrec += msb / ITEMS_PER_HTBL_RECORD;
221     rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL );
222     if( rc ) {
223         log_error( db_name, "update_keyhashtbl read failed: %s\n",
224                                                         g10_errstr(rc) );
225         return rc;
226     }
227
228     item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
229     if( !item ) { /* insert new one */
230         rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = kr->recnum;
231         rc = tdbio_write_record( &rec );
232         if( rc ) {
233             log_error( db_name, "update_keyhashtbl write htbl failed: %s\n",
234                                                             g10_errstr(rc) );
235             return rc;
236         }
237     }
238     else if( item != kr->recnum ) {  /* must do an update */
239         lastrec = rec;
240         rc = tdbio_read_record( item, &rec, 0 );
241         if( rc ) {
242             log_error( db_name, "update_keyhashtbl read item failed: %s\n",
243                                                             g10_errstr(rc) );
244             return rc;
245         }
246         if( rec.rectype == RECTYPE_HTBL ) {
247             hashrec = item;
248             level++;
249             if( level >= kr->r.key.fingerprint_len ) {
250                 log_error( db_name, "keyhashtbl has invalid indirections\n");
251                 return G10ERR_TRUSTDB;
252             }
253             goto next_level;
254         }
255         else if( rec.rectype == RECTYPE_HLST ) { /* extend list */
256             /* see whether the key is already in this list */
257             for(;;) {
258                 for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
259                     if( rec.r.hlst.rnum[i] == kr->recnum ) {
260                         log_debug("HTBL: no update needed for keyrec %lu\n",
261                                     kr->recnum );
262                         return 0;
263                     }
264                 }
265                 if( rec.r.hlst.next ) {
266                     rc = tdbio_read_record( rec.r.hlst.next,
267                                                         &rec, RECTYPE_HLST);
268                     if( rc ) {
269                         log_error( db_name,
270                                    "scan keyhashtbl read hlst failed: %s\n",
271                                                              g10_errstr(rc) );
272                         return rc;
273                     }
274                 }
275                 else
276                     break; /* not there */
277             }
278             /* find the next free entry and put it in */
279             for(;;) {
280                 for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
281                     if( !rec.r.hlst.rnum[i] ) {
282                         rec.r.hlst.rnum[i] = kr->recnum;
283                         rc = tdbio_write_record( &rec );
284                         if( rc )
285                             log_error( db_name,
286                                    "update_keyhashtbl write hlst failed: %s\n",
287                                                               g10_errstr(rc) );
288                         return rc; /* ready */
289                     }
290                 }
291                 if( rec.r.hlst.next ) {
292                     rc = tdbio_read_record( rec.r.hlst.next,
293                                                       &rec, RECTYPE_HLST );
294                     if( rc ) {
295                         log_error( db_name,
296                                    "update_keyhashtbl read hlst failed: %s\n",
297                                                              g10_errstr(rc) );
298                         return rc;
299                     }
300                 }
301                 else { /* add a new list record */
302                     rec.r.hlst.next = item = tdbio_new_recnum();
303                     rc = tdbio_write_record( &rec );
304                     if( rc ) {
305                         log_error( db_name,
306                                "update_keyhashtbl write hlst failed: %s\n",
307                                                           g10_errstr(rc) );
308                         return rc;
309                     }
310                     memset( &rec, 0, sizeof rec );
311                     rec.rectype = RECTYPE_HLST;
312                     rec.recnum = item;
313                     rec.r.hlst.rnum[0] = kr->recnum;
314                     if( rc )
315                         log_error( db_name,
316                                "update_keyhashtbl write ext hlst failed: %s\n",
317                                                           g10_errstr(rc) );
318                     return rc; /* ready */
319                 }
320             }
321         }
322         else if( rec.rectype == RECTYPE_KEY ) { /* insert a list record */
323             if( rec.recnum == kr->recnum ) {
324                 log_debug("HTBL: no update needed for keyrec %lu\n",
325                                                          kr->recnum );
326                 return 0;
327             }
328             item = rec.recnum; /* save number of key record */
329             memset( &rec, 0, sizeof rec );
330             rec.rectype = RECTYPE_HLST;
331             rec.recnum = tdbio_new_recnum();
332             rec.r.hlst.rnum[0] = item;       /* old keyrecord */
333             rec.r.hlst.rnum[1] = kr->recnum; /* and new one */
334             rc = tdbio_write_record( &rec );
335             if( rc ) {
336                 log_error( db_name,
337                        "update_keyhashtbl write new hlst failed: %s\n",
338                                                   g10_errstr(rc) );
339                 return rc;
340             }
341             /* update the hashtable record */
342             lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum;
343             rc = tdbio_write_record( &lastrec );
344             if( rc )
345                 log_error( db_name,
346                        "update_keyhashtbl update htbl failed: %s\n",
347                                                   g10_errstr(rc) );
348             return rc; /* ready */
349         }
350         else {
351             log_error( db_name, "keyhashtbl %lu points to an invalid record\n",
352                                                                     item);
353             return G10ERR_TRUSTDB;
354         }
355     }
356
357     return 0;
358 }
359
360
361
362 void
363 tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
364 {
365     int i;
366     ulong rnum = rec->recnum;
367     byte *p;
368
369     fprintf(fp, "rec %5lu, ", rnum );
370
371     switch( rec->rectype ) {
372       case 0: fprintf(fp, "free\n");
373         break;
374       case RECTYPE_VER: fprintf(fp, "version, keyhashtbl=%lu\n",
375             rec->r.ver.keyhashtbl );
376         break;
377       case RECTYPE_DIR:
378         fprintf(fp, "dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x",
379                     rec->r.dir.lid,
380                     rec->r.dir.keylist,
381                     rec->r.dir.uidlist,
382                     rec->r.dir.cacherec,
383                     rec->r.dir.ownertrust );
384         if( rec->r.dir.dirflags & DIRF_ERROR )
385             fputs(", error", fp );
386         if( rec->r.dir.dirflags & DIRF_CHECKED )
387             fputs(", checked", fp );
388         if( rec->r.dir.dirflags & DIRF_REVOKED )
389             fputs(", revoked", fp );
390         if( rec->r.dir.dirflags & DIRF_MISKEY )
391             fputs(", miskey", fp );
392         putc('\n', fp);
393         break;
394       case RECTYPE_KEY:
395         fprintf(fp, "key %lu, next=%lu, algo=%d, ",
396                    rec->r.key.lid,
397                    rec->r.key.next,
398                    rec->r.key.pubkey_algo );
399         for(i=0; i < rec->r.key.fingerprint_len; i++ )
400             fprintf(fp, "%02X", rec->r.key.fingerprint[i] );
401         if( rec->r.key.keyflags & KEYF_REVOKED )
402             fputs(", revoked", fp );
403         putc('\n', fp);
404         break;
405       case RECTYPE_UID:
406         fprintf(fp, "uid %lu, next=%lu, pref=%lu, sig=%lu, hash=%02X%02X",
407                     rec->r.uid.lid,
408                     rec->r.uid.next,
409                     rec->r.uid.prefrec,
410                     rec->r.uid.siglist,
411                     rec->r.uid.namehash[18], rec->r.uid.namehash[19]);
412         if( rec->r.uid.uidflags & UIDF_REVOKED )
413             fputs(", revoked", fp );
414         putc('\n', fp);
415         break;
416       case RECTYPE_PREF:
417         fprintf(fp, "pref %lu, next=%lu,",
418                     rec->r.pref.lid, rec->r.pref.next);
419         for(i=0,p=rec->r.pref.data; i < ITEMS_PER_PREF_RECORD; i+=2,p+=2 ) {
420             if( *p )
421                 fprintf(fp, " %c%d", *p == PREFTYPE_SYM    ? 'S' :
422                                      *p == PREFTYPE_HASH   ? 'H' :
423                                      *p == PREFTYPE_COMPR  ? 'Z' : '?', p[1]);
424         }
425         putc('\n', fp);
426         break;
427       case RECTYPE_SIG:
428         fprintf(fp, "sig %lu, next=%lu,",
429                          rec->r.sig.lid, rec->r.sig.next );
430         for(i=0; i < SIGS_PER_RECORD; i++ ) {
431             if( rec->r.sig.sig[i].lid )
432                 fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].lid,
433                                           rec->r.sig.sig[i].flag );
434         }
435         putc('\n', fp);
436         break;
437       case RECTYPE_CACH:
438         fprintf(fp, "cach\n");
439         break;
440       case RECTYPE_HTBL:
441         fprintf(fp, "htbl,");
442         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ )
443             fprintf(fp, " %lu", rec->r.htbl.item[i] );
444         putc('\n', fp);
445         break;
446       case RECTYPE_HLST:
447         fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next );
448         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ )
449             fprintf(fp, " %lu", rec->r.hlst.rnum[i] );
450         putc('\n', fp);
451         break;
452       default:
453         fprintf(fp, "unknown type %d\n", rec->rectype );
454         break;
455     }
456 }
457
458 /****************
459  * read the record with number recnum
460  * returns: -1 on error, 0 on success
461  */
462 int
463 tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
464 {
465     byte buf[TRUST_RECORD_LEN], *p;
466     int rc = 0;
467     int n, i;
468
469     if( db_fd == -1 )
470         open_db();
471     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
472         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
473         return G10ERR_READ_FILE;
474     }
475     n = read( db_fd, buf, TRUST_RECORD_LEN);
476     if( !n ) {
477         return -1; /* eof */
478     }
479     else if( n != TRUST_RECORD_LEN ) {
480         log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) );
481         return G10ERR_READ_FILE;
482     }
483     rec->recnum = recnum;
484     p = buf;
485     rec->rectype = *p++;
486     if( expected && rec->rectype != expected ) {
487         log_error("%lu: read expected rec type %d, got %d\n",
488                     recnum, expected, rec->rectype );
489         return G10ERR_TRUSTDB;
490     }
491     p++;    /* skip reserved byte */
492     switch( rec->rectype ) {
493       case 0:  /* unused (free) record */
494         break;
495       case RECTYPE_VER: /* version record */
496         if( memcmp(buf+1, "gpg", 3 ) ) {
497             log_error_f( db_name, _("not a trustdb file\n") );
498             rc = G10ERR_TRUSTDB;
499         }
500         p += 2; /* skip "pgp" */
501         rec->r.ver.version  = *p++;
502         p += 3; /* reserved bytes */
503         p += 4; /* lock flags */
504         rec->r.ver.created  = buftoulong(p); p += 4;
505         rec->r.ver.modified = buftoulong(p); p += 4;
506         rec->r.ver.validated= buftoulong(p); p += 4;
507         rec->r.ver.keyhashtbl=buftoulong(p); p += 4;
508         if( recnum ) {
509             log_error_f( db_name, "version record with recnum %lu\n",
510                                                              (ulong)recnum );
511             rc = G10ERR_TRUSTDB;
512         }
513         else if( rec->r.ver.version != 2 ) {
514             log_error_f( db_name, "invalid file version %d\n",
515                                                         rec->r.ver.version );
516             rc = G10ERR_TRUSTDB;
517         }
518         break;
519       case RECTYPE_DIR:   /*directory record */
520         rec->r.dir.lid      = buftoulong(p); p += 4;
521         rec->r.dir.keylist  = buftoulong(p); p += 4;
522         rec->r.dir.uidlist  = buftoulong(p); p += 4;
523         rec->r.dir.cacherec = buftoulong(p); p += 4;
524         rec->r.dir.ownertrust = *p++;
525         rec->r.dir.dirflags   = *p++;
526         if( rec->r.dir.lid != recnum ) {
527             log_error_f( db_name, "dir LID != recnum (%lu,%lu)\n",
528                                          rec->r.dir.lid, (ulong)recnum );
529             rc = G10ERR_TRUSTDB;
530         }
531         break;
532       case RECTYPE_KEY:   /* public key record */
533         rec->r.key.lid      = buftoulong(p); p += 4;
534         rec->r.key.next     = buftoulong(p); p += 4;
535         p += 7;
536         rec->r.key.keyflags = *p++;
537         rec->r.key.pubkey_algo = *p++;
538         rec->r.key.fingerprint_len = *p++;
539         if( rec->r.key.fingerprint_len < 1 || rec->r.key.fingerprint_len > 20 )
540             rec->r.key.fingerprint_len = 20;
541         memcpy( rec->r.key.fingerprint, p, 20);
542         break;
543       case RECTYPE_UID:   /* user id record */
544         rec->r.uid.lid      = buftoulong(p); p += 4;
545         rec->r.uid.next     = buftoulong(p); p += 4;
546         rec->r.uid.prefrec  = buftoulong(p); p += 4;
547         rec->r.uid.siglist  = buftoulong(p); p += 4;
548         rec->r.uid.uidflags = *p++;
549         p ++;
550         memcpy( rec->r.uid.namehash, p, 20);
551         break;
552       case RECTYPE_PREF:  /* preference record */
553         rec->r.pref.lid     = buftoulong(p); p += 4;
554         rec->r.pref.next    = buftoulong(p); p += 4;
555         memcpy( rec->r.pref.data, p, 30 );
556         break;
557       case RECTYPE_SIG:
558         rec->r.sig.lid     = buftoulong(p); p += 4;
559         rec->r.sig.next    = buftoulong(p); p += 4;
560         for(i=0; i < SIGS_PER_RECORD; i++ ) {
561             rec->r.sig.sig[i].lid  = buftoulong(p); p += 4;
562             rec->r.sig.sig[i].flag = *p++;
563         }
564         break;
565       case RECTYPE_CACH:   /* cache record (FIXME)*/
566         rec->r.cache.lid    = buftoulong(p); p += 4;
567         memcpy(rec->r.cache.blockhash, p, 20); p += 20;
568         rec->r.cache.trustlevel = *p++;
569         break;
570       case RECTYPE_HTBL:
571         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
572             rec->r.htbl.item[i] = buftoulong(p); p += 4;
573         }
574         break;
575       case RECTYPE_HLST:
576         rec->r.hlst.next = buftoulong(p); p += 4;
577         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
578             rec->r.hlst.rnum[i] = buftoulong(p); p += 4;
579         }
580         break;
581       default:
582         log_error_f( db_name, "invalid record type %d at recnum %lu\n",
583                                               rec->rectype, (ulong)recnum );
584         rc = G10ERR_TRUSTDB;
585         break;
586     }
587
588     return rc;
589 }
590
591 /****************
592  * Write the record at RECNUM
593  * FIXME: create/update keyhash record.
594  */
595 int
596 tdbio_write_record( TRUSTREC *rec )
597 {
598     byte buf[TRUST_RECORD_LEN], *p;
599     int rc = 0;
600     int i, n;
601     ulong recnum = rec->recnum;
602
603     if( db_fd == -1 )
604         open_db();
605
606     memset(buf, 0, TRUST_RECORD_LEN);
607     p = buf;
608     *p++ = rec->rectype; p++;
609     switch( rec->rectype ) {
610       case 0:  /* unused record */
611         break;
612       case RECTYPE_VER: /* version record */
613         if( recnum )
614             BUG();
615         memcpy(p-1, "gpg", 3 ); p += 2;
616         *p++ = rec->r.ver.version;
617         p += 7; /* skip reserved bytes and lock flags */
618         ulongtobuf(p, rec->r.ver.created); p += 4;
619         ulongtobuf(p, rec->r.ver.modified); p += 4;
620         ulongtobuf(p, rec->r.ver.validated); p += 4;
621         ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4;
622         break;
623
624       case RECTYPE_DIR:   /*directory record */
625         ulongtobuf(p, rec->r.dir.lid); p += 4;
626         ulongtobuf(p, rec->r.dir.keylist); p += 4;
627         ulongtobuf(p, rec->r.dir.uidlist); p += 4;
628         ulongtobuf(p, rec->r.dir.cacherec); p += 4;
629         *p++ = rec->r.dir.ownertrust;
630         *p++ = rec->r.dir.dirflags;
631         assert( rec->r.dir.lid == recnum );
632         break;
633
634       case RECTYPE_KEY:
635         ulongtobuf(p, rec->r.key.lid); p += 4;
636         ulongtobuf(p, rec->r.key.next); p += 4;
637         p += 7;
638         *p++ = rec->r.key.keyflags;
639         *p++ = rec->r.key.pubkey_algo;
640         *p++ = rec->r.key.fingerprint_len;
641         memcpy( p, rec->r.key.fingerprint, 20); p += 20;
642         break;
643
644       case RECTYPE_UID:   /* user id record */
645         ulongtobuf(p, rec->r.uid.lid); p += 4;
646         ulongtobuf(p, rec->r.uid.next); p += 4;
647         ulongtobuf(p, rec->r.uid.prefrec); p += 4;
648         ulongtobuf(p, rec->r.uid.siglist); p += 4;
649         *p++ = rec->r.uid.uidflags;
650         p++;
651         memcpy( p, rec->r.uid.namehash, 20 ); p += 20;
652         break;
653
654       case RECTYPE_PREF:
655         ulongtobuf(p, rec->r.pref.lid); p += 4;
656         ulongtobuf(p, rec->r.pref.next); p += 4;
657         memcpy( p, rec->r.pref.data, 30 );
658         break;
659
660       case RECTYPE_SIG:
661         ulongtobuf(p, rec->r.sig.lid); p += 4;
662         ulongtobuf(p, rec->r.sig.next); p += 4;
663         for(i=0; i < SIGS_PER_RECORD; i++ ) {
664             ulongtobuf(p, rec->r.sig.sig[i].lid); p += 4;
665             *p++ = rec->r.sig.sig[i].flag;
666         }
667         break;
668
669       case RECTYPE_CACH:   /* FIXME*/
670         ulongtobuf(p, rec->r.cache.lid); p += 4;
671         memcpy(p, rec->r.cache.blockhash, 20); p += 20;
672         *p++ = rec->r.cache.trustlevel;
673         break;
674
675       case RECTYPE_HTBL:
676         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
677             ulongtobuf( p, rec->r.htbl.item[i]); p += 4;
678         }
679         break;
680
681       case RECTYPE_HLST:
682         ulongtobuf( p, rec->r.hlst.next); p += 4;
683         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
684             ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4;
685         }
686         break;
687
688       default:
689         BUG();
690     }
691
692     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
693         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
694         return G10ERR_WRITE_FILE;
695     }
696     n = write( db_fd, buf, TRUST_RECORD_LEN);
697     if( n != TRUST_RECORD_LEN ) {
698         log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) );
699         return G10ERR_WRITE_FILE;
700     }
701     else if( rec->rectype == RECTYPE_KEY )
702         rc = update_keyhashtbl( rec );
703
704     return rc;
705 }
706
707 int
708 tdbio_delete_record( ulong recnum )
709 {
710     TRUSTREC rec;
711
712     rec.recnum = recnum;
713     rec.rectype = 0;
714     return tdbio_write_record( &rec );
715 }
716
717 /****************
718  * create a new record and return its record number
719  */
720 ulong
721 tdbio_new_recnum()
722 {
723     off_t offset;
724     ulong recnum;
725     TRUSTREC rec;
726     int rc;
727
728     /* fixme: look for unused records */
729     offset = lseek( db_fd, 0, SEEK_END );
730     if( offset == -1 )
731         log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
732     recnum = offset / TRUST_RECORD_LEN;
733     assert(recnum); /* this is will never be the first record */
734
735     /* we must write a record, so that the next call to this function
736      * returns another recnum */
737     memset( &rec, 0, sizeof rec );
738     rec.rectype = 0; /* free record */
739     rec.recnum = recnum;
740     rc = tdbio_write_record( &rec );
741     if( rc )
742         log_fatal_f(db_name,_("failed to append a record: %s\n"),
743                                             g10_errstr(rc));
744     return recnum ;
745 }
746
747
748
749 /****************
750  * Search the trustdb for a key which matches PK and return the dir record
751  * The local_id of PK is set to the correct value
752  */
753 int
754 tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec )
755 {
756     byte *fingerprint;
757     size_t fingerlen;
758     u32 keyid[2];
759     int rc;
760
761     keyid_from_pk( pk, keyid );
762     fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen );
763     rc = tdbio_search_dir_byfpr( fingerprint, fingerlen,
764                                  pk->pubkey_algo, rec );
765
766     if( !rc ) {
767         if( pk->local_id && pk->local_id != rec->recnum )
768             log_error_f(db_name,
769                        "found record, but LID from memory does "
770                        "not match recnum (%lu,%lu)\n",
771                                       pk->local_id, rec->recnum );
772         pk->local_id = rec->recnum;
773     }
774     return rc;
775 }
776
777
778 int
779 tdbio_search_dir_byfpr( const byte *fingerprint, size_t fingerlen,
780                         int pubkey_algo, TRUSTREC *rec )
781 {
782     ulong recnum;
783     int rc;
784     ulong hashrec, item;
785     int msb;
786     int level=0;
787
788     assert( fingerlen == 20 || fingerlen == 16 );
789
790     /* locate the key using the hash table */
791     hashrec = get_keyhashrec();
792   next_level:
793     msb = fingerprint[level];
794     hashrec += msb / ITEMS_PER_HTBL_RECORD;
795     rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL );
796     if( rc ) {
797         log_error( db_name, "scan keyhashtbl failed: %s\n", g10_errstr(rc) );
798         return rc;
799     }
800
801     item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
802     if( !item )
803         return -1; /* not found */
804
805     rc = tdbio_read_record( item, rec, 0 );
806     if( rc ) {
807         log_error( db_name, "keyhashtbl read failed: %s\n", g10_errstr(rc) );
808         return rc;
809     }
810     if( rec->rectype == RECTYPE_HTBL ) {
811         hashrec = item;
812         level++;
813         if( level >= fingerlen ) {
814             log_error( db_name, "keyhashtbl has invalid indirections\n");
815             return G10ERR_TRUSTDB;
816         }
817         goto next_level;
818     }
819     else if( rec->rectype == RECTYPE_HLST ) {
820         for(;;) {
821             int i;
822
823             for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
824                 if( rec->r.hlst.rnum[i] ) {
825                     TRUSTREC tmp;
826
827                     rc = tdbio_read_record( rec->r.hlst.rnum[i],
828                                              &tmp, RECTYPE_KEY );
829                     if( rc ) {
830                         log_error( db_name,
831                                    "scan keyhashtbl read key failed: %s\n",
832                                                              g10_errstr(rc) );
833                         return rc;
834                     }
835                     if( (!pubkey_algo || tmp.r.key.pubkey_algo == pubkey_algo)
836                         && tmp.r.key.fingerprint_len == fingerlen
837                         && !memcmp(tmp.r.key.fingerprint,
838                                             fingerprint, fingerlen) ) {
839                         *rec = tmp;
840                         goto found;
841                     }
842                 }
843             }
844             if( rec->r.hlst.next ) {
845                 rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST );
846                 if( rc ) {
847                     log_error( db_name,
848                                "scan keyhashtbl read hlst failed: %s\n",
849                                                          g10_errstr(rc) );
850                     return rc;
851                 }
852             }
853             else
854                 return -1; /* not found */
855         }
856       found:
857         ;
858     }
859     else if( rec->rectype == RECTYPE_KEY ) {
860         /* must check that it is the requested key */
861         if( (pubkey_algo && rec->r.key.pubkey_algo != pubkey_algo)
862             || rec->r.key.fingerprint_len != fingerlen
863             || memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) )
864             return -1; /* no: not found */
865     }
866     else {
867         log_error( db_name, "keyhashtbl %lu points to an invalid record\n",
868                                                                 item);
869         return G10ERR_TRUSTDB;
870     }
871
872     recnum = rec->r.key.lid;
873     /* Now read the dir record */
874     rc = tdbio_read_record( recnum, rec, RECTYPE_DIR);
875     if( rc )
876         log_error_f(db_name, "can't read dirrec %lu: %s\n",
877                                                 recnum, g10_errstr(rc) );
878
879     return rc;
880 }
881
882 static int
883 del_reclist( ulong recno, int type )
884 {
885     TRUSTREC rec;
886     int rc;
887
888     while( recno ) {
889         rc = tdbio_read_record( recno, &rec, type);
890         if( rc ) {
891             log_error_f(db_name, "can't read record %lu: %s\n",
892                                                 recno, g10_errstr(rc));
893             return rc;
894         }
895         switch( type ) {
896             case RECTYPE_PREF: recno = rec.r.pref.next; break;
897             case RECTYPE_UID:  recno = rec.r.uid.next;  break;
898             default: BUG();
899         }
900         rc = tdbio_delete_record( rec.recnum );
901         if( rc ) {
902             log_error_f(db_name, "can't delete record %lu: %s\n",
903                                                 rec.recnum, g10_errstr(rc));
904             return rc;
905         }
906     }
907     return 0;
908 }
909
910 /****************
911  * Delete the Userid UIDLID from DIRLID
912  */
913 int
914 tdbio_delete_uidrec( ulong dirlid, ulong uidlid )
915 {
916     TRUSTREC dirrec, rec;
917     ulong recno;
918     int rc;
919
920     rc = tdbio_read_record( dirlid, &dirrec, RECTYPE_DIR);
921     if( rc ) {
922         log_error_f(db_name, "can't read dirrec %lu: %s\n", dirlid, g10_errstr(rc));
923         return rc;
924     }
925     recno = dirrec.r.dir.uidlist;
926     for( ; recno; recno = rec.r.uid.next ) {
927         rc = tdbio_read_record( recno, &rec, RECTYPE_UID);
928         if( rc ) {
929             log_error_f(db_name, "can't read uidrec %lu: %s\n",
930                                                     recno, g10_errstr(rc));
931             return rc;
932         }
933         if( recno == uidlid ) {
934             rc = del_reclist( rec.r.uid.prefrec, RECTYPE_PREF );
935             if( rc )
936                 return rc;
937             rc = del_reclist( rec.r.uid.siglist, RECTYPE_SIG );
938             if( rc )
939                 return rc;
940             rc = tdbio_delete_record( recno );
941             if( rc ) {
942                 log_error_f(db_name, "can't delete uidrec %lu: %s\n",
943                                                     recno, g10_errstr(rc));
944                 return rc;
945             }
946             dirrec.r.dir.uidlist = 0;
947             rc = tdbio_write_record( &dirrec );
948             if( rc ) {
949                 log_error_f(db_name, "can't update dirrec %lu: %s\n",
950                                                   dirrec.recnum, g10_errstr(rc));
951                 return rc;
952             }
953             return 0;
954         }
955     }
956     return -1; /* not found */
957 }
958
959