aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/findkey.c20
-rw-r--r--src/gdbm.h.in4
-rw-r--r--src/gdbmdefs.h17
-rw-r--r--src/gdbmerrno.c3
-rw-r--r--src/gdbmtool.c27
-rw-r--r--src/mmap.c22
-rw-r--r--src/recover.c16
-rw-r--r--src/systems.h1
8 files changed, 102 insertions, 8 deletions
diff --git a/src/findkey.c b/src/findkey.c
index 7638b04..bd9fd83 100644
--- a/src/findkey.c
+++ b/src/findkey.c
@@ -1,65 +1,85 @@
/* findkey.c - The routine that finds a key entry in the file. */
/* This file is part of GDBM, the GNU data base manager.
Copyright (C) 1990-1991, 1993, 2007, 2011, 2013, 2016-2018 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 system configuration before all else. */
#include "autoconf.h"
#include "gdbmdefs.h"
+int
+gdbm_bucket_element_valid_p (GDBM_FILE dbf, int elem_loc)
+{
+ return
+ elem_loc < dbf->header->bucket_elems
+ && dbf->bucket->h_table[elem_loc].hash_value != -1
+ && dbf->bucket->h_table[elem_loc].key_size >= 0
+ && off_t_sum_ok (dbf->bucket->h_table[elem_loc].data_pointer,
+ dbf->bucket->h_table[elem_loc].key_size)
+ && dbf->bucket->h_table[elem_loc].data_size >= 0
+ && off_t_sum_ok (dbf->bucket->h_table[elem_loc].data_pointer
+ + dbf->bucket->h_table[elem_loc].key_size,
+ dbf->bucket->h_table[elem_loc].data_size);
+}
/* Read the data found in bucket entry ELEM_LOC in file DBF and
return a pointer to it. Also, cache the read value. */
char *
_gdbm_read_entry (GDBM_FILE dbf, int elem_loc)
{
int rc;
int key_size;
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;
+ if (!gdbm_bucket_element_valid_p (dbf, elem_loc))
+ {
+ GDBM_SET_ERRNO (dbf, GDBM_BAD_HASH_TABLE, TRUE);
+ return NULL;
+ }
+
/* Set sizes and pointers. */
key_size = dbf->bucket->h_table[elem_loc].key_size;
data_size = dbf->bucket->h_table[elem_loc].data_size;
data_ca = &dbf->cache_entry->ca_data;
/* Set up the cache. */
if (data_ca->dptr != NULL) free (data_ca->dptr);
data_ca->key_size = key_size;
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 (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);
if (data_ca->dptr == NULL)
{
GDBM_SET_ERRNO2 (dbf, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_LOOKUP);
_gdbm_fatal (dbf, _("malloc error"));
return NULL;
}
diff --git a/src/gdbm.h.in b/src/gdbm.h.in
index 61d5707..e576c69 100644
--- a/src/gdbm.h.in
+++ b/src/gdbm.h.in
@@ -131,48 +131,49 @@ extern int gdbm_export_to_file (GDBM_FILE dbf, FILE *fp);
extern int gdbm_import (GDBM_FILE, const char *, int);
extern int gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag);
extern int gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount);
typedef struct gdbm_recovery_s
{
/* Input members.
These are initialized before call to gdbm_recover. The flags argument
specifies which of them are initialized. */
void (*errfun) (void *data, char const *fmt, ...);
void *data;
size_t max_failed_keys;
size_t max_failed_buckets;
size_t max_failures;
/* Output members.
The gdbm_recover function fills these before returning. */
size_t recovered_keys;
size_t recovered_buckets;
size_t failed_keys;
size_t failed_buckets;
+ size_t duplicate_keys;
char *backup_name;
} gdbm_recovery;
#define GDBM_RCVR_DEFAULT 0x00 /* Default settings */
#define GDBM_RCVR_ERRFUN 0x01 /* errfun is initialized */
#define GDBM_RCVR_MAX_FAILED_KEYS 0x02 /* max_failed_keys is initialized */
#define GDBM_RCVR_MAX_FAILED_BUCKETS 0x04 /* max_failed_buckets is initialized */
#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);
#define GDBM_DUMP_FMT_BINARY 0
#define GDBM_DUMP_FMT_ASCII 1
#define GDBM_META_MASK_MODE 0x01
#define GDBM_META_MASK_OWNER 0x02
extern int gdbm_dump (GDBM_FILE, const char *, int fmt, int open_flags,
int mode);
@@ -201,51 +202,52 @@ extern int gdbm_copy_meta (GDBM_FILE dst, GDBM_FILE src);
# define GDBM_READER_CANT_DELETE 11
# define GDBM_READER_CANT_STORE 12
# define GDBM_READER_CANT_REORGANIZE 13
# define GDBM_UNKNOWN_ERROR 14
# define GDBM_ITEM_NOT_FOUND 15
# define GDBM_REORGANIZE_FAILED 16
# define GDBM_CANNOT_REPLACE 17
# define GDBM_ILLEGAL_DATA 18
# define GDBM_OPT_ALREADY_SET 19
# define GDBM_OPT_ILLEGAL 20
# define GDBM_BYTE_SWAPPED 21
# define GDBM_BAD_FILE_OFFSET 22
# define GDBM_BAD_OPEN_FLAGS 23
# define GDBM_FILE_STAT_ERROR 24
# define GDBM_FILE_EOF 25
# define GDBM_NO_DBNAME 26
# define GDBM_ERR_FILE_OWNER 27
# define GDBM_ERR_FILE_MODE 28
# define GDBM_NEED_RECOVERY 29
# define GDBM_BACKUP_FAILED 30
# define GDBM_DIR_OVERFLOW 31
# define GDBM_BAD_BUCKET 32
# define GDBM_BAD_HEADER 33
# define GDBM_BAD_AVAIL 34
+# define GDBM_BAD_HASH_TABLE 35
# define _GDBM_MIN_ERRNO 0
-# define _GDBM_MAX_ERRNO GDBM_BAD_AVAIL
+# define _GDBM_MAX_ERRNO GDBM_BAD_HASH_TABLE
/* This one was never used and will be removed in the future */
# define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR
typedef int gdbm_error;
extern int *gdbm_errno_location (void);
#define gdbm_errno (*gdbm_errno_location ())
extern const char * const gdbm_errlist[];
extern int const gdbm_syserr[];
extern gdbm_error gdbm_last_errno (GDBM_FILE dbf);
extern int gdbm_last_syserr (GDBM_FILE dbf);
extern void gdbm_set_errno (GDBM_FILE dbf, gdbm_error ec, int fatal);
extern void gdbm_clear_error (GDBM_FILE dbf);
extern int gdbm_needs_recovery (GDBM_FILE dbf);
extern int gdbm_check_syserr (gdbm_error n);
/* extra prototypes */
extern const char *gdbm_strerror (gdbm_error);
extern const char *gdbm_db_strerror (GDBM_FILE dbf);
extern int gdbm_version_cmp (int const a[], int const b[]);
diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h
index 5305b0d..1bb519b 100644
--- a/src/gdbmdefs.h
+++ b/src/gdbmdefs.h
@@ -5,48 +5,64 @@
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 "systems.h"
#include "gdbmconst.h"
#include "gdbm.h"
#define DEFAULT_TEXT_DOMAIN PACKAGE
#include "gettext.h"
#define _(s) gettext (s)
#define N_(s) s
+/* The width in bits of the integer type or expression T. */
+#define TYPE_WIDTH(t) (sizeof (t) * CHAR_BIT)
+
+#define SIGNED_TYPE_MAXIMUM(t) \
+ ((t) ((((t) 1 << (TYPE_WIDTH (t) - 2)) - 1) * 2 + 1))
+
+/* Maximum value for off_t */
+#define OFF_T_MAX SIGNED_TYPE_MAXIMUM (off_t)
+
+/* Return true if A can be added to B without integer overflow */
+static inline off_t
+off_t_sum_ok (off_t a, off_t b)
+{
+ return OFF_T_MAX - a >= b;
+}
+
/* The type definitions are next. */
/* The available file space is stored in an "avail" table. The one with
most activity is contained in the file header. (See below.) When that
one filles up, it is split in half and half is pushed on an "avail
stack." When the active avail table is empty and the "avail stack" is
not empty, the top of the stack is popped into the active avail table. */
/* The following structure is the element of the avaliable table. */
typedef struct
{
int av_size; /* The size of the available block. */
off_t av_adr; /* The file address of the available block. */
} avail_elem;
/* This is the actual table. The in-memory images of the avail blocks are
allocated by malloc using a calculated size. */
typedef struct
{
int size; /* The number of avail elements in the table.*/
int count; /* The number of entries in the table. */
off_t next_block; /* The file address of the next avail block. */
avail_elem av_table[1]; /* The table. Make it look like an array. */
} avail_block;
@@ -72,48 +88,49 @@ typedef struct
int bucket_elems; /* Number of elements in a hash bucket. */
off_t next_block; /* The next unallocated block address. */
avail_block avail; /* This must be last because of the pseudo
array in avail. This avail grows to fill
the entire block. */
} gdbm_file_header;
/* The dbm hash bucket element contains the full 31 bit hash value, the
"pointer" to the key and data (stored together) with their sizes. It also
has a small part of the actual key value. It is used to verify the first
part of the key has the correct value without having to read the actual
key. */
typedef struct
{
int hash_value; /* The complete 31 bit value. */
char key_start[SMALL]; /* Up to the first SMALL bytes of the key. */
off_t data_pointer; /* The file address of the key record. The
data record directly follows the key. */
int key_size; /* Size of key data in the file. */
int data_size; /* Size of associated data in the file. */
} bucket_element;
+extern int gdbm_bucket_element_valid_p (GDBM_FILE dbf, int elem_loc);
/* A bucket is a small hash table. This one consists of a number of
bucket elements plus some bookkeeping fields. The number of elements
depends on the optimum blocksize for the storage device and on a
parameter given at file creation time. This bucket takes one block.
When one of these tables gets full, it is split into two hash buckets.
The contents are split between them by the use of the first few bits
of the 31 bit hash function. The location in a bucket is the hash
value modulo the size of the bucket. The in-memory images of the
buckets are allocated by malloc using a calculated size depending of
the file system buffer size. To speed up write, each bucket will have
BUCKET_AVAIL avail elements with the bucket. */
typedef struct
{
int av_count; /* The number of bucket_avail entries. */
avail_elem bucket_avail[BUCKET_AVAIL]; /* Distributed avail. */
int bucket_bits; /* The number of bits used to get here. */
int count; /* The number of element buckets full. */
bucket_element h_table[1]; /* The table. Make it look like an array.*/
} hash_bucket;
/* We want to keep from reading buckets as much as possible. The following is
to implement a bucket cache. When full, buckets will be dropped in a
diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c
index 896bf70..52cfe30 100644
--- a/src/gdbmerrno.c
+++ b/src/gdbmerrno.c
@@ -113,49 +113,50 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = {
[GDBM_CANT_BE_WRITER] = N_("Can't be writer"),
[GDBM_READER_CANT_DELETE] = N_("Reader can't delete"),
[GDBM_READER_CANT_STORE] = N_("Reader can't store"),
[GDBM_READER_CANT_REORGANIZE] = N_("Reader can't reorganize"),
[GDBM_UNKNOWN_ERROR] = N_("Should not happen: unused error code"),
[GDBM_ITEM_NOT_FOUND] = N_("Item not found"),
[GDBM_REORGANIZE_FAILED] = N_("Reorganize failed"),
[GDBM_CANNOT_REPLACE] = N_("Cannot replace"),
[GDBM_ILLEGAL_DATA] = N_("Illegal data"),
[GDBM_OPT_ALREADY_SET] = N_("Option already set"),
[GDBM_OPT_ILLEGAL] = N_("Illegal option"),
[GDBM_BYTE_SWAPPED] = N_("Byte-swapped file"),
[GDBM_BAD_FILE_OFFSET] = N_("File header assumes wrong off_t size"),
[GDBM_BAD_OPEN_FLAGS] = N_("Bad file flags"),
[GDBM_FILE_STAT_ERROR] = N_("Cannot stat file"),
[GDBM_FILE_EOF] = N_("Unexpected end of file"),
[GDBM_NO_DBNAME] = N_("Database name not given"),
[GDBM_ERR_FILE_OWNER] = N_("Failed to restore file owner"),
[GDBM_ERR_FILE_MODE] = N_("Failed to restore file mode"),
[GDBM_NEED_RECOVERY] = N_("Database needs recovery"),
[GDBM_BACKUP_FAILED] = N_("Failed to create backup copy"),
[GDBM_DIR_OVERFLOW] = N_("Bucket directory overflow"),
[GDBM_BAD_BUCKET] = N_("Malformed bucket header"),
[GDBM_BAD_HEADER] = N_("Malformed database file header"),
- [GDBM_BAD_AVAIL] = N_("Malforemd avail_block")
+ [GDBM_BAD_AVAIL] = N_("Malformed avail_block"),
+ [GDBM_BAD_HASH_TABLE] = N_("Malformed hash table")
};
const char *
gdbm_strerror (gdbm_error error)
{
if (error < _GDBM_MIN_ERRNO || error > _GDBM_MAX_ERRNO)
error = GDBM_UNKNOWN_ERROR;
return gettext (gdbm_errlist[error]);
}
char const *
gdbm_db_strerror (GDBM_FILE dbf)
{
if (!dbf->last_errstr)
{
char const *errstr = gdbm_strerror (dbf->last_error);
if (dbf->last_syserror)
{
char const *syserrstr = strerror (dbf->last_syserror);
size_t len = strlen (errstr) + strlen (syserrstr) + 2;
dbf->last_errstr = malloc (len + 1);
if (!dbf->last_errstr)
return errstr;
diff --git a/src/gdbmtool.c b/src/gdbmtool.c
index 33bdf93..9c6eebe 100644
--- a/src/gdbmtool.c
+++ b/src/gdbmtool.c
@@ -531,113 +531,131 @@ nextkey_handler (struct handler_param *param)
terror (_("Can't find key: %s"), gdbm_strerror (gdbm_errno));
}
/* reorganize */
void
reorganize_handler (struct handler_param *param GDBM_ARG_UNUSED)
{
if (gdbm_reorganize (gdbm_file))
terror ("%s", _("Reorganization failed."));
else
fprintf (param->fp, _("Reorganization succeeded."));
}
static void
err_printer (void *data GDBM_ARG_UNUSED, char const *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fprintf (stderr, "\n");
}
-/* recover verbose backup max-failed-keys=N max-failed-buckets=N max-failures=N */
+/* recover sumamry verbose backup max-failed-keys=N max-failed-buckets=N max-failures=N */
void
recover_handler (struct handler_param *param)
{
gdbm_recovery rcvr;
int flags = 0;
int rc;
int i;
char *p;
+ int summary = 0;
- for (i = 1; i < param->argc; i++)
+ for (i = 0; i < param->argc; i++)
{
char *arg = PARAM_STRING (param, i);
if (strcmp (arg, "verbose") == 0)
{
rcvr.errfun = err_printer;
flags |= GDBM_RCVR_ERRFUN;
}
+ else if (strcmp (arg, "summary") == 0)
+ {
+ summary = 1;
+ }
else if (strcmp (arg, "backup") == 0)
{
rcvr.errfun = err_printer;
flags |= GDBM_RCVR_BACKUP;
}
else if (strncmp (arg, "max-failures=", 13) == 0)
{
rcvr.max_failures = strtoul (arg + 13, &p, 10);
if (*p)
{
printf (_("not a number (stopped near %s)\n"), p);
return;
}
flags |= GDBM_RCVR_MAX_FAILURES;
}
else if (strncmp (arg, "max-failed-keys=", 16) == 0)
{
rcvr.max_failed_keys = strtoul (arg + 16, &p, 10);
if (*p)
{
printf (_("not a number (stopped near %s)\n"), p);
return;
}
flags |= GDBM_RCVR_MAX_FAILED_KEYS;
}
else if (strncmp (arg, "max-failed-buckets=", 19) == 0)
{
rcvr.max_failures = strtoul (arg + 19, &p, 10);
if (*p)
{
printf (_("not a number (stopped near %s)\n"), p);
return;
}
flags |= GDBM_RCVR_MAX_FAILED_BUCKETS;
}
else
{
terror (_("unrecognized argument: %s"), arg);
return;
}
}
rc = gdbm_recover (gdbm_file, &rcvr, flags);
if (rc == 0)
{
fprintf (param->fp, _("Recovery succeeded.\n"));
+ if (summary)
+ {
+ fprintf (param->fp,
+ _("Keys recovered: %lu, failed: %lu, duplicate: %lu\n"),
+ (unsigned long) rcvr.recovered_keys,
+ (unsigned long) rcvr.failed_keys,
+ (unsigned long) rcvr.duplicate_keys);
+ fprintf (param->fp,
+ _("Buckets recovered: %lu, failed: %lu\n"),
+ (unsigned long) rcvr.recovered_buckets,
+ (unsigned long) rcvr.failed_buckets);
+ }
+
if (rcvr.backup_name)
{
fprintf (param->fp,
_("Original database preserved in file %s"),
rcvr.backup_name);
free (rcvr.backup_name);
}
fputc ('\n', param->fp);
}
else
{
fprintf (stderr, _("Recovery failed: %s"), gdbm_strerror (gdbm_errno));
if (gdbm_syserr[gdbm_errno])
fprintf (stderr, ": %s", strerror (errno));
fputc ('\n', stderr);
}
}
/* avail - print available list */
int
avail_begin (struct handler_param *param GDBM_ARG_UNUSED, size_t *exp_count)
{
if (checkdb ())
return 1;
@@ -904,83 +922,83 @@ list_handler (struct handler_param *param)
free (key.dptr);
key = nextkey;
}
}
/* quit - quit the program */
void
quit_handler (struct handler_param *param GDBM_ARG_UNUSED)
{
if (gdbm_file != NULL)
gdbm_close (gdbm_file);
input_done ();
exit (EXIT_OK);
}
/* export FILE [truncate] - export to a flat file format */
void
export_handler (struct handler_param *param)
{
int format = GDBM_DUMP_FMT_ASCII;
int flags = GDBM_WRCREAT;
int i;
int filemode;
- for (i = 1; i < param->argc; i++)
+ for (i = 0; i < param->argc; i++)
{
if (strcmp (PARAM_STRING (param, i), "truncate") == 0)
flags = GDBM_NEWDB;
else if (strcmp (PARAM_STRING (param, i), "binary") == 0)
format = GDBM_DUMP_FMT_BINARY;
else if (strcmp (PARAM_STRING (param, i), "ascii") == 0)
format = GDBM_DUMP_FMT_ASCII;
else
{
terror (_("unrecognized argument: %s"), PARAM_STRING (param, i));
return;
}
}
if (variable_get ("filemode", VART_INT, (void**) &filemode))
abort ();
if (gdbm_dump (gdbm_file, PARAM_STRING (param, 0), format, flags, filemode))
{
terror (_("error dumping database: %s"),
gdbm_strerror (gdbm_errno));
}
}
/* import FILE [replace] [nometa] - import from a flat file */
void
import_handler (struct handler_param *param)
{
int flag = GDBM_INSERT;
unsigned long err_line;
int meta_mask = 0;
int i;
int rc;
- for (i = 1; i < param->argc; i++)
+ for (i = 0; i < param->argc; i++)
{
if (strcmp (PARAM_STRING (param, i), "replace") == 0)
flag = GDBM_REPLACE;
else if (strcmp (PARAM_STRING (param, i), "nometa") == 0)
meta_mask = GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER;
else
{
terror (_("unrecognized argument: %s"),
PARAM_STRING (param, i));
return;
}
}
rc = gdbm_load (&gdbm_file, PARAM_STRING (param, 0), flag,
meta_mask, &err_line);
if (rc && gdbm_errno == GDBM_NO_DBNAME)
{
int t = open_mode;
open_mode = GDBM_NEWDB;
rc = checkdb ();
open_mode = t;
if (rc)
@@ -1199,48 +1217,49 @@ struct command command_tab[] = {
N_("nextkey") },
{ S(store), T_CMD,
checkdb, store_handler, NULL,
{ { N_("KEY"), GDBM_ARG_DATUM, DS_KEY },
{ N_("DATA"), GDBM_ARG_DATUM, DS_CONTENT },
{ NULL } },
FALSE,
REPEAT_NEVER,
N_("store") },
{ S(first), T_CMD,
checkdb, firstkey_handler, NULL,
{ { NULL } },
FALSE,
REPEAT_NEVER,
N_("firstkey") },
{ S(reorganize), T_CMD,
checkdb, reorganize_handler, NULL,
{ { NULL } },
FALSE,
REPEAT_NEVER,
N_("reorganize") },
{ S(recover), T_CMD,
checkdb, recover_handler, NULL,
{ { "[verbose]", GDBM_ARG_STRING },
+ { "[summary]", GDBM_ARG_STRING },
{ "[backup]", GDBM_ARG_STRING },
{ "[max-failed-keys=N]", GDBM_ARG_STRING },
{ "[max-failed-buckets=N]", GDBM_ARG_STRING },
{ "[max-failures=N]", GDBM_ARG_STRING },
{ NULL } },
FALSE,
REPEAT_NEVER,
N_("recover the database") },
{ S(avail), T_CMD,
avail_begin, avail_handler, NULL,
{ { NULL } },
FALSE,
REPEAT_NEVER,
N_("print avail list") },
{ S(bucket), T_CMD,
print_bucket_begin, print_current_bucket_handler, NULL,
{ { N_("NUMBER"), GDBM_ARG_STRING },
{ NULL } },
FALSE,
REPEAT_NEVER,
N_("print a bucket") },
{ S(current), T_CMD,
print_current_bucket_begin, print_current_bucket_handler, NULL,
{ { NULL } },
diff --git a/src/mmap.c b/src/mmap.c
index 114d8b2..24ede29 100644
--- a/src/mmap.c
+++ b/src/mmap.c
@@ -23,50 +23,57 @@
# include <sys/types.h>
# include <sys/time.h>
# include <sys/file.h>
# include <sys/stat.h>
# include <sys/mman.h>
# include <stdio.h>
/* Some systems fail to define this */
# ifndef MAP_FAILED
# define MAP_FAILED ((void*)-1)
# endif
/* Translate current offset in the mapped region into the absolute position */
# define _GDBM_MMAPPED_POS(dbf) ((dbf)->mapped_off + (dbf)->mapped_pos)
/* Return true if the absolute offset OFF lies within the currentlty mmapped
region */
# define _GDBM_IN_MAPPED_REGION_P(dbf, off) \
((off) >= (dbf)->mapped_off \
&& ((off) - (dbf)->mapped_off) < (dbf)->mapped_size)
/* Return true if the current region needs to be remapped */
# define _GDBM_NEED_REMAP(dbf) \
(!(dbf)->mapped_region || (dbf)->mapped_pos == (dbf)->mapped_size)
/* Return the sum of the currently mapped size and DELTA */
-# define SUM_FILE_SIZE(dbf, delta) \
- ((dbf)->mapped_off + (dbf)->mapped_size + (delta))
+static inline off_t
+SUM_FILE_SIZE (GDBM_FILE dbf, off_t delta)
+{
+ if (delta >= 0
+ && off_t_sum_ok (dbf->mapped_off, dbf->mapped_size)
+ && off_t_sum_ok (dbf->mapped_off + dbf->mapped_size, delta))
+ return dbf->mapped_off + dbf->mapped_size + delta;
+ return -1;
+}
/* Store the size of the GDBM file DBF in *PSIZE.
Return 0 on success and -1 on failure. */
int
_gdbm_file_size (GDBM_FILE dbf, off_t *psize)
{
struct stat sb;
if (fstat (dbf->desc, &sb))
{
GDBM_SET_ERRNO (dbf, GDBM_FILE_STAT_ERROR, FALSE);
return -1;
}
*psize = sb.st_size;
return 0;
}
/* Unmap the region. Reset all mapped fields to initial values. */
void
_gdbm_mapped_unmap (GDBM_FILE dbf)
{
if (dbf->mapped_region)
{
munmap (dbf->mapped_region, dbf->mapped_size);
dbf->mapped_region = NULL;
@@ -161,48 +168,59 @@ _gdbm_file_extend (GDBM_FILE dbf, off_t size)
# define _REMAP_EXTEND 1
# define _REMAP_END 2
/* Remap the GDBM file so that its mapped region ends on SIZEth byte.
If the file is opened with write permissions, FLAG controls how
it is expanded. The value _REMAP_DEFAULT truncates SIZE to the
actual file size. The value _REMAP_EXTEND extends the file, if
necessary, to accomodate max(SIZE,dbf->header->next_block) bytes.
Finally, the value _REMAP_END instructs the function to use
max(SIZE, file_size) as the upper bound of the mapped region.
If the file is opened read-only, FLAG is ignored and SIZE is
truncated to the actual file size.
The upper bound obtained that way is used as a *hint* to select
the actual size of the mapped region. which can never exceed
dbf->mapped_size_max.
The function returns 0 on success, -1 on failure. */
int
_gdbm_mapped_remap (GDBM_FILE dbf, off_t size, int flag)
{
off_t file_size, pos;
+ if (size < 0)
+ {
+ errno = EINVAL;
+ GDBM_SET_ERRNO (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
+ return -1;
+ }
+
+ if (size < dbf->mapped_size)
+ /* Nothing to do */
+ return 0;
+
if (_gdbm_file_size (dbf, &file_size))
{
SAVE_ERRNO (_gdbm_mapped_unmap (dbf));
return -1;
}
if (flag == _REMAP_END && size < file_size)
size = file_size;
if (dbf->read_write)
{
if (size > file_size)
{
if (flag != _REMAP_DEFAULT)
{
if (size < dbf->header->next_block)
size = dbf->header->next_block;
if (_gdbm_file_extend (dbf, size))
return -1;
file_size = size;
}
else
{
size = file_size;
diff --git a/src/recover.c b/src/recover.c
index d6d4ff9..721c23f 100644
--- a/src/recover.c
+++ b/src/recover.c
@@ -291,92 +291,108 @@ run_recovery (GDBM_FILE dbf, GDBM_FILE new_dbf, gdbm_recovery *rcvr, int flags)
_("can't read key pair %d:%d (%lu:%d): %s"),
bucket_dir, i,
(unsigned long) dbf->bucket->h_table[i].data_pointer,
dbf->bucket->h_table[i].key_size
+ dbf->bucket->h_table[i].data_size,
gdbm_db_strerror (dbf));
rcvr->failed_keys++;
if ((flags & GDBM_RCVR_MAX_FAILED_KEYS)
&& rcvr->failed_keys == rcvr->max_failed_keys)
return -1;
if ((flags & GDBM_RCVR_MAX_FAILURES)
&& (rcvr->failed_buckets + rcvr->failed_keys) == rcvr->max_failures)
return -1;
continue;
}
key.dptr = dptr;
key.dsize = dbf->bucket->h_table[i].key_size;
data.dptr = dptr + key.dsize;
data.dsize = dbf->bucket->h_table[i].data_size;
if (gdbm_store (new_dbf, key, data, GDBM_INSERT) != 0)
{
+ switch (gdbm_last_errno (new_dbf))
+ {
+ case GDBM_CANNOT_REPLACE:
+ rcvr->duplicate_keys++;
+ if (flags & GDBM_RCVR_ERRFUN)
+ rcvr->errfun (rcvr->data,
+ _("ignoring duplicate key %d:%d (%lu:%d)"),
+ bucket_dir, i,
+ (unsigned long) dbf->bucket->h_table[i].data_pointer,
+ dbf->bucket->h_table[i].key_size
+ + dbf->bucket->h_table[i].data_size);
+ break;
+
+ default:
if (flags & GDBM_RCVR_ERRFUN)
rcvr->errfun (rcvr->data,
_("fatal: can't store element %d:%d (%lu:%d): %s"),
bucket_dir, i,
(unsigned long) dbf->bucket->h_table[i].data_pointer,
dbf->bucket->h_table[i].key_size
+ dbf->bucket->h_table[i].data_size,
gdbm_db_strerror (new_dbf));
return -1;
}
}
}
}
+ }
return 0;
}
int
gdbm_recover (GDBM_FILE dbf, gdbm_recovery *rcvr, int flags)
{
GDBM_FILE new_dbf; /* The new file. */
char *new_name; /* A temporary name. */
size_t len;
int fd;
int rc;
gdbm_recovery rs;
/* Readers can not reorganize! */
if (dbf->read_write == GDBM_READER)
{
GDBM_SET_ERRNO (dbf, GDBM_READER_CANT_REORGANIZE, dbf->need_recovery);
return -1;
}
/* Initialize gdbm_recovery structure */
if (!rcvr)
{
rcvr = &rs;
flags = 0;
}
rcvr->recovered_keys = 0;
rcvr->recovered_buckets = 0;
rcvr->failed_keys = 0;
rcvr->failed_buckets = 0;
+ rcvr->duplicate_keys = 0;
rcvr->backup_name = NULL;
rc = 0;
if ((flags & GDBM_RCVR_FORCE) || check_db (dbf))
{
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)
{
GDBM_SET_ERRNO (NULL, GDBM_FILE_OPEN_ERROR, FALSE);
free (new_name);
return -1;
}
new_dbf = gdbm_fd_open (fd, new_name, dbf->header->block_size,
GDBM_WRCREAT
diff --git a/src/systems.h b/src/systems.h
index 66955dd..c678573 100644
--- a/src/systems.h
+++ b/src/systems.h
@@ -12,48 +12,49 @@
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 all system headers first. */
#include <sys/types.h>
#include <stdio.h>
#if HAVE_SYS_FILE_H
# include <sys/file.h>
#endif
#include <sys/stat.h>
#include <stdlib.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <limits.h>
#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#ifndef O_CLOEXEC
# define O_CLOEXEC 0
#endif
/* Default block size. Some systems do not have blocksize in their
stat record. This code uses the BSD blocksize from stat. */
#if HAVE_STRUCT_STAT_ST_BLKSIZE
# define STATBLKSIZE(st) (st).st_blksize
#else
# define STATBLKSIZE(st) 1024
#endif
/* Do we have ftruncate? */
#if HAVE_FTRUNCATE
# define TRUNCATE(dbf) ftruncate (dbf->desc, 0)
#else
# define TRUNCATE(dbf) close( open (dbf->name, O_RDWR|O_TRUNC, mode));
#endif

Return to:

Send suggestions and report system problems to the System administrator.