aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2016-07-19 12:01:48 +0300
committerSergey Poznyakoff <gray@gnu.org>2016-07-19 12:58:16 +0300
commit85f1e72da83e3078e2ae1f78093ef0966e43cec1 (patch)
treeb00707979e9088d5f7b7880d438260eb063c9160 /src
parent8e83f209342e2b035bbb19efa87ec5295158ef65 (diff)
downloadgdbm-85f1e72da83e3078e2ae1f78093ef0966e43cec1.tar.gz
gdbm-85f1e72da83e3078e2ae1f78093ef0966e43cec1.tar.bz2
Implement gdbm_recover function
* configure.ac: Don't check for rename. * src/Makefile.am (libgdbm_la_SOURCES): Add recover.c * src/recover.c: New file. * src/bucket.c (_gdbm_get_bucket): Remove extra space before [ * src/err.c (prerror): Take additional argument (gdbm_perror): Print system errno if necessary. * src/gdbm.h.in (GDBM_CLOERROR): New flag. (gdbm_fd_open, gdbm_copy_meta): New proto. (gdbm_last_syserr,gdbm_db_strerror,gdbm_recover): New proto. (gdbm_syserr): New extern. (gdbm_recovery): New struct. (GDBM_RCVR_DEFAULT,GDBM_RCVR_ERRFUN) (GDBM_RCVR_MAX_FAILED_KEYS) (GDBM_RCVR_MAX_FAILED_BUCKETS) (GDBM_RCVR_MAX_FAILURES) (GDBM_RCVR_BACKUP): New flags. (GDBM_BACKUP_FAILED): New error code. * src/gdbmclose.c (gdbm_close): Work correctly if dbf->desc == -1. * src/gdbmcount.c (gdbm_count): Remove spurious sorting. Use _gdbm_next_bucket_dir for iterating over the buckets. * src/gdbmdefs.h (struct gdbm_file_info)<last_syserror> <last_errstr>: New members. * src/gdbmerrno.c (gdbm_set_errno): Set last_syserror as well. (gdbm_clear_error): Reset last_syserror. (gdbm_last_syserr): New function. (gdbm_errlist): New entry for GDBM_BACKUP_FAILED. (gdbm_db_strerror): New function. (gdbm_syserr): New global. * src/gdbmload.c (get_parms): Buffer can be NULL. * src/gdbmopen.c (gdbm_fd_open): New function. (gdbm_open): Rewrite as a wrapper over gdbm_fd_open. * src/gdbmreorg.c (gdbm_reorganize): Rewrite as a wrapper over gdbm_recover. * src/proto.h (_gdbm_next_bucket_dir): New proto. * src/gdbmtool.c: New command: recover. * tests/.gitignore: Add gtrecover * tests/gtrecover.c: New test program. * tests/Makefile.am: Build gtrecover
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/bucket.c2
-rw-r--r--src/err.c11
-rw-r--r--src/gdbm.h.in49
-rw-r--r--src/gdbmclose.c45
-rw-r--r--src/gdbmcount.c44
-rw-r--r--src/gdbmdefs.h6
-rw-r--r--src/gdbmerrno.c74
-rw-r--r--src/gdbmload.c3
-rw-r--r--src/gdbmopen.c196
-rw-r--r--src/gdbmreorg.c189
-rw-r--r--src/gdbmtool.c105
-rw-r--r--src/proto.h2
-rw-r--r--src/recover.c359
14 files changed, 747 insertions, 339 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e1eb65c..af48a1a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -62,6 +62,7 @@ libgdbm_la_SOURCES = \
hash.c\
lock.c\
mmap.c\
+ recover.c\
update.c\
version.c
diff --git a/src/bucket.c b/src/bucket.c
index 2696c10..7eb3898 100644
--- a/src/bucket.c
+++ b/src/bucket.c
@@ -59,7 +59,7 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
/* Initial set up. */
dbf->bucket_dir = dir_index;
- bucket_adr = dbf->dir [dir_index];
+ bucket_adr = dbf->dir[dir_index];
if (dbf->bucket_cache == NULL)
{
diff --git a/src/err.c b/src/err.c
index db0b87a..cbd4191 100644
--- a/src/err.c
+++ b/src/err.c
@@ -22,19 +22,21 @@
# include <string.h>
static void
-prerror (const char *fmt, va_list ap, const char *diag)
+prerror (const char *fmt, va_list ap, const char *diag, const char *sysdiag)
{
fprintf (stderr, "%s: ", progname);
vfprintf (stderr, fmt, ap);
if (diag)
fprintf (stderr, ": %s", diag);
+ if (sysdiag)
+ fprintf (stderr, ": %s", sysdiag);
fputc ('\n', stderr);
}
void
verror (const char *fmt, va_list ap)
{
- prerror (fmt, ap, NULL);
+ prerror (fmt, ap, NULL, NULL);
}
void
@@ -51,7 +53,7 @@ sys_perror (int code, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
- prerror (fmt, ap, strerror (code));
+ prerror (fmt, ap, strerror (code), NULL);
va_end (ap);
}
@@ -60,7 +62,8 @@ gdbm_perror (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
- prerror (fmt, ap, gdbm_strerror (gdbm_errno));
+ prerror (fmt, ap, gdbm_strerror (gdbm_errno),
+ gdbm_syserr[gdbm_errno] ? strerror (errno) : NULL);
va_end (ap);
}
diff --git a/src/gdbm.h.in b/src/gdbm.h.in
index 404d079..fc60e46 100644
--- a/src/gdbm.h.in
+++ b/src/gdbm.h.in
@@ -52,6 +52,7 @@ extern "C" {
# define GDBM_BSEXACT 0x200 /* Don't adjust block_size. Bail out with
GDBM_BLOCK_SIZE_ERROR error if unable to
set it. */
+# define GDBM_CLOERROR 0x400 /* Only for gdbm_fd_open: close fd on error. */
/* Parameters to gdbm_store for simple insertion or replacement in the
case that the key is already in the database. */
@@ -107,6 +108,8 @@ extern int const gdbm_version_number[3];
/* GDBM external functions. */
+extern GDBM_FILE gdbm_fd_open (int fd, const char *file_name, int block_size,
+ int flags, void (*fatal_func) (const char *));
extern GDBM_FILE gdbm_open (const char *, int, int, int,
void (*)(const char *));
extern void gdbm_close (GDBM_FILE);
@@ -116,6 +119,7 @@ extern int gdbm_delete (GDBM_FILE, datum);
extern datum gdbm_firstkey (GDBM_FILE);
extern datum gdbm_nextkey (GDBM_FILE, datum);
extern int gdbm_reorganize (GDBM_FILE);
+
extern void gdbm_sync (GDBM_FILE);
extern int gdbm_exists (GDBM_FILE, datum);
extern int gdbm_setopt (GDBM_FILE, int, void *, int);
@@ -128,7 +132,39 @@ 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;
+ 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 */
+extern int gdbm_recover (GDBM_FILE dbf, gdbm_recovery *rcvr, int flags);
+
+
#define GDBM_DUMP_FMT_BINARY 0
#define GDBM_DUMP_FMT_ASCII 1
@@ -146,6 +182,8 @@ extern int gdbm_load_from_file (GDBM_FILE *, FILE *, int replace,
int meta_flags,
unsigned long *line);
+extern int gdbm_copy_meta (GDBM_FILE dst, GDBM_FILE src);
+
# define GDBM_NO_ERROR 0
# define GDBM_MALLOC_ERROR 1
# define GDBM_BLOCK_SIZE_ERROR 2
@@ -176,15 +214,18 @@ extern int gdbm_load_from_file (GDBM_FILE *, FILE *, int replace,
# define GDBM_ERR_FILE_OWNER 27
# define GDBM_ERR_FILE_MODE 28
# define GDBM_NEED_RECOVERY 29
+# define GDBM_BACKUP_FAILED 30
-# define _GDBM_MIN_ERRNO 0
-# define _GDBM_MAX_ERRNO GDBM_NEED_RECOVERY
+# define _GDBM_MIN_ERRNO 0
+# define _GDBM_MAX_ERRNO GDBM_BACKUP_FAILED
typedef int gdbm_error; /* For compatibilities sake. */
extern gdbm_error gdbm_errno;
extern const char * const gdbm_errlist[];
-
+extern int const gdbm_syserr[];
+
extern int 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);
@@ -192,6 +233,8 @@ extern int gdbm_needs_recovery (GDBM_FILE dbf);
/* 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[]);
# if defined(__cplusplus) || defined(c_plusplus)
diff --git a/src/gdbmclose.c b/src/gdbmclose.c
index f711a8f..adf654d 100644
--- a/src/gdbmclose.c
+++ b/src/gdbmclose.c
@@ -31,31 +31,36 @@ gdbm_close (GDBM_FILE dbf)
{
int index; /* For freeing the bucket cache. */
- /* Make sure the database is all on disk. */
- if (dbf->read_write != GDBM_READER)
- __fsync (dbf);
+ if (dbf->desc != -1)
+ {
+ /* Make sure the database is all on disk. */
+ if (dbf->read_write != GDBM_READER)
+ __fsync (dbf);
- /* Close the file and free all malloced memory. */
+ /* Close the file and free all malloced memory. */
#if HAVE_MMAP
- _gdbm_mapped_unmap(dbf);
+ _gdbm_mapped_unmap (dbf);
#endif
- if (dbf->file_locking)
- {
- _gdbm_unlock_file (dbf);
+ if (dbf->file_locking)
+ _gdbm_unlock_file (dbf);
+
+ close (dbf->desc);
}
- close (dbf->desc);
+
+ gdbm_clear_error (dbf);
+
free (dbf->name);
- if (dbf->dir != NULL) free (dbf->dir);
-
- if (dbf->bucket_cache != NULL) {
- for (index = 0; index < dbf->cache_size; index++) {
- if (dbf->bucket_cache[index].ca_bucket != NULL)
- free (dbf->bucket_cache[index].ca_bucket);
- if (dbf->bucket_cache[index].ca_data.dptr != NULL)
- free (dbf->bucket_cache[index].ca_data.dptr);
+ free (dbf->dir);
+
+ if (dbf->bucket_cache != NULL)
+ {
+ for (index = 0; index < dbf->cache_size; index++)
+ {
+ free (dbf->bucket_cache[index].ca_bucket);
+ free (dbf->bucket_cache[index].ca_data.dptr);
+ }
+ free (dbf->bucket_cache);
}
- free (dbf->bucket_cache);
- }
- if ( dbf->header != NULL ) free (dbf->header);
+ free (dbf->header);
free (dbf);
}
diff --git a/src/gdbmcount.c b/src/gdbmcount.c
index 861a708..b84696c 100644
--- a/src/gdbmcount.c
+++ b/src/gdbmcount.c
@@ -21,54 +21,22 @@
#include "autoconf.h"
#include "gdbmdefs.h"
-static int
-compoff (const void *a, const void *b)
-{
- if (*(off_t*)a < *(off_t*)b)
- return -1;
- if (*(off_t*)a > *(off_t*)b)
- return 1;
- return 0;
-}
-
int
gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount)
{
- hash_bucket bucket;
int nbuckets = GDBM_DIR_COUNT (dbf);
- off_t *sdir;
gdbm_count_t count = 0;
- int i, last;
- int result;
+ int i;
/* Return immediately if the database needs recovery */
GDBM_ASSERT_CONSISTENCY (dbf, -1);
- sdir = malloc (dbf->header->dir_size);
- if (!sdir)
+ for (i = 0; i < nbuckets; i = _gdbm_next_bucket_dir (dbf, i))
{
- gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, FALSE);
- return -1;
+ if (_gdbm_get_bucket (dbf, i))
+ return -1;
+ count += dbf->bucket->count;
}
-
- memcpy (sdir, dbf->dir, dbf->header->dir_size);
- qsort (sdir, nbuckets, sizeof (off_t), compoff);
-
- result = 0;
- for (i = last = 0; i < nbuckets; i++)
- {
- if (i == 0 || sdir[i] != sdir[last])
- {
- if (_gdbm_read_bucket_at (dbf, sdir[i], &bucket, sizeof bucket))
- {
- result = -1;
- break;
- }
- count += bucket.count;
- last = i;
- }
- }
- free (sdir);
*pcount = count;
- return result;
+ return 0;
}
diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h
index ebe412b..6b0348b 100644
--- a/src/gdbmdefs.h
+++ b/src/gdbmdefs.h
@@ -171,8 +171,12 @@ struct gdbm_file_info
/* Last error was fatal, the database needs recovery */
unsigned need_recovery :1;
- /* Last error number */
+ /* Last GDBM error number */
int last_error;
+ /* Last system error number */
+ int last_syserror;
+ /* Last formatted error */
+ char *last_errstr;
/* Type of file locking in use. */
enum { LOCKING_NONE = 0, LOCKING_FLOCK, LOCKING_LOCKF,
diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c
index 8882489..1c94872 100644
--- a/src/gdbmerrno.c
+++ b/src/gdbmerrno.c
@@ -32,7 +32,14 @@ gdbm_set_errno (GDBM_FILE dbf, gdbm_error ec, int fatal)
{
if (dbf)
{
+ free (dbf->last_errstr);
+ dbf->last_errstr = NULL;
+
dbf->last_error = ec;
+ if (gdbm_syserr[ec])
+ dbf->last_syserror = errno;
+ else
+ dbf->last_syserror = 0;
dbf->need_recovery = fatal;
}
gdbm_errno = ec;
@@ -51,6 +58,17 @@ gdbm_last_errno (GDBM_FILE dbf)
}
int
+gdbm_last_syserr (GDBM_FILE dbf)
+{
+ if (!dbf)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ return dbf->last_syserror;
+}
+
+int
gdbm_needs_recovery (GDBM_FILE dbf)
{
if (!dbf)
@@ -62,8 +80,13 @@ gdbm_needs_recovery (GDBM_FILE dbf)
void
gdbm_clear_error (GDBM_FILE dbf)
{
- if (dbf)
- dbf->last_error = GDBM_NO_ERROR;
+ if (dbf)
+ {
+ dbf->last_error = GDBM_NO_ERROR;
+ dbf->last_syserror = 0;
+ free (dbf->last_errstr);
+ dbf->last_errstr = NULL;
+ }
}
/* this is not static so that applications may access the array if they
@@ -99,7 +122,8 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = {
[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_NEED_RECOVERY] = N_("Database needs recovery"),
+ [GDBM_BACKUP_FAILED] = N_("Failed to create backup copy")
};
const char *
@@ -114,3 +138,47 @@ gdbm_strerror (gdbm_error error)
return gettext (gdbm_errlist[(int)error]);
}
}
+
+char const *
+gdbm_db_strerror (GDBM_FILE dbf)
+{
+ if (!dbf->last_errstr)
+ {
+ char const *errstr;
+
+ if (dbf->last_error == GDBM_MALLOC_ERROR)
+ return _("Out of memory");
+
+ if ((dbf->last_error < _GDBM_MIN_ERRNO)
+ || (dbf->last_error > _GDBM_MAX_ERRNO))
+ errstr = _("Unknown error");
+ else
+ errstr = gettext (gdbm_errlist[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;
+
+ strcpy (dbf->last_errstr, errstr);
+ strcat (dbf->last_errstr, ": ");
+ strcat (dbf->last_errstr, syserrstr);
+ }
+ else
+ return errstr;
+ }
+ return dbf->last_errstr;
+}
+
+int const gdbm_syserr[_GDBM_MAX_ERRNO+1] = {
+ [GDBM_FILE_OPEN_ERROR] = 1,
+ [GDBM_FILE_WRITE_ERROR] = 1,
+ [GDBM_FILE_SEEK_ERROR] = 1,
+ [GDBM_FILE_READ_ERROR] = 1,
+ [GDBM_FILE_STAT_ERROR] = 1,
+ [GDBM_BACKUP_FAILED] = 1
+};
+
diff --git a/src/gdbmload.c b/src/gdbmload.c
index 04e1f0a..ab250d7 100644
--- a/src/gdbmload.c
+++ b/src/gdbmload.c
@@ -220,7 +220,8 @@ get_parms (struct dump_file *file)
file->lblevel = 0;
}
- file->buffer[file->buflevel] = 0;
+ if (file->buffer)
+ file->buffer[file->buflevel] = 0;
return ferror (file->fp) ? GDBM_FILE_READ_ERROR : 0;
}
diff --git a/src/gdbmopen.c b/src/gdbmopen.c
index 5210001..d256a0d 100644
--- a/src/gdbmopen.c
+++ b/src/gdbmopen.c
@@ -49,52 +49,40 @@ compute_directory_size (GDBM_FILE dbf, blksize_t block_size,
*ret_dir_bits = dir_bits;
}
-
-/* Initialize dbm system. FILE is a pointer to the file name. If the file
- has a size of zero bytes, a file initialization procedure is performed,
- setting up the initial structure in the file. BLOCK_SIZE is used during
- initialization to determine the size of various constructs. If the value
- is less than GDBM_MIN_BLOCK_SIZE, the file system blocksize is used,
- otherwise the value of BLOCK_SIZE is used. BLOCK_SIZE is ignored if the
- file has previously initialized. If FLAGS is set to GDBM_READ the user
- wants to just read the database and any call to dbm_store or dbm_delete
- will fail. Many readers can access the database at the same time. If FLAGS
- is set to GDBM_WRITE, the user wants both read and write access to the
- database and requires exclusive access. If FLAGS is GDBM_WRCREAT, the user
- wants both read and write access to the database and if the database does
- not exist, create a new one. If FLAGS is GDBM_NEWDB, the user want a
- new database created, regardless of whether one existed, and wants read
- and write access to the new database. Any error detected will cause a
- return value of null and an approprate value will be in gdbm_errno. If
- no errors occur, a pointer to the "gdbm file descriptor" will be
- returned. */
-
-
GDBM_FILE
-gdbm_open (const char *file, int block_size, int flags, int mode,
- void (*fatal_func) (const char *))
+gdbm_fd_open (int fd, const char *file_name, int block_size,
+ int flags, void (*fatal_func) (const char *))
{
GDBM_FILE dbf; /* The record to return. */
struct stat file_stat; /* Space for the stat information. */
- int len; /* Length of the file name. */
off_t file_pos; /* Used with seeks. */
int index; /* Used as a loop index. */
- char need_trunc; /* Used with GDBM_NEWDB and locking to avoid
- truncating a file from under a reader. */
int rc; /* temporary error code */
- int fbits = 0; /* additional bits for open(2) flags */
/* Initialize the gdbm_errno variable. */
gdbm_set_errno (NULL, GDBM_NO_ERROR, FALSE);
+ /* Get the status of the file. */
+ if (fstat (fd, &file_stat))
+ {
+ if (flags & GDBM_CLOERROR)
+ SAVE_ERRNO (close (fd));
+ gdbm_set_errno (NULL, GDBM_FILE_STAT_ERROR, FALSE);
+ return NULL;
+ }
+
/* Allocate new info structure. */
dbf = (GDBM_FILE) malloc (sizeof (*dbf));
if (dbf == NULL)
{
+ if (flags & GDBM_CLOERROR)
+ SAVE_ERRNO (close (fd));
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return NULL;
}
+ dbf->desc = fd;
+
/* Initialize some fields for known values. This is done so gdbm_close
will work if called before allocating some structures. */
dbf->dir = NULL;
@@ -111,15 +99,15 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->mapped_off = 0;
/* Save name of file. */
- len = strlen (file);
- dbf->name = (char *) malloc (len + 1);
+ dbf->name = strdup (file_name);
if (dbf->name == NULL)
{
+ if (flags & GDBM_CLOERROR)
+ close (fd);
free (dbf);
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return NULL;
}
- strcpy (dbf->name, file);
/* Initialize the fatal error routine. */
dbf->fatal_err = fatal_func;
@@ -131,6 +119,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->need_recovery = FALSE;
dbf->last_error = GDBM_NO_ERROR;
+ dbf->last_syserror = 0;
+ dbf->last_errstr = NULL;
/* GDBM_FAST used to determine whether or not we set fast_write. */
if (flags & GDBM_SYNC)
@@ -142,58 +132,14 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
{
dbf->file_locking = FALSE;
}
- if (flags & GDBM_CLOEXEC)
- {
- fbits = O_CLOEXEC;
- dbf->cloexec = TRUE;
- }
- else
- dbf->cloexec = FALSE;
-
- /* Open the file. */
- need_trunc = FALSE;
- switch (flags & GDBM_OPENMASK)
- {
- case GDBM_READER:
- dbf->desc = open (dbf->name, O_RDONLY|fbits, 0);
- break;
-
- case GDBM_WRITER:
- dbf->desc = open (dbf->name, O_RDWR|fbits, 0);
- break;
- case GDBM_NEWDB:
- dbf->desc = open (dbf->name, O_RDWR|O_CREAT|fbits, mode);
- need_trunc = TRUE;
- break;
-
- default:
- dbf->desc = open (dbf->name, O_RDWR|O_CREAT|fbits, mode);
- break;
-
- }
- if (dbf->desc < 0)
- {
- SAVE_ERRNO (free (dbf->name);
- free (dbf));
- gdbm_set_errno (NULL, GDBM_FILE_OPEN_ERROR, FALSE);
- return NULL;
- }
-
- /* Get the status of the file. */
- if (fstat (dbf->desc, &file_stat))
- {
- SAVE_ERRNO (close (dbf->desc);
- free (dbf->name);
- free (dbf));
- gdbm_set_errno (NULL, GDBM_FILE_STAT_ERROR, FALSE);
- return NULL;
- }
+ dbf->cloexec = !!(flags & GDBM_CLOEXEC);
/* Zero-length file can't be a reader... */
if (((flags & GDBM_OPENMASK) == GDBM_READER) && (file_stat.st_size == 0))
{
- close (dbf->desc);
+ if (flags & GDBM_CLOERROR)
+ close (dbf->desc);
free (dbf->name);
free (dbf);
gdbm_set_errno (NULL, GDBM_EMPTY_DATABASE, FALSE);
@@ -208,7 +154,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
{
if (_gdbm_lock_file (dbf) == -1)
{
- close (dbf->desc);
+ if (flags & GDBM_CLOERROR)
+ close (dbf->desc);
free (dbf->name);
free (dbf);
gdbm_set_errno (NULL,
@@ -220,7 +167,7 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
/* If we do have a write lock and it was a GDBM_NEWDB, it is
now time to truncate the file. */
- if (need_trunc && file_stat.st_size != 0)
+ if ((flags & GDBM_OPENMASK) == GDBM_NEWDB && file_stat.st_size != 0)
{
TRUNCATE (dbf);
fstat (dbf->desc, &file_stat);
@@ -244,6 +191,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
{
if (flags & GDBM_BSEXACT)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
gdbm_close (dbf);
gdbm_set_errno (NULL, GDBM_BLOCK_SIZE_ERROR, FALSE);
return NULL;
@@ -257,6 +206,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->header = (gdbm_file_header *) calloc (1, block_size);
if (dbf->header == NULL)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
gdbm_close (dbf);
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return NULL;
@@ -272,6 +223,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->dir = (off_t *) malloc (dbf->header->dir_size);
if (dbf->dir == NULL)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
gdbm_close (dbf);
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return NULL;
@@ -286,6 +239,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->bucket = (hash_bucket *) malloc (dbf->header->bucket_size);
if (dbf->bucket == NULL)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
gdbm_close (dbf);
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return NULL;
@@ -312,6 +267,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
rc = _gdbm_full_write (dbf, dbf->header, dbf->header->block_size);
if (rc)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
SAVE_ERRNO (gdbm_close (dbf));
gdbm_set_errno (NULL, rc, FALSE);
return NULL;
@@ -321,6 +278,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
rc = _gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size);
if (rc)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
SAVE_ERRNO (gdbm_close (dbf));
gdbm_set_errno (NULL, rc, FALSE);
return NULL;
@@ -330,6 +289,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
rc = _gdbm_full_write (dbf, dbf->bucket, dbf->header->bucket_size);
if (rc)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
SAVE_ERRNO (gdbm_close (dbf));
gdbm_set_errno (NULL, rc, FALSE);
return NULL;
@@ -351,6 +312,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
rc = _gdbm_full_read (dbf, &partial_header, sizeof (gdbm_file_header));
if (rc)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
SAVE_ERRNO (gdbm_close (dbf));
gdbm_set_errno (NULL, rc, FALSE);
return NULL;
@@ -360,6 +323,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
if (partial_header.header_magic != GDBM_MAGIC
&& partial_header.header_magic != GDBM_OMAGIC)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
gdbm_close (dbf);
switch (partial_header.header_magic)
{
@@ -382,6 +347,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->header = (gdbm_file_header *) malloc (partial_header.block_size);
if (dbf->header == NULL)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
gdbm_close (dbf);
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return NULL;
@@ -391,6 +358,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->header->block_size-sizeof (gdbm_file_header));
if (rc)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
SAVE_ERRNO (gdbm_close (dbf));
gdbm_set_errno (NULL, rc, FALSE);
return NULL;
@@ -400,6 +369,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
dbf->dir = (off_t *) malloc (dbf->header->dir_size);
if (dbf->dir == NULL)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
gdbm_close (dbf);
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return NULL;
@@ -409,6 +380,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
file_pos = __lseek (dbf, dbf->header->dir, SEEK_SET);
if (file_pos != dbf->header->dir)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
SAVE_ERRNO (gdbm_close (dbf));
gdbm_set_errno (NULL, GDBM_FILE_SEEK_ERROR, FALSE);
return NULL;
@@ -417,6 +390,8 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
rc = _gdbm_full_read (dbf, dbf->dir, dbf->header->dir_size);
if (rc)
{
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
SAVE_ERRNO (gdbm_close (dbf));
gdbm_set_errno (NULL, rc, FALSE);
return NULL;
@@ -432,9 +407,9 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
else
{
/* gdbm_errno should already be set. */
- SAVE_ERRNO (close (dbf->desc);
- free (dbf->name);
- free (dbf));
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
+ SAVE_ERRNO (gdbm_close (dbf));
return NULL;
}
}
@@ -453,7 +428,64 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
/* Everything is fine, return the pointer to the file
information structure. */
return dbf;
+}
+
+/* Initialize dbm system. FILE is a pointer to the file name. If the file
+ has a size of zero bytes, a file initialization procedure is performed,
+ setting up the initial structure in the file. BLOCK_SIZE is used during
+ initialization to determine the size of various constructs. If the value
+ is less than GDBM_MIN_BLOCK_SIZE, the file system blocksize is used,
+ otherwise the value of BLOCK_SIZE is used. BLOCK_SIZE is ignored if the
+ file has previously initialized. If FLAGS is set to GDBM_READ the user
+ wants to just read the database and any call to dbm_store or dbm_delete
+ will fail. Many readers can access the database at the same time. If FLAGS
+ is set to GDBM_WRITE, the user wants both read and write access to the
+ database and requires exclusive access. If FLAGS is GDBM_WRCREAT, the user
+ wants both read and write access to the database and if the database does
+ not exist, create a new one. If FLAGS is GDBM_NEWDB, the user want a
+ new database created, regardless of whether one existed, and wants read
+ and write access to the new database. Any error detected will cause a
+ return value of null and an approprate value will be in gdbm_errno. If
+ no errors occur, a pointer to the "gdbm file descriptor" will be
+ returned. */
+
+
+GDBM_FILE
+gdbm_open (const char *file, int block_size, int flags, int mode,
+ void (*fatal_func) (const char *))
+{
+ int fd;
+ /* additional bits for open(2) flags */
+ int fbits = 0;
+
+ switch (flags & GDBM_OPENMASK)
+ {
+ case GDBM_READER:
+ fbits = O_RDONLY;
+ break;
+
+ case GDBM_WRITER:
+ fbits = O_RDWR;
+ break;
+ case GDBM_NEWDB:
+ fbits = O_RDWR|O_CREAT;
+ break;
+
+ default:
+ fbits = O_RDWR|O_CREAT;
+ }
+ if (flags & GDBM_CLOEXEC)
+ fbits |= O_CLOEXEC;
+
+ fd = open (file, fbits, mode);
+ if (fd < 0)
+ {
+ gdbm_set_errno (NULL, GDBM_FILE_OPEN_ERROR, FALSE);
+ return NULL;
+ }
+ return gdbm_fd_open (fd, file, block_size, flags | GDBM_CLOERROR,
+ fatal_func);
}
/* Initialize the bucket cache. */
diff --git a/src/gdbmreorg.c b/src/gdbmreorg.c
index 13fa7fc..e9b7742 100644
--- a/src/gdbmreorg.c
+++ b/src/gdbmreorg.c
@@ -19,35 +19,8 @@
/* Include system configuration before all else. */
#include "autoconf.h"
-
#include "gdbmdefs.h"
-#if !HAVE_RENAME
-
-/* Rename takes OLD_NAME and renames it as NEW_NAME. If it can not rename
- the file a non-zero value is returned. OLD_NAME is guaranteed to
- remain if it can't be renamed. It assumes NEW_NAME always exists (due
- to being used in gdbm). */
-
-static int
-_gdbm_rename (char *old_name, char *new_name)
-{
- if (unlink (new_name) != 0)
- return -1;
-
- if (link (old_name, new_name) != 0)
- return -1;
-
- unlink (old_name);
- return 0;
-
-}
-
-# define rename _gdbm_rename
-#endif
-
-
-
/* Reorganize the database. This requires creating a new file and inserting
all the elements in the old file DBF into the new file. The new file
is then renamed to the same name as the old file and DBF is updated to
@@ -58,165 +31,11 @@ _gdbm_rename (char *old_name, char *new_name)
int
gdbm_reorganize (GDBM_FILE dbf)
{
- GDBM_FILE new_dbf; /* The new file. */
- char *new_name; /* A temporary name. */
- int len; /* Used in new_name construction. */
- datum key, nextkey, data; /* For the sequential sweep. */
- struct stat fileinfo; /* Information about the file. */
- int index; /* Use in moving the bucket cache. */
-
+ gdbm_recovery rcvr;
+
/* Return immediately if the database needs recovery */
GDBM_ASSERT_CONSISTENCY (dbf, -1);
- /* Readers can not reorganize! */
- if (dbf->read_write == GDBM_READER)
- {
- gdbm_set_errno (dbf, GDBM_READER_CANT_REORGANIZE, FALSE);
- return -1;
- }
-
- /* Get the mode for the old file */
- if (fstat (dbf->desc, &fileinfo))
- {
- gdbm_set_errno (dbf, GDBM_FILE_STAT_ERROR, FALSE);
- return -1;
- }
-
- /* Initialize the gdbm_errno variable. */
- gdbm_set_errno (dbf, GDBM_NO_ERROR, FALSE);
-
- /* Construct new name for temporary file. */
- len = strlen (dbf->name);
- new_name = (char *) malloc (len + 3);
- if (new_name == NULL)
- {
- gdbm_set_errno (dbf, GDBM_MALLOC_ERROR, FALSE);
- return -1;
- }
- strcpy (&new_name[0], dbf->name);
- new_name[len+2] = 0;
- new_name[len+1] = '#';
- while ( (len > 0) && new_name[len-1] != '/')
- {
- new_name[len] = new_name[len-1];
- len -= 1;
- }
- new_name[len] = '#';
-
- /* Open the new database. */
- new_dbf = gdbm_open (new_name, dbf->header->block_size,
- GDBM_WRCREAT | (dbf->cloexec ? GDBM_CLOEXEC : 0),
- fileinfo.st_mode, dbf->fatal_err);
-
- if (new_dbf == NULL)
- {
- free (new_name);
- gdbm_set_errno (NULL, GDBM_REORGANIZE_FAILED, FALSE);
- return -1;
- }
-
-
- /* For each item in the old database, add an entry in the new. */
- key = gdbm_firstkey (dbf);
-
- while (key.dptr != NULL)
- {
- data = gdbm_fetch (dbf, key);
- if (data.dptr != NULL)
- {
- /* Add the data to the new file. */
- if (gdbm_store (new_dbf, key, data, GDBM_INSERT) != 0)
- {
- gdbm_close (new_dbf);
- gdbm_set_errno (NULL, GDBM_REORGANIZE_FAILED, FALSE);
- unlink (new_name);
- free (new_name);
- return -1;
- }
- }
<