36f0ac503c73c108aab7362bfa057c94f965d71a
[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, "w" );
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             db_fd = open( db_name, O_RDWR );
96             if( db_fd == -1 )
97                 log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) );
98
99             memset( &rec, 0, sizeof rec );
100             rec.r.ver.version = 2;
101             rec.r.ver.created = make_timestamp();
102             rec.rectype = RECTYPE_VER;
103             rec.recnum = 0;
104             rc = tdbio_write_record( &rec );
105             if( rc )
106                 log_fatal_f( fname, _("failed to create version record: %s"),
107                                                                g10_errstr(rc));
108             /* and read again to check that we are okay */
109             if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
110                 log_fatal_f( db_name, "invalid trust-db created\n" );
111             return 0;
112         }
113     }
114     m_free(db_name);
115     db_name = fname;
116     return 0;
117 }
118
119
120 const char *
121 tdbio_get_dbname()
122 {
123     return db_name;
124 }
125
126
127
128 static void
129 open_db()
130 {
131     TRUSTREC rec;
132     assert( db_fd == -1 );
133
134     db_fd = open( db_name, O_RDWR );
135     if( db_fd == -1 )
136         log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) );
137     if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
138         log_fatal_f( db_name, _("invalid trust-db\n") );
139     /* fixme: check ->locked and other stuff */
140 }
141
142
143 /****************
144  * Return the record number of the keyhash tbl or create a new one.
145  */
146 static ulong
147 get_keyhashrec()
148 {
149     static ulong keyhashtbl; /* record number of the key hashtable */
150     TRUSTREC vr;
151     int rc;
152
153     if( keyhashtbl )
154         return keyhashtbl;
155
156     rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
157     if( rc )
158         log_fatal_f( db_name, _("error reading version record: %s\n"),
159                                                         g10_errstr(rc) );
160     if( vr.r.ver.keyhashtbl )
161         keyhashtbl = vr.r.ver.keyhashtbl;
162     else {
163         TRUSTREC rec;
164         off_t offset;
165         ulong recnum;
166         int i, n;
167
168         offset = lseek( db_fd, 0, SEEK_END );
169         if( offset == -1 )
170             log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
171         recnum = offset / TRUST_RECORD_LEN;
172         assert(recnum); /* this is will never be the first record */
173
174         keyhashtbl = recnum;
175         /* Now write the records */
176         n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD;
177         for(i=0; i < n; i++, recnum++ ) {
178              memset( &rec, 0, sizeof rec );
179              rec.rectype = RECTYPE_HTBL; /* free record */
180              rec.recnum = recnum;
181              rc = tdbio_write_record( &rec );
182              if( rc )
183                  log_fatal_f(db_name,_("failed to create hashtable: %s\n"),
184                                                      g10_errstr(rc));
185         }
186         /* update the version record */
187         vr.r.ver.keyhashtbl = keyhashtbl;
188         rc = tdbio_write_record( &vr );
189         if( rc )
190             log_fatal_f( db_name, _("error updating version record: %s\n"),
191                                                              g10_errstr(rc));
192     }
193     return keyhashtbl;
194 }
195
196
197 /****************
198  * Update the key hashtbl or create the table if it does not exist
199  */
200 static int
201 update_keyhashtbl( TRUSTREC *kr )
202 {
203     TRUSTREC lastrec, rec;
204     ulong hashrec, item;
205     int msb;
206     int level=0;
207     int rc, i;
208
209     hashrec = get_keyhashrec();
210   next_level:
211     msb = kr->r.key.fingerprint[level];
212     hashrec += msb / ITEMS_PER_HTBL_RECORD;
213     rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL );
214     if( rc ) {
215         log_error( db_name, "update_keyhashtbl read failed: %s\n",
216                                                         g10_errstr(rc) );
217         return rc;
218     }
219
220     item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
221     if( !item ) { /* insert new one */
222         rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = kr->recnum;
223         rc = tdbio_write_record( &rec );
224         if( rc ) {
225             log_error( db_name, "update_keyhashtbl write htbl failed: %s\n",
226                                                             g10_errstr(rc) );
227             return rc;
228         }
229     }
230     else if( item != kr->recnum ) {  /* must do an update */
231         lastrec = rec;
232         rc = tdbio_read_record( item, &rec, 0 );
233         if( rc ) {
234             log_error( db_name, "update_keyhashtbl read item failed: %s\n",
235                                                             g10_errstr(rc) );
236             return rc;
237         }
238         if( rec.rectype == RECTYPE_HTBL ) {
239             hashrec = item;
240             level++;
241             if( level >= kr->r.key.fingerprint_len ) {
242                 log_error( db_name, "keyhashtbl has invalid indirections\n");
243                 return G10ERR_TRUSTDB;
244             }
245             goto next_level;
246         }
247         else if( rec.rectype == RECTYPE_HLST ) { /* extend list */
248             /* see whether the key is already in this list */
249             for(;;) {
250                 for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
251                     if( rec.r.hlst.rnum[i] == kr->recnum ) {
252                         log_debug("HTBL: no update needed for keyrec %lu\n",
253                                     kr->recnum );
254                         return 0;
255                     }
256                 }
257                 if( rec.r.hlst.next ) {
258                     rc = tdbio_read_record( rec.r.hlst.next,
259                                                         &rec, RECTYPE_HLST);
260                     if( rc ) {
261                         log_error( db_name,
262                                    "scan keyhashtbl read hlst failed: %s\n",
263                                                              g10_errstr(rc) );
264                         return rc;
265                     }
266                 }
267                 else
268                     break; /* not there */
269             }
270             /* find the next free entry and put it in */
271             for(;;) {
272                 for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
273                     if( !rec.r.hlst.rnum[i] ) {
274                         rec.r.hlst.rnum[i] = kr->recnum;
275                         rc = tdbio_write_record( &rec );
276                         if( rc )
277                             log_error( db_name,
278                                    "update_keyhashtbl write hlst failed: %s\n",
279                                                               g10_errstr(rc) );
280                         return rc; /* ready */
281                     }
282                 }
283                 if( rec.r.hlst.next ) {
284                     rc = tdbio_read_record( rec.r.hlst.next,
285                                                       &rec, RECTYPE_HLST );
286                     if( rc ) {
287                         log_error( db_name,
288                                    "update_keyhashtbl read hlst failed: %s\n",
289                                                              g10_errstr(rc) );
290                         return rc;
291                     }
292                 }
293                 else { /* add a new list record */
294                     rec.r.hlst.next = item = tdbio_new_recnum();
295                     rc = tdbio_write_record( &rec );
296                     if( rc ) {
297                         log_error( db_name,
298                                "update_keyhashtbl write hlst failed: %s\n",
299                                                           g10_errstr(rc) );
300                         return rc;
301                     }
302                     memset( &rec, 0, sizeof rec );
303                     rec.rectype = RECTYPE_HLST;
304                     rec.recnum = item;
305                     rec.r.hlst.rnum[0] = kr->recnum;
306                     if( rc )
307                         log_error( db_name,
308                                "update_keyhashtbl write ext hlst failed: %s\n",
309                                                           g10_errstr(rc) );
310                     return rc; /* ready */
311                 }
312             }
313         }
314         else if( rec.rectype == RECTYPE_KEY ) { /* insert a list record */
315             if( rec.recnum == kr->recnum ) {
316                 log_debug("HTBL: no update needed for keyrec %lu\n",
317                                                          kr->recnum );
318                 return 0;
319             }
320             item = rec.recnum; /* save number of key record */
321             memset( &rec, 0, sizeof rec );
322             rec.rectype = RECTYPE_HLST;
323             rec.recnum = tdbio_new_recnum();
324             rec.r.hlst.rnum[0] = item;       /* old keyrecord */
325             rec.r.hlst.rnum[1] = kr->recnum; /* and new one */
326             rc = tdbio_write_record( &rec );
327             if( rc ) {
328                 log_error( db_name,
329                        "update_keyhashtbl write new hlst failed: %s\n",
330                                                   g10_errstr(rc) );
331                 return rc;
332             }
333             /* update the hashtable record */
334             lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum;
335             rc = tdbio_write_record( &lastrec );
336             if( rc )
337                 log_error( db_name,
338                        "update_keyhashtbl update htbl failed: %s\n",
339                                                   g10_errstr(rc) );
340             return rc; /* ready */
341         }
342         else {
343             log_error( db_name, "keyhashtbl %lu points to an invalid record\n",
344                                                                     item);
345             return G10ERR_TRUSTDB;
346         }
347     }
348
349     return 0;
350 }
351
352
353
354 void
355 tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
356 {
357     int i;
358     ulong rnum = rec->recnum;
359
360     fprintf(fp, "rec %5lu, ", rnum );
361
362     switch( rec->rectype ) {
363       case 0: fprintf(fp, "free\n");
364         break;
365       case RECTYPE_VER: fprintf(fp, "version, keyhashtbl=%lu\n",
366             rec->r.ver.keyhashtbl );
367         break;
368       case RECTYPE_DIR:
369         fprintf(fp, "dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x",
370                     rec->r.dir.lid,
371                     rec->r.dir.keylist,
372                     rec->r.dir.uidlist,
373                     rec->r.dir.cacherec,
374                     rec->r.dir.ownertrust );
375         if( rec->r.dir.dirflags & DIRF_ERROR )
376             fputs(", error", fp );
377         if( rec->r.dir.dirflags & DIRF_CHECKED )
378             fputs(", checked", fp );
379         if( rec->r.dir.dirflags & DIRF_REVOKED )
380             fputs(", revoked", fp );
381         if( rec->r.dir.dirflags & DIRF_MISKEY )
382             fputs(", miskey", fp );
383         putc('\n', fp);
384         break;
385       case RECTYPE_KEY:
386         fprintf(fp, "key %lu, next=%lu, algo=%d, ",
387                    rec->r.key.lid,
388                    rec->r.key.next,
389                    rec->r.key.pubkey_algo );
390         for(i=0; i < rec->r.key.fingerprint_len; i++ )
391             fprintf(fp, "%02X", rec->r.key.fingerprint[i] );
392         if( rec->r.key.keyflags & KEYF_REVOKED )
393             fputs(", revoked", fp );
394         putc('\n', fp);
395         break;
396       case RECTYPE_UID:
397         fprintf(fp, "uid %lu, next=%lu, pref=%lu, sig=%lu, hash=%02X%02X",
398                     rec->r.uid.lid,
399                     rec->r.uid.next,
400                     rec->r.uid.prefrec,
401                     rec->r.uid.siglist,
402                     rec->r.uid.namehash[18], rec->r.uid.namehash[19]);
403         if( rec->r.uid.uidflags & UIDF_REVOKED )
404             fputs(", revoked", fp );
405         putc('\n', fp);
406         break;
407       case RECTYPE_PREF:
408         fprintf(fp, "pref %lu, next=%lu\n",
409                     rec->r.uid.lid,
410                     rec->r.uid.next);
411         break;
412       case RECTYPE_SIG:
413         fprintf(fp, "sig %lu, next=%lu,",
414                          rec->r.sig.lid, rec->r.sig.next );
415         for(i=0; i < SIGS_PER_RECORD; i++ ) {
416             if( rec->r.sig.sig[i].lid )
417                 fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].lid,
418                                           rec->r.sig.sig[i].flag );
419         }
420         putc('\n', fp);
421         break;
422       case RECTYPE_CACH:
423         fprintf(fp, "cach\n");
424         break;
425       case RECTYPE_HTBL:
426         fprintf(fp, "htbl,");
427         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ )
428             fprintf(fp, " %lu", rec->r.htbl.item[i] );
429         putc('\n', fp);
430         break;
431       case RECTYPE_HLST:
432         fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next );
433         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ )
434             fprintf(fp, " %lu", rec->r.hlst.rnum[i] );
435         putc('\n', fp);
436         break;
437       default:
438         fprintf(fp, "unknown type %d\n", rec->rectype );
439         break;
440     }
441 }
442
443 /****************
444  * read the record with number recnum
445  * returns: -1 on error, 0 on success
446  */
447 int
448 tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
449 {
450     byte buf[TRUST_RECORD_LEN], *p;
451     int rc = 0;
452     int n, i;
453
454     if( db_fd == -1 )
455         open_db();
456     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
457         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
458         return G10ERR_READ_FILE;
459     }
460     n = read( db_fd, buf, TRUST_RECORD_LEN);
461     if( !n ) {
462         return -1; /* eof */
463     }
464     else if( n != TRUST_RECORD_LEN ) {
465         log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) );
466         return G10ERR_READ_FILE;
467     }
468     rec->recnum = recnum;
469     p = buf;
470     rec->rectype = *p++;
471     if( expected && rec->rectype != expected ) {
472         log_error("%lu: read expected rec type %d, got %d\n",
473                     recnum, expected, rec->rectype );
474         return G10ERR_TRUSTDB;
475     }
476     p++;    /* skip reserved byte */
477     switch( rec->rectype ) {
478       case 0:  /* unused (free) record */
479         break;
480       case RECTYPE_VER: /* version record */
481         if( memcmp(buf+1, "gpg", 3 ) ) {
482             log_error_f( db_name, _("not a trustdb file\n") );
483             rc = G10ERR_TRUSTDB;
484         }
485         p += 2; /* skip "pgp" */
486         rec->r.ver.version  = *p++;
487         p += 3; /* reserved bytes */
488         p += 4; /* lock flags */
489         rec->r.ver.created  = buftoulong(p); p += 4;
490         rec->r.ver.modified = buftoulong(p); p += 4;
491         rec->r.ver.validated= buftoulong(p); p += 4;
492         rec->r.ver.keyhashtbl=buftoulong(p); p += 4;
493         if( recnum ) {
494             log_error_f( db_name, "version record with recnum %lu\n",
495                                                              (ulong)recnum );
496             rc = G10ERR_TRUSTDB;
497         }
498         else if( rec->r.ver.version != 2 ) {
499             log_error_f( db_name, "invalid file version %d\n",
500                                                         rec->r.ver.version );
501             rc = G10ERR_TRUSTDB;
502         }
503         break;
504       case RECTYPE_DIR:   /*directory record */
505         rec->r.dir.lid      = buftoulong(p); p += 4;
506         rec->r.dir.keylist  = buftoulong(p); p += 4;
507         rec->r.dir.uidlist  = buftoulong(p); p += 4;
508         rec->r.dir.cacherec = buftoulong(p); p += 4;
509         rec->r.dir.ownertrust = *p++;
510         rec->r.dir.dirflags   = *p++;
511         if( rec->r.dir.lid != recnum ) {
512             log_error_f( db_name, "dir LID != recnum (%lu,%lu)\n",
513                                          rec->r.dir.lid, (ulong)recnum );
514             rc = G10ERR_TRUSTDB;
515         }
516         break;
517       case RECTYPE_KEY:   /* public key record */
518         rec->r.key.lid      = buftoulong(p); p += 4;
519         rec->r.key.next     = buftoulong(p); p += 4;
520         p += 7;
521         rec->r.key.keyflags = *p++;
522         rec->r.key.pubkey_algo = *p++;
523         rec->r.key.fingerprint_len = *p++;
524         if( rec->r.key.fingerprint_len < 1 || rec->r.key.fingerprint_len > 20 )
525             rec->r.key.fingerprint_len = 20;
526         memcpy( rec->r.key.fingerprint, p, 20);
527         break;
528       case RECTYPE_UID:   /* user id record */
529         rec->r.uid.lid      = buftoulong(p); p += 4;
530         rec->r.uid.next     = buftoulong(p); p += 4;
531         rec->r.uid.prefrec  = buftoulong(p); p += 4;
532         rec->r.uid.siglist  = buftoulong(p); p += 4;
533         rec->r.uid.uidflags = *p++;
534         p ++;
535         memcpy( rec->r.uid.namehash, p, 20);
536         break;
537       case RECTYPE_PREF:  /* preference record */
538         rec->r.pref.lid     = buftoulong(p); p += 4;
539         rec->r.pref.next    = buftoulong(p); p += 4;
540         break;
541       case RECTYPE_SIG:
542         rec->r.sig.lid     = buftoulong(p); p += 4;
543         rec->r.sig.next    = buftoulong(p); p += 4;
544         for(i=0; i < SIGS_PER_RECORD; i++ ) {
545             rec->r.sig.sig[i].lid  = buftoulong(p); p += 4;
546             rec->r.sig.sig[i].flag = *p++;
547         }
548         break;
549       case RECTYPE_CACH:   /* cache record (FIXME)*/
550         rec->r.cache.lid    = buftoulong(p); p += 4;
551         memcpy(rec->r.cache.blockhash, p, 20); p += 20;
552         rec->r.cache.trustlevel = *p++;
553         break;
554       case RECTYPE_HTBL:
555         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
556             rec->r.htbl.item[i] = buftoulong(p); p += 4;
557         }
558         break;
559       case RECTYPE_HLST:
560         rec->r.hlst.next = buftoulong(p); p += 4;
561         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
562             rec->r.hlst.rnum[i] = buftoulong(p); p += 4;
563         }
564         break;
565       default:
566         log_error_f( db_name, "invalid record type %d at recnum %lu\n",
567                                               rec->rectype, (ulong)recnum );
568         rc = G10ERR_TRUSTDB;
569         break;
570     }
571
572     return rc;
573 }
574
575 /****************
576  * Write the record at RECNUM
577  * FIXME: create/update keyhash record.
578  */
579 int
580 tdbio_write_record( TRUSTREC *rec )
581 {
582     byte buf[TRUST_RECORD_LEN], *p;
583     int rc = 0;
584     int i, n;
585     ulong recnum = rec->recnum;
586
587     if( db_fd == -1 )
588         open_db();
589
590     memset(buf, 0, TRUST_RECORD_LEN);
591     p = buf;
592     *p++ = rec->rectype; p++;
593     switch( rec->rectype ) {
594       case 0:  /* unused record */
595         break;
596       case RECTYPE_VER: /* version record */
597         if( recnum )
598             BUG();
599         memcpy(p-1, "gpg", 3 ); p += 2;
600         *p++ = rec->r.ver.version;
601         p += 7; /* skip reserved bytes and lock flags */
602         ulongtobuf(p, rec->r.ver.created); p += 4;
603         ulongtobuf(p, rec->r.ver.modified); p += 4;
604         ulongtobuf(p, rec->r.ver.validated); p += 4;
605         ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4;
606         break;
607
608       case RECTYPE_DIR:   /*directory record */
609         ulongtobuf(p, rec->r.dir.lid); p += 4;
610         ulongtobuf(p, rec->r.dir.keylist); p += 4;
611         ulongtobuf(p, rec->r.dir.uidlist); p += 4;
612         ulongtobuf(p, rec->r.dir.cacherec); p += 4;
613         *p++ = rec->r.dir.ownertrust;
614         *p++ = rec->r.dir.dirflags;
615         assert( rec->r.dir.lid == recnum );
616         break;
617
618       case RECTYPE_KEY:
619         ulongtobuf(p, rec->r.key.lid); p += 4;
620         ulongtobuf(p, rec->r.key.next); p += 4;
621         p += 7;
622         *p++ = rec->r.key.keyflags;
623         *p++ = rec->r.key.pubkey_algo;
624         *p++ = rec->r.key.fingerprint_len;
625         memcpy( p, rec->r.key.fingerprint, 20); p += 20;
626         break;
627
628       case RECTYPE_UID:   /* user id record */
629         ulongtobuf(p, rec->r.uid.lid); p += 4;
630         ulongtobuf(p, rec->r.uid.next); p += 4;
631         ulongtobuf(p, rec->r.uid.prefrec); p += 4;
632         ulongtobuf(p, rec->r.uid.siglist); p += 4;
633         *p++ = rec->r.uid.uidflags;
634         p++;
635         memcpy( p, rec->r.uid.namehash, 20 ); p += 20;
636         break;
637
638       case RECTYPE_PREF:
639         ulongtobuf(p, rec->r.pref.lid); p += 4;
640         ulongtobuf(p, rec->r.pref.next); p += 4;
641         break;
642
643       case RECTYPE_SIG:
644         ulongtobuf(p, rec->r.sig.lid); p += 4;
645         ulongtobuf(p, rec->r.sig.next); p += 4;
646         for(i=0; i < SIGS_PER_RECORD; i++ ) {
647             ulongtobuf(p, rec->r.sig.sig[i].lid); p += 4;
648             *p++ = rec->r.sig.sig[i].flag;
649         }
650         break;
651
652       case RECTYPE_CACH:   /* FIXME*/
653         ulongtobuf(p, rec->r.cache.lid); p += 4;
654         memcpy(p, rec->r.cache.blockhash, 20); p += 20;
655         *p++ = rec->r.cache.trustlevel;
656         break;
657
658       case RECTYPE_HTBL:
659         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
660             ulongtobuf( p, rec->r.htbl.item[i]); p += 4;
661         }
662         break;
663
664       case RECTYPE_HLST:
665         ulongtobuf( p, rec->r.hlst.next); p += 4;
666         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
667             ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4;
668         }
669         break;
670
671       default:
672         BUG();
673     }
674
675     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
676         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
677         return G10ERR_WRITE_FILE;
678     }
679     n = write( db_fd, buf, TRUST_RECORD_LEN);
680     if( n != TRUST_RECORD_LEN ) {
681         log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) );
682         return G10ERR_WRITE_FILE;
683     }
684     else if( rec->rectype == RECTYPE_KEY )
685         rc = update_keyhashtbl( rec );
686
687     return rc;
688 }
689
690 int
691 tdbio_delete_record( ulong recnum )
692 {
693     TRUSTREC rec;
694
695     rec.recnum = recnum;
696     rec.rectype = 0;
697     return tdbio_write_record( &rec );
698 }
699
700 /****************
701  * create a new record and return its record number
702  */
703 ulong
704 tdbio_new_recnum()
705 {
706     off_t offset;
707     ulong recnum;
708     TRUSTREC rec;
709     int rc;
710
711     /* fixme: look for unused records */
712     offset = lseek( db_fd, 0, SEEK_END );
713     if( offset == -1 )
714         log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
715     recnum = offset / TRUST_RECORD_LEN;
716     assert(recnum); /* this is will never be the first record */
717
718     /* we must write a record, so that the next call to this function
719      * returns another recnum */
720     memset( &rec, 0, sizeof rec );
721     rec.rectype = 0; /* free record */
722     rec.recnum = recnum;
723     rc = tdbio_write_record( &rec );
724     if( rc )
725         log_fatal_f(db_name,_("failed to append a record: %s\n"),
726                                             g10_errstr(rc));
727     return recnum ;
728 }
729
730
731
732 /****************
733  * Search the trustdb for a key which matches PK and return the dir record
734  * The local_id of PK is set to the correct value
735  */
736 int
737 tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec )
738 {
739     ulong recnum;
740     u32 keyid[2];
741     byte *fingerprint;
742     size_t fingerlen;
743     int rc;
744     ulong hashrec, item;
745     int msb;
746     int level=0;
747
748     keyid_from_pk( pk, keyid );
749     fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen );
750     assert( fingerlen == 20 || fingerlen == 16 );
751
752     /* locate the key using the hash table */
753     hashrec = get_keyhashrec();
754   next_level:
755     msb = fingerprint[level];
756     hashrec += msb / ITEMS_PER_HTBL_RECORD;
757     rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL );
758     if( rc ) {
759         log_error( db_name, "scan keyhashtbl failed: %s\n", g10_errstr(rc) );
760         return rc;
761     }
762
763     item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
764     if( !item )
765         return -1; /* not found */
766
767     rc = tdbio_read_record( item, rec, 0 );
768     if( rc ) {
769         log_error( db_name, "keyhashtbl read failed: %s\n", g10_errstr(rc) );
770         return rc;
771     }
772     if( rec->rectype == RECTYPE_HTBL ) {
773         hashrec = item;
774         level++;
775         if( level >= fingerlen ) {
776             log_error( db_name, "keyhashtbl has invalid indirections\n");
777             return G10ERR_TRUSTDB;
778         }
779         goto next_level;
780     }
781     else if( rec->rectype == RECTYPE_HLST ) {
782         for(;;) {
783             int i;
784
785             for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
786                 if( rec->r.hlst.rnum[i] ) {
787                     TRUSTREC tmp;
788
789                     rc = tdbio_read_record( rec->r.hlst.rnum[i],
790                                              &tmp, RECTYPE_KEY );
791                     if( rc ) {
792                         log_error( db_name,
793                                    "scan keyhashtbl read key failed: %s\n",
794                                                              g10_errstr(rc) );
795                         return rc;
796                     }
797                     if( tmp.r.key.pubkey_algo == pk->pubkey_algo
798                         && tmp.r.key.fingerprint_len == fingerlen
799                         && !memcmp(tmp.r.key.fingerprint,
800                                             fingerprint, fingerlen) ) {
801                         *rec = tmp;
802                         goto found;
803                     }
804                 }
805             }
806             if( rec->r.hlst.next ) {
807                 rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST );
808                 if( rc ) {
809                     log_error( db_name,
810                                "scan keyhashtbl read hlst failed: %s\n",
811                                                          g10_errstr(rc) );
812                     return rc;
813                 }
814             }
815             else
816                 return -1; /* not found */
817         }
818       found:
819         ;
820     }
821     else if( rec->rectype == RECTYPE_KEY ) {
822         /* must check that it is the requested key */
823         if( rec->r.key.pubkey_algo != pk->pubkey_algo
824             || rec->r.key.fingerprint_len != fingerlen
825             || memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) )
826             return -1; /* no: not found */
827     }
828     else {
829         log_error( db_name, "keyhashtbl %lu points to an invalid record\n",
830                                                                 item);
831         return G10ERR_TRUSTDB;
832     }
833
834     recnum = rec->r.key.lid;
835
836     if( pk->local_id && pk->local_id != recnum )
837         log_error_f(db_name,
838                    "found record, but LID from memory does "
839                    "not match recnum (%lu,%lu)\n",
840                                         pk->local_id, recnum );
841     pk->local_id = recnum;
842
843     /* Now read the dir record */
844     rc = tdbio_read_record( recnum, rec, RECTYPE_DIR);
845     if( rc )
846         log_error_f(db_name, "can't read dirrec %lu: %s\n",
847                                                 recnum, g10_errstr(rc) );
848
849     return rc;
850 }
851
852
853 /****************
854  * Delete the Userid UIDLID from DIRLID
855  */
856 int
857 tdbio_delete_uidrec( ulong dirlid, ulong uidlid )
858 {
859     return G10ERR_GENERAL; /* not implemented */
860 }
861
862