diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-06-23 12:18:41 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-06-23 12:18:41 +0300 |
commit | a5f5662d3919580b7a7b0760a935358bca81174a (patch) | |
tree | 90058fe8dee84dd6b2bc64dc1b02f9703b1aee94 /src | |
parent | 9b7336575bf6bfa4b2683289308fea2670dfa3e3 (diff) | |
download | gdbm-a5f5662d3919580b7a7b0760a935358bca81174a.tar.gz gdbm-a5f5662d3919580b7a7b0760a935358bca81174a.tar.bz2 |
Silently restore sorting order of avail array when checking it.
* src/gdbmdefs.h (gdbm_avail_table_valid_p): Remove const qualifier from
the AV parameter.
* src/gdbmopen.c (gdbm_avail_table_valid_p): Don't insist on proper
ordering of the array. Restore it if neccessary.
Diffstat (limited to 'src')
-rw-r--r-- | src/gdbmdefs.h | 6 | ||||
-rw-r--r-- | src/gdbmopen.c | 39 |
2 files changed, 38 insertions, 7 deletions
diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h index af6f09c..9740b16 100644 --- a/src/gdbmdefs.h +++ b/src/gdbmdefs.h @@ -65,27 +65,27 @@ typedef struct int count; /* The number of entries in the table. */ off_t next_block; /* The file address of the next avail block. */ avail_elem av_table[1]; /* The table. Make it look like an array. */ } avail_block; /* Return true if avail_block is valid */ static int inline gdbm_avail_block_valid_p (avail_block const *av) { return (av->size > 1 && av->count >= 0 && av->count <= av->size); } -/* Return true if both AV and the size of AV->av_table are valid */ -extern int gdbm_avail_table_valid_p (GDBM_FILE dbf, - avail_elem const *av, int count); +/* Return true if both AV and the size of AV->av_table are valid. + See comment to this function in gdbmopen.c */ +extern int gdbm_avail_table_valid_p (GDBM_FILE dbf, avail_elem *av, int count); /* The dbm file header keeps track of the current location of the hash directory and the free space in the file. */ typedef struct { int header_magic; /* Version of file. */ int block_size; /* The optimal i/o blocksize from stat. */ off_t dir; /* File address of hash directory table. */ int dir_size; /* Size in bytes of the table. */ int dir_bits; /* The number of address bits used in the table.*/ int bucket_size; /* Size in bytes of a hash bucket struct. */ diff --git a/src/gdbmopen.c b/src/gdbmopen.c index 677f1cf..9747517 100644 --- a/src/gdbmopen.c +++ b/src/gdbmopen.c @@ -47,39 +47,70 @@ compute_directory_size (blksize_t block_size, } *ret_dir_size = dir_size; *ret_dir_bits = dir_bits; } static inline int bucket_element_count (size_t bucket_size) { return (bucket_size - sizeof (hash_bucket)) / sizeof (bucket_element) + 1; } +static int +avail_comp (void const *a, void const *b) +{ + avail_elem const *ava = a; + avail_elem const *avb = b; + return ava->av_size - avb->av_size; +} + +/* Returns true if the avail array AV[0]@COUNT is valid. + + As a side effect, ensures the array is sorted by element size + in increasing order and restores the ordering if necessary. + + The proper ordering could have been clobbered in versions of GDBM<=1.14, + by a call to _gdbm_put_av_elem with the can_merge parameter set to + TRUE. This happened in two cases: either because the GDBM_COALESCEBLKS + was set, and (quite unfortunately) when _gdbm_put_av_elem was called + from pop_avail_block in falloc.c. The latter case is quite common, + which means that there can be lots of existing databases with broken + ordering of avail arrays. Thus, restoring of the proper ordering + is essential for people to be able to use their existing databases. +*/ int -gdbm_avail_table_valid_p (GDBM_FILE dbf, avail_elem const *av, int count) +gdbm_avail_table_valid_p (GDBM_FILE dbf, avail_elem *av, int count) { off_t prev = 0; int i; - + int needs_sorting = 0; + prev = 0; for (i = 0; i < count; i++, av++) { - if (!(av->av_size >= prev - && av->av_adr >= dbf->header->bucket_size + if (!(av->av_adr >= dbf->header->bucket_size && av->av_adr + av->av_size <= dbf->header->next_block)) return 0; + if (av->av_size < prev) + needs_sorting = 1; prev = av->av_size; } + + if (needs_sorting && dbf->read_write) + { + GDBM_DEBUG (GDBM_DEBUG_ERR, "restoring sort order"); + qsort (av, count, sizeof av[0], avail_comp); + } + return 1; } int gdbm_avail_block_validate (GDBM_FILE dbf, avail_block *avblk) { if (!(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); return -1; } |