diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2021-07-29 10:24:18 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2021-07-29 10:32:27 +0300 |
commit | 827ef17081ff2b53ba6daa86d5ea55982e7e8d5a (patch) | |
tree | 300aad3aa9faf80398732c13a72a40fb83da2cc7 | |
parent | 016aaadca5bf994678a56399c63ae0ac28adfc48 (diff) | |
download | gdbm-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.c | 2 | ||||
-rw-r--r-- | src/gdbm.h.in | 2 | ||||
-rw-r--r-- | src/gdbmdefs.h | 31 | ||||
-rw-r--r-- | src/gdbmopen.c | 212 | ||||
-rw-r--r-- | src/gdbmtool.c | 74 |
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 } }; |