Line | Count | Source (jump to first uncovered line) |
1 | | /* gdbmopen.c - Open the dbm file and initialize data structures for use. */ |
2 | | |
3 | | /* This file is part of GDBM, the GNU data base manager. |
4 | | Copyright (C) 1990-2023 Free Software Foundation, Inc. |
5 | | |
6 | | GDBM is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GDBM is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ |
18 | | |
19 | | /* Include system configuration before all else. */ |
20 | | #include "autoconf.h" |
21 | | |
22 | | #include "gdbmdefs.h" |
23 | | #include <stddef.h> |
24 | | |
25 | | static void |
26 | | compute_directory_size (blksize_t block_size, |
27 | | int *ret_dir_size, int *ret_dir_bits) |
28 | 17.7k | { |
29 | | /* Create the initial hash table directory. */ |
30 | 17.7k | int dir_size = 8 * sizeof (off_t); |
31 | 17.7k | int dir_bits = 3; |
32 | | |
33 | 17.7k | if (block_size > INT_MAX / 2) |
34 | 13 | block_size = INT_MAX / 2; |
35 | 73.0k | while (dir_size < block_size && dir_bits < GDBM_HASH_BITS - 3) |
36 | 55.3k | { |
37 | 55.3k | dir_size <<= 1; |
38 | 55.3k | dir_bits++; |
39 | 55.3k | } |
40 | | |
41 | 17.7k | *ret_dir_size = dir_size; |
42 | 17.7k | *ret_dir_bits = dir_bits; |
43 | 17.7k | } |
44 | | |
45 | | static inline int |
46 | | bucket_element_count (size_t bucket_size) |
47 | 11.2k | { |
48 | 11.2k | return (bucket_size - sizeof (hash_bucket)) / sizeof (bucket_element) + 1; |
49 | 11.2k | } |
50 | | |
51 | | static void |
52 | | gdbm_header_avail (gdbm_file_header *hdr, |
53 | | avail_block **avail_ptr, size_t *avail_size, |
54 | | gdbm_ext_header **exhdr) |
55 | 13.5k | { |
56 | 13.5k | switch (hdr->header_magic) |
57 | 13.5k | { |
58 | 947 | case GDBM_OMAGIC: |
59 | 10.1k | case GDBM_MAGIC: |
60 | 10.1k | *exhdr = NULL; |
61 | 10.1k | *avail_ptr = &((gdbm_file_standard_header*)hdr)->avail; |
62 | 10.1k | *avail_size = (hdr->block_size - |
63 | 10.1k | offsetof (gdbm_file_standard_header, avail)); |
64 | 10.1k | break; |
65 | | |
66 | 3.34k | case GDBM_NUMSYNC_MAGIC: |
67 | 3.34k | *exhdr = &((gdbm_file_extended_header*)hdr)->ext; |
68 | 3.34k | *avail_ptr = &((gdbm_file_extended_header*)hdr)->avail; |
69 | 3.34k | *avail_size = (hdr->block_size - |
70 | 3.34k | offsetof (gdbm_file_extended_header, avail)); |
71 | 3.34k | break; |
72 | 13.5k | } |
73 | 13.5k | } |
74 | | |
75 | | static int |
76 | | validate_header_std (gdbm_file_header const *hdr, struct stat const *st) |
77 | 5.68k | { |
78 | 5.68k | int result = GDBM_NO_ERROR; |
79 | 5.68k | int dir_size, dir_bits; |
80 | | |
81 | 5.68k | if (!(hdr->block_size > 0 |
82 | 5.68k | && hdr->block_size > sizeof (gdbm_file_header) |
83 | 5.68k | && hdr->block_size - sizeof (gdbm_file_header) >= |
84 | 5.64k | sizeof(avail_block))) |
85 | 44 | { |
86 | 44 | return GDBM_BLOCK_SIZE_ERROR; |
87 | 44 | } |
88 | | |
89 | | /* Technically speaking, the condition below should read |
90 | | hdr->next_block != st->st_size |
91 | | However, gdbm versions prior to commit 4e819c98 could leave |
92 | | hdr->next_block pointing beyond current end of file. To ensure |
93 | | backward compatibility with these versions, the condition has been |
94 | | slackened to this: */ |
95 | 5.64k | if (hdr->next_block < st->st_size) |
96 | 480 | result = GDBM_NEED_RECOVERY; |
97 | | |
98 | | /* Make sure dir and dir + dir_size fall within the file boundary */ |
99 | 5.64k | if (!(hdr->dir > 0 |
100 | 5.64k | && hdr->dir < st->st_size |
101 | 5.64k | && hdr->dir_size > 0 |
102 | 5.64k | && hdr->dir + hdr->dir_size < st->st_size)) |
103 | 311 | return GDBM_BAD_HEADER; |
104 | | |
105 | 5.32k | compute_directory_size (hdr->block_size, &dir_size, &dir_bits); |
106 | 5.32k | if (!(hdr->dir_size >= dir_size)) |
107 | 66 | return GDBM_BAD_HEADER; |
108 | 5.26k | compute_directory_size (hdr->dir_size, &dir_size, &dir_bits); |
109 | 5.26k | if (hdr->dir_bits != dir_bits) |
110 | 78 | return GDBM_BAD_HEADER; |
111 | | |
112 | 5.18k | if (!(hdr->bucket_size > 0 && hdr->bucket_size > sizeof (hash_bucket))) |
113 | 38 | return GDBM_BAD_HEADER; |
114 | | |
115 | 5.14k | if (hdr->bucket_elems != bucket_element_count (hdr->bucket_size)) |
116 | 61 | return GDBM_BAD_HEADER; |
117 | | |
118 | 5.08k | return result; |
119 | 5.14k | } |
120 | | |
121 | | static int |
122 | | validate_header_numsync (gdbm_file_header const *hdr, struct stat const *st) |
123 | 1.24k | { |
124 | 1.24k | int result = GDBM_NO_ERROR; |
125 | 1.24k | int dir_size, dir_bits; |
126 | | |
127 | 1.24k | if (!(hdr->block_size > 0 |
128 | 1.24k | && hdr->block_size > (sizeof (gdbm_file_header) + sizeof (gdbm_ext_header)) |
129 | 1.24k | && hdr->block_size - (sizeof (gdbm_file_header) + sizeof (gdbm_ext_header)) >= |
130 | 1.20k | sizeof(avail_block))) |
131 | 44 | { |
132 | 44 | return GDBM_BLOCK_SIZE_ERROR; |
133 | 44 | } |
134 | | |
135 | | /* Technically speaking, the condition below should read |
136 | | hdr->next_block != st->st_size |
137 | | However, gdbm versions prior to commit 4e819c98 could leave |
138 | | hdr->next_block pointing beyond current end of file. To ensure |
139 | | backward compatibility with these versions, the condition has been |
140 | | slackened to this: */ |
141 | 1.20k | if (hdr->next_block < st->st_size) |
142 | 179 | result = GDBM_NEED_RECOVERY; |
143 | | |
144 | | /* Make sure dir and dir + dir_size fall within the file boundary */ |
145 | 1.20k | if (!(hdr->dir > 0 |
146 | 1.20k | && hdr->dir < st->st_size |
147 | 1.20k | && hdr->dir_size > 0 |
148 | 1.20k | && hdr->dir + hdr->dir_size < st->st_size)) |
149 | 307 | return GDBM_BAD_HEADER; |
150 | | |
151 | 893 | compute_directory_size (hdr->block_size, &dir_size, &dir_bits); |
152 | 893 | if (!(hdr->dir_size >= dir_size)) |
153 | 66 | return GDBM_BAD_HEADER; |
154 | 827 | compute_directory_size (hdr->dir_size, &dir_size, &dir_bits); |
155 | 827 | if (hdr->dir_bits != dir_bits) |
156 | 78 | return GDBM_BAD_HEADER; |
157 | | |
158 | 749 | if (!(hdr->bucket_size > 0 && hdr->bucket_size > sizeof (hash_bucket))) |
159 | 41 | return GDBM_BAD_HEADER; |
160 | | |
161 | 708 | if (hdr->bucket_elems != bucket_element_count (hdr->bucket_size)) |
162 | 65 | return GDBM_BAD_HEADER; |
163 | | |
164 | 643 | return result; |
165 | 708 | } |
166 | | |
167 | | static int |
168 | | validate_header (gdbm_file_header const *hdr, struct stat const *st) |
169 | 7.29k | { |
170 | | /* Is the magic number good? */ |
171 | 7.29k | switch (hdr->header_magic) |
172 | 7.29k | { |
173 | 2.02k | case GDBM_OMAGIC: |
174 | 5.68k | case GDBM_MAGIC: |
175 | 5.68k | return validate_header_std (hdr, st); |
176 | | |
177 | 1.24k | case GDBM_NUMSYNC_MAGIC: |
178 | 1.24k | return validate_header_numsync (hdr, st); |
179 | | |
180 | 363 | default: |
181 | 363 | switch (hdr->header_magic) |
182 | 363 | { |
183 | 1 | case GDBM_OMAGIC_SWAP: |
184 | 2 | case GDBM_MAGIC32_SWAP: |
185 | 3 | case GDBM_MAGIC64_SWAP: |
186 | 4 | case GDBM_NUMSYNC_MAGIC32_SWAP: |
187 | 5 | case GDBM_NUMSYNC_MAGIC64_SWAP: |
188 | 5 | return GDBM_BYTE_SWAPPED; |
189 | | |
190 | 1 | case GDBM_MAGIC32: |
191 | 1 | case GDBM_MAGIC64: |
192 | 2 | case GDBM_NUMSYNC_MAGIC32: |
193 | 2 | case GDBM_NUMSYNC_MAGIC64: |
194 | 2 | return GDBM_BAD_FILE_OFFSET; |
195 | | |
196 | 356 | default: |
197 | 356 | return GDBM_BAD_MAGIC_NUMBER; |
198 | 363 | } |
199 | 7.29k | } |
200 | 7.29k | } |
201 | | |
202 | | int |
203 | | _gdbm_validate_header (GDBM_FILE dbf) |
204 | 2.71k | { |
205 | 2.71k | struct stat file_stat; |
206 | 2.71k | int rc; |
207 | | |
208 | 2.71k | if (fstat (dbf->desc, &file_stat)) |
209 | 0 | return GDBM_FILE_STAT_ERROR; |
210 | | |
211 | 2.71k | rc = validate_header (dbf->header, &file_stat); |
212 | 2.71k | if (rc == 0) |
213 | 2.55k | { |
214 | 2.55k | if (gdbm_avail_block_validate (dbf, dbf->avail, dbf->avail_size)) |
215 | 0 | rc = GDBM_BAD_AVAIL; |
216 | 2.55k | } |
217 | 2.71k | return rc; |
218 | 2.71k | } |
219 | | |
220 | | /* Do we have ftruncate? */ |
221 | | static inline int |
222 | | _gdbm_ftruncate (GDBM_FILE dbf) |
223 | 0 | { |
224 | 0 | #if HAVE_FTRUNCATE |
225 | 0 | return ftruncate (dbf->desc, 0); |
226 | | #else |
227 | | int fd; |
228 | | fd = open (dbf->name, O_RDWR|O_TRUNC, mode); |
229 | | if (fd == -1) |
230 | | return -1; |
231 | | return close (fd); |
232 | | #endif |
233 | 0 | } |
234 | | |
235 | | GDBM_FILE |
236 | | gdbm_fd_open (int fd, const char *file_name, int block_size, |
237 | | int flags, void (*fatal_func) (const char *)) |
238 | 10.0k | { |
239 | 10.0k | GDBM_FILE dbf; /* The record to return. */ |
240 | 10.0k | struct stat file_stat; /* Space for the stat information. */ |
241 | 10.0k | off_t file_pos; /* Used with seeks. */ |
242 | 10.0k | int index; /* Used as a loop index. */ |
243 | | |
244 | | /* Initialize the gdbm_errno variable. */ |
245 | 10.0k | gdbm_set_errno (NULL, GDBM_NO_ERROR, FALSE); |
246 | | |
247 | | /* Get the status of the file. */ |
248 | 10.0k | if (fstat (fd, &file_stat)) |
249 | 0 | { |
250 | 0 | if (flags & GDBM_CLOERROR) |
251 | 0 | SAVE_ERRNO (close (fd)); |
252 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_FILE_STAT_ERROR, FALSE, GDBM_DEBUG_OPEN); |
253 | 0 | return NULL; |
254 | 0 | } |
255 | | |
256 | | /* Allocate new info structure. */ |
257 | 10.0k | dbf = calloc (1, sizeof (*dbf)); |
258 | 10.0k | if (dbf == NULL) |
259 | 0 | { |
260 | 0 | if (flags & GDBM_CLOERROR) |
261 | 0 | SAVE_ERRNO (close (fd)); |
262 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); |
263 | 0 | return NULL; |
264 | 0 | } |
265 | | |
266 | 10.0k | dbf->desc = fd; |
267 | | |
268 | | /* Initialize some fields for known values. This is done so gdbm_close |
269 | | will work if called before allocating some structures. */ |
270 | 10.0k | dbf->dir = NULL; |
271 | 10.0k | dbf->bucket = NULL; |
272 | 10.0k | dbf->header = NULL; |
273 | | |
274 | 10.0k | dbf->file_size = -1; |
275 | | |
276 | 10.0k | dbf->memory_mapping = FALSE; |
277 | 10.0k | dbf->mapped_size_max = SIZE_T_MAX; |
278 | 10.0k | dbf->mapped_region = NULL; |
279 | 10.0k | dbf->mapped_size = 0; |
280 | 10.0k | dbf->mapped_pos = 0; |
281 | 10.0k | dbf->mapped_off = 0; |
282 | | |
283 | | /* Save name of file. */ |
284 | 10.0k | dbf->name = strdup (file_name); |
285 | 10.0k | if (dbf->name == NULL) |
286 | 0 | { |
287 | 0 | if (flags & GDBM_CLOERROR) |
288 | 0 | close (fd); |
289 | 0 | _gdbm_cache_free (dbf); |
290 | 0 | free (dbf); |
291 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); |
292 | 0 | return NULL; |
293 | 0 | } |
294 | | |
295 | | /* Initialize the fatal error routine. */ |
296 | 10.0k | dbf->fatal_err = fatal_func; |
297 | | |
298 | 10.0k | dbf->fast_write = TRUE; /* Default to setting fast_write. */ |
299 | 10.0k | dbf->file_locking = TRUE; /* Default to doing file locking. */ |
300 | 10.0k | dbf->central_free = FALSE; /* Default to not using central_free. */ |
301 | 10.0k | dbf->coalesce_blocks = FALSE; /* Default to not coalesce blocks. */ |
302 | | |
303 | 10.0k | dbf->need_recovery = FALSE; |
304 | 10.0k | dbf->last_error = GDBM_NO_ERROR; |
305 | 10.0k | dbf->last_syserror = 0; |
306 | 10.0k | dbf->last_errstr = NULL; |
307 | | |
308 | 10.0k | _gdbmsync_init (dbf); |
309 | | |
310 | | /* GDBM_FAST used to determine whether or not we set fast_write. */ |
311 | 10.0k | if (flags & GDBM_SYNC) |
312 | 0 | { |
313 | | /* If GDBM_SYNC has been requested, don't do fast_write. */ |
314 | 0 | dbf->fast_write = FALSE; |
315 | 0 | } |
316 | 10.0k | if (flags & GDBM_NOLOCK) |
317 | 0 | { |
318 | 0 | dbf->file_locking = FALSE; |
319 | 0 | } |
320 | | |
321 | 10.0k | dbf->cloexec = !!(flags & GDBM_CLOEXEC); |
322 | | |
323 | | /* Zero-length file can't be a reader... */ |
324 | 10.0k | if (((flags & GDBM_OPENMASK) == GDBM_READER) && (file_stat.st_size == 0)) |
325 | 0 | { |
326 | 0 | if (!(flags & GDBM_CLOERROR)) |
327 | 0 | dbf->desc = -1; |
328 | 0 | gdbm_close (dbf); |
329 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_EMPTY_DATABASE, FALSE, GDBM_DEBUG_OPEN); |
330 | 0 | return NULL; |
331 | 0 | } |
332 | | |
333 | | /* Record the kind of user. */ |
334 | 10.0k | dbf->read_write = (flags & GDBM_OPENMASK); |
335 | | |
336 | | /* Lock the file in the appropriate way. */ |
337 | 10.0k | if (dbf->file_locking) |
338 | 10.0k | { |
339 | 10.0k | if (_gdbm_lock_file (dbf) == -1) |
340 | 0 | { |
341 | 0 | if (!(flags & GDBM_CLOERROR)) |
342 | 0 | dbf->desc = -1; |
343 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
344 | 0 | GDBM_SET_ERRNO2 (NULL, |
345 | 0 | (flags & GDBM_OPENMASK) == GDBM_READER |
346 | 0 | ? GDBM_CANT_BE_READER : GDBM_CANT_BE_WRITER, |
347 | 0 | FALSE, |
348 | 0 | GDBM_DEBUG_OPEN); |
349 | 0 | return NULL; |
350 | 0 | } |
351 | 10.0k | } |
352 | | |
353 | | /* If we do have a write lock and it was a GDBM_NEWDB, it is |
354 | | now time to truncate the file. */ |
355 | 10.0k | if ((flags & GDBM_OPENMASK) == GDBM_NEWDB && file_stat.st_size != 0) |
356 | 0 | { |
357 | 0 | if (_gdbm_ftruncate (dbf)) |
358 | 0 | { |
359 | 0 | GDBM_SET_ERRNO2 (dbf, GDBM_FILE_TRUNCATE_ERROR, FALSE, |
360 | 0 | GDBM_DEBUG_OPEN); |
361 | 0 | } |
362 | 0 | else if (fstat (dbf->desc, &file_stat)) |
363 | 0 | { |
364 | 0 | GDBM_SET_ERRNO2 (dbf, GDBM_FILE_STAT_ERROR, FALSE, GDBM_DEBUG_OPEN); |
365 | 0 | } |
366 | |
|
367 | 0 | if (gdbm_last_errno (dbf)) |
368 | 0 | { |
369 | 0 | if (!(flags & GDBM_CLOERROR)) |
370 | 0 | dbf->desc = -1; |
371 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
372 | 0 | return NULL; |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | /* Decide if this is a new file or an old file. */ |
377 | 10.0k | if (file_stat.st_size == 0) |
378 | 5.41k | { |
379 | | /* This is a new file. Create an empty database. */ |
380 | 5.41k | int dir_size, dir_bits; |
381 | | |
382 | | /* Start with the blocksize. */ |
383 | 5.41k | if (block_size < GDBM_MIN_BLOCK_SIZE) |
384 | 1.88k | { |
385 | 1.88k | block_size = STATBLKSIZE (file_stat); |
386 | 1.88k | flags &= ~GDBM_BSEXACT; |
387 | 1.88k | } |
388 | 5.41k | compute_directory_size (block_size, &dir_size, &dir_bits); |
389 | 5.41k | GDBM_DEBUG (GDBM_DEBUG_OPEN, "%s: computed dir_size=%d, dir_bits=%d", |
390 | 5.41k | dbf->name, dir_size, dir_bits); |
391 | | /* Check for correct block_size. */ |
392 | 5.41k | if (dir_size != block_size) |
393 | 30 | { |
394 | 30 | if (flags & GDBM_BSEXACT) |
395 | 0 | { |
396 | 0 | if (!(flags & GDBM_CLOERROR)) |
397 | 0 | dbf->desc = -1; |
398 | 0 | gdbm_close (dbf); |
399 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_BLOCK_SIZE_ERROR, FALSE, |
400 | 0 | GDBM_DEBUG_OPEN); |
401 | 0 | return NULL; |
402 | 0 | } |
403 | 30 | else |
404 | 30 | block_size = dir_size; |
405 | 30 | } |
406 | 5.41k | GDBM_DEBUG (GDBM_DEBUG_OPEN, "%s: block_size=%d", dbf->name, block_size); |
407 | | |
408 | | /* Get space for the file header. It will be written to disk, so |
409 | | make sure there's no garbage in it. */ |
410 | 5.41k | dbf->header = calloc (1, block_size); |
411 | 5.41k | if (dbf->header == NULL) |
412 | 0 | { |
413 | 0 | if (!(flags & GDBM_CLOERROR)) |
414 | 0 | dbf->desc = -1; |
415 | 0 | gdbm_close (dbf); |
416 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); |
417 | 0 | return NULL; |
418 | 0 | } |
419 | | |
420 | | /* Set the magic number and the block_size. */ |
421 | 5.41k | if (flags & GDBM_NUMSYNC) |
422 | 631 | dbf->header->header_magic = GDBM_NUMSYNC_MAGIC; |
423 | 4.78k | else |
424 | 4.78k | dbf->header->header_magic = GDBM_MAGIC; |
425 | | |
426 | | /* |
427 | | * Set block size. It must be initialized for gdbm_header_avail to work. |
428 | | */ |
429 | 5.41k | dbf->header->block_size = block_size; |
430 | 5.41k | gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader); |
431 | 5.41k | dbf->header->dir_size = dir_size; |
432 | 5.41k | dbf->header->dir_bits = dir_bits; |
433 | | |
434 | | /* Allocate the space for the directory. */ |
435 | 5.41k | dbf->dir = (off_t *) malloc (dbf->header->dir_size); |
436 | 5.41k | if (dbf->dir == NULL) |
437 | 0 | { |
438 | 0 | if (!(flags & GDBM_CLOERROR)) |
439 | 0 | dbf->desc = -1; |
440 | 0 | gdbm_close (dbf); |
441 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); |
442 | 0 | return NULL; |
443 | 0 | } |
444 | 5.41k | dbf->header->dir = dbf->header->block_size; |
445 | | |
446 | | /* Create the first and only hash bucket. */ |
447 | 5.41k | dbf->header->bucket_elems = bucket_element_count (dbf->header->block_size); |
448 | 5.41k | dbf->header->bucket_size = dbf->header->block_size; |
449 | 5.41k | dbf->bucket = calloc (1, dbf->header->bucket_size); |
450 | 5.41k | if (dbf->bucket == NULL) |
451 | 0 | { |
452 | 0 | if (!(flags & GDBM_CLOERROR)) |
453 | 0 | dbf->desc = -1; |
454 | 0 | gdbm_close (dbf); |
455 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); |
456 | 0 | return NULL; |
457 | 0 | } |
458 | 5.41k | _gdbm_new_bucket (dbf, dbf->bucket, 0); |
459 | 5.41k | dbf->bucket->av_count = 1; |
460 | 5.41k | dbf->bucket->bucket_avail[0].av_adr = 3*dbf->header->block_size; |
461 | 5.41k | dbf->bucket->bucket_avail[0].av_size = dbf->header->block_size; |
462 | | |
463 | | /* Set table entries to point to hash buckets. */ |
464 | 2.07M | for (index = 0; index < GDBM_DIR_COUNT (dbf); index++) |
465 | 2.07M | dbf->dir[index] = 2*dbf->header->block_size; |
466 | | |
467 | | /* Initialize the active avail block. */ |
468 | 5.41k | dbf->avail->size = (dbf->avail_size - offsetof(avail_block, av_table)) |
469 | 5.41k | / sizeof (avail_elem); |
470 | 5.41k | dbf->avail->count = 0; |
471 | 5.41k | dbf->avail->next_block = 0; |
472 | | |
473 | 5.41k | dbf->header->next_block = 4*dbf->header->block_size; |
474 | | |
475 | | /* Write initial configuration to the file. */ |
476 | | /* Block 0 is the file header and active avail block. */ |
477 | 5.41k | if (_gdbm_full_write (dbf, dbf->header, dbf->header->block_size)) |
478 | 0 | { |
479 | 0 | GDBM_DEBUG (GDBM_DEBUG_OPEN|GDBM_DEBUG_ERR, |
480 | 0 | "%s: error writing header: %s", |
481 | 0 | dbf->name, gdbm_db_strerror (dbf)); |
482 | 0 | if (!(flags & GDBM_CLOERROR)) |
483 | 0 | dbf->desc = -1; |
484 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
485 | 0 | return NULL; |
486 | 0 | } |
487 | | |
488 | | /* Block 1 is the initial bucket directory. */ |
489 | 5.41k | if (_gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size)) |
490 | 0 | { |
491 | 0 | GDBM_DEBUG (GDBM_DEBUG_OPEN|GDBM_DEBUG_ERR, |
492 | 0 | "%s: error writing directory: %s", |
493 | 0 | dbf->name, gdbm_db_strerror (dbf)); |
494 | 0 | if (!(flags & GDBM_CLOERROR)) |
495 | 0 | dbf->desc = -1; |
496 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
497 | 0 | return NULL; |
498 | 0 | } |
499 | | |
500 | | /* Block 2 is the only bucket. */ |
501 | 5.41k | if (_gdbm_full_write (dbf, dbf->bucket, dbf->header->bucket_size)) |
502 | 0 | { |
503 | 0 | GDBM_DEBUG (GDBM_DEBUG_OPEN|GDBM_DEBUG_ERR, |
504 | 0 | "%s: error writing bucket: %s", |
505 | 0 | dbf->name, gdbm_db_strerror (dbf)); |
506 | 0 | if (!(flags & GDBM_CLOERROR)) |
507 | 0 | dbf->desc = -1; |
508 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
509 | 0 | return NULL; |
510 | 0 | } |
511 | | |
512 | 5.41k | if (_gdbm_file_extend (dbf, dbf->header->next_block)) |
513 | 0 | { |
514 | 0 | GDBM_DEBUG (GDBM_DEBUG_OPEN|GDBM_DEBUG_ERR, |
515 | 0 | "%s: error extending file: %s", |
516 | 0 | dbf->name, gdbm_db_strerror (dbf)); |
517 | 0 | if (!(flags & GDBM_CLOERROR)) |
518 | 0 | dbf->desc = -1; |
519 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
520 | 0 | return NULL; |
521 | 0 | } |
522 | | |
523 | | /* Wait for initial configuration to be written to disk. */ |
524 | 5.41k | gdbm_file_sync (dbf); |
525 | | |
526 | 5.41k | free (dbf->bucket); |
527 | 5.41k | } |
528 | 4.59k | else |
529 | 4.59k | { |
530 | | /* This is an old database. Read in the information from the file |
531 | | header and initialize the hash directory. */ |
532 | | |
533 | 4.59k | gdbm_file_header partial_header; /* For the first part of it. */ |
534 | 4.59k | int rc; |
535 | | |
536 | | /* Read the partial file header. */ |
537 | 4.59k | if (_gdbm_full_read (dbf, &partial_header, sizeof (partial_header))) |
538 | 15 | { |
539 | 15 | GDBM_DEBUG (GDBM_DEBUG_ERR|GDBM_DEBUG_OPEN, |
540 | 15 | "%s: error reading partial header: %s", |
541 | 15 | dbf->name, gdbm_db_strerror (dbf)); |
542 | 15 | if (!(flags & GDBM_CLOERROR)) |
543 | 0 | dbf->desc = -1; |
544 | 15 | SAVE_ERRNO (gdbm_close (dbf)); |
545 | 15 | return NULL; |
546 | 15 | } |
547 | | |
548 | | /* Is the header valid? */ |
549 | 4.58k | rc = validate_header (&partial_header, &file_stat); |
550 | 4.58k | if (rc == GDBM_NEED_RECOVERY) |
551 | 188 | { |
552 | 188 | dbf->need_recovery = 1; |
553 | 188 | } |
554 | 4.39k | else if (rc != GDBM_NO_ERROR) |
555 | 1.56k | { |
556 | 1.56k | if (!(flags & GDBM_CLOERROR)) |
557 | 0 | dbf->desc = -1; |
558 | 1.56k | gdbm_close (dbf); |
559 | 1.56k | GDBM_SET_ERRNO2 (NULL, rc, FALSE, GDBM_DEBUG_OPEN); |
560 | 1.56k | return NULL; |
561 | 1.56k | } |
562 | | |
563 | | /* It is a good database, read the entire header. */ |
564 | 3.01k | dbf->header = malloc (partial_header.block_size); |
565 | 3.01k | if (dbf->header == NULL) |
566 | 0 | { |
567 | 0 | if (!(flags & GDBM_CLOERROR)) |
568 | 0 | dbf->desc = -1; |
569 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
570 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); |
571 | 0 | return NULL; |
572 | 0 | } |
573 | | |
574 | 3.01k | memcpy (dbf->header, &partial_header, sizeof (partial_header)); |
575 | 3.01k | if (_gdbm_full_read (dbf, dbf->header + 1, |
576 | 3.01k | dbf->header->block_size - sizeof (gdbm_file_header))) |
577 | 0 | { |
578 | 0 | if (!(flags & GDBM_CLOERROR)) |
579 | 0 | dbf->desc = -1; |
580 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
581 | 0 | return NULL; |
582 | 0 | } |
583 | 3.01k | gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader); |
584 | | |
585 | 3.01k | if (((dbf->header->block_size - |
586 | 3.01k | (GDBM_HEADER_AVAIL_OFFSET (dbf) + |
587 | 3.01k | sizeof (avail_block))) / sizeof (avail_elem) + 1) != dbf->avail->size) |
588 | 75 | { |
589 | 75 | if (!(flags & GDBM_CLOERROR)) |
590 | 0 | dbf->desc = -1; |
591 | 75 | gdbm_close (dbf); |
592 | 75 | GDBM_SET_ERRNO2 (NULL, GDBM_BAD_HEADER, FALSE, GDBM_DEBUG_OPEN); |
593 | 75 | return NULL; |
594 | 75 | } |
595 | | |
596 | 2.94k | if (gdbm_avail_block_validate (dbf, dbf->avail, dbf->avail_size)) |
597 | 232 | { |
598 | 232 | if (!(flags & GDBM_CLOERROR)) |
599 | 0 | dbf->desc = -1; |
600 | 232 | SAVE_ERRNO (gdbm_close (dbf)); |
601 | 232 | return NULL; |
602 | 232 | } |
603 | | |
604 | | /* Allocate space for the hash table directory. */ |
605 | 2.71k | dbf->dir = malloc (dbf->header->dir_size); |
606 | 2.71k | if (dbf->dir == NULL) |
607 | 0 | { |
608 | 0 | if (!(flags & GDBM_CLOERROR)) |
609 | 0 | dbf->desc = -1; |
610 | 0 | gdbm_close (dbf); |
611 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); |
612 | 0 | return NULL; |
613 | 0 | } |
614 | | |
615 | | /* Read the hash table directory. */ |
616 | 2.71k | file_pos = gdbm_file_seek (dbf, dbf->header->dir, SEEK_SET); |
617 | 2.71k | if (file_pos != dbf->header->dir) |
618 | 0 | { |
619 | 0 | if (!(flags & GDBM_CLOERROR)) |
620 | 0 | dbf->desc = -1; |
621 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
622 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_FILE_SEEK_ERROR, FALSE, GDBM_DEBUG_OPEN); |
623 | 0 | return NULL; |
624 | 0 | } |
625 | | |
626 | 2.71k | if (_gdbm_full_read (dbf, dbf->dir, dbf->header->dir_size)) |
627 | 0 | { |
628 | 0 | GDBM_DEBUG (GDBM_DEBUG_ERR|GDBM_DEBUG_OPEN, |
629 | 0 | "%s: error reading dir: %s", |
630 | 0 | dbf->name, gdbm_db_strerror (dbf)); |
631 | 0 | if (!(flags & GDBM_CLOERROR)) |
632 | 0 | dbf->desc = -1; |
633 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
634 | 0 | return NULL; |
635 | 0 | } |
636 | | |
637 | 2.71k | } |
638 | | |
639 | 8.12k | if (_gdbm_cache_init (dbf, DEFAULT_CACHESIZE)) |
640 | 0 | { |
641 | 0 | GDBM_DEBUG (GDBM_DEBUG_ERR|GDBM_DEBUG_OPEN, |
642 | 0 | "%s: error initializing cache: %s", |
643 | 0 | dbf->name, gdbm_db_strerror (dbf)); |
644 | 0 | if (!(flags & GDBM_CLOERROR)) |
645 | 0 | dbf->desc = -1; |
646 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
647 | 0 | return NULL; |
648 | 0 | } |
649 | | |
650 | 8.12k | #if HAVE_MMAP |
651 | 8.12k | if (!(flags & GDBM_NOMMAP)) |
652 | 8.12k | { |
653 | 8.12k | dbf->mmap_preread = (flags & GDBM_PREREAD) != 0; |
654 | 8.12k | if (_gdbm_mapped_init (dbf) == 0) |
655 | 8.12k | dbf->memory_mapping = TRUE; |
656 | 0 | else |
657 | 0 | { |
658 | | /* gdbm_errno should already be set. */ |
659 | 0 | GDBM_DEBUG (GDBM_DEBUG_ERR|GDBM_DEBUG_OPEN, |
660 | 0 | "%s: _gdbm_mapped_init failed: %s", |
661 | 0 | dbf->name, strerror (errno)); |
662 | |
|
663 | 0 | if (!(flags & GDBM_CLOERROR)) |
664 | 0 | dbf->desc = -1; |
665 | 0 | SAVE_ERRNO (gdbm_close (dbf)); |
666 | 0 | return NULL; |
667 | 0 | } |
668 | 8.12k | } |
669 | 8.12k | #endif |
670 | | |
671 | | /* Finish initializing dbf. */ |
672 | 8.12k | dbf->bucket = NULL; |
673 | 8.12k | dbf->bucket_dir = 0; |
674 | 8.12k | dbf->header_changed = FALSE; |
675 | 8.12k | dbf->directory_changed = FALSE; |
676 | | |
677 | 8.12k | if (flags & GDBM_XVERIFY) |
678 | 0 | { |
679 | 0 | gdbm_avail_verify (dbf); |
680 | 0 | } |
681 | | |
682 | 8.12k | GDBM_DEBUG (GDBM_DEBUG_ALL, "%s: opened %s", dbf->name, |
683 | 8.12k | dbf->need_recovery ? "for recovery" : "successfully"); |
684 | | |
685 | | /* Everything is fine, return the pointer to the file |
686 | | information structure. */ |
687 | 8.12k | return dbf; |
688 | 8.12k | } |
689 | | |
690 | | /* Initialize dbm system. FILE is a pointer to the file name. If the file |
691 | | has a size of zero bytes, a file initialization procedure is performed, |
692 | | setting up the initial structure in the file. BLOCK_SIZE is used during |
693 | | initialization to determine the size of various constructs. If the value |
694 | | is less than GDBM_MIN_BLOCK_SIZE, the file system blocksize is used, |
695 | | otherwise the value of BLOCK_SIZE is used. BLOCK_SIZE is ignored if the |
696 | | file has previously initialized. If FLAGS is set to GDBM_READ the user |
697 | | wants to just read the database and any call to dbm_store or dbm_delete |
698 | | will fail. Many readers can access the database at the same time. If FLAGS |
699 | | is set to GDBM_WRITE, the user wants both read and write access to the |
700 | | database and requires exclusive access. If FLAGS is GDBM_WRCREAT, the user |
701 | | wants both read and write access to the database and if the database does |
702 | | not exist, create a new one. If FLAGS is GDBM_NEWDB, the user want a |
703 | | new database created, regardless of whether one existed, and wants read |
704 | | and write access to the new database. Any error detected will cause a |
705 | | return value of null and an appropriate value will be in gdbm_errno. If |
706 | | no errors occur, a pointer to the "gdbm file descriptor" will be |
707 | | returned. */ |
708 | | |
709 | | |
710 | | GDBM_FILE |
711 | | gdbm_open (const char *file, int block_size, int flags, int mode, |
712 | | void (*fatal_func) (const char *)) |
713 | 0 | { |
714 | 0 | int fd; |
715 | | /* additional bits for open(2) flags */ |
716 | 0 | int fbits = 0; |
717 | |
|
718 | 0 | switch (flags & GDBM_OPENMASK) |
719 | 0 | { |
720 | 0 | case GDBM_READER: |
721 | 0 | fbits = O_RDONLY; |
722 | 0 | break; |
723 | | |
724 | 0 | case GDBM_WRITER: |
725 | 0 | fbits = O_RDWR; |
726 | 0 | break; |
727 | | |
728 | 0 | case GDBM_WRCREAT: |
729 | 0 | case GDBM_NEWDB: |
730 | 0 | fbits = O_RDWR|O_CREAT; |
731 | 0 | break; |
732 | | |
733 | 0 | default: |
734 | 0 | errno = EINVAL; |
735 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_FILE_OPEN_ERROR, FALSE, GDBM_DEBUG_OPEN); |
736 | 0 | return NULL; |
737 | 0 | } |
738 | 0 | if (flags & GDBM_CLOEXEC) |
739 | 0 | fbits |= O_CLOEXEC; |
740 | | |
741 | 0 | fd = open (file, fbits, mode); |
742 | 0 | if (fd < 0) |
743 | 0 | { |
744 | 0 | GDBM_SET_ERRNO2 (NULL, GDBM_FILE_OPEN_ERROR, FALSE, GDBM_DEBUG_OPEN); |
745 | 0 | return NULL; |
746 | 0 | } |
747 | 0 | return gdbm_fd_open (fd, file, block_size, flags | GDBM_CLOERROR, |
748 | 0 | fatal_func); |
749 | 0 | } |
750 | | |
751 | | int |
752 | | _gdbm_file_size (GDBM_FILE dbf, off_t *psize) |
753 | 8.96M | { |
754 | 8.96M | if (dbf->file_size == -1) |
755 | 193k | { |
756 | 193k | struct stat sb; |
757 | 193k | if (fstat (dbf->desc, &sb)) |
758 | 0 | { |
759 | 0 | GDBM_SET_ERRNO (dbf, GDBM_FILE_STAT_ERROR, FALSE); |
760 | 0 | return -1; |
761 | 0 | } |
762 | 193k | dbf->file_size = sb.st_size; |
763 | 193k | } |
764 | 8.96M | *psize = dbf->file_size; |
765 | 8.96M | return 0; |
766 | 8.96M | } |
767 | | |
768 | | /* |
769 | | * Convert from numsync to the standard GDBM format. This is pretty |
770 | | * straightforward: the avail block gets expanded by |
771 | | * sizeof(gdbm_ext_header), so we only need to shift the avail block |
772 | | * up this number of bytes, change the avail and avail_size pointers |
773 | | * and update the magic number. |
774 | | */ |
775 | | static int |
776 | | _gdbm_convert_from_numsync (GDBM_FILE dbf) |
777 | 2.71k | { |
778 | 2.71k | avail_block *old_avail = dbf->avail; |
779 | | |
780 | | /* Change the magic number */ |
781 | 2.71k | dbf->header->header_magic = GDBM_MAGIC; |
782 | | /* Update avail pointer and size */ |
783 | 2.71k | gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader); |
784 | | |
785 | | /* Move data up */ |
786 | 2.71k | memmove (dbf->avail, old_avail, dbf->avail_size - sizeof (gdbm_ext_header)); |
787 | | |
788 | | /* Fix up the avail table size */ |
789 | 2.71k | dbf->avail->size = (dbf->avail_size - offsetof (avail_block, av_table)) |
790 | 2.71k | / sizeof (avail_elem); |
791 | | |
792 | 2.71k | dbf->header_changed = TRUE; |
793 | | |
794 | 2.71k | return 0; |
795 | 2.71k | } |
796 | | |
797 | | /* |
798 | | * Convert the database from the standard to extended (numsync) format. |
799 | | * The avail block shrinks by sizeof(gdbm_ext_header) bytes. The av_table |
800 | | * entries that don't fit into the new size need to be returned to the |
801 | | * avail pool using the _gdbm_free call. |
802 | | */ |
803 | | static int |
804 | | _gdbm_convert_to_numsync (GDBM_FILE dbf) |
805 | 2.39k | { |
806 | 2.39k | avail_block *old_avail = dbf->avail; |
807 | 2.39k | size_t old_avail_size = dbf->avail->size; |
808 | 2.39k | size_t n; /* Number of elements to return to the pool */ |
809 | 2.39k | int rc; |
810 | 2.39k | avail_elem *av = NULL; |
811 | | |
812 | | /* Change the magic number */ |
813 | 2.39k | dbf->header->header_magic = GDBM_NUMSYNC_MAGIC; |
814 | | /* Update avail pointer and size */ |
815 | 2.39k | gdbm_header_avail (dbf->header, &dbf->avail, &dbf->avail_size, &dbf->xheader); |
816 | | /* |
817 | | * Compute new av_table size. |
818 | | * NOTE: Don't try to modify dbf->avail until the final move, otherwise |
819 | | * the available block would end up clobbered. All modifications are |
820 | | * applied to old_avail. |
821 | | */ |
822 | 2.39k | old_avail->size = (dbf->avail_size - offsetof (avail_block, av_table)) |
823 | 2.39k | / sizeof (avail_elem); |
824 | | /* Compute the number of avail elements that don't fit in the new table. */ |
825 | 2.39k | n = old_avail_size - old_avail->size; |
826 | 2.39k | if (n > 0) |
827 | 2.39k | { |
828 | | /* Stash them away */ |
829 | 2.39k | av = calloc (n, sizeof (av[0])); |
830 | 2.39k | if (!av) |
831 | 0 | { |
832 | 0 | GDBM_SET_ERRNO (dbf, GDBM_MALLOC_ERROR, FALSE); |
833 | 0 | return -1; |
834 | 0 | } |
835 | 2.39k | n = 0; |
836 | 2.46k | while (old_avail->count > old_avail->size) |
837 | 68 | { |
838 | 68 | old_avail->count--; |
839 | 68 | av[n++] = old_avail->av_table[old_avail->count]; |
840 | 68 | } |
841 | 2.39k | } |
842 | | |
843 | | /* |
844 | | * Move the modified avail block into its new place. From now on, |
845 | | * old_avail may not be used. The database header is in consistent |
846 | | * state and all modifications should be applied to it directly. |
847 | | */ |
848 | 2.39k | memmove (dbf->avail, old_avail, dbf->avail_size); |
849 | | |
850 | | /* Initialize the extended header */ |
851 | 2.39k | memset (dbf->xheader, 0, sizeof (dbf->xheader[0])); |
852 | | |
853 | 2.39k | rc = 0; /* Assume success */ |
854 | | |
855 | 2.39k | if (av) |
856 | 2.39k | { |
857 | | /* Return stashed av_table elements to the available pool. */ |
858 | | /* _gdbm_free needs a non-NULL bucket, so get one: */ |
859 | 2.39k | if (!dbf->bucket) |
860 | 0 | rc = _gdbm_get_bucket (dbf, 0); |
861 | 2.39k | if (rc == 0) |
862 | 2.39k | { |
863 | 2.39k | size_t i; |
864 | | |
865 | 2.46k | for (i = 0; i < n; i++) |
866 | 68 | { |
867 | 68 | rc = _gdbm_free (dbf, av[i].av_adr, av[i].av_size); |
868 | 68 | if (rc) |
869 | 0 | break; |
870 | 68 | } |
871 | 2.39k | } |
872 | 2.39k | free (av); |
873 | 2.39k | } |
874 | | |
875 | 2.39k | dbf->header_changed = TRUE; |
876 | | |
877 | 2.39k | return rc; |
878 | 2.39k | } |
879 | | |
880 | | int |
881 | | gdbm_convert (GDBM_FILE dbf, int flag) |
882 | 5.42k | { |
883 | 5.42k | int rc; |
884 | | |
885 | | /* Return immediately if the database needs recovery */ |
886 | 5.42k | GDBM_ASSERT_CONSISTENCY (dbf, -1); |
887 | | |
888 | | /* First check to make sure this guy is a writer. */ |
889 | 5.42k | if (dbf->read_write == GDBM_READER) |
890 | 0 | { |
891 | 0 | GDBM_SET_ERRNO2 (dbf, GDBM_READER_CANT_STORE, FALSE, |
892 | 0 | GDBM_DEBUG_STORE); |
893 | 0 | return -1; |
894 | 0 | } |
895 | | |
896 | 5.42k | switch (flag) |
897 | 5.42k | { |
898 | 2.71k | case 0: |
899 | 5.42k | case GDBM_NUMSYNC: |
900 | 5.42k | break; |
901 | | |
902 | 0 | default: |
903 | 0 | GDBM_SET_ERRNO2 (dbf, GDBM_MALFORMED_DATA, FALSE, |
904 | 0 | GDBM_DEBUG_STORE); |
905 | 0 | return -1; |
906 | 5.42k | } |
907 | | |
908 | 5.42k | rc = 0; |
909 | 5.42k | switch (dbf->header->header_magic) |
910 | 5.42k | { |
911 | 0 | case GDBM_OMAGIC: |
912 | 2.39k | case GDBM_MAGIC: |
913 | 2.39k | if (flag == GDBM_NUMSYNC) |
914 | 2.39k | rc = _gdbm_convert_to_numsync (dbf); |
915 | 2.39k | break; |
916 | | |
917 | 3.03k | case GDBM_NUMSYNC_MAGIC: |
918 | 3.03k | if (flag == 0) |
919 | 2.71k | rc = _gdbm_convert_from_numsync (dbf); |
920 | 5.42k | } |
921 | | |
922 | 5.42k | if (rc == 0) |
923 | 5.42k | rc = _gdbm_end_update (dbf); |
924 | | |
925 | 5.42k | return 0; |
926 | 5.42k | } |