summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-07-29 10:24:18 +0300
committerSergey Poznyakoff <gray@gnu.org>2021-07-29 10:32:27 +0300
commit827ef17081ff2b53ba6daa86d5ea55982e7e8d5a (patch)
tree300aad3aa9faf80398732c13a72a40fb83da2cc7
parent016aaadca5bf994678a56399c63ae0ac28adfc48 (diff)
downloadgdbm-827ef17081ff2b53ba6daa86d5ea55982e7e8d5a.tar.gz
Simplify gdbm_file_header
* src/gdbm.h.in (gdbm_convert): New proto. * src/gdbmdefs.h: Define GDBM_MAGIC and GDBM_NUMSYNC_MAGIC here. (gdbm_file_header): Remove the union. * src/gdbmopen.c (gdbm_header_avail): Return pointer to gdbm_ext_header in 4th argument. (validate_header_std,validate_header_numsync): Remove avail size verification. It will be checked later in gdbm_fd_open. (gdbm_fd_open): Check avail table size. (gdbm_convert): New function. * src/avail.c (gdbm_avail_traverse): Change the avail table offset calculation. * src/gdbmtool.c: New commands: upgrade, downgrade and sync. Fix output of the "header" command.
-rw-r--r--src/avail.c2
-rw-r--r--src/gdbm.h.in2
-rw-r--r--src/gdbmdefs.h31
-rw-r--r--src/gdbmopen.c212
-rw-r--r--src/gdbmtool.c74
5 files changed, 276 insertions, 45 deletions
diff --git a/src/avail.c b/src/avail.c
index 79eaeee..db7061c 100644
--- a/src/avail.c
+++ b/src/avail.c
@@ -219,7 +219,7 @@ gdbm_avail_traverse (GDBM_FILE dbf,
return -1;
// FIXME
- if (off_map_lookup (&map, offsetof (gdbm_file_header, v.avail_tab)))
+ if (off_map_lookup (&map, (char*)dbf->avail - (char*)dbf->header))
{
GDBM_SET_ERRNO (dbf, GDBM_MALLOC_ERROR, FALSE);
return -1;
diff --git a/src/gdbm.h.in b/src/gdbm.h.in
index f6a457a..4484e0c 100644
--- a/src/gdbm.h.in
+++ b/src/gdbm.h.in
@@ -129,6 +129,8 @@ extern int gdbm_reorganize (GDBM_FILE);
extern int gdbm_sync (GDBM_FILE);
extern int gdbm_failure_atomic (GDBM_FILE, const char *, const char *);
+extern int gdbm_convert (GDBM_FILE dbf, int flag);
+
enum gdbm_latest_snapshot_status
{
GDBM_SNAPSHOT_OK,
diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h
index 25ea6d9..fb90cf4 100644
--- a/src/gdbmdefs.h
+++ b/src/gdbmdefs.h
@@ -19,6 +19,18 @@
#include "systems.h"
#include "gdbmconst.h"
#include "gdbm.h"
+
+/* Determine our native magic number and bail if we can't. */
+#if SIZEOF_OFF_T == 4
+# define GDBM_MAGIC GDBM_MAGIC32
+# define GDBM_NUMSYNC_MAGIC GDBM_NUMSYNC_MAGIC32
+#elif SIZEOF_OFF_T == 8
+# define GDBM_MAGIC GDBM_MAGIC64
+# define GDBM_NUMSYNC_MAGIC GDBM_NUMSYNC_MAGIC64
+#else
+# error "Unsupported off_t size, contact GDBM maintainer. What crazy system is this?!?"
+#endif
+
#define DEFAULT_TEXT_DOMAIN PACKAGE
#include "gettext.h"
@@ -73,7 +85,6 @@ typedef struct
typedef struct
{
unsigned numsync; /* Number of synchronizations */
- avail_block avail_tab;
} gdbm_ext_header;
typedef struct
@@ -86,15 +97,14 @@ typedef struct
int bucket_size; /* Size in bytes of a hash bucket struct. */
int bucket_elems; /* Number of elements in a hash bucket. */
off_t next_block; /* The next unallocated block address. */
- union
- {
- gdbm_ext_header ext;
- avail_block avail_tab; /* This must be last because of the pseudo
- array in avail. This avail grows to fill
- the entire block. */
- } v;
+ /* In traditional GDBM database file, this header is followed by
+ avail_block with av_table occupying the rest of the disk block.
+
+ In extended GDBM database file, it is followed by a gdbm_ext_header,
+ and then by avail_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
@@ -240,7 +250,10 @@ struct gdbm_file_info
/* The table of available file space */
avail_block *avail;
- size_t avail_size;
+ size_t avail_size; /* Size of avail, in bytes */
+
+ /* Extended header (or NULL) */
+ gdbm_ext_header *xheader;
/* The hash table directory from extendable hashing. See Fagin et al,
ACM Trans on Database Systems, Vol 4, No 3. Sept 1979, 315-344 */
diff --git a/src/gdbmopen.c b/src/gdbmopen.c
index 54fcabc..14685bc 100644
--- a/src/gdbmopen.c
+++ b/src/gdbmopen.c
@@ -22,17 +22,6 @@
#include "gdbmdefs.h"
#include <stddef.h>
-/* Determine our native magic number and bail if we can't. */
-#if SIZEOF_OFF_T == 4
-# define GDBM_MAGIC GDBM_MAGIC32
-# define GDBM_NUMSYNC_MAGIC GDBM_NUMSYNC_MAGIC32
-#elif SIZEOF_OFF_T == 8
-# define GDBM_MAGIC GDBM_MAGIC64
-# define GDBM_NUMSYNC_MAGIC GDBM_NUMSYNC_MAGIC64
-#else
-# error "Unsupported off_t size, contact GDBM maintainer. What crazy system is this?!?"
-#endif
-
static void
compute_directory_size (blksize_t block_size,
int *ret_dir_size, int *ret_dir_bits)
@@ -61,19 +50,22 @@ bucket_element_count (size_t bucket_size)
static void
gdbm_header_avail (gdbm_file_header const *hdr,
- avail_block **avail_ptr, size_t *avail_size)
+ avail_block **avail_ptr, size_t *avail_size,
+ gdbm_ext_header **exhdr)
{
switch (hdr->header_magic)
{
case GDBM_OMAGIC:
case GDBM_MAGIC:
- *avail_ptr = (avail_block *) &hdr->v.avail_tab;
- *avail_size = (hdr->block_size - offsetof (gdbm_file_header, v.avail_tab));
+ *avail_ptr = (avail_block *) (hdr + 1);
+ *avail_size = (hdr->block_size - sizeof (gdbm_file_header));
+ *exhdr = NULL;
break;
case GDBM_NUMSYNC_MAGIC:
- *avail_ptr = (avail_block *) &hdr->v.ext.avail_tab;
- *avail_size = (hdr->block_size - offsetof (gdbm_file_header, v.ext.avail_tab));
+ *exhdr = (gdbm_ext_header *) (hdr + 1);
+ *avail_ptr = (avail_block *) (*exhdr + 1);
+ *avail_size = (hdr->block_size - (sizeof (gdbm_file_header) + sizeof (gdbm_ext_header)));
break;
}
}
@@ -87,7 +79,7 @@ validate_header_std (gdbm_file_header const *hdr, struct stat const *st)
if (!(hdr->block_size > 0
&& hdr->block_size > sizeof (gdbm_file_header)
&& hdr->block_size - sizeof (gdbm_file_header) >=
- sizeof(hdr->v.avail_tab.av_table[0])))
+ sizeof(avail_block)))
{
return GDBM_BLOCK_SIZE_ERROR;
}
@@ -121,10 +113,6 @@ validate_header_std (gdbm_file_header const *hdr, struct stat const *st)
if (hdr->bucket_elems != bucket_element_count (hdr->bucket_size))
return GDBM_BAD_HEADER;
- if (((hdr->block_size - sizeof (gdbm_file_header)) / sizeof (avail_elem) + 1)
- != hdr->v.avail_tab.size)
- return GDBM_BAD_HEADER;
-
return result;
}
@@ -135,9 +123,9 @@ validate_header_numsync (gdbm_file_header const *hdr, struct stat const *st)
int dir_size, dir_bits;
if (!(hdr->block_size > 0
- && hdr->block_size > sizeof (gdbm_file_header)
- && hdr->block_size - sizeof (gdbm_file_header) >=
- sizeof(hdr->v.ext.avail_tab.av_table[0])))
+ && hdr->block_size > (sizeof (gdbm_file_header) + sizeof (gdbm_ext_header))
+ && hdr->block_size - (sizeof (gdbm_file_header) + sizeof (gdbm_ext_header)) >=
+ sizeof(avail_block)))
{
return GDBM_BLOCK_SIZE_ERROR;
}
@@ -171,10 +159,6 @@ validate_header_numsync (gdbm_file_header const *hdr, struct stat const *st)
if (hdr->bucket_elems != bucket_element_count (hdr->bucket_size))
return GDBM_BAD_HEADER;
- if (((hdr->block_size - sizeof (gdbm_file_header)) / sizeof (avail_elem) + 1)
- != hdr->v.ext.avail_tab.size)
- return GDBM_BAD_HEADER;
-
return result;
}
@@ -448,7 +432,7 @@ gdbm_fd_open (int fd, const char *file_name, int block_size,
* Set block size. It must be initialized for gdbm_header_avail to work.
*/
dbf->header->block_size = block_size;
- gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size);
+ gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader);
dbf->header->dir_size = dir_size;
dbf->header->dir_bits = dir_bits;
@@ -601,7 +585,15 @@ gdbm_fd_open (int fd, const char *file_name, int block_size,
SAVE_ERRNO (gdbm_close (dbf));
return NULL;
}
- gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size);
+ gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader);
+
+ if (((dbf->header->block_size - (((char*)dbf->avail-(char*)dbf->header) + sizeof (avail_block))) / sizeof (avail_elem) + 1) != dbf->avail->size)
+ {
+ if (!(flags & GDBM_CLOERROR))
+ dbf->desc = -1;
+ GDBM_SET_ERRNO2 (NULL, GDBM_BAD_HEADER, FALSE, GDBM_DEBUG_OPEN);
+ return NULL;
+ }
if (gdbm_avail_block_validate (dbf, dbf->avail,
GDBM_HEADER_AVAIL_SIZE (dbf)))
@@ -766,3 +758,163 @@ _gdbm_file_size (GDBM_FILE dbf, off_t *psize)
*psize = dbf->file_size;
return 0;
}
+
+/*
+ * Convert from numsync to the standard GDBM format. This is pretty
+ * straightforward: the avail block gets expanded by
+ * sizeof(gdbm_ext_header), so we only need to shift the avail block
+ * up this number of bytes, change the avail and avail_size pointers
+ * and update the magic number.
+ */
+static int
+_gdbm_convert_from_numsync (GDBM_FILE dbf)
+{
+ avail_block *old_avail = dbf->avail;
+
+ /* Change the magic number */
+ dbf->header->header_magic = GDBM_MAGIC;
+ /* Update avail pointer and size */
+ gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader);
+
+ /* Move data up */
+ memmove (dbf->avail, old_avail, dbf->avail_size - sizeof (gdbm_ext_header));
+
+ /* Fix up the avail table size */
+ dbf->avail->size = (dbf->avail_size - offsetof (avail_block, av_table))
+ / sizeof (avail_elem);
+
+ dbf->header_changed = TRUE;
+
+ return 0;
+}
+
+/*
+ * Convert the database from the standard to extended (numsync) format.
+ * The avail block shrinks by sizeof(gdbm_ext_header) bytes. The av_table
+ * entries that don't fit into the new size need to be returned to the
+ * avail pool using the _gdbm_free call.
+ */
+static int
+_gdbm_convert_to_numsync (GDBM_FILE dbf)
+{
+ avail_block *old_avail = dbf->avail;
+ size_t old_avail_size = dbf->avail->size;
+ size_t n; /* Number of elements to return to the pool */
+ int rc;
+ avail_elem *av = NULL;
+
+ /* Change the magic number */
+ dbf->header->header_magic = GDBM_NUMSYNC_MAGIC;
+ /* Update avail pointer and size */
+ gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader);
+ /*
+ * Compute new av_table size.
+ * NOTE: Don't try to modify dbf->avail until the final move, otherwise
+ * the available block would end up clobbered. All modifications are
+ * applied to old_avail.
+ */
+ old_avail->size = (dbf->avail_size - offsetof (avail_block, av_table))
+ / sizeof (avail_elem);
+ /* Compute the number of avail elements that don't fit in the new table. */
+ n = old_avail_size - old_avail->size;
+ if (n > 0)
+ {
+ /* Stash them away */
+ av = calloc (n, sizeof (av[0]));
+ if (!av)
+ {
+ GDBM_SET_ERRNO (dbf, GDBM_MALLOC_ERROR, FALSE);
+ return -1;
+ }
+ n = 0;
+ while (old_avail->count > old_avail->size)
+ {
+ old_avail->count--;
+ av[n++] = old_avail->av_table[old_avail->count];
+ }
+ }
+
+ /*
+ * Move the modified avail block into its new place. From now on,
+ * old_avail may not be use. The database header is in consistent
+ * state and all modifications should be applied to it directly.
+ */
+ memmove (dbf->avail, old_avail, dbf->avail_size);
+
+ /* Initialize the extended header */
+ memset (dbf->xheader, 0, sizeof (dbf->xheader[0]));
+
+ rc = 0; /* Assume success */
+
+ if (av)
+ {
+ /* Return stashed av_table elements to the available pool. */
+ /* _gdbm_free needs a non-NULL bucket, so get one: */
+ if (!dbf->bucket)
+ rc = _gdbm_get_bucket (dbf, 0);
+ if (rc == 0)
+ {
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ {
+ rc = _gdbm_free (dbf, av[i].av_adr, av[i].av_size);
+ if (rc)
+ break;
+ }
+ }
+ free (av);
+ }
+
+ dbf->header_changed = TRUE;
+
+ return rc;
+}
+
+int
+gdbm_convert (GDBM_FILE dbf, int flag)
+{
+ int rc;
+
+ /* Return immediately if the database needs recovery */
+ GDBM_ASSERT_CONSISTENCY (dbf, -1);
+
+ /* First check to make sure this guy is a writer. */
+ if (dbf->read_write == GDBM_READER)
+ {
+ GDBM_SET_ERRNO2 (dbf, GDBM_READER_CANT_STORE, FALSE,
+ GDBM_DEBUG_STORE);
+ return -1;
+ }
+
+ switch (flag)
+ {
+ case 0:
+ case GDBM_NUMSYNC:
+ break;
+
+ default:
+ GDBM_SET_ERRNO2 (dbf, GDBM_ILLEGAL_DATA, FALSE,
+ GDBM_DEBUG_STORE);
+ return -1;
+ }
+
+ rc = 0;
+ switch (dbf->header->header_magic)
+ {
+ case GDBM_OMAGIC:
+ case GDBM_MAGIC:
+ if (flag == GDBM_NUMSYNC)
+ rc = _gdbm_convert_to_numsync (dbf);
+ break;
+
+ case GDBM_NUMSYNC_MAGIC:
+ if (flag == 0)
+ rc = _gdbm_convert_from_numsync (dbf);
+ }
+
+ if (rc == 0)
+ rc = _gdbm_end_update (dbf);
+
+ return 0;
+}
diff --git a/src/gdbmtool.c b/src/gdbmtool.c
index 66b3811..d1e9552 100644
--- a/src/gdbmtool.c
+++ b/src/gdbmtool.c
@@ -785,8 +785,28 @@ void
print_header_handler (struct handler_param *param)
{
FILE *fp = param->fp;
+ char const *type;
+
+ switch (gdbm_file->header->header_magic)
+ {
+ case GDBM_OMAGIC:
+ type = "Old GDBM";
+ break;
+
+ case GDBM_MAGIC:
+ type = "GDBM";
+ break;
+
+ case GDBM_NUMSYNC_MAGIC:
+ type = "GDBM numsync";
+ break;
+
+ default:
+ abort ();
+ }
fprintf (fp, _("\nFile Header: \n\n"));
+ fprintf (fp, _(" type = %s\n"), type);
fprintf (fp, _(" table = %lu\n"),
(unsigned long) gdbm_file->header->dir);
fprintf (fp, _(" table size = %d\n"), gdbm_file->header->dir_size);
@@ -797,12 +817,37 @@ print_header_handler (struct handler_param *param)
fprintf (fp, _(" header magic = %x\n"), gdbm_file->header->header_magic);
fprintf (fp, _(" next block = %lu\n"),
(unsigned long) gdbm_file->header->next_block);
- //FIXME
- fprintf (fp, _(" avail size = %d\n"), gdbm_file->header->v.avail_tab.size);
- fprintf (fp, _(" avail count = %d\n"), gdbm_file->header->v.avail_tab.count);
+
+ fprintf (fp, _(" avail size = %d\n"), gdbm_file->avail->size);
+ fprintf (fp, _(" avail count = %d\n"), gdbm_file->avail->count);
fprintf (fp, _(" avail nx blk = %lu\n"),
- (unsigned long) gdbm_file->header->v.avail_tab.next_block);
-}
+ (unsigned long) gdbm_file->avail->next_block);
+
+ // FIXME
+ if (gdbm_file->xheader)
+ fprintf (fp, _(" numsync = %u\n"), gdbm_file->xheader->numsync);
+}
+
+static void
+sync_handler (struct handler_param *param)
+{
+ if (gdbm_sync (gdbm_file))
+ terror ("%s", gdbm_db_strerror (gdbm_file));
+}
+
+static void
+upgrade_handler (struct handler_param *param)
+{
+ if (gdbm_convert (gdbm_file, GDBM_NUMSYNC))
+ terror ("%s", gdbm_db_strerror (gdbm_file));
+}
+
+static void
+downgrade_handler (struct handler_param *param)
+{
+ if (gdbm_convert (gdbm_file, 0))
+ terror ("%s", gdbm_db_strerror (gdbm_file));
+}
/* hash KEY - hash the key */
void
@@ -1272,6 +1317,24 @@ struct command command_tab[] = {
FALSE,
REPEAT_NEVER,
N_("print current program status") },
+ { S(sync), T_CMD,
+ checkdb_begin, sync_handler, NULL,
+ { { NULL } },
+ FALSE,
+ REPEAT_NEVER,
+ N_("Synchronize the database with disk copy") },
+ { S(upgrade), T_CMD,
+ checkdb_begin, upgrade_handler, NULL,
+ { { NULL } },
+ FALSE,
+ REPEAT_NEVER,
+ N_("Upgrade the database") },
+ { S(downgrade), T_CMD,
+ checkdb_begin, downgrade_handler, NULL,
+ { { NULL } },
+ FALSE,
+ REPEAT_NEVER,
+ N_("Downgrade the database") },
{ S(version), T_CMD,
NULL, print_version_handler, NULL,
{ { NULL } },
@@ -1345,6 +1408,7 @@ struct command command_tab[] = {
TRUE,
REPEAT_NEVER,
N_("query/set debug level") },
+
#undef S
{ 0 }
};