diff options
-rw-r--r-- | configure.ac | 31 | ||||
-rw-r--r-- | doc/gdbm.texi | 7 | ||||
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/bucket.c | 19 | ||||
-rw-r--r-- | src/debug.c | 155 | ||||
-rw-r--r-- | src/falloc.c | 20 | ||||
-rw-r--r-- | src/findkey.c | 17 | ||||
-rw-r--r-- | src/gdbm.h.in | 4 | ||||
-rw-r--r-- | src/gdbmdefs.h | 20 | ||||
-rw-r--r-- | src/gdbmopen.c | 14 | ||||
-rw-r--r-- | src/gdbmreorg.c | 2 | ||||
-rw-r--r-- | src/gdbmstore.c | 10 | ||||
-rw-r--r-- | src/gdbmtool.c | 4 | ||||
-rw-r--r-- | src/recover.c | 120 | ||||
-rw-r--r-- | src/update.c | 15 | ||||
-rw-r--r-- | tests/Makefile.am | 17 | ||||
-rw-r--r-- | tests/gtload.c | 177 |
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); |