aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac31
-rw-r--r--doc/gdbm.texi7
-rw-r--r--src/Makefile.am5
-rw-r--r--src/bucket.c19
-rw-r--r--src/debug.c155
-rw-r--r--src/falloc.c20
-rw-r--r--src/findkey.c17
-rw-r--r--src/gdbm.h.in4
-rw-r--r--src/gdbmdefs.h20
-rw-r--r--src/gdbmopen.c14
-rw-r--r--src/gdbmreorg.c2
-rw-r--r--src/gdbmstore.c10
-rw-r--r--src/gdbmtool.c4
-rw-r--r--src/recover.c120
-rw-r--r--src/update.c15
-rw-r--r--tests/Makefile.am17
-rw-r--r--tests/gtload.c177
17 files changed, 532 insertions, 105 deletions
diff --git a/configure.ac b/configure.ac
index 1424ea0..da5b938 100644
--- a/configure.ac
+++ b/configure.ac
@@ -173,15 +173,46 @@ fi
AM_CONDITIONAL([GDBM_COND_READLINE], [test "$status_readline" = "yes"])
+# Additional debugging
+AC_ARG_ENABLE([debug],
+ AC_HELP_STRING([--enable-debug],
+ [provide additional debugging functions]),
+ [status_debug=$withval],
+ [status_debug=no])
+
+AM_CONDITIONAL([GDBM_COND_DEBUG_ENABLE], [test "$status_debug" = "yes"])
+
# Initialize the test suite.
AC_CONFIG_TESTDIR(tests)
AC_CONFIG_FILES([tests/Makefile tests/atlocal po/Makefile.in])
AM_MISSING_PROG([AUTOM4TE], [autom4te])
+AC_CONFIG_COMMANDS([status],[
+cat <<EOF
+
+*******************************************************************
+GDBM settings summary:
+
+Compatibility library ......................... $status_compat
+Compatibility export tool ..................... $status_export
+Memory mapped I/O ............................. $mapped_io
+GNU Readline .................................. $status_readline
+Debugging functions ........................... $status_debug
+*******************************************************************
+
+EOF
+],
+[status_compat=$want_compat
+status_export=$want_export
+mapped_io=$mapped_io
+status_readline=$status_readline
+status_debug=$status_debug])
+
AC_CONFIG_FILES([Makefile
src/Makefile
src/gdbm.h
doc/Makefile
compat/Makefile
export/Makefile])
+
AC_OUTPUT
diff --git a/doc/gdbm.texi b/doc/gdbm.texi
index 866a672..7f93046 100644
--- a/doc/gdbm.texi
+++ b/doc/gdbm.texi
@@ -1135,6 +1135,13 @@ The caller is responsible for freeing that memory when no longer needed.
@end deftypecv
@end deftypefn
+@kwindex GDBM_RCVR_FORCE
+By default, @code{gdbm_recovery} first checks the database fo
+inconsistencies and attempts recovery only if some were found.
+The special flag bit @code{GDBM_RCVR_FORCE} instructs
+@code{gdbm_recovery} to omit this check and to force recovery
+unconditionally.
+
@node Options
@chapter Setting options
@cindex database options
diff --git a/src/Makefile.am b/src/Makefile.am
index af48a1a..0eed6e3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -66,6 +66,11 @@ libgdbm_la_SOURCES = \
update.c\
version.c
+if GDBM_COND_DEBUG_ENABLE
+ libgdbm_la_SOURCES += debug.c
+ AM_CPPFLAGS += -DGDBM_DEBUG_ENABLE=1
+endif
+
libgdbm_la_LDFLAGS = -version-info $(VI_CURRENT):$(VI_REVISION):$(VI_AGE)
noinst_LIBRARIES = libgdbmapp.a
diff --git a/src/bucket.c b/src/bucket.c
index 7eb3898..cd9575c 100644
--- a/src/bucket.c
+++ b/src/bucket.c
@@ -98,7 +98,8 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
dbf->cache_entry->ca_changed = FALSE;
/* Read the bucket. */
- file_pos = __lseek (dbf, bucket_adr, SEEK_SET);
+ file_pos = GDBM_DEBUG_OVERRIDE ("_gdbm_get_bucket:seek-failure",
+ __lseek (dbf, bucket_adr, SEEK_SET));
if (file_pos != bucket_adr)
{
_gdbm_fatal (dbf, _("lseek error"));
@@ -106,7 +107,8 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
return -1;
}
- rc = _gdbm_full_read (dbf, dbf->bucket, dbf->header->bucket_size);
+ rc = GDBM_DEBUG_OVERRIDE ("_gdbm_get_bucket:read-failure",
+ _gdbm_full_read (dbf, dbf->bucket, dbf->header->bucket_size));
if (rc)
{
_gdbm_fatal (dbf, gdbm_strerror (rc));
@@ -248,7 +250,8 @@ _gdbm_split_bucket (GDBM_FILE dbf, int next_insert)
dir_adr = _gdbm_alloc (dbf, dir_size);
if (dir_adr == 0)
return -1;
- new_dir = (off_t *) malloc (dir_size);
+ new_dir = GDBM_DEBUG_ALLOC ("_gdbm_split_bucket:malloc-failure",
+ malloc (dir_size));
if (new_dir == NULL)
{
gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, TRUE);
@@ -286,7 +289,7 @@ _gdbm_split_bucket (GDBM_FILE dbf, int next_insert)
while (bucket[select]->h_table[elem_loc].hash_value != -1)
elem_loc = (elem_loc + 1) % dbf->header->bucket_elems;
bucket[select]->h_table[elem_loc] = *old_el;
- bucket[select]->count += 1;
+ bucket[select]->count++;
}
/* Allocate avail space for the bucket[1]. */
@@ -378,15 +381,17 @@ _gdbm_write_bucket (GDBM_FILE dbf, cache_elem *ca_entry)
{
int rc;
off_t file_pos; /* The return value for lseek. */
-
- file_pos = __lseek (dbf, ca_entry->ca_adr, SEEK_SET);
+
+ file_pos = GDBM_DEBUG_OVERRIDE ("_gdbm_write_bucket:seek-failure",
+ __lseek (dbf, ca_entry->ca_adr, SEEK_SET));
if (file_pos != ca_entry->ca_adr)
{
gdbm_set_errno (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
_gdbm_fatal (dbf, _("lseek error"));
return -1;
}
- rc = _gdbm_full_write (dbf, ca_entry->ca_bucket, dbf->header->bucket_size);
+ rc = GDBM_DEBUG_OVERRIDE ("_gdbm_write_bucket:write-failure",
+ _gdbm_full_write (dbf, ca_entry->ca_bucket, dbf->header->bucket_size));
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
diff --git a/src/debug.c b/src/debug.c
new file mode 100644
index 0000000..ceb0f02
--- /dev/null
+++ b/src/debug.c
@@ -0,0 +1,155 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright 2016 Free Software Foundation, Inc.
+
+ GDBM is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GDBM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "autoconf.h"
+#include "gdbmdefs.h"
+
+struct hook_list
+{
+ struct hook_list *next;
+ struct hook_list *prev;
+ char *id;
+ gdbm_debug_hook hook;
+ void *data;
+ int retval;
+};
+
+static struct hook_list *hook_head, *hook_tail;
+static struct hook_list *hook_recent;
+
+static struct hook_list *
+hook_lookup_or_install (char const *id, int install)
+{
+ struct hook_list *p;
+
+ for (p = hook_head; p; p = p->next)
+ {
+ int res = strcmp (p->id, id);
+ if (res == 0)
+ return p;
+ if (res > 0)
+ break;
+ }
+
+ if (install)
+ {
+ struct hook_list *elt = malloc (sizeof *elt);
+ if (!elt)
+ return NULL;
+ elt->id = strdup (id);
+ if (!elt->id)
+ {
+ SAVE_ERRNO (free (elt));
+ return NULL;
+ }
+ elt->hook = NULL;
+ elt->next = p;
+ if (p)
+ {
+ if (p->prev)
+ p->prev->next = elt;
+ else
+ hook_head = elt;
+ elt->prev = p->prev;
+ }
+ else
+ {
+ elt->prev = hook_tail;
+ if (hook_tail)
+ hook_tail->next = elt;
+ else
+ hook_head = elt;
+ hook_tail = elt;
+ }
+ return elt;
+ }
+
+ return NULL;
+}
+
+static struct hook_list *
+hook_lookup (char const *id)
+{
+ if (!(hook_recent && strcmp (hook_recent->id, id) == 0))
+ hook_recent = hook_lookup_or_install (id, FALSE);
+ return hook_recent;
+}
+
+static void
+hook_remove (char const *id)
+{
+ struct hook_list *p;
+
+ p = hook_lookup (id);
+ if (!p)
+ return;
+
+ hook_recent = NULL;
+
+ if (p->prev)
+ p->prev->next = p->next;
+ else
+ hook_head = p->next;
+
+ if (p->next)
+ p->next->prev = p->prev;
+ else
+ hook_tail = p->prev;
+
+ free (p->id);
+ free (p);
+}
+
+static int
+default_hook (char const *file, int line, char const *id, void *data)
+{
+ fprintf (stderr, "%s:%d: hit debug hook %s\n", file, line, id);
+ return 1;
+}
+
+void
+_gdbm_debug_hook_install (char const *id, gdbm_debug_hook hook, void *data)
+{
+ struct hook_list *p;
+
+ p = hook_lookup_or_install (id, TRUE);
+ p->hook = hook ? hook : default_hook;
+ p->data = data;
+}
+
+void
+_gdbm_debug_hook_remove (char const *id)
+{
+ hook_remove (id);
+}
+
+int
+_gdbm_debug_hook_check (char const *file, int line, char const *id)
+{
+ struct hook_list *p = hook_lookup (id);
+ if (p)
+ return p->retval = p->hook (file, line, id, p->data);
+ return 0;
+}
+
+int
+_gdbm_debug_hook_val (char const *id)
+{
+ struct hook_list *p = hook_lookup (id);
+ if (p)
+ return p->retval;
+ return 0;
+}
diff --git a/src/falloc.c b/src/falloc.c
index 58a9431..8e53a61 100644
--- a/src/falloc.c
+++ b/src/falloc.c
@@ -179,7 +179,8 @@ pop_avail_block (GDBM_FILE dbf)
+ sizeof (avail_block));
/* Allocate space for the block. */
- new_blk = (avail_block *) malloc (new_el.av_size);
+ new_blk = GDBM_DEBUG_ALLOC ("pop_avail_block:malloc-failure",
+ malloc (new_el.av_size));
if (new_blk == NULL)
{
gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, TRUE);
@@ -188,7 +189,8 @@ pop_avail_block (GDBM_FILE dbf)
}
/* Read the block. */
- file_pos = __lseek (dbf, new_el.av_adr, SEEK_SET);
+ file_pos = GDBM_DEBUG_OVERRIDE ("pop_avail_block:lseek-failure",
+ __lseek (dbf, new_el.av_adr, SEEK_SET));
if (file_pos != new_el.av_adr)
{
gdbm_set_errno (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
@@ -196,7 +198,8 @@ pop_avail_block (GDBM_FILE dbf)
return -1;
}
- rc = _gdbm_full_read (dbf, new_blk, new_el.av_size);
+ rc = GDBM_DEBUG_OVERRIDE ("pop_avail_block:read-failure",
+ _gdbm_full_read (dbf, new_blk, new_el.av_size));
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
@@ -275,7 +278,8 @@ push_avail_block (GDBM_FILE dbf)
av_adr = new_loc.av_adr;
/* Split the header block. */
- temp = (avail_block *) malloc (av_size);
+ temp = GDBM_DEBUG_ALLOC ("push_avail_block:malloc-failure",
+ malloc (av_size));
if (temp == NULL)
{
gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, TRUE);
@@ -304,15 +308,17 @@ push_avail_block (GDBM_FILE dbf)
_gdbm_free (dbf, new_loc.av_adr, new_loc.av_size);
/* Update the disk. */
- file_pos = __lseek (dbf, av_adr, SEEK_SET);
+ file_pos = GDBM_DEBUG_OVERRIDE ("push_avail_block:lseek-failure",
+ __lseek (dbf, av_adr, SEEK_SET));
if (file_pos != av_adr)
{
gdbm_set_errno (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
_gdbm_fatal (dbf, _("lseek error"));
return -1;
}
-
- rc = _gdbm_full_write (dbf, temp, av_size);
+
+ rc = GDBM_DEBUG_OVERRIDE ("push_avail_block:write-failure",
+ _gdbm_full_write (dbf, temp, av_size));
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
diff --git a/src/findkey.c b/src/findkey.c
index 3cd90ce..1f56083 100644
--- a/src/findkey.c
+++ b/src/findkey.c
@@ -34,7 +34,7 @@ _gdbm_read_entry (GDBM_FILE dbf, int elem_loc)
int data_size;
off_t file_pos;
data_cache_elem *data_ca;
-
+
/* Is it already in the cache? */
if (dbf->cache_entry->ca_data.elem_loc == elem_loc)
return dbf->cache_entry->ca_data.dptr;
@@ -50,10 +50,13 @@ _gdbm_read_entry (GDBM_FILE dbf, int elem_loc)
data_ca->data_size = data_size;
data_ca->elem_loc = elem_loc;
data_ca->hash_val = dbf->bucket->h_table[elem_loc].hash_value;
- if (key_size+data_size == 0)
+
+ if (GDBM_DEBUG_HOOK ("_gdbm_read_entry:malloc-failure"))
+ data_ca->dptr = NULL;
+ else if (key_size + data_size == 0)
data_ca->dptr = (char *) malloc (1);
else
- data_ca->dptr = (char *) malloc (key_size+data_size);
+ data_ca->dptr = (char *) malloc (key_size + data_size);
if (data_ca->dptr == NULL)
{
gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, FALSE);
@@ -62,8 +65,9 @@ _gdbm_read_entry (GDBM_FILE dbf, int elem_loc)
}
/* Read into the cache. */
- file_pos = __lseek (dbf, dbf->bucket->h_table[elem_loc].data_pointer,
- SEEK_SET);
+ file_pos = GDBM_DEBUG_OVERRIDE ("_gdbm_read_entry:lseek-failure",
+ __lseek (dbf, dbf->bucket->h_table[elem_loc].data_pointer,
+ SEEK_SET));
if (file_pos != dbf->bucket->h_table[elem_loc].data_pointer)
{
gdbm_set_errno (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
@@ -71,7 +75,8 @@ _gdbm_read_entry (GDBM_FILE dbf, int elem_loc)
return NULL;
}
- rc = _gdbm_full_read (dbf, data_ca->dptr, key_size+data_size);
+ rc = GDBM_DEBUG_OVERRIDE ("_gdbm_read_entry:read-failure",
+ _gdbm_full_read (dbf, data_ca->dptr, key_size+data_size));
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
diff --git a/src/gdbm.h.in b/src/gdbm.h.in
index dec1ea6..4ee3a11 100644
--- a/src/gdbm.h.in
+++ b/src/gdbm.h.in
@@ -161,7 +161,9 @@ typedef struct gdbm_recovery_s
#define GDBM_RCVR_MAX_FAILURES 0x08 /* max_failures is initialized */
#define GDBM_RCVR_BACKUP 0x10 /* Keep backup copy of the
original database on success */
-
+#define GDBM_RCVR_FORCE 0x20 /* Force recovery by skipping the
+ check pass */
+
extern int gdbm_recover (GDBM_FILE dbf, gdbm_recovery *rcvr, int flags);
diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h
index cdaa1eb..035fbbe 100644
--- a/src/gdbmdefs.h
+++ b/src/gdbmdefs.h
@@ -250,6 +250,24 @@ struct gdbm_file_info
} \
} \
while (0)
-
+
+/* Debugging hooks */
+#ifdef GDBM_DEBUG_ENABLE
+typedef int (*gdbm_debug_hook) (char const *, int, char const *, void *);
+extern void _gdbm_debug_hook_install (char const *, gdbm_debug_hook, void *);
+extern void _gdbm_debug_hook_remove (char const *);
+extern int _gdbm_debug_hook_check (char const *, int, char const *);
+extern int _gdbm_debug_hook_val (char const *);
+# define GDBM_DEBUG_HOOK(id) _gdbm_debug_hook_check(__FILE__,__LINE__,id)
+# define GDBM_DEBUG_OVERRIDE(id, stmt) \
+ (GDBM_DEBUG_HOOK(id) ? _gdbm_debug_hook_val(id) : (stmt))
+# define GDBM_DEBUG_ALLOC(id, stmt) \
+ (GDBM_DEBUG_HOOK(id) ? NULL : (stmt))
+#else
+# define GDBM_DEBUG_HOOK(id) 0
+# define GDBM_DEBUG_OVERRIDE(id, stmt) (stmt)
+# define GDBM_DEBUG_ALLOC(id, stmt) (stmt)
+#endif
+
/* Now define all the routines in use. */
#include "proto.h"
diff --git a/src/gdbmopen.c b/src/gdbmopen.c
index d256a0d..3891b7c 100644
--- a/src/gdbmopen.c
+++ b/src/gdbmopen.c
@@ -490,24 +490,26 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
/* Initialize the bucket cache. */
int
-_gdbm_init_cache(GDBM_FILE dbf, size_t size)
+_gdbm_init_cache (GDBM_FILE dbf, size_t size)
{
int index;
if (dbf->bucket_cache == NULL)
{
- dbf->bucket_cache = (cache_elem *) malloc(sizeof(cache_elem) * size);
- if(dbf->bucket_cache == NULL)
+ dbf->bucket_cache = GDBM_DEBUG_ALLOC ("_gdbm_init_cache:malloc-failure",
+ malloc (sizeof(cache_elem) * size));
+ if (dbf->bucket_cache == NULL)
{
gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, TRUE);
return -1;
}
dbf->cache_size = size;
- for(index = 0; index < size; index++)
+ for (index = 0; index < size; index++)
{
- (dbf->bucket_cache[index]).ca_bucket
- = (hash_bucket *) malloc (dbf->header->bucket_size);
+ (dbf->bucket_cache[index]).ca_bucket =
+ GDBM_DEBUG_ALLOC ("_gdbm_init_cache:bucket-malloc-failure",
+ malloc (dbf->header->bucket_size));
if ((dbf->bucket_cache[index]).ca_bucket == NULL)
{
gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, TRUE);
diff --git a/src/gdbmreorg.c b/src/gdbmreorg.c
index e9b7742..f7723c1 100644
--- a/src/gdbmreorg.c
+++ b/src/gdbmreorg.c
@@ -37,5 +37,5 @@ gdbm_reorganize (GDBM_FILE dbf)
GDBM_ASSERT_CONSISTENCY (dbf, -1);
rcvr.max_failures = 0;
- return gdbm_recover (dbf, &rcvr, GDBM_RCVR_MAX_FAILURES);
+ return gdbm_recover (dbf, &rcvr, GDBM_RCVR_MAX_FAILURES|GDBM_RCVR_FORCE);
}
diff --git a/src/gdbmstore.c b/src/gdbmstore.c
index 7aec604..1d7c648 100644
--- a/src/gdbmstore.c
+++ b/src/gdbmstore.c
@@ -144,7 +144,8 @@ gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags)
dbf->bucket->h_table[elem_loc].data_size = content.dsize;
/* Write the data to the file. */
- file_pos = __lseek (dbf, file_adr, SEEK_SET);
+ file_pos = GDBM_DEBUG_OVERRIDE ("gdbm_store:seek-failure",
+ __lseek (dbf, file_adr, SEEK_SET));
if (file_pos != file_adr)
{
gdbm_set_errno (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
@@ -152,7 +153,8 @@ gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags)
return -1;
}
- rc = _gdbm_full_write (dbf, key.dptr, key.dsize);
+ rc = GDBM_DEBUG_OVERRIDE ("gdbm_store:write-1-failure",
+ _gdbm_full_write (dbf, key.dptr, key.dsize));
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
@@ -160,7 +162,9 @@ gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags)
return -1;
}
- rc = _gdbm_full_write (dbf, content.dptr, content.dsize);
+ rc = GDBM_DEBUG_OVERRIDE ("gdbm_store:write-2-failure",
+ _gdbm_full_write (dbf,
+ content.dptr, content.dsize));
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
diff --git a/src/gdbmtool.c b/src/gdbmtool.c
index 9c49403..97ba0c1 100644
--- a/src/gdbmtool.c
+++ b/src/gdbmtool.c
@@ -1815,7 +1815,7 @@ main (int argc, char *argv[])
break;
case 'g':
- file_name = optarg;
+ file_name = estrdup (optarg);
break;
case 'q':
@@ -1839,7 +1839,7 @@ main (int argc, char *argv[])
}
if (argc == 1)
- file_name = argv[0];
+ file_name = estrdup (argv[0]);
signal (SIGPIPE, SIG_IGN);
diff --git a/src/recover.c b/src/recover.c
index ad786b3..69644f9 100644
--- a/src/recover.c
+++ b/src/recover.c
@@ -199,6 +199,53 @@ _gdbm_next_bucket_dir (GDBM_FILE dbf, int bucket_dir)
return bucket_dir;
}
+static int
+check_db (GDBM_FILE dbf)
+{
+ int bucket_dir, i;
+ int nbuckets = GDBM_DIR_COUNT (dbf);
+
+ for (bucket_dir = 0; bucket_dir < nbuckets;
+ bucket_dir = _gdbm_next_bucket_dir (dbf, bucket_dir))
+ {
+ if (_gdbm_get_bucket (dbf, bucket_dir))
+ return 1;
+ else
+ {
+ if (dbf->bucket->count < 0
+ || dbf->bucket->count > dbf->header->bucket_elems)
+ return 1;
+ for (i = 0; i < dbf->header->bucket_elems; i++)
+ {
+ char *dptr;
+ datum key;
+ int hashval, bucket, off;
+
+ if (dbf->bucket->h_table[i].hash_value == -1)
+ continue;
+ dptr = _gdbm_read_entry (dbf, i);
+ if (!dptr)
+ return 1;
+
+ key.dptr = dptr;
+ key.dsize = dbf->bucket->h_table[i].key_size;
+
+ if (memcmp (dbf->bucket->h_table[i].key_start, key.dptr,
+ (SMALL < key.dsize ? SMALL : key.dsize)))
+ return 1;
+
+ _gdbm_hash_key (dbf, key, &hashval, &bucket, &off);
+ if (bucket >= nbuckets)
+ return 1;
+ if (hashval != dbf->bucket->h_table[i].hash_value)
+ return 1;
+ if (dbf->dir[bucket] != dbf->dir[bucket_dir])
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
static int
run_recovery (GDBM_FILE dbf, GDBM_FILE new_dbf, gdbm_recovery *rcvr, int flags)
@@ -310,50 +357,53 @@ gdbm_recover (GDBM_FILE dbf, gdbm_recovery *rcvr, int flags)
rcvr->failed_keys = 0;
rcvr->failed_buckets = 0;
rcvr->backup_name = NULL;
-
- len = strlen (dbf->name);
- new_name = malloc (len + sizeof (TMPSUF));
- if (!new_name)
- {
- gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
- return -1;
- }
- strcat (strcpy (new_name, dbf->name), TMPSUF);
-
- fd = mkstemp (new_name);
- if (fd == -1)
+
+ rc = 0;
+ if ((flags & GDBM_RCVR_FORCE) || check_db (dbf))
{
- gdbm_set_errno (NULL, GDBM_FILE_OPEN_ERROR, FALSE);
- free (new_name);
- return -1;
- }
+ len = strlen (dbf->name);
+ new_name = malloc (len + sizeof (TMPSUF));
+ if (!new_name)
+ {
+ gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
+ return -1;
+ }
+ strcat (strcpy (new_name, dbf->name), TMPSUF);
- new_dbf = gdbm_fd_open (fd, new_name, dbf->header->block_size,
- GDBM_WRCREAT
- | (dbf->cloexec ? GDBM_CLOEXEC : 0)
- | GDBM_CLOERROR, dbf->fatal_err);
+ fd = mkstemp (new_name);
+ if (fd == -1)
+ {
+ gdbm_set_errno (NULL, GDBM_FILE_OPEN_ERROR, FALSE);
+ free (new_name);
+ return -1;
+ }
- SAVE_ERRNO (free (new_name));
+ new_dbf = gdbm_fd_open (fd, new_name, dbf->header->block_size,
+ GDBM_WRCREAT
+ | (dbf->cloexec ? GDBM_CLOEXEC : 0)
+ | GDBM_CLOERROR, dbf->fatal_err);
- if (new_dbf == NULL)
- {
- gdbm_set_errno (NULL, GDBM_REORGANIZE_FAILED, FALSE);
- return -1;
- }
+ SAVE_ERRNO (free (new_name));
- rc = run_recovery (dbf, new_dbf, rcvr, flags);
+ if (new_dbf == NULL)
+ {
+ gdbm_set_errno (NULL, GDBM_REORGANIZE_FAILED, FALSE);
+ return -1;
+ }
+
+ rc = run_recovery (dbf, new_dbf, rcvr, flags);
+ if (rc == 0)
+ rc = _gdbm_finish_transfer (dbf, new_dbf, rcvr, flags);
+ else
+ gdbm_close (new_dbf);
+ }
+
if (rc == 0)
{
- rc = _gdbm_finish_transfer (dbf, new_dbf, rcvr, flags);
- if (rc == 0)
- {
- gdbm_clear_error (dbf);
- dbf->need_recovery = FALSE;
- }
+ gdbm_clear_error (dbf);
+ dbf->need_recovery = FALSE;
}
- else
- gdbm_close (new_dbf);
return rc;
}
diff --git a/src/update.c b/src/update.c
index 2ddfa4b..90a6524 100644
--- a/src/update.c
+++ b/src/update.c
@@ -29,8 +29,9 @@ write_header (GDBM_FILE dbf)
{
off_t file_pos; /* Return value for lseek. */
int rc;
-
- file_pos = __lseek (dbf, 0L, SEEK_SET);
+
+ file_pos = GDBM_DEBUG_OVERRIDE ("write_header:lseek-failure",
+ __lseek (dbf, 0L, SEEK_SET));
if (file_pos != 0)
{
gdbm_set_errno (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
@@ -38,7 +39,9 @@ write_header (GDBM_FILE dbf)
return -1;
}
- rc = _gdbm_full_write (dbf, dbf->header, dbf->header->block_size);
+ rc = GDBM_DEBUG_OVERRIDE ("write_header:write-failure",
+ _gdbm_full_write (dbf, dbf->header, dbf->header->block_size));
+
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
@@ -92,7 +95,8 @@ _gdbm_end_update (GDBM_FILE dbf)
/* Write the directory. */
if (dbf->directory_changed)
{
- file_pos = __lseek (dbf, dbf->header->dir, SEEK_SET);
+ file_pos = GDBM_DEBUG_OVERRIDE ("_gdbm_end_update:lseek-failure",
+ __lseek (dbf, dbf->header->dir, SEEK_SET));
if (file_pos != dbf->header->dir)
{
gdbm_set_errno (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
@@ -100,7 +104,8 @@ _gdbm_end_update (GDBM_FILE dbf)
return -1;
}
- rc = _gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size);
+ rc = GDBM_DEBUG_OVERRIDE ("_gdbm_end_update:write-dir-failure",
+ _gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size));
if (rc)
{
gdbm_set_errno (dbf, rc, TRUE);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a95db7e..ae6cbb9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -119,17 +119,14 @@ check_PROGRAMS = \
$(DBMPROGS)
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src $(DBMINCLUDES)
+
+if GDBM_COND_DEBUG_ENABLE
+ AM_CPPFLAGS += -DGDBM_DEBUG_ENABLE=1
+endif
+
noinst_HEADERS=progname.h
-gtdel_LDADD = ../src/libgdbm.la
-gtload_LDADD = ../src/libgdbm.la
-gtdump_LDADD = ../src/libgdbm.la
-gtfetch_LDADD = ../src/libgdbm.la
-gtver_LDADD = ../src/libgdbm.la
-gtopt_LDADD = ../src/libgdbm.la
-gtrecover_LDADD = ../src/libgdbm.la
-
-g_open_ce_LDADD = ../src/libgdbm.la
-g_reorg_ce_LDADD = ../src/libgdbm.la
+
+LDADD = ../src/libgdbm.la
dtload_LDADD = ../src/libgdbm.la ../compat/libgdbm_compat.la
dtdump_LDADD = ../src/libgdbm.la ../compat/libgdbm_compat.la
diff --git a/tests/gtload.c b/tests/gtload.c
index 60e8504..f9f1bb1 100644
--- a/tests/gtload.c
+++ b/tests/gtload.c
@@ -17,15 +17,115 @@
#include "autoconf.h"
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <errno.h>
+#include <assert.h>
#include "gdbm.h"
#include "progname.h"
+const char *progname;
+int verbose;
+
+void
+err_printer (void *data, char const *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (stderr, "%s: ", progname);
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, "\n");
+}
+
+struct hook_closure
+{
+ unsigned skip;
+ unsigned hits;
+ int disabled;
+};
+
+static int
+hookfn (char const *file, int line, char const *id, void *data)
+{
+ struct hook_closure *clos = data;
+
+ if (clos->disabled)
+ return 0;
+ if (clos->skip)
+ {
+ --clos->skip;
+ return 0;
+ }
+ if (clos->hits)
+ {
+ if (--clos->hits == 0)
+ clos->disabled = 1;
+ }
+
+ if (verbose)
+ fprintf (stderr, "%s:%d: hit debug hook %s\n", file, line, id);
+ return -1;
+}
+
+size_t
+read_size (char const *arg)
+{
+ char *p;
+ size_t ret;
+
+ errno = 0;
+ ret = strtoul (arg, &p, 10);
+
+ if (errno)
+ {
+ fprintf (stderr, "%s: ", progname);
+ perror (arg);
+ exit (1);
+ }
+
+ if (*p)
+ {
+ fprintf (stderr, "%s: bad number: %s\n", progname, arg);
+ exit (1);
+ }
+
+ return ret;
+}
+
+void
+install_hook (char *id)
+{
+ char *p = strchr (id, ';');
+ struct hook_closure *clos = malloc (sizeof (*clos));
+ assert (clos != NULL);
+ memset (clos, 0, sizeof (*clos));
+ if (p)
+ {
+ char *q;
+
+ *p++ = 0;
+ for (q = strtok (p, ";"); q; q = strtok (NULL, ";"))
+ {
+ if (strncmp (q, "skip=", 5) == 0)
+ clos->skip = strtoul (q + 5, NULL, 10);
+ else if (strncmp (q, "hits=", 5) == 0)
+ clos->hits = strtoul (q + 5, NULL, 10);
+ else
+ {
+ fprintf (stderr, "%s: unknown parameter for hook %s: %s",
+ progname, id, q);
+ exit (1);
+ }
+ }
+ }
+ _gdbm_debug_hook_install (id, hookfn, clos);
+}
+
int
main (int argc, char **argv)
{
- const char *progname = canonical_progname (argv[0]);
const char *dbname;
int line = 0;
char buf[1024];
@@ -41,7 +141,11 @@ main (int argc, char **argv)
size_t mapped_size_max = 0;
int blksize;
int verbose = 0;
+ int recover = 0;
+ gdbm_recovery rcvr;
+ int rcvr_flags = 0;
+ progname = canonical_progname (argv[0]);
while (--argc)
{
char *arg = *++argv;
@@ -70,27 +174,41 @@ main (int argc, char **argv)
else if (strncmp (arg, "-blocksize=", 11) == 0)
block_size = atoi (arg + 11);
else if (strncmp (arg, "-maxmap=", 8) == 0)
- {
- char *p;
-
- errno = 0;
- mapped_size_max = strtoul (arg + 8, &p, 10);
-
- if (errno)
- {
- fprintf (stderr, "%s: ", progname);
- perror ("maxmap");
- exit (1);
- }
-
- if (*p)
- {
- fprintf (stderr, "%s: bad maxmap\n", progname);
- exit (1);
- }
- }
+ mapped_size_max = read_size (arg + 8);
else if (strncmp (arg, "-delim=", 7) == 0)
delim = arg[7];
+#if GDBM_DEBUG_ENABLE
+ else if (strncmp (arg, "-hook=", 6) == 0)
+ {
+ install_hook (arg + 6);
+ recover = 1;
+ }
+#endif
+ else if (strcmp (arg, "-recover") == 0)
+ recover = 1;
+ else if (strcmp (arg, "-verbose") == 0)
+ {
+ verbose = 1;
+ rcvr.errfun = err_printer;
+ rcvr_flags |= GDBM_RCVR_ERRFUN;
+ }
+ else if (strcmp (arg, "-backup") == 0)
+ rcvr_flags |= GDBM_RCVR_BACKUP;
+ else if (strncmp (arg, "-max-failures=", 14) == 0)
+ {
+ rcvr.max_failures = read_size (arg + 14);
+ rcvr_flags |= GDBM_RCVR_MAX_FAILURES;
+ }
+ else if (strncmp (arg, "-max-failed-keys=", 17) == 0)
+ {
+ rcvr.max_failed_keys = read_size (arg + 17);
+ rcvr_flags |= GDBM_RCVR_MAX_FAILED_KEYS;
+ }
+ else if (strncmp (arg, "-max-failed-buckets=", 20) == 0)
+ {
+ rcvr.max_failures = read_size (arg + 20);
+ rcvr_flags |= GDBM_RCVR_MAX_FAILED_BUCKETS;
+ }
else if (strcmp (arg, "--") == 0)
{
--argc;
@@ -184,7 +302,24 @@ main (int argc, char **argv)
{
fprintf (stderr, "%s: %d: item not inserted\n",
progname, line);
- exit (1);
+ if (gdbm_needs_recovery (dbf) && recover)
+ {
+ int rc = gdbm_recover (dbf, &rcvr, rcvr_flags);
+ if (rc)
+ {
+ int ec = errno;
+ fprintf (stderr, "%s: recovery failed: %s",
+ progname, gdbm_strerror (gdbm_errno));
+ if (gdbm_check_syserr (gdbm_errno))
+ fprintf (stderr, ": %s", strerror (ec));
+ fputc ('\n', stderr);
+ }
+ --recover;
+ }
+ else
+ {
+ exit (1);
+ }
}
}
gdbm_close (dbf);

Return to:

Send suggestions and report system problems to the System administrator.