diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2021-03-16 09:30:29 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2021-03-16 09:30:29 +0200 |
commit | fd5cf245ea6d3e2f36dc349d8013ef62f4a9d532 (patch) | |
tree | 9d4b6310592ac2ae72a8cd31269bb7adde9c58aa | |
parent | 40a464d322c720c88a50c3eb0a8c1d87e6079f89 (diff) | |
download | gdbm-fd5cf245ea6d3e2f36dc349d8013ef62f4a9d532.tar.gz gdbm-fd5cf245ea6d3e2f36dc349d8013ef62f4a9d532.tar.bz2 |
Improve reading and validation of available blocks (avail_block).
This fixes remaining failures in http://puszcza.gnu.org.ua/bugs/?501.
* src/falloc.c (_gdbm_avail_block_read): New function.
(pop_avail_block): Use _gdbm_avail_block_read.
* src/gdbmdefs.h: Fix typo in the comment.
* src/gdbmopen.c (gdbm_avail_block_validate): Take size of the avail
block in bytes as the third argument. All uses changed.
(PARTIAL_HEADER_SIZE)
(HEADER_AVAIL_SIZE): New macros.
(_gdbm_validate_header): Validate the avail block as well.
(validate_header): Don't validate the avail block fields here.
(gdbm_fd_open): Read partial header up to the avail field, then read
entire avail block using _gdbm_avail_block_read.
* src/gdbmtool.c (_gdbm_print_avail_list): Fix avail block calculation
(one extra entry was assumed).
Use _gdbm_avail_block_read to obtain and validate block.
* src/proto.h (_gdbm_avail_block_read): New proto.
(gdbm_avail_block_validate): Change signature.
-rw-r--r-- | src/falloc.c | 28 | ||||
-rw-r--r-- | src/gdbmdefs.h | 3 | ||||
-rw-r--r-- | src/gdbmopen.c | 49 | ||||
-rw-r--r-- | src/gdbmtool.c | 10 | ||||
-rw-r--r-- | src/proto.h | 3 |
5 files changed, 51 insertions, 42 deletions
diff --git a/src/falloc.c b/src/falloc.c index 94ff70f..4598064 100644 --- a/src/falloc.c +++ b/src/falloc.c @@ -31,6 +31,23 @@ static int push_avail_block (GDBM_FILE); static int pop_avail_block (GDBM_FILE); static int adjust_bucket_avail (GDBM_FILE); +int +_gdbm_avail_block_read (GDBM_FILE dbf, avail_block *avblk, size_t size) +{ + int rc; + + rc = _gdbm_full_read (dbf, avblk, size); + if (rc) + { + GDBM_DEBUG (GDBM_DEBUG_ERR|GDBM_DEBUG_OPEN, + "%s: error reading av_table: %s", + dbf->name, gdbm_db_strerror (dbf)); + } + else + rc = gdbm_avail_block_validate (dbf, avblk, size); + return rc; +} + /* Allocate space in the file DBF for a block NUM_BYTES in length. Return the file address of the start of the block. @@ -159,7 +176,6 @@ _gdbm_free (GDBM_FILE dbf, off_t file_adr, int num_bytes) static int pop_avail_block (GDBM_FILE dbf) { - int rc; off_t file_pos; /* For use with the lseek system call. */ avail_elem new_el; avail_block *new_blk; @@ -197,15 +213,7 @@ pop_avail_block (GDBM_FILE dbf) return -1; } - rc = _gdbm_full_read (dbf, new_blk, new_el.av_size); - if (rc) - { - free (new_blk); - _gdbm_fatal (dbf, gdbm_db_strerror (dbf)); - return -1; - } - - if (gdbm_avail_block_validate (dbf, new_blk)) + if (_gdbm_avail_block_read (dbf, new_blk, new_el.av_size)) { free (new_blk); _gdbm_fatal (dbf, gdbm_db_strerror (dbf)); diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h index 098a53b..4a71503 100644 --- a/src/gdbmdefs.h +++ b/src/gdbmdefs.h @@ -45,7 +45,7 @@ off_t_sum_ok (off_t a, off_t b) /* 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 + one fills 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. */ @@ -95,7 +95,6 @@ typedef struct 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 diff --git a/src/gdbmopen.c b/src/gdbmopen.c index dcd0fa2..6d0ab00 100644 --- a/src/gdbmopen.c +++ b/src/gdbmopen.c @@ -106,9 +106,10 @@ gdbm_avail_table_valid_p (GDBM_FILE dbf, avail_elem *av, int count) } int -gdbm_avail_block_validate (GDBM_FILE dbf, avail_block *avblk) +gdbm_avail_block_validate (GDBM_FILE dbf, avail_block *avblk, size_t size) { - if (!(gdbm_avail_block_valid_p (avblk) + if (!(((size - sizeof (avail_block)) / sizeof (avail_elem) + 1) >= avblk->count + && gdbm_avail_block_valid_p (avblk) && gdbm_avail_table_valid_p (dbf, avblk->av_table, avblk->count))) { GDBM_SET_ERRNO (dbf, GDBM_BAD_AVAIL, TRUE); @@ -197,22 +198,35 @@ validate_header (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->avail.size) - return GDBM_BAD_HEADER; - return result; } +/* + * GDBM file header is read in two chunks: first, all fields up to avail, + * then the avail block. PARTIAL_HEADER_SIZE is the size of the first + * chunk, and HEADER_AVAIL_SIZE(dbf) gives the size of the second one. + */ +#define PARTIAL_HEADER_SIZE (offsetof (gdbm_file_header, avail)) +#define HEADER_AVAIL_SIZE(dbf) \ + ((dbf)->header->block_size - PARTIAL_HEADER_SIZE) + int _gdbm_validate_header (GDBM_FILE dbf) { struct stat file_stat; - + int rc; + if (fstat (dbf->desc, &file_stat)) return GDBM_FILE_STAT_ERROR; - return validate_header (dbf->header, &file_stat); + rc = validate_header (dbf->header, &file_stat); + if (rc == 0) + { + if (gdbm_avail_block_validate (dbf, &dbf->header->avail, + HEADER_AVAIL_SIZE (dbf))) + rc = GDBM_BAD_AVAIL; + } + return rc; } /* Do we have ftruncate? */ @@ -524,7 +538,7 @@ gdbm_fd_open (int fd, const char *file_name, int block_size, int rc; /* Read the partial file header. */ - if (_gdbm_full_read (dbf, &partial_header, sizeof (gdbm_file_header))) + if (_gdbm_full_read (dbf, &partial_header, PARTIAL_HEADER_SIZE)) { GDBM_DEBUG (GDBM_DEBUG_ERR|GDBM_DEBUG_OPEN, "%s: error reading partial header: %s", @@ -561,27 +575,16 @@ gdbm_fd_open (int fd, const char *file_name, int block_size, return NULL; } - memcpy (dbf->header, &partial_header, sizeof (gdbm_file_header)); - if (_gdbm_full_read (dbf, &dbf->header->avail.av_table[1], - dbf->header->block_size - sizeof (gdbm_file_header))) + memcpy (dbf->header, &partial_header, PARTIAL_HEADER_SIZE); + if (_gdbm_avail_block_read (dbf, &dbf->header->avail, + HEADER_AVAIL_SIZE (dbf))) { - GDBM_DEBUG (GDBM_DEBUG_ERR|GDBM_DEBUG_OPEN, - "%s: error reading av_table: %s", - dbf->name, gdbm_db_strerror (dbf)); if (!(flags & GDBM_CLOERROR)) dbf->desc = -1; SAVE_ERRNO (gdbm_close (dbf)); return NULL; } - if (gdbm_avail_block_validate (dbf, &dbf->header->avail)) - { - if (!(flags & GDBM_CLOERROR)) - dbf->desc = -1; - SAVE_ERRNO (gdbm_close (dbf)); - return NULL; - } - /* Allocate space for the hash table directory. */ dbf->dir = malloc (dbf->header->dir_size); if (dbf->dir == NULL) diff --git a/src/gdbmtool.c b/src/gdbmtool.c index d736411..1c473c9 100644 --- a/src/gdbmtool.c +++ b/src/gdbmtool.c @@ -286,7 +286,7 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) /* Initialize the variables for a pass throught the avail stack. */ temp = dbf->header->avail.next_block; - size = (dbf->header->avail.size * sizeof (avail_elem)) + size = ((dbf->header->avail.size - 1) * sizeof (avail_elem)) + sizeof (avail_block); av_stk = emalloc (size); @@ -299,7 +299,7 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) break; } - if (_gdbm_full_read (dbf, av_stk, size)) + if (_gdbm_avail_block_read (dbf, av_stk, size)) { terror ("read: %s", gdbm_db_strerror (dbf)); break; @@ -308,10 +308,8 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) /* Print the block! */ fprintf (fp, _("\nblock = %d\nsize = %d\ncount = %d\n"), temp, av_stk->size, av_stk->count); - if (gdbm_avail_block_validate (dbf, av_stk) == 0) - av_table_display (av_stk->av_table, av_stk->count, fp); - else - terror ("%s", gdbm_strerror (GDBM_BAD_AVAIL)); + av_table_display (av_stk->av_table, av_stk->count, fp); + temp = av_stk->next_block; } free (av_stk); diff --git a/src/proto.h b/src/proto.h index 2171250..920f718 100644 --- a/src/proto.h +++ b/src/proto.h @@ -30,6 +30,7 @@ int _gdbm_write_bucket (GDBM_FILE, cache_elem *); off_t _gdbm_alloc (GDBM_FILE, int); int _gdbm_free (GDBM_FILE, off_t, int); void _gdbm_put_av_elem (avail_elem, avail_elem [], int *, int); +int _gdbm_avail_block_read (GDBM_FILE dbf, avail_block *avblk, size_t size); /* From findkey.c */ char *_gdbm_read_entry (GDBM_FILE, int); @@ -49,7 +50,7 @@ void _gdbm_fatal (GDBM_FILE, const char *); int _gdbm_init_cache (GDBM_FILE, size_t); void _gdbm_cache_entry_invalidate (GDBM_FILE, int); -int gdbm_avail_block_validate (GDBM_FILE dbf, avail_block *avblk); +int gdbm_avail_block_validate (GDBM_FILE dbf, avail_block *avblk, size_t size); int gdbm_bucket_avail_table_validate (GDBM_FILE dbf, hash_bucket *bucket); int _gdbm_validate_header (GDBM_FILE dbf); |