aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-05-25 14:17:11 +0300
committerSergey Poznyakoff <gray@gnu.org>2018-05-25 15:00:01 +0300
commit655cd193549e20ea8a8e77125adec7c5909c067e (patch)
treed59f997331bfcec8c0c17afb7bc8438215f09102 /src
parent8d2f483b28f8418703982658b3e7dda7a96ad335 (diff)
downloadgdbm-655cd193549e20ea8a8e77125adec7c5909c067e.tar.gz
gdbm-655cd193549e20ea8a8e77125adec7c5909c067e.tar.bz2
More database consistency checks
* NEWS: Update. * THANKS: Update. * src/bucket.c (_gdbm_get_bucket): Check if directory entry is valid. Don't cache invalid buckets. * src/gdbm.h.in (GDBM_BAD_DIR_ENTRY): New error code. * src/gdbmerrno.c: Likewise. * src/gdbmopen.c (validate_header): Compute expected number of bucket elements based on the bucket size, not on the block size. (_gdbm_init_cache_entry): New function. * src/proto.h (_gdbm_init_cache_entry): New proto. * src/recover.c (gdbm_recover): Clear error state after return from check_db indicating failure.
Diffstat (limited to 'src')
-rw-r--r--src/bucket.c67
-rw-r--r--src/gdbm.h.in3
-rw-r--r--src/gdbmdefs.h2
-rw-r--r--src/gdbmerrno.c3
-rw-r--r--src/gdbmopen.c25
-rw-r--r--src/gdbmtool.c2
-rw-r--r--src/mmap.c1
-rw-r--r--src/proto.h1
-rw-r--r--src/recover.c1
9 files changed, 72 insertions, 33 deletions
diff --git a/src/bucket.c b/src/bucket.c
index ce1915a..1d961e2 100644
--- a/src/bucket.c
+++ b/src/bucket.c
@@ -42,11 +42,27 @@ _gdbm_new_bucket (GDBM_FILE dbf, hash_bucket *bucket, int bits)
42 bucket->h_table[index].hash_value = -1; 42 bucket->h_table[index].hash_value = -1;
43} 43}
44 44
45/* Return true if the directory entry at DIR_INDEX can be considered
46 valid. This means that DIR_INDEX is in the valid range for addressing
47 the dir array, and the offset stored in dir[DIR_INDEX] points past
48 first two blocks in file. This does not necessarily mean that there's
49 a valid bucket or data block at that offset. All this implies is that
50 it is safe to use the offset for look up in the bucket cache and to
51 attempt to read a block at that offset. */
52int
53gdbm_dir_entry_valid_p (GDBM_FILE dbf, int dir_index)
54{
55 return dir_index >= 0
56 && dir_index < GDBM_DIR_COUNT (dbf)
57 && dbf->dir[dir_index] >= 2*dbf->header->block_size;
58}
59
45/* Find a bucket for DBF that is pointed to by the bucket directory from 60/* Find a bucket for DBF that is pointed to by the bucket directory from
46 location DIR_INDEX. The bucket cache is first checked to see if it 61 location DIR_INDEX. The bucket cache is first checked to see if it
47 is already in memory. If not, a bucket may be tossed to read the new 62 is already in memory. If not, a bucket may be tossed to read the new
48 bucket. In any case, the requested bucket becomes the "current" bucket 63 bucket. On success, the requested bucket becomes the "current" bucket
49 and dbf->bucket points to the correct bucket. */ 64 and dbf->bucket points to the correct bucket. On error, the current
65 bucket remains unchanged. */
50 66
51int 67int
52_gdbm_get_bucket (GDBM_FILE dbf, int dir_index) 68_gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
@@ -56,6 +72,13 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
56 off_t file_pos; /* The return address for lseek. */ 72 off_t file_pos; /* The return address for lseek. */
57 int index; /* Loop index. */ 73 int index; /* Loop index. */
58 74
75 if (!gdbm_dir_entry_valid_p (dbf, dir_index))
76 {
77 /* FIXME: negative caching? */
78 GDBM_SET_ERRNO (dbf, GDBM_BAD_DIR_ENTRY, TRUE);
79 return -1;
80 }
81
59 /* Initial set up. */ 82 /* Initial set up. */
60 dbf->bucket_dir = dir_index; 83 dbf->bucket_dir = dir_index;
61 bucket_adr = dbf->dir[dir_index]; 84 bucket_adr = dbf->dir[dir_index];
@@ -69,9 +92,11 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
69 } 92 }
70 } 93 }
71 94
72 /* Is that one is not already current, we must find it. */ 95 /* If that one is not already current, we must find it. */
73 if (dbf->cache_entry->ca_adr != bucket_adr) 96 if (dbf->cache_entry->ca_adr != bucket_adr)
74 { 97 {
98 size_t lru;
99
75 /* Look in the cache. */ 100 /* Look in the cache. */
76 for (index = 0; index < dbf->cache_size; index++) 101 for (index = 0; index < dbf->cache_size; index++)
77 { 102 {
@@ -84,30 +109,30 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
84 } 109 }
85 110
86 /* It is not in the cache, read it from the disk. */ 111 /* It is not in the cache, read it from the disk. */
87 dbf->last_read = (dbf->last_read + 1) % dbf->cache_size;
88 if (dbf->bucket_cache[dbf->last_read].ca_changed)
89 {
90 if (_gdbm_write_bucket (dbf, &dbf->bucket_cache[dbf->last_read]))
91 return -1;
92 }
93 dbf->bucket_cache[dbf->last_read].ca_adr = bucket_adr;
94 dbf->bucket = dbf->bucket_cache[dbf->last_read].ca_bucket;
95 dbf->cache_entry = &dbf->bucket_cache[dbf->last_read];
96 dbf->cache_entry->ca_data.elem_loc = -1;
97 dbf->cache_entry->ca_changed = FALSE;
98 112
99 /* Read the bucket. */ 113 /* Position the file pointer */
100 file_pos = GDBM_DEBUG_OVERRIDE ("_gdbm_get_bucket:seek-failure", 114 file_pos = GDBM_DEBUG_OVERRIDE ("_gdbm_get_bucket:seek-failure",
101 __lseek (dbf, bucket_adr, SEEK_SET)); 115 __lseek (dbf, bucket_adr, SEEK_SET));
102 if (file_pos != bucket_adr) 116 if (file_pos != bucket_adr)
103 { 117 {
104 _gdbm_fatal (dbf, _("lseek error"));
105 GDBM_SET_ERRNO (dbf, GDBM_FILE_SEEK_ERROR, TRUE); 118 GDBM_SET_ERRNO (dbf, GDBM_FILE_SEEK_ERROR, TRUE);
119 _gdbm_fatal (dbf, _("lseek error"));
106 return -1; 120 return -1;
107 } 121 }
108 122
123 /* Flush and drop the last recently used cache entry */
124 lru = (dbf->last_read + 1) % dbf->cache_size;
125 if (dbf->bucket_cache[lru].ca_changed)
126 {
127 if (_gdbm_write_bucket (dbf, &dbf->bucket_cache[lru]))
128 return -1;
129 }
130 _gdbm_init_cache_entry (dbf, lru);
131
132 /* Read the bucket. */
109 rc = GDBM_DEBUG_OVERRIDE ("_gdbm_get_bucket:read-failure", 133 rc = GDBM_DEBUG_OVERRIDE ("_gdbm_get_bucket:read-failure",
110 _gdbm_full_read (dbf, dbf->bucket, dbf->header->bucket_size)); 134 _gdbm_full_read (dbf, dbf->bucket_cache[lru].ca_bucket,
135 dbf->header->bucket_size));
111 if (rc) 136 if (rc)
112 { 137 {
113 GDBM_DEBUG (GDBM_DEBUG_ERR, 138 GDBM_DEBUG (GDBM_DEBUG_ERR,
@@ -126,8 +151,14 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
126 GDBM_SET_ERRNO (dbf, GDBM_BAD_BUCKET, TRUE); 151 GDBM_SET_ERRNO (dbf, GDBM_BAD_BUCKET, TRUE);
127 return -1; 152 return -1;
128 } 153 }
154 /* Finally, store it in cache */
155 dbf->last_read = lru;
156 dbf->bucket_cache[lru].ca_adr = bucket_adr;
157 dbf->bucket = dbf->bucket_cache[lru].ca_bucket;
158 dbf->cache_entry = &dbf->bucket_cache[lru];
159 dbf->cache_entry->ca_data.elem_loc = -1;
160 dbf->cache_entry->ca_changed = FALSE;
129 } 161 }
130
131 return 0; 162 return 0;
132} 163}
133 164
diff --git a/src/gdbm.h.in b/src/gdbm.h.in
index e576c69..e6bdc58 100644
--- a/src/gdbm.h.in
+++ b/src/gdbm.h.in
@@ -224,9 +224,10 @@ extern int gdbm_copy_meta (GDBM_FILE dst, GDBM_FILE src);
224# define GDBM_BAD_HEADER 33 224# define GDBM_BAD_HEADER 33
225# define GDBM_BAD_AVAIL 34 225# define GDBM_BAD_AVAIL 34
226# define GDBM_BAD_HASH_TABLE 35 226# define GDBM_BAD_HASH_TABLE 35
227# define GDBM_BAD_DIR_ENTRY 36
227 228
228# define _GDBM_MIN_ERRNO 0 229# define _GDBM_MIN_ERRNO 0
229# define _GDBM_MAX_ERRNO GDBM_BAD_HASH_TABLE 230# define _GDBM_MAX_ERRNO GDBM_BAD_DIR_ENTRY
230 231
231/* This one was never used and will be removed in the future */ 232/* This one was never used and will be removed in the future */
232# define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR 233# define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR
diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h
index 37a5637..a53f637 100644
--- a/src/gdbmdefs.h
+++ b/src/gdbmdefs.h
@@ -225,7 +225,7 @@ struct gdbm_file_info
225 /* The bucket cache. */ 225 /* The bucket cache. */
226 cache_elem *bucket_cache; 226 cache_elem *bucket_cache;
227 size_t cache_size; 227 size_t cache_size;
228 int last_read; 228 size_t last_read;
229 229
230 /* Points to the current hash bucket in the cache. */ 230 /* Points to the current hash bucket in the cache. */
231 hash_bucket *bucket; 231 hash_bucket *bucket;
diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c
index 52cfe30..8e24b82 100644
--- a/src/gdbmerrno.c
+++ b/src/gdbmerrno.c
@@ -135,7 +135,8 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = {
135 [GDBM_BAD_BUCKET] = N_("Malformed bucket header"), 135 [GDBM_BAD_BUCKET] = N_("Malformed bucket header"),
136 [GDBM_BAD_HEADER] = N_("Malformed database file header"), 136 [GDBM_BAD_HEADER] = N_("Malformed database file header"),
137 [GDBM_BAD_AVAIL] = N_("Malformed avail_block"), 137 [GDBM_BAD_AVAIL] = N_("Malformed avail_block"),
138 [GDBM_BAD_HASH_TABLE] = N_("Malformed hash table") 138 [GDBM_BAD_HASH_TABLE] = N_("Malformed hash table"),
139 [GDBM_BAD_DIR_ENTRY] = N_("Invalid directory entry")
139}; 140};
140 141
141const char * 142const char *
diff --git a/src/gdbmopen.c b/src/gdbmopen.c
index 52b2739..974e9e2 100644
--- a/src/gdbmopen.c
+++ b/src/gdbmopen.c
@@ -50,10 +50,9 @@ compute_directory_size (blksize_t block_size,
50} 50}
51 51
52static inline int 52static inline int
53bucket_element_count (gdbm_file_header const *hdr) 53bucket_element_count (size_t bucket_size)
54{ 54{
55 return (hdr->block_size - sizeof (hash_bucket)) 55 return (bucket_size - sizeof (hash_bucket)) / sizeof (bucket_element) + 1;
56 / sizeof (bucket_element) + 1;
57} 56}
58 57
59int 58int
@@ -115,7 +114,7 @@ validate_header (gdbm_file_header const *hdr, struct stat const *st)
115 if (!(hdr->bucket_size > 0 && hdr->bucket_size > sizeof(hash_bucket))) 114 if (!(hdr->bucket_size > 0 && hdr->bucket_size > sizeof(hash_bucket)))
116 return GDBM_BAD_HEADER; 115 return GDBM_BAD_HEADER;
117 116
118 if (hdr->bucket_elems != bucket_element_count (hdr)) 117 if (hdr->bucket_elems != bucket_element_count (hdr->bucket_size))
119 return GDBM_BAD_HEADER; 118 return GDBM_BAD_HEADER;
120 119
121 /* Validate the avail block */ 120 /* Validate the avail block */
@@ -325,7 +324,7 @@ gdbm_fd_open (int fd, const char *file_name, int block_size,
325 dbf->header->dir = dbf->header->block_size; 324 dbf->header->dir = dbf->header->block_size;
326 325
327 /* Create the first and only hash bucket. */ 326 /* Create the first and only hash bucket. */
328 dbf->header->bucket_elems = bucket_element_count (dbf->header); 327 dbf->header->bucket_elems = bucket_element_count (dbf->header->block_size);
329 dbf->header->bucket_size = dbf->header->block_size; 328 dbf->header->bucket_size = dbf->header->block_size;
330 dbf->bucket = calloc (1, dbf->header->bucket_size); 329 dbf->bucket = calloc (1, dbf->header->bucket_size);
331 if (dbf->bucket == NULL)