add salted and iterated mode
[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     byte *p;
360
361     fprintf(fp, "rec %5lu, ", rnum );
362
363     switch( rec->rectype ) {
364       case 0: fprintf(fp, "free\n");
365         break;
366       case RECTYPE_VER: fprintf(fp, "version, keyhashtbl=%lu\n",
367             rec->r.ver.keyhashtbl );
368         break;
369       case RECTYPE_DIR:
370         fprintf(fp, "dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x",
371                     rec->r.dir.lid,
372                     rec->r.dir.keylist,
373                     rec->r.dir.uidlist,
374                     rec->r.dir.cacherec,
375                     rec->r.dir.ownertrust );
376         if( rec->r.dir.dirflags & DIRF_ERROR )
377             fputs(", error", fp );
378         if( rec->r.dir.dirflags & DIRF_CHECKED )
379             fputs(", checked", fp );
380         if( rec->r.dir.dirflags & DIRF_REVOKED )
381             fputs(", revoked", fp );
382         if( rec->r.dir.dirflags & DIRF_MISKEY )
383             fputs(", miskey", fp );
384         putc('\n', fp);
385         break;
386       case RECTYPE_KEY:
387         fprintf(fp, "key %lu, next=%lu, algo=%d, ",
388                    rec->r.key.lid,
389                    rec->r.key.next,
390                    rec->r.key.pubkey_algo );
391         for(i=0; i < rec->r.key.fingerprint_len; i++ )
392             fprintf(fp, "%02X", rec->r.key.fingerprint[i] );
393         if( rec->r.key.keyflags & KEYF_REVOKED )
394             fputs(", revoked", fp );
395         putc('\n', fp);
396         break;
397       case RECTYPE_UID:
398         fprintf(fp, "uid %lu, next=%lu, pref=%lu, sig=%lu, hash=%02X%02X",
399                     rec->r.uid.lid,
400                     rec->r.uid.next,
401                     rec->r.uid.prefrec,
402                     rec->r.uid.siglist,
403                     rec->r.uid.namehash[18], rec->r.uid.namehash[19]);
404         if( rec->r.uid.uidflags & UIDF_REVOKED )
405             fputs(", revoked", fp );
406         putc('\n', fp);
407         break;
408       case RECTYPE_PREF:
409         fprintf(fp, "pref %lu, next=%lu,",
410                     rec->r.pref.lid, rec->r.pref.next);
411         for(i=0,p=rec->r.pref.data; i < ITEMS_PER_PREF_RECORD; i+=2,p+=2 ) {
412             if( *p )
413                 fprintf(fp, " %c%d", *p == PREFTYPE_SYM    ? 'S' :
414                                      *p == PREFTYPE_HASH   ? 'H' :
415                                      *p == PREFTYPE_COMPR  ? 'Z' : '?', p[1]);
416         }
417         putc('\n', fp);
418         break;
419       case RECTYPE_SIG:
420         fprintf(fp, "sig %lu, next=%lu,",
421                          rec->r.sig.lid, rec->r.sig.next );
422         for(i=0; i < SIGS_PER_RECORD; i++ ) {
423             if( rec->r.sig.sig[i].lid )
424                 fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].lid,
425                                           rec->r.sig.sig[i].flag );
426         }
427         putc('\n', fp);
428         break;
429       case RECTYPE_CACH:
430         fprintf(fp, "cach\n");
431         break;
432       case RECTYPE_HTBL:
433         fprintf(fp, "htbl,");
434         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ )
435             fprintf(fp, " %lu", rec->r.htbl.item[i] );
436         putc('\n', fp);
437         break;
438       case RECTYPE_HLST:
439         fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next );
440         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ )
441             fprintf(fp, " %lu", rec->r.hlst.rnum[i] );
442         putc('\n', fp);
443         break;
444       default:
445         fprintf(fp, "unknown type %d\n", rec->rectype );
446         break;
447     }
448 }
449
450 /****************
451  * read the record with number recnum
452  * returns: -1 on error, 0 on success
453  */
454 int
455 tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
456 {
457     byte buf[TRUST_RECORD_LEN], *p;
458     int rc = 0;
459     int n, i;
460
461     if( db_fd == -1 )
462         open_db();
463     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
464         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
465         return G10ERR_READ_FILE;
466     }
467     n = read( db_fd, buf, TRUST_RECORD_LEN);
468     if( !n ) {
469         return -1; /* eof */
470     }
471     else if( n != TRUST_RECORD_LEN ) {
472         log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) );
473         return G10ERR_READ_FILE;
474     }
475     rec->recnum = recnum;
476     p = buf;
477     rec->rectype = *p++;
478     if( expected && rec->rectype != expected ) {
479         log_error("%lu: read expected rec type %d, got %d\n",
480                     recnum, expected, rec->rectype );
481         return G10ERR_TRUSTDB;
482     }
483     p++;    /* skip reserved byte */
484     switch( rec->rectype ) {
485       case 0:  /* unused (free) record */
486         break;
487       case RECTYPE_VER: /* version record */
488         if( memcmp(buf+1, "gpg", 3 ) ) {
489             log_error_f( db_name, _("not a trustdb file\n") );
490             rc = G10ERR_TRUSTDB;
491         }
492         p += 2; /* skip "pgp" */
493         rec->r.ver.version  = *p++;
494         p += 3; /* reserved bytes */
495         p += 4; /* lock flags */
496         rec->r.ver.created  = buftoulong(p); p += 4;
497         rec->r.ver.modified = buftoulong(p); p += 4;
498         rec->r.ver.validated= buftoulong(p); p += 4;
499         rec->r.ver.keyhashtbl=buftoulong(p); p += 4;
500         if( recnum ) {
501             log_error_f( db_name, "version record with recnum %lu\n",
502                                                              (ulong)recnum );
503             rc = G10ERR_TRUSTDB;
504         }
505         else if( rec->r.ver.version != 2 ) {
506             log_error_f( db_name, "invalid file version %d\n",
507                                                         rec->r.ver.version );
508             rc = G10ERR_TRUSTDB;
509         }
510         break;
511       case RECTYPE_DIR:   /*directory record */
512         rec->r.dir.lid      = buftoulong(p); p += 4;
513         rec->r.dir.keylist  = buftoulong(p); p += 4;
514         rec->r.dir.uidlist  = buftoulong(p); p += 4;
515         rec->r.dir.cacherec = buftoulong(p); p += 4;
516         rec->r.dir.ownertrust = *p++;
517         rec->r.dir.dirflags   = *p++;
518         if( rec->r.dir.lid != recnum ) {
519             log_error_f( db_name, "dir LID != recnum (%lu,%lu)\n",
520                                          rec->r.dir.lid, (ulong)recnum );
521             rc = G10ERR_TRUSTDB;
522         }
523         break;
524       case RECTYPE_KEY:   /* public key record */
525         rec->r.key.lid      = buftoulong(p); p += 4;
526         rec->r.key.next     = buftoulong(p); p += 4;
527         p += 7;
528         rec->r.key.keyflags = *p++;
529         rec->r.key.pubkey_algo = *p++;
530         rec->r.key.fingerprint_len = *p++;
531         if( rec->r.key.fingerprint_len < 1 || rec->r.key.fingerprint_len > 20 )
532             rec->r.key.fingerprint_len = 20;
533         memcpy( rec->r.key.fingerprint, p, 20);
534         break;
535       case RECTYPE_UID:   /* user id record */
536         rec->r.uid.lid      = buftoulong(p); p += 4;
537         rec->r.uid.next     = buftoulong(p); p += 4;
538         rec->r.uid.prefrec  = buftoulong(p); p += 4;
539         rec->r.uid.siglist  = buftoulong(p); p += 4;
540         rec->r.uid.uidflags = *p++;
541         p ++;
542         memcpy( rec->r.uid.namehash, p, 20);
543         break;
544       case RECTYPE_PREF:  /* preference record */
545         rec->r.pref.lid     = buftoulong(p); p += 4;
546         rec->r.pref.next    = buftoulong(p); p += 4;
547         memcpy( rec->r.pref.data, p, 30 );
548         break;
549       case RECTYPE_SIG:
550         rec->r.sig.lid     = buftoulong(p); p += 4;
551         rec->r.sig.next    = buftoulong(p); p += 4;
552         for(i=0; i < SIGS_PER_RECORD; i++ ) {
553             rec->r.sig.sig[i].lid  = buftoulong(p); p += 4;
554             rec->r.sig.sig[i].flag = *p++;
555         }
556         break;
557       case RECTYPE_CACH:   /* cache record (FIXME)*/
558         rec->r.cache.lid    = buftoulong(p); p += 4;
559         memcpy(rec->r.cache.blockhash, p, 20); p += 20;
560         rec->r.cache.trustlevel = *p++;
561         break;
562       case RECTYPE_HTBL:
563         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
564             rec->r.htbl.item[i] = buftoulong(p); p += 4;
565         }
566         break;
567       case RECTYPE_HLST:
568         rec->r.hlst.next = buftoulong(p); p += 4;
569         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
570             rec->r.hlst.rnum[i] = buftoulong(p); p += 4;
571         }
572         break;
573       default:
574         log_error_f( db_name, "invalid record type %d at recnum %lu\n",
575                                               rec->rectype, (ulong)recnum );
576         rc = G10ERR_TRUSTDB;
577         break;
578     }
579
580     return rc;
581 }
582
583 /****************
584  * Write the record at RECNUM
585  * FIXME: create/update keyhash record.
586  */
587 int
588 tdbio_write_record( TRUSTREC *rec )
589 {
590     byte buf[TRUST_RECORD_LEN], *p;
591     int rc = 0;
592     int i, n;
593     ulong recnum = rec->recnum;
594
595     if( db_fd == -1 )
596         open_db();
597
598     memset(buf, 0, TRUST_RECORD_LEN);
599     p = buf;
600     *p++ = rec->rectype; p++;
601     switch( rec->rectype ) {
602       case 0:  /* unused record */
603         break;
604       case RECTYPE_VER: /* version record */
605         if( recnum )
606             BUG();
607         memcpy(p-1, "gpg", 3 ); p += 2;
608         *p++ = rec->r.ver.version;
609         p += 7; /* skip reserved bytes and lock flags */
610         ulongtobuf(p, rec->r.ver.created); p += 4;
611         ulongtobuf(p, rec->r.ver.modified); p += 4;
612         ulongtobuf(p, rec->r.ver.validated); p += 4;
613         ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4;
614         break;
615
616       case RECTYPE_DIR:   /*directory record */
617         ulongtobuf(p, rec->r.dir.lid); p += 4;
618         ulongtobuf(p, rec->r.dir.keylist); p += 4;
619         ulongtobuf(p, rec->r.dir.uidlist); p += 4;
620         ulongtobuf(p, rec->r.dir.cacherec); p += 4;
621         *p++ = rec->r.dir.ownertrust;
622         *p++ = rec->r.dir.dirflags;
623         assert( rec->r.dir.lid == recnum );
624         break;
625
626       case RECTYPE_KEY:
627         ulongtobuf(p, rec->r.key.lid); p += 4;
628         ulongtobuf(p, rec->r.key.next); p += 4;
629         p += 7;
630         *p++ = rec->r.key.keyflags;
631         *p++ = rec->r.key.pubkey_algo;
632         *p++ = rec->r.key.fingerprint_len;
633         memcpy( p, rec->r.key.fingerprint, 20); p += 20;
634         break;
635
636       case RECTYPE_UID:   /* user id record */
637         ulongtobuf(p, rec->r.uid.lid); p += 4;
638         ulongtobuf(p, rec->r.uid.next); p += 4;
639         ulongtobuf(p, rec->r.uid.prefrec); p += 4;
640         ulongtobuf(p, rec->r.uid.siglist); p += 4;
641         *p++ = rec->r.uid.uidflags;
642         p++;
643         memcpy( p, rec->r.uid.namehash, 20 ); p += 20;
644         break;
645
646       case RECTYPE_PREF:
647         ulongtobuf(p, rec->r.pref.lid); p += 4;
648         ulongtobuf(p, rec->r.pref.next); p += 4;
649         memcpy( p, rec->r.pref.data, 30 );
650         break;
651
652       case RECTYPE_SIG:
653         ulongtobuf(p, rec->r.sig.lid); p += 4;
654         ulongtobuf(p, rec->r.sig.next); p += 4;
655         for(i=0; i < SIGS_PER_RECORD; i++ ) {
656             ulongtobuf(p, rec->r.sig.sig[i].lid); p += 4;
657             *p++ = rec->r.sig.sig[i].flag;
658         }
659         break;
660
661       case RECTYPE_CACH:   /* FIXME*/
662         ulongtobuf(p, rec->r.cache.lid); p += 4;
663         memcpy(p, rec->r.cache.blockhash, 20); p += 20;
664         *p++ = rec->r.cache.trustlevel;
665         break;
666
667       case RECTYPE_HTBL:
668         for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
669             ulongtobuf( p, rec->r.htbl.item[i]); p += 4;
670         }
671         break;
672
673       case RECTYPE_HLST:
674         ulongtobuf( p, rec->r.hlst.next); p += 4;
675         for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
676             ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4;
677         }
678         break;
679
680       default:
681         BUG();
682     }
683
684     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
685         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
686         return G10ERR_WRITE_FILE;
687     }
688     n = write( db_fd, buf, TRUST_RECORD_LEN);
689     if( n != TRUST_RECORD_LEN ) {
690         log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) );
691         return G10ERR_WRITE_FILE;
692     }
693     else if( rec->rectype == RECTYPE_KEY )
694         rc = update_keyhashtbl( rec );
695
696     return rc;
697 }
698
699 int
700 tdbio_delete_record( ulong recnum )
701 {
702     TRUSTREC rec;
703
704     rec.recnum = recnum;
705     rec.rectype = 0;
706     return tdbio_write_record( &rec );
707 }
708
709 /****************
710  * create a new record and return its record number
711  */
712 ulong
713 tdbio_new_recnum()
714 {
715     off_t offset;
716     ulong recnum;
717     TRUSTREC rec;
718     int rc;
719
720     /* fixme: look for unused records */
721     offset = lseek( db_fd, 0, SEEK_END );
722     if( offset == -1 )
723         log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
724     recnum = offset / TRUST_RECORD_LEN;
725     assert(recnum); /* this is will never be the first record */
726
727     /* we must write a record, so that the next call to this function
728      * returns another recnum */
729     memset( &rec, 0, sizeof rec );
730     rec.rectype = 0; /* free record */
731     rec.recnum = recnum;
732     rc = tdbio_write_record( &rec );
733     if( rc )
734         log_fatal_f(db_name,_("failed to append a record: %s\n"),
735                                             g10_errstr(rc));
736     return recnum ;
737 }
738
739
740
741 /****************
742  * Search the trustdb for a key which matches PK and return the dir record
743  * The local_id of PK is set to the correct value
744  */
745 int
746 tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec )
747 {
748     byte *fingerprint;
749     size_t fingerlen;
750     u32 keyid[2];
751     int rc;
752
753     keyid_from_pk( pk, keyid );
754     fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen );
755     rc = tdbio_search_dir_byfpr( fingerprint, fingerlen,
756                                  pk->pubkey_algo, rec );
757
758     if( !rc ) {
759         if( pk->local_id && pk->local_id != rec->recnum )
760             log_error_f(db_name,
761                        "found record, but LID from memory does "
762                        "not match recnum (%lu,%lu)\n",
763                                       pk->local_id, rec->recnum );
764         pk->local_id = rec->recnum;
765     }
766     return rc;
767 }
768
769
770 int
771 tdbio_search_dir_byfpr( const byte *fingerprint, size_t fingerlen,
772                         int pubkey_algo, TRUSTREC *rec )
773 {
774     ulong recnum;
775     int rc;
776     ulong hashrec, item;
777     int msb;
778     int level=0;
779
780     assert( fingerlen == 20 || fingerlen == 16 );
781
782     /* locate the key using the hash table */
783     hashrec = get_keyhashrec();
784   next_level:
785     msb = fingerprint[level];
786     hashrec += msb / ITEMS_PER_HTBL_RECORD;
787     rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL );
788     if( rc ) {
789         log_error( db_name, "scan keyhashtbl failed: %s\n", g10_errstr(rc) );
790         return rc;
791     }
792
793     item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
794     if( !item )
795         return -1; /* not found */
796
797     rc = tdbio_read_record( item, rec, 0 );
798     if( rc ) {
799         log_error( db_name, "keyhashtbl read failed: %s\n", g10_errstr(rc) );
800         return rc;
801     }
802     if( rec->rectype == RECTYPE_HTBL ) {
803         hashrec = item;
804         level++;
805         if( level >= fingerlen ) {
806             log_error( db_name, "keyhashtbl has invalid indirections\n");
807             return G10ERR_TRUSTDB;
808         }
809         goto next_level;
810     }
811     else if( rec->rectype == RECTYPE_HLST ) {
812         for(;;) {
813             int i;
814
815             for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
816                 if( rec->r.hlst.rnum[i] ) {
817                     TRUSTREC tmp;
818
819                     rc = tdbio_read_record( rec->r.hlst.rnum[i],
820                                              &tmp, RECTYPE_KEY );
821                     if( rc ) {
822                         log_error( db_name,
823                                    "scan keyhashtbl read key failed: %s\n",
824                                                              g10_errstr(rc) );
825                         return rc;
826                     }
827                     if( (!pubkey_algo || tmp.r.key.pubkey_algo == pubkey_algo)
828                         && tmp.r.key.fingerprint_len == fingerlen
829                         && !memcmp(tmp.r.key.fingerprint,
830                                             fingerprint, fingerlen) ) {
831                         *rec = tmp;
832                         goto found;
833                     }
834                 }
835             }
836             if( rec->r.hlst.next ) {
837                 rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST );
838                 if( rc ) {
839                     log_error( db_name,
840                                "scan keyhashtbl read hlst failed: %s\n",
841                                                          g10_errstr(rc) );
842                     return rc;
843                 }
844             }
845             else
846                 return -1; /* not found */
847         }
848       found:
849         ;
850     }
851     else if( rec->rectype == RECTYPE_KEY ) {
852         /* must check that it is the requested key */
853         if( (pubkey_algo && rec->r.key.pubkey_algo != pubkey_algo)
854             || rec->r.key.fingerprint_len != fingerlen
855             || memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) )
856             return -1; /* no: not found */
857     }
858     else {
859         log_error( db_name, "keyhashtbl %lu points to an invalid record\n",
860                                                                 item);
861         return G10ERR_TRUSTDB;
862     }
863
864     recnum = rec->r.key.lid;
865     /* Now read the dir record */
866     rc = tdbio_read_record( recnum, rec, RECTYPE_DIR);
867     if( rc )
868         log_error_f(db_name, "can't read dirrec %lu: %s\n",
869                                                 recnum, g10_errstr(rc) );
870
871     return rc;
872 }
873
874
875 /****************
876  * Delete the Userid UIDLID from DIRLID
877  */
878 int
879 tdbio_delete_uidrec( ulong dirlid, ulong uidlid )
880 {
881     return G10ERR_GENERAL; /* not implemented */
882 }
883
884