Question

I have a program that can use either GDBM or Kyoto Cabinet as a DBM library. I've written some functions to abstract away the difference between the two and I pass around void pointers in place of the database file (GDBM_FILE in the case of GDBM and KCDB * in the case of Kyoto Cabinet). Everything with KC works fine, however when I try using the GDBM backend, the database somehow gets "lost" in passing it around to different functions. When I try to cast the pointer and dereference it, and then pass that to one of the GDBM functions, it segfaults and in a debugger it complains about the db file not existing.

Here's some code that can reproduce the problem:

#include <gdbm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

void *
dbopen (void)
{
  printf ("opening\n");
  GDBM_FILE database = gdbm_open ("test.db", 512, GDBM_WRCREAT,
                                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, NULL);
  if (!database)
    {
      printf ("cannot open database\n");
      return NULL;
    }
  void *db = &database;
  return db;
}

void
dbclose (void *db, void *foo)
{
  printf ("%d\n", *(int *)foo);
  GDBM_FILE database = *(GDBM_FILE *)db;
  if (!database)
    {
      printf ("database lost\n");
      return;
    }
  printf ("closing\n");
  gdbm_close (database);
  return;
}

void
fun (void *db, void *foo)
{
  GDBM_FILE database = *(GDBM_FILE *)db;
  datum key, value;
  int bar = *(int *)foo;  /* and yet, if I remove this line and the next */
  printf ("%d\n", bar);   /* one, it works! */
  printf ("%d\n", *(int *)foo);
  if (!database)
    printf ("no db?\n");
  key.dptr = "baz";
  key.dsize = 4;
  value.dptr = "quux";
  value.dsize = 4;
  printf ("storing\n");
  gdbm_store (database, key, value, GDBM_REPLACE);
  printf ("all done\n");
  return;
}

int
main (void)
{
  int foo = 5;
  void *dbp = dbopen ();
  void *foop = &foo;
  fun (dbp, foop);
  dbclose (dbp, foop);
}

When I run that code, it segfaults with the "file not found" error on the call to gdbm_close(). As the comments indicate, if I don't explicitly store the other void pointer to an int, then the program runs just fine.

In my actual program, it gets "lost" when I call gdbm_store(), and it's the only void pointer I'm using (in this test program, the foo pointer was just supposed to be a sanity check).

I'm sure there is something in the vagaries of memory allocation in C that I'm forgetting or not understanding. Why does the void pointer referencing to the GDBM database get lost/corrupted when the void pointer referencing the int does not? Why, when I don't try to store the dereferenced void pointer foo to an int, does it suddenly work?

Was it helpful?

Solution

Your issue is in getting the address of the pointer returned from gdbm_open instead if its value. The confusing part comes from the definition of GDBM_FILE - its a pointer to a struct:

typedef struct {int dummy[10];} *GDBM_FILE;

You get its address, that is the location of the pointer, not the pointer value:

void *db = &database;

The problem can be solved by replacing the conversion lines with:

void *db = (void *)database;

and

GDBM_FILE database = (GDBM_FILE)db;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top