int tdbio_delete_record( ulong recnum ) { TRUSTREC vr, rec; int rc; /* Must read the record fist, so we can drop it from the hash tables */ rc = tdbio_read_record( recnum, &rec, 0 ); if( rc ) ; else if( rec.rectype == RECTYPE_TRUST ) { rc = drop_from_hashtable( get_trusthashrec(), rec.r.trust.fingerprint, 20, rec.recnum ); } if( rc ) return rc; /* now we can chnage it to a free record */ rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); rec.recnum = recnum; rec.rectype = RECTYPE_FREE; rec.r.free.next = vr.r.ver.firstfree; vr.r.ver.firstfree = recnum; rc = tdbio_write_record( &rec ); if( !rc ) rc = tdbio_write_record( &vr ); return rc; }
/**************** * Print a list of all defined owner trust value. */ void export_ownertrust (ctrl_t ctrl) { TRUSTREC rec; ulong recnum; int i; byte *p; init_trustdb (ctrl, 0); es_printf (_("# List of assigned trustvalues, created %s\n" "# (Use \"gpg --import-ownertrust\" to restore them)\n"), asctimestamp( make_timestamp() ) ); for (recnum=0; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { if (rec.rectype == RECTYPE_TRUST) { if (!rec.r.trust.ownertrust) continue; p = rec.r.trust.fingerprint; for (i=0; i < 20; i++, p++ ) es_printf("%02X", *p ); es_printf (":%u:\n", (unsigned int)rec.r.trust.ownertrust ); } } }
static void open_db() { TRUSTREC rec; assert( db_fd == -1 ); db_fd = open (db_name, O_RDWR | MY_O_BINARY ); if (db_fd == -1 && (errno == EACCES #ifdef EROFS || errno == EROFS #endif ) ) { db_fd = open (db_name, O_RDONLY | MY_O_BINARY ); if (db_fd != -1 && !opt.quiet) log_info (_("NOTE: trustdb not writable\n")); } if ( db_fd == -1 ) log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); register_secured_file (db_name); /* Read the version record. */ if (tdbio_read_record (0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb\n"), db_name ); }
byte tdbio_read_model(void) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); return vr.r.ver.trust_model; }
/**************** * Return the nextstamp value. */ ulong tdbio_read_nextcheck () { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); return vr.r.ver.nextcheck; }
static void open_db() { byte buf[10]; int n; TRUSTREC rec; assert( db_fd == -1 ); if (!lockhandle ) lockhandle = create_dotlock( db_name ); if (!lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); #ifdef __riscos__ if (make_dotlock( lockhandle, -1 ) ) log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ db_fd = open (db_name, O_RDWR | MY_O_BINARY ); if (db_fd == -1 && (errno == EACCES #ifdef EROFS || errno == EROFS #endif ) ) { db_fd = open (db_name, O_RDONLY | MY_O_BINARY ); if (db_fd != -1) log_info (_("NOTE: trustdb not writable\n")); } if ( db_fd == -1 ) log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); register_secured_file (db_name); /* check whether we need to do a version migration */ do n = read (db_fd, buf, 5); while (n==-1 && errno == EINTR); if (n == 5 && !memcmp (buf, "\x01gpg\x02", 5)) { migrate_from_v2 (); } /* read the version record */ if (tdbio_read_record (0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb\n"), db_name ); }
/* * Open the trustdb. This may only be called if it has not yet been * opened and after a successful call to tdbio_set_dbname. On return * the trustdb handle (DB_FD) is guaranteed to be open. */ static void open_db () { TRUSTREC rec; assert( db_fd == -1 ); #ifdef HAVE_W32CE_SYSTEM { DWORD prevrc = 0; wchar_t *wname = utf8_to_wchar (db_name); if (wname) { db_fd = (int)CreateFile (wname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); xfree (wname); } if (db_fd == -1) log_fatal ("can't open '%s': %d, %d\n", db_name, (int)prevrc, (int)GetLastError ()); } #else /*!HAVE_W32CE_SYSTEM*/ db_fd = open (db_name, O_RDWR | MY_O_BINARY ); if (db_fd == -1 && (errno == EACCES #ifdef EROFS || errno == EROFS #endif ) ) { /* Take care of read-only trustdbs. */ db_fd = open (db_name, O_RDONLY | MY_O_BINARY ); if (db_fd != -1 && !opt.quiet) log_info (_("Note: trustdb not writable\n")); } if ( db_fd == -1 ) log_fatal( _("can't open '%s': %s\n"), db_name, strerror(errno) ); #endif /*!HAVE_W32CE_SYSTEM*/ register_secured_file (db_name); /* Read the version record. */ if (tdbio_read_record (0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb\n"), db_name ); }
/**************** * Return the record number of the trusthash tbl or create a new one. */ static ulong get_trusthashrec(void) { static ulong trusthashtbl; /* record number of the trust hashtable */ if( !trusthashtbl ) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); if( !vr.r.ver.trusthashtbl ) create_hashtable( &vr, 0 ); trusthashtbl = vr.r.ver.trusthashtbl; } return trusthashtbl; }
/* Return true when the stamp was actually changed. */ int tdbio_write_nextcheck (ulong stamp) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); if (vr.r.ver.nextcheck == stamp) return 0; vr.r.ver.nextcheck = stamp; rc = tdbio_write_record( &vr ); if( rc ) log_fatal( _("%s: error writing version record: %s\n"), db_name, g10_errstr(rc) ); return 1; }
/* Caller must sync */ int tdbio_update_version_record (void) { TRUSTREC rec; int rc; memset( &rec, 0, sizeof rec ); rc=tdbio_read_record( 0, &rec, RECTYPE_VER); if(rc==0) { rec.r.ver.created = make_timestamp(); rec.r.ver.marginals = opt.marginals_needed; rec.r.ver.completes = opt.completes_needed; rec.r.ver.cert_depth = opt.max_cert_depth; rec.r.ver.trust_model = opt.trust_model; rc=tdbio_write_record(&rec); } return rc; }
/* * Dump the entire trustdb to FP or only the entries of one key. */ void list_trustdb (ctrl_t ctrl, estream_t fp, const char *username) { TRUSTREC rec; (void)username; init_trustdb (ctrl, 0); /* For now we ignore the user ID. */ if (1) { ulong recnum; int i; es_fprintf (fp, "TrustDB: %s\n", tdbio_get_dbname ()); for (i = 9 + strlen (tdbio_get_dbname()); i > 0; i-- ) es_fputc ('-', fp); es_putc ('\n', fp); for (recnum=0; !tdbio_read_record (recnum, &rec, 0); recnum++) tdbio_dump_record (&rec, fp); } }
int tdbio_db_matches_options() { static int yes_no = -1; if( yes_no == -1 ) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); yes_no = vr.r.ver.marginals == opt.marginals_needed && vr.r.ver.completes == opt.completes_needed && vr.r.ver.cert_depth == opt.max_cert_depth && vr.r.ver.trust_model == opt.trust_model; } return yes_no; }
/**************** * Lookup a record via the hashtable tablewith key/keylen and return the * result in rec. cmp() should return if the record is the desired one. * Returns -1 if not found, 0 if found or another errocode */ static int lookup_hashtable( ulong table, const byte *key, size_t keylen, int (*cmpfnc)(void*, const TRUSTREC *), void *cmpdata, TRUSTREC *rec ) { int rc; ulong hashrec, item; int msb; int level=0; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL ); if( rc ) { log_error("lookup_hashtable failed: %s\n", g10_errstr(rc) ); return rc; } item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) return -1; /* not found */ rc = tdbio_read_record( item, rec, 0 ); if( rc ) { log_error( "hashtable read failed: %s\n", g10_errstr(rc) ); return rc; } if( rec->rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error("hashtable has invalid indirections\n"); return G10ERR_TRUSTDB; } goto next_level; } else if( rec->rectype == RECTYPE_HLST ) { for(;;) { int i; for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec->r.hlst.rnum[i] ) { TRUSTREC tmp; rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 ); if( rc ) { log_error( "lookup_hashtable: read item failed: %s\n", g10_errstr(rc) ); return rc; } if( (*cmpfnc)( cmpdata, &tmp ) ) { *rec = tmp; return 0; } } } if( rec->r.hlst.next ) { rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST ); if( rc ) { log_error( "lookup_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else return -1; /* not found */ } } if( (*cmpfnc)( cmpdata, rec ) ) return 0; /* really found */ return -1; /* no: not found */ }
/* * Update a hashtable in the trustdb. TABLE gives the start of the * table, KEY and KEYLEN are the key, NEWRECNUM is the record number * to insert into the table. * * Return: 0 on success or an error code. */ static int upd_hashtable (ulong table, byte *key, int keylen, ulong newrecnum) { TRUSTREC lastrec, rec; ulong hashrec, item; int msb; int level = 0; int rc, i; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record (hashrec, &rec, RECTYPE_HTBL); if (rc) { log_error ("upd_hashtable: read failed: %s\n", gpg_strerror (rc)); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if (!item) /* Insert a new item into the hash table. */ { rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum; rc = tdbio_write_record (&rec); if (rc) { log_error ("upd_hashtable: write htbl failed: %s\n", gpg_strerror (rc)); return rc; } } else if (item != newrecnum) /* Must do an update. */ { lastrec = rec; rc = tdbio_read_record (item, &rec, 0); if (rc) { log_error ("upd_hashtable: read item failed: %s\n", gpg_strerror (rc)); return rc; } if (rec.rectype == RECTYPE_HTBL) { hashrec = item; level++; if (level >= keylen) { log_error ("hashtable has invalid indirections.\n"); return GPG_ERR_TRUSTDB; } goto next_level; } else if (rec.rectype == RECTYPE_HLST) /* Extend the list. */ { /* Check whether the key is already in this list. */ for (;;) { for (i=0; i < ITEMS_PER_HLST_RECORD; i++) { if (rec.r.hlst.rnum[i] == newrecnum) { return 0; /* Okay, already in the list. */ } } if (rec.r.hlst.next) { rc = tdbio_read_record (rec.r.hlst.next, &rec, RECTYPE_HLST); if (rc) { log_error ("upd_hashtable: read hlst failed: %s\n", gpg_strerror (rc) ); return rc; } } else break; /* key is not in the list */ } /* Find the next free entry and put it in. */ for (;;) { for (i=0; i < ITEMS_PER_HLST_RECORD; i++) { if (!rec.r.hlst.rnum[i]) { /* Empty slot found. */ rec.r.hlst.rnum[i] = newrecnum; rc = tdbio_write_record (&rec); if (rc) log_error ("upd_hashtable: write hlst failed: %s\n", gpg_strerror (rc)); return rc; /* Done. */ } } if (rec.r.hlst.next) { /* read the next reord of the list. */ rc = tdbio_read_record (rec.r.hlst.next, &rec, RECTYPE_HLST); if (rc) { log_error ("upd_hashtable: read hlst failed: %s\n", gpg_strerror (rc)); return rc; } } else { /* Append a new record to the list. */ rec.r.hlst.next = item = tdbio_new_recnum (); rc = tdbio_write_record (&rec); if (rc) { log_error ("upd_hashtable: write hlst failed: %s\n", gpg_strerror (rc)); return rc; } memset (&rec, 0, sizeof rec); rec.rectype = RECTYPE_HLST; rec.recnum = item; rec.r.hlst.rnum[0] = newrecnum; rc = tdbio_write_record (&rec); if (rc) log_error ("upd_hashtable: write ext hlst failed: %s\n", gpg_strerror (rc)); return rc; /* Done. */ } } /* end loop over list slots */ } else if (rec.rectype == RECTYPE_TRUST) /* Insert a list record. */ { if (rec.recnum == newrecnum) { return 0; } item = rec.recnum; /* Save number of key record. */ memset (&rec, 0, sizeof rec); rec.rectype = RECTYPE_HLST; rec.recnum = tdbio_new_recnum (); rec.r.hlst.rnum[0] = item; /* Old key record */ rec.r.hlst.rnum[1] = newrecnum; /* and new key record */ rc = tdbio_write_record (&rec); if (rc) { log_error( "upd_hashtable: write new hlst failed: %s\n", gpg_strerror (rc) ); return rc; } /* Update the hashtable record. */ lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum; rc = tdbio_write_record (&lastrec); if (rc) log_error ("upd_hashtable: update htbl failed: %s\n", gpg_strerror (rc)); return rc; /* Ready. */ } else { log_error ("hashtbl %lu: %lu/%d points to an invalid record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); if (opt.verbose > 1) list_trustdb (es_stderr, NULL); return GPG_ERR_TRUSTDB; } } return 0; }
int tdbio_set_dbname( const char *new_dbname, int create ) { char *fname; static int initialized = 0; if( !initialized ) { atexit( cleanup ); initialized = 1; } if(new_dbname==NULL) fname=make_filename(opt.homedir,"trustdb" EXTSEP_S "gpg", NULL); else if (*new_dbname != DIRSEP_C ) { if (strchr(new_dbname, DIRSEP_C) ) fname = make_filename (new_dbname, NULL); else fname = make_filename (opt.homedir, new_dbname, NULL); } else fname = xstrdup (new_dbname); if( access( fname, R_OK ) ) { if( errno != ENOENT ) { log_error( _("can't access `%s': %s\n"), fname, strerror(errno) ); xfree(fname); return G10ERR_TRUSTDB; } if( create ) { FILE *fp; TRUSTREC rec; int rc; char *p = strrchr( fname, DIRSEP_C ); mode_t oldmask; assert(p); *p = 0; if( access( fname, F_OK ) ) { try_make_homedir( fname ); log_fatal( _("%s: directory does not exist!\n"), fname ); } *p = DIRSEP_C; xfree(db_name); db_name = fname; #ifdef __riscos__ if( !lockhandle ) lockhandle = create_dotlock( db_name ); if( !lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); if( make_dotlock( lockhandle, -1 ) ) log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ oldmask=umask(077); if (is_secured_filename (fname)) { fp = NULL; errno = EPERM; } else fp =fopen( fname, "wb" ); umask(oldmask); if( !fp ) log_fatal( _("can't create `%s': %s\n"), fname, strerror(errno) ); fclose(fp); db_fd = open( db_name, O_RDWR | MY_O_BINARY ); if( db_fd == -1 ) log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); #ifndef __riscos__ if( !lockhandle ) lockhandle = create_dotlock( db_name ); if( !lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); #endif /* !__riscos__ */ rc = create_version_record (); if( rc ) log_fatal( _("%s: failed to create version record: %s"), fname, g10_errstr(rc)); /* and read again to check that we are okay */ if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb created\n"), db_name ); if( !opt.quiet ) log_info(_("%s: trustdb created\n"), db_name); return 0; } } xfree(db_name); db_name = fname; return 0; }
/**************** * create a new record and return its record number */ ulong tdbio_new_recnum() { off_t offset; ulong recnum; TRUSTREC vr, rec; int rc; /* look for unused records */ rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); if( vr.r.ver.firstfree ) { recnum = vr.r.ver.firstfree; rc = tdbio_read_record( recnum, &rec, RECTYPE_FREE ); if( rc ) { log_error( _("%s: error reading free record: %s\n"), db_name, g10_errstr(rc) ); return rc; } /* update dir record */ vr.r.ver.firstfree = rec.r.free.next; rc = tdbio_write_record( &vr ); if( rc ) { log_error( _("%s: error writing dir record: %s\n"), db_name, g10_errstr(rc) ); return rc; } /*zero out the new record */ memset( &rec, 0, sizeof rec ); rec.rectype = 0; /* unused record */ rec.recnum = recnum; rc = tdbio_write_record( &rec ); if( rc ) log_fatal(_("%s: failed to zero a record: %s\n"), db_name, g10_errstr(rc)); } else { /* not found, append a new record */ offset = lseek( db_fd, 0, SEEK_END ); if( offset == -1 ) log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) ); recnum = offset / TRUST_RECORD_LEN; assert(recnum); /* this is will never be the first record */ /* we must write a record, so that the next call to this function * returns another recnum */ memset( &rec, 0, sizeof rec ); rec.rectype = 0; /* unused record */ rec.recnum = recnum; 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(_("%s: failed to append a record: %s\n"), db_name, g10_errstr(rc)); } return recnum ; }
/* * Set the file name for the trustdb to NEW_DBNAME and if CREATE is * true create that file. If NEW_DBNAME is NULL a default name is * used, if the it does not contain a path component separator ('/') * the global GnuPG home directory is used. * * Returns: 0 on success or an error code. * * On the first call this function registers an atexit handler. * */ int tdbio_set_dbname (const char *new_dbname, int create, int *r_nofile) { char *fname, *p; struct stat statbuf; static int initialized = 0; int save_slash; if (!initialized) { atexit (cleanup); initialized = 1; } *r_nofile = 0; if (!new_dbname) { fname = make_filename (opt.homedir, "trustdb" EXTSEP_S GPGEXT_GPG, NULL); } else if (*new_dbname != DIRSEP_C ) { if (strchr (new_dbname, DIRSEP_C)) fname = make_filename (new_dbname, NULL); else fname = make_filename (opt.homedir, new_dbname, NULL); } else { fname = xstrdup (new_dbname); } xfree (db_name); db_name = fname; /* Quick check for (likely) case where there already is a * trustdb.gpg. This check is not required in theory, but it helps * in practice avoiding costly operations of preparing and taking * the lock. */ if (!stat (fname, &statbuf) && statbuf.st_size > 0) { /* OK, we have the valid trustdb.gpg already. */ return 0; } else if (!create) { *r_nofile = 1; return 0; } /* Here comes: No valid trustdb.gpg AND CREATE==1 */ /* * Make sure the directory exists. This should be done before * acquiring the lock, which assumes the existence of the directory. */ p = strrchr (fname, DIRSEP_C); #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *pp = strrchr (fname, '/'); if (!p || pp > p) p = pp; } #endif /*HAVE_W32_SYSTEM*/ assert (p); save_slash = *p; *p = 0; if (access (fname, F_OK)) { try_make_homedir (fname); if (access (fname, F_OK)) log_fatal (_("%s: directory does not exist!\n"), fname); } *p = save_slash; take_write_lock (); if (access (fname, R_OK)) { FILE *fp; TRUSTREC rec; int rc; mode_t oldmask; #ifdef HAVE_W32CE_SYSTEM /* We know how the cegcc implementation of access works ;-). */ if (GetLastError () == ERROR_FILE_NOT_FOUND) gpg_err_set_errno (ENOENT); else gpg_err_set_errno (EIO); #endif /*HAVE_W32CE_SYSTEM*/ if (errno != ENOENT) log_fatal ( _("can't access '%s': %s\n"), fname, strerror (errno)); oldmask = umask (077); if (is_secured_filename (fname)) { fp = NULL; gpg_err_set_errno (EPERM); } else fp = fopen (fname, "wb"); umask(oldmask); if (!fp) log_fatal (_("can't create '%s': %s\n"), fname, strerror (errno)); fclose (fp); db_fd = open (db_name, O_RDWR | MY_O_BINARY); if (db_fd == -1) log_fatal (_("can't open '%s': %s\n"), db_name, strerror (errno)); rc = create_version_record (); if (rc) log_fatal (_("%s: failed to create version record: %s"), fname, gpg_strerror (rc)); /* Read again to check that we are okay. */ if (tdbio_read_record (0, &rec, RECTYPE_VER)) log_fatal (_("%s: invalid trustdb created\n"), db_name); if (!opt.quiet) log_info (_("%s: trustdb created\n"), db_name); } release_write_lock (); return 0; }
/**************** * Update a hashtable. * table gives the start of the table, key and keylen is the key, * newrecnum is the record number to insert. */ static int upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) { TRUSTREC lastrec, rec; ulong hashrec, item; int msb; int level=0; int rc, i; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error("upd_hashtable: read failed: %s\n", g10_errstr(rc) ); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) { /* insert a new item into the hash table */ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) { log_error("upd_hashtable: write htbl failed: %s\n", g10_errstr(rc) ); return rc; } } else if( item != newrecnum ) { /* must do an update */ lastrec = rec; rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { 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( "hashtable has invalid indirections.\n"); return G10ERR_TRUSTDB; } goto next_level; } else if( rec.rectype == RECTYPE_HLST ) { /* extend list */ /* see whether the key is already in this list */ for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec.r.hlst.rnum[i] == newrecnum ) { return 0; /* okay, already in the list */ } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { log_error( "upd_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else break; /* not there */ } /* find the next free entry and put it in */ for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( !rec.r.hlst.rnum[i] ) { rec.r.hlst.rnum[i] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); return rc; /* done */ } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST ); if( rc ) { log_error( "upd_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else { /* add a new list record */ rec.r.hlst.next = item = tdbio_new_recnum(); rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); return rc; } memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HLST; rec.recnum = item; rec.r.hlst.rnum[0] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) 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_TRUST ) { /* insert a list record */ if( rec.recnum == newrecnum ) { return 0; } item = rec.recnum; /* save number of key record */ memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HLST; rec.recnum = tdbio_new_recnum(); rec.r.hlst.rnum[0] = item; /* old keyrecord */ rec.r.hlst.rnum[1] = newrecnum; /* and new one */ rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write new hlst failed: %s\n", g10_errstr(rc) ); return rc; } /* update the hashtable record */ lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum; rc = tdbio_write_record( &lastrec ); if( rc ) log_error( "upd_hashtable: update htbl failed: %s\n", g10_errstr(rc) ); return rc; /* ready */ } else { log_error( "hashtbl %lu: %lu/%d points to an invalid record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); list_trustdb(NULL); return G10ERR_TRUSTDB; } } return 0; }
int tdbio_set_dbname( const char *new_dbname, int create, int *r_nofile) { char *fname; struct stat statbuf; static int initialized = 0; if( !initialized ) { atexit( cleanup ); initialized = 1; } *r_nofile = 0; if(new_dbname==NULL) fname=make_filename(opt.homedir,"trustdb" EXTSEP_S "gpg", NULL); else if (*new_dbname != DIRSEP_C ) { if (strchr(new_dbname, DIRSEP_C) ) fname = make_filename (new_dbname, NULL); else fname = make_filename (opt.homedir, new_dbname, NULL); } else fname = xstrdup (new_dbname); xfree (db_name); db_name = fname; /* * Quick check for (likely) case where there is trustdb.gpg * already. This check is not required in theory, but it helps in * practice, avoiding costly operations of preparing and taking * the lock. */ if (stat (fname, &statbuf) == 0 && statbuf.st_size > 0) /* OK, we have the valid trustdb.gpg already. */ return 0; take_write_lock (); if( access( fname, R_OK ) ) { if( errno != ENOENT ) log_fatal( _("can't access `%s': %s\n"), fname, strerror(errno) ); if (!create) *r_nofile = 1; else { FILE *fp; TRUSTREC rec; int rc; char *p = strrchr( fname, DIRSEP_C ); mode_t oldmask; int save_slash; #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *pp = strrchr (fname, '/'); if (!p || pp > p) p = pp; } #endif /*HAVE_W32_SYSTEM*/ assert (p); save_slash = *p; *p = 0; if( access( fname, F_OK ) ) { try_make_homedir( fname ); if (access (fname, F_OK )) log_fatal (_("%s: directory does not exist!\n"), fname); } *p = save_slash; oldmask=umask(077); if (is_secured_filename (fname)) { fp = NULL; errno = EPERM; } else fp =fopen( fname, "wb" ); umask(oldmask); if( !fp ) log_fatal( _("can't create `%s': %s\n"), fname, strerror(errno) ); fclose(fp); db_fd = open( db_name, O_RDWR | MY_O_BINARY ); if( db_fd == -1 ) log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); rc = create_version_record (); if( rc ) log_fatal( _("%s: failed to create version record: %s"), fname, g10_errstr(rc)); /* and read again to check that we are okay */ if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb created\n"), db_name ); if( !opt.quiet ) log_info(_("%s: trustdb created\n"), db_name); } } release_write_lock (); return 0; }
/**************** * Drop an entry from a hashtable * table gives the start of the table, key and keylen is the key, */ static int drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) { TRUSTREC rec; ulong hashrec, item; int msb; int level=0; int rc, i; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error("drop_from_hashtable: read failed: %s\n", g10_errstr(rc) ); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) /* not found - forget about it */ return 0; if( item == recnum ) { /* tables points direct to the record */ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = 0; rc = tdbio_write_record( &rec ); if( rc ) log_error("drop_from_hashtable: write htbl failed: %s\n", g10_errstr(rc) ); return rc; } rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "drop_from_hashtable: read item failed: %s\n", g10_errstr(rc) ); return rc; } if( rec.rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); return G10ERR_TRUSTDB; } goto next_level; } if( rec.rectype == RECTYPE_HLST ) { for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec.r.hlst.rnum[i] == recnum ) { rec.r.hlst.rnum[i] = 0; /* drop */ rc = tdbio_write_record( &rec ); if( rc ) log_error("drop_from_hashtable: write htbl failed: %s\n", g10_errstr(rc) ); return rc; } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { log_error( "drop_from_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else return 0; /* key not in table */ } } log_error( "hashtbl %lu: %lu/%d points to wrong record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); return G10ERR_TRUSTDB; }
/* * Create a new record and return its record number. */ ulong tdbio_new_recnum () { off_t offset; ulong recnum; TRUSTREC vr, rec; int rc; /* Look for unused records. */ rc = tdbio_read_record (0, &vr, RECTYPE_VER); if (rc) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc)); if (vr.r.ver.firstfree) { recnum = vr.r.ver.firstfree; rc = tdbio_read_record (recnum, &rec, RECTYPE_FREE); if (rc) { log_error (_("%s: error reading free record: %s\n"), db_name, gpg_strerror (rc)); return rc; } /* Update dir record. */ vr.r.ver.firstfree = rec.r.free.next; rc = tdbio_write_record (&vr); if (rc) { log_error (_("%s: error writing dir record: %s\n"), db_name, gpg_strerror (rc)); return rc; } /* Zero out the new record. */ memset (&rec, 0, sizeof rec); rec.rectype = 0; /* Mark as unused record (actually already done my the memset). */ rec.recnum = recnum; rc = tdbio_write_record (&rec); if (rc) log_fatal (_("%s: failed to zero a record: %s\n"), db_name, gpg_strerror (rc)); } else /* Not found - append a new record. */ { offset = lseek (db_fd, 0, SEEK_END); if (offset == (off_t)(-1)) log_fatal ("trustdb: lseek to end failed: %s\n", strerror (errno)); recnum = offset / TRUST_RECORD_LEN; assert (recnum); /* this is will never be the first record */ /* We must write a record, so that the next call to this * function returns another recnum. */ memset (&rec, 0, sizeof rec); rec.rectype = 0; /* unused record */ rec.recnum = recnum; rc = 0; if (lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET) == -1) { rc = gpg_error_from_syserror (); log_error (_("trustdb rec %lu: lseek failed: %s\n"), recnum, strerror (errno)); } else { int n; n = write (db_fd, &rec, TRUST_RECORD_LEN); if (n != TRUST_RECORD_LEN) { rc = gpg_error_from_syserror (); log_error (_("trustdb rec %lu: write failed (n=%d): %s\n"), recnum, n, strerror (errno)); } } if (rc) log_fatal (_("%s: failed to append a record: %s\n"), db_name, gpg_strerror (rc)); } return recnum ; }