aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-03-16 09:30:29 +0200
committerSergey Poznyakoff <gray@gnu.org>2021-03-16 09:30:29 +0200
commitfd5cf245ea6d3e2f36dc349d8013ef62f4a9d532 (patch)
tree9d4b6310592ac2ae72a8cd31269bb7adde9c58aa
parent40a464d322c720c88a50c3eb0a8c1d87e6079f89 (diff)
downloadgdbm-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.c28
-rw-r--r--src/gdbmdefs.h3
-rw-r--r--src/gdbmopen.c49
-rw-r--r--src/gdbmtool.c10
-rw-r--r--src/proto.h3
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);

Return to:

Send suggestions and report system problems to the System administrator.