diff options
Diffstat (limited to 'g10/tdbio.c')
-rw-r--r-- | g10/tdbio.c | 344 |
1 files changed, 278 insertions, 66 deletions
diff --git a/g10/tdbio.c b/g10/tdbio.c index 82b22b43a..425919338 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -39,6 +39,27 @@ #include "trustdb.h" #include "tdbio.h" + +/**************** + * Yes, this is a very simple implementation. We should really + * use a page aligned buffer and read complete pages. + * To implement a simple trannsaction system, this is sufficient. + */ +typedef struct cache_ctrl_struct *CACHE_CTRL; +struct cache_ctrl_struct { + CACHE_CTRL next; + struct { + unsigned used:1; + unsigned dirty:1; + } flags; + ulong recno; + char data[TRUST_RECORD_LEN]; +}; + +#define MAX_CACHE_ENTRIES 200 +static CACHE_CTRL cache_list; +static int cache_entries; + /* a type used to pass infomation to cmp_krec_fpr */ struct cmp_krec_fpr_struct { int pubkey_algo; @@ -59,6 +80,184 @@ static int db_fd = -1; static void open_db(void); + +/************************************* + ************* record cache ********** + *************************************/ + +/**************** + * Get the data from therecord cache and return a + * pointer into that cache. Caller should copy + * the return data. NULL is returned on a cache miss. + */ +static const char * +get_record_from_cache( ulong recno ) +{ + CACHE_CTRL r; + + for( r = cache_list; r; r = r->next ) { + if( r->flags.used && r->recno == recno ) + return r->data; + } + return NULL; +} + + +static int +write_cache_item( CACHE_CTRL r ) +{ + int n; + + if( lseek( db_fd, r->recno * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { + log_error(_("trustdb rec %lu: lseek failed: %s\n"), + r->recno, strerror(errno) ); + return G10ERR_WRITE_FILE; + } + n = write( db_fd, r->data, TRUST_RECORD_LEN); + if( n != TRUST_RECORD_LEN ) { + log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"), + r->recno, n, strerror(errno) ); + return G10ERR_WRITE_FILE; + } + r->flags.dirty = 0; + return 0; +} + +/**************** + * Put data into the cache. This function may flush the + * some cache entries if there is not enough space available. + */ +int +put_record_into_cache( ulong recno, const char *data ) +{ + CACHE_CTRL r, unused; + int dirty_count = 0; + int clean_count = 0; + + /* see whether we already cached this one */ + for( unused = NULL, r = cache_list; r; r = r->next ) { + if( !r->flags.used ) { + if( !unused ) + unused = r; + } + else if( r->recno == recno ) { + if( !r->flags.dirty ) { + /* Hmmm: should we use a a copy and compare? */ + if( memcmp(r->data, data, TRUST_RECORD_LEN ) ) + r->flags.dirty = 1; + } + memcpy( r->data, data, TRUST_RECORD_LEN ); + return 0; + } + if( r->flags.used ) { + if( r->flags.dirty ) + dirty_count++; + else + clean_count++; + } + } + /* not in the cache: add a new entry */ + if( unused ) { /* reuse this entry */ + r = unused; + r->flags.used = 1; + r->recno = recno; + memcpy( r->data, data, TRUST_RECORD_LEN ); + r->flags.dirty = 1; + cache_entries++; + return 0; + } + /* see whether we reached the limit */ + if( cache_entries < MAX_CACHE_ENTRIES ) { /* no */ + r = m_alloc( sizeof *r ); + r->flags.used = 1; + r->recno = recno; + memcpy( r->data, data, TRUST_RECORD_LEN ); + r->flags.dirty = 1; + r->next = cache_list; + cache_list = r; + cache_entries++; + return 0; + } + /* cache is full: discard some clean entries */ + if( clean_count ) { + int n = clean_count / 3; /* discard a third of the clean entries */ + if( !n ) + n = 1; + for( unused = NULL, r = cache_list; r; r = r->next ) { + if( r->flags.used && !r->flags.dirty ) { + if( !unused ) + unused = r; + r->flags.used = 0; + cache_entries--; + if( !--n ) + break; + } + } + assert( unused ); + r = unused; + r->flags.used = 1; + r->recno = recno; + memcpy( r->data, data, TRUST_RECORD_LEN ); + r->flags.dirty = 1; + cache_entries++; + return 0; + } + /* no clean entries: have to flush some dirty entries */ + if( dirty_count ) { + int n = dirty_count / 5; /* discard some dirty entries */ + if( !n ) + n = 1; + for( unused = NULL, r = cache_list; r; r = r->next ) { + if( r->flags.used && r->flags.dirty ) { + int rc = write_cache_item( r ); + if( rc ) + return rc; + if( !unused ) + unused = r; + r->flags.used = 0; + cache_entries--; + if( !--n ) + break; + } + } + assert( unused ); + r = unused; + r->flags.used = 1; + r->recno = recno; + memcpy( r->data, data, TRUST_RECORD_LEN ); + r->flags.dirty = 1; + cache_entries++; + return 0; + } + BUG(); +} + + + +/**************** + * Sync the cache to disk + */ + +int +tdbio_sync() +{ + CACHE_CTRL r; + + for( r = cache_list; r; r = r->next ) { + if( r->flags.used && r->flags.dirty ) { + int rc = write_cache_item( r ); + if( rc ) + return rc; + } + } + return 0; +} + + + +/******************************************************** + **************** cached I/O functions ****************** + ********************************************************/ int tdbio_set_dbname( const char *new_dbname, int create ) @@ -118,6 +317,8 @@ tdbio_set_dbname( const char *new_dbname, int create ) rec.rectype = RECTYPE_VER; rec.recnum = 0; rc = tdbio_write_record( &rec ); + if( !rc ) + tdbio_sync(); if( rc ) log_fatal_f( fname, _("failed to create version record: %s"), g10_errstr(rc)); @@ -185,7 +386,7 @@ create_hashtable( TRUSTREC *vr, int type ) n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD; for(i=0; i < n; i++, recnum++ ) { memset( &rec, 0, sizeof rec ); - rec.rectype = RECTYPE_HTBL; /* free record */ + rec.rectype = RECTYPE_HTBL; rec.recnum = recnum; rc = tdbio_write_record( &rec ); if( rc ) @@ -194,6 +395,8 @@ create_hashtable( TRUSTREC *vr, int type ) } /* update the version record */ rc = tdbio_write_record( vr ); + if( !rc ) + rc = tdbio_sync(); if( rc ) log_fatal_f( db_name, _("error updating version record: %s\n"), g10_errstr(rc)); @@ -208,21 +411,21 @@ static ulong get_keyhashrec() { static ulong keyhashtbl; /* record number of the key hashtable */ - TRUSTREC vr; - int rc; - if( keyhashtbl ) - return keyhashtbl; - - rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); - if( rc ) - log_fatal_f( db_name, _("error reading version record: %s\n"), - g10_errstr(rc) ); - if( !vr.r.ver.keyhashtbl ) - create_hashtable( &vr, 0 ); + if( !keyhashtbl ) { + TRUSTREC vr; + int rc; + rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); + if( rc ) + log_fatal_f( db_name, _("error reading version record: %s\n"), + g10_errstr(rc) ); + if( !vr.r.ver.keyhashtbl ) + create_hashtable( &vr, 0 ); - return vr.r.ver.keyhashtbl; + keyhashtbl = vr.r.ver.keyhashtbl; + } + return keyhashtbl; } /**************** @@ -233,20 +436,21 @@ static ulong get_sdirhashrec() { static ulong sdirhashtbl; /* record number of the hashtable */ - TRUSTREC vr; - int rc; - if( sdirhashtbl ) - return sdirhashtbl; + if( !sdirhashtbl ) { + TRUSTREC vr; + int rc; - rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); - if( rc ) - log_fatal_f( db_name, _("error reading version record: %s\n"), - g10_errstr(rc) ); - if( !vr.r.ver.sdirhashtbl ) - create_hashtable( &vr, 1 ); + rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); + if( rc ) + log_fatal_f( db_name, _("error reading version record: %s\n"), + g10_errstr(rc) ); + if( !vr.r.ver.sdirhashtbl ) + create_hashtable( &vr, 1 ); - return vr.r.ver.sdirhashtbl; + sdirhashtbl = vr.r.ver.sdirhashtbl; + } + return sdirhashtbl; } @@ -289,15 +493,16 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) lastrec = rec; rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { - log_error( db_name, "upd_hashtable: read item failed: %s\n", + log_error( "upd_hashtable: read item failed: %s\n", g10_errstr(rc) ); return rc; } + if( rec.rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { - log_error( db_name, "hashtable has invalid indirections.\n"); + log_error( "hashtable has invalid indirections.\n"); return G10ERR_TRUSTDB; } goto next_level; @@ -314,8 +519,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { - log_error( db_name, - "scan keyhashtbl read hlst failed: %s\n", + log_error( "scan keyhashtbl read hlst failed: %s\n", g10_errstr(rc) ); return rc; } @@ -330,8 +534,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rec.r.hlst.rnum[i] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) - log_error( db_name, - "upd_hashtable: write hlst failed: %s\n", + log_error( "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); return rc; /* done */ } @@ -340,8 +543,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST ); if( rc ) { - log_error( db_name, - "upd_hashtable: read hlst failed: %s\n", + log_error( "upd_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } @@ -350,8 +552,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rec.r.hlst.next = item = tdbio_new_recnum(); rc = tdbio_write_record( &rec ); if( rc ) { - log_error( db_name, - "upd_hashtable: write hlst failed: %s\n", + log_error( "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); return rc; } @@ -361,14 +562,14 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rec.r.hlst.rnum[0] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) - log_error( db_name, - "upd_hashtable: write ext hlst failed: %s\n", + log_error( "upd_hashtable: write ext hlst failed: %s\n", g10_errstr(rc) ); return rc; /* done */ } } /* end loop over hlst slots */ } else if( rec.rectype == RECTYPE_KEY + || rec.rectype == RECTYPE_DIR || rec.rectype == RECTYPE_SDIR ) { /* insert a list record */ if( rec.recnum == newrecnum ) { return 0; @@ -381,8 +582,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rec.r.hlst.rnum[1] = newrecnum; /* and new one */ rc = tdbio_write_record( &rec ); if( rc ) { - log_error( db_name, - "upd_hashtable: write new hlst failed: %s\n", + log_error( "upd_hashtable: write new hlst failed: %s\n", g10_errstr(rc) ); return rc; } @@ -390,12 +590,12 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum; rc = tdbio_write_record( &lastrec ); if( rc ) - log_error( db_name, "upd_hashtable: update htbl failed: %s\n", + log_error( "upd_hashtable: update htbl failed: %s\n", g10_errstr(rc) ); return rc; /* ready */ } else { - log_error( db_name, "hashtbl %lu points to an invalid record\n", + log_error( "hashtbl %lu points to an invalid record\n", item); return G10ERR_TRUSTDB; } @@ -637,23 +837,29 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) int tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) { - byte buf[TRUST_RECORD_LEN], *p; + byte readbuf[TRUST_RECORD_LEN]; + const byte *buf, *p; int rc = 0; int n, i; if( db_fd == -1 ) open_db(); - if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { - log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) ); - return G10ERR_READ_FILE; - } - n = read( db_fd, buf, TRUST_RECORD_LEN); - if( !n ) { - return -1; /* eof */ - } - else if( n != TRUST_RECORD_LEN ) { - log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) ); - return G10ERR_READ_FILE; + buf = get_record_from_cache( recnum ); + if( !buf ) { + if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { + log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) ); + return G10ERR_READ_FILE; + } + n = read( db_fd, readbuf, TRUST_RECORD_LEN); + if( !n ) { + return -1; /* eof */ + } + else if( n != TRUST_RECORD_LEN ) { + log_error(_("trustdb: read failed (n=%d): %s\n"), n, + strerror(errno) ); + return G10ERR_READ_FILE; + } + buf = readbuf; } rec->recnum = recnum; rec->dirty = 0; @@ -791,14 +997,12 @@ tdbio_write_record( TRUSTREC *rec ) { byte buf[TRUST_RECORD_LEN], *p; int rc = 0; - int i, n; + int i; ulong recnum = rec->recnum; if( db_fd == -1 ) open_db(); -tdbio_dump_record( rec, stdout ); - memset(buf, 0, TRUST_RECORD_LEN); p = buf; *p++ = rec->rectype; p++; @@ -900,16 +1104,10 @@ tdbio_dump_record( rec, stdout ); BUG(); } - if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { - log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) ); - return G10ERR_WRITE_FILE; - } - n = write( db_fd, buf, TRUST_RECORD_LEN); - if( n != TRUST_RECORD_LEN ) { - log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) ); - return G10ERR_WRITE_FILE; - } - else if( rec->rectype == RECTYPE_KEY ) + rc = put_record_into_cache( recnum, buf ); + if( rc ) + ; + if( rec->rectype == RECTYPE_KEY ) rc = update_keyhashtbl( rec ); else if( rec->rectype == RECTYPE_SDIR ) rc = update_sdirhashtbl( rec ); @@ -990,7 +1188,21 @@ tdbio_new_recnum() memset( &rec, 0, sizeof rec ); rec.rectype = 0; /* unused record */ rec.recnum = recnum; - rc = tdbio_write_record( &rec ); + rc = 0; + if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { + log_error(_("trustdb rec %lu: lseek failed: %s\n"), + recnum, strerror(errno) ); + rc = G10ERR_WRITE_FILE; + } + else { + int n = write( db_fd, &rec, TRUST_RECORD_LEN); + if( n != TRUST_RECORD_LEN ) { + log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"), + recnum, n, strerror(errno) ); + rc = G10ERR_WRITE_FILE; + } + } + if( rc ) log_fatal_f(db_name,_("failed to append a record: %s\n"), g10_errstr(rc)); |