diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-05-25 14:17:11 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-05-25 15:00:01 +0300 |
commit | 655cd193549e20ea8a8e77125adec7c5909c067e (patch) | |
tree | d59f997331bfcec8c0c17afb7bc8438215f09102 /src | |
parent | 8d2f483b28f8418703982658b3e7dda7a96ad335 (diff) | |
download | gdbm-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.c | 67 | ||||
-rw-r--r-- | src/gdbm.h.in | 3 | ||||
-rw-r--r-- | src/gdbmdefs.h | 2 | ||||
-rw-r--r-- | src/gdbmerrno.c | 3 | ||||
-rw-r--r-- | src/gdbmopen.c | 25 | ||||
-rw-r--r-- | src/gdbmtool.c | 2 | ||||
-rw-r--r-- | src/mmap.c | 1 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/recover.c | 1 |
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. */ | ||
52 | int | ||
53 | gdbm_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 | ||
51 | int | 67 | int |
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 | ||
141 | const char * | 142 | const 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 | ||
52 | static inline int | 52 | static inline int |
53 | bucket_element_count (gdbm_file_header const *hdr) | 53 | bucket_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 | ||
59 | int | 58 | int |
@@ -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) |