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?
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;