g10: Fix another race condition for trustdb access.
[gnupg.git] / g10 / tdbio.c
index 3cc8bd3..e27788e 100644 (file)
@@ -23,7 +23,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -120,6 +119,7 @@ static int in_transaction;
 
 \f
 static void open_db (void);
+static void create_hashtable (TRUSTREC *vr, int type);
 
 
 \f
@@ -317,7 +317,7 @@ put_record_into_cache (ulong recno, const char *data)
        }
 
       /* Now put into the cache.  */
-      assert (unused);
+      log_assert (unused);
       r = unused;
       r->flags.used = 1;
       r->recno = recno;
@@ -383,7 +383,7 @@ put_record_into_cache (ulong recno, const char *data)
       release_write_lock ();
 
       /* Now put into the cache.  */
-      assert (unused);
+      log_assert (unused);
       r = unused;
       r->flags.used = 1;
       r->recno = recno;
@@ -583,8 +583,13 @@ create_version_record (void)
   rec.rectype = RECTYPE_VER;
   rec.recnum = 0;
   rc = tdbio_write_record (&rec);
+
   if (!rc)
     tdbio_sync ();
+
+  if (!rc)
+    create_hashtable (&rec, 0);
+
   return rc;
 }
 
@@ -603,9 +608,10 @@ create_version_record (void)
 int
 tdbio_set_dbname (const char *new_dbname, int create, int *r_nofile)
 {
-  char *fname;
+  char *fname, *p;
   struct stat statbuf;
   static int initialized = 0;
+  int save_slash;
 
   if (!initialized)
     {
@@ -617,14 +623,15 @@ tdbio_set_dbname (const char *new_dbname, int create, int *r_nofile)
 
   if (!new_dbname)
     {
-      fname = make_filename (opt.homedir, "trustdb" EXTSEP_S GPGEXT_GPG, NULL);
+      fname = make_filename (gnupg_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);
+        fname = make_filename (gnupg_homedir (), new_dbname, NULL);
     }
   else
     {
@@ -643,11 +650,48 @@ tdbio_set_dbname (const char *new_dbname, int create, int *r_nofile)
       /* 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*/
+  log_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)
@@ -658,66 +702,34 @@ tdbio_set_dbname (const char *new_dbname, int create, int *r_nofile)
       if (errno != ENOENT)
         log_fatal ( _("can't access '%s': %s\n"), fname, strerror (errno));
 
-      if (!create)
-        *r_nofile = 1;
-      else
+      oldmask = umask (077);
+      if (is_secured_filename (fname))
         {
-          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;
-              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);
+          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));
+      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));
+      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);
+      /* 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);
-       }
+      if (!opt.quiet)
+        log_info (_("%s: trustdb created\n"), db_name);
     }
 
   release_write_lock ();
@@ -745,7 +757,7 @@ open_db ()
 {
   TRUSTREC rec;
 
-  assert( db_fd == -1 );
+  log_assert( db_fd == -1 );
 
 #ifdef HAVE_W32CE_SYSTEM
   {
@@ -805,7 +817,7 @@ create_hashtable( TRUSTREC *vr, int type )
   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. */
+  log_assert (recnum); /* This is will never be the first record. */
 
   if (!type)
     vr->r.ver.trusthashtbl = recnum;
@@ -951,8 +963,6 @@ get_trusthashrec(void)
       if (rc)
         log_fatal (_("%s: error reading version record: %s\n"),
                    db_name, gpg_strerror (rc) );
-      if (!vr.r.ver.trusthashtbl)
-        create_hashtable (&vr, 0);
 
       trusthashtbl = vr.r.ver.trusthashtbl;
     }
@@ -1764,7 +1774,7 @@ tdbio_new_recnum ()
       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 */
+      log_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);