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 14:04:12 +0200
commit76a412165f6056787f5c3fc016673c98384cb369 (patch)
tree9ce612af1bd096dbf9c431fba85ce55cc53e7033
parent9b2d62a43e81d6c105bf443d55a00d1c271556ca (diff)
downloadgdbm-76a412165f6056787f5c3fc016673c98384cb369.tar.gz
gdbm-76a412165f6056787f5c3fc016673c98384cb369.tar.bz2
Improve reading and validation of available blocks (avail_block).
Fixes 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 ea9df3e..b1e53f9 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 7806c7a..cbb7195 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? */
@@ -526,7 +540,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",
@@ -563,27 +577,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 20e85ea..1d6b8dd 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 df5d83d..c4b1a08 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -32,6 +32,7 @@ int _gdbm_cache_flush (GDBM_FILE dbf);
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);
@@ -48,7 +49,7 @@ int _gdbm_end_update (GDBM_FILE);
void _gdbm_fatal (GDBM_FILE, const char *);
/* From gdbmopen.c */
-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.