source: gsdl/trunk/common-src/packages/gdbm/gdbm-1.8.3/gdbmopen.c@ 20976

Last change on this file since 20976 was 20976, checked in by mdewsnip, 14 years ago

Added a safety check provided by Andrew Brooks (University of Dundee) for ensuring that a database with the wrong byte order for the machine cannot be opened for writing. (The byte swapping code that I did has only been done for reading, not writing).

File size: 15.5 KB
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, by Philip A. Nelson.
4 Copyright (C) 1990, 1991, 1993 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 2, 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; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 You may contact the author by:
21 e-mail: [email protected]
22 us-mail: Philip A. Nelson
23 Computer Science Department
24 Western Washington University
25 Bellingham, WA 98226
26
27*************************************************************************/
28
29
30/* include system configuration before all else. */
31#include "autoconf.h"
32
33#include "gdbmdefs.h"
34#include "gdbmerrno.h"
35
36#include "gsdlmods.h"
37
38
39/* Initialize dbm system. FILE is a pointer to the file name. If the file
40 has a size of zero bytes, a file initialization procedure is performed,
41 setting up the initial structure in the file. BLOCK_SIZE is used during
42 initialization to determine the size of various constructs. If the value
43 is less than 512, the file system blocksize is used, otherwise the value
44 of BLOCK_SIZE is used. BLOCK_SIZE is ignored if the file has previously
45 initialized. If FLAGS is set to GDBM_READ the user wants to just
46 read the database and any call to dbm_store or dbm_delete will fail. Many
47 readers can access the database at the same time. If FLAGS is set to
48 GDBM_WRITE, the user wants both read and write access to the database and
49 requires exclusive access. If FLAGS is GDBM_WRCREAT, the user wants
50 both read and write access to the database and if the database does not
51 exist, create a new one. If FLAGS is GDBM_NEWDB, the user want a
52 new database created, regardless of whether one existed, and wants read
53 and write access to the new database. Any error detected will cause a
54 return value of null and an approprate value will be in gdbm_errno. If
55 no errors occur, a pointer to the "gdbm file descriptor" will be
56 returned. */
57
58
59gdbm_file_info *
60#ifdef MSDOS
61gdbm_open (file, block_size, flags, mode, fatal_func, need_filelock)
62#else
63gdbm_open (file, block_size, flags, mode, fatal_func)
64#endif
65 char *file;
66 int block_size;
67 int flags;
68 int mode;
69 void (*fatal_func) ();
70#ifdef MSDOS
71 int need_filelock;
72#endif
73{
74 gdbm_file_info *dbf; /* The record to return. */
75 struct stat file_stat; /* Space for the stat information. */
76 int len; /* Length of the file name. */
77 int num_bytes; /* Used in reading and writing. */
78 off_t file_pos; /* Used with seeks. */
79 int lock_val; /* Returned by the flock call. */
80 int file_block_size; /* Block size to use for a new file. */
81 int index; /* Used as a loop index. */
82 char need_trunc; /* Used with GDBM_NEWDB and locking to avoid
83 truncating a file from under a reader. */
84
85 /* Initialize the gdbm_errno variable. */
86 gdbm_errno = GDBM_NO_ERROR;
87
88 /* Allocate new info structure. */
89 dbf = (gdbm_file_info *) malloc (sizeof (gdbm_file_info));
90 if (dbf == NULL)
91 {
92 gdbm_errno = GDBM_MALLOC_ERROR;
93 return NULL;
94 }
95
96 /* Initialize some fields for known values. This is done so gdbm_close
97 will work if called before allocating some structures. */
98#ifdef MSDOS
99 dbf->need_filelock = need_filelock;
100#endif
101 dbf->dir = NULL;
102 dbf->bucket = NULL;
103 dbf->header = NULL;
104 dbf->bucket_cache = NULL;
105 dbf->cache_size = 0;
106
107 /* Save name of file. */
108 len = strlen (file);
109 dbf->name = (char *) malloc (len + 1);
110 if (dbf->name == NULL)
111 {
112 free (dbf);
113 gdbm_errno = GDBM_MALLOC_ERROR;
114 return NULL;
115 }
116 strcpy (dbf->name, file);
117
118 /* Initialize the fatal error routine. */
119 dbf->fatal_err = fatal_func;
120
121 dbf->fast_write = TRUE; /* Default to setting fast_write. */
122 dbf->file_locking = TRUE; /* Default to doing file locking. */
123 dbf->central_free = FALSE; /* Default to not using central_free. */
124 dbf->coalesce_blocks = FALSE; /* Default to not coalescing blocks. */
125
126 /* GDBM_FAST used to determine whethere or not we set fast_write. */
127 if (flags & GDBM_SYNC)
128 {
129 /* If GDBM_SYNC has been requested, don't do fast_write. */
130 dbf->fast_write = FALSE;
131 }
132 if (flags & GDBM_NOLOCK)
133 {
134 dbf->file_locking = FALSE;
135 }
136
137 /* Open the file. */
138 need_trunc = FALSE;
139 switch (flags & GDBM_OPENMASK)
140 {
141 case GDBM_READER:
142#ifdef MSDOS
143 dbf->desc = open (dbf->name, O_RDONLY|O_BINARY, 0);
144#else
145 dbf->desc = open (dbf->name, O_RDONLY, 0);
146#endif
147 break;
148
149 case GDBM_WRITER:
150#ifdef MSDOS
151 dbf->desc = open (dbf->name, O_RDWR|O_BINARY, 0);
152#else
153 dbf->desc = open (dbf->name, O_RDWR, 0);
154#endif
155 break;
156
157 case GDBM_NEWDB:
158#ifdef MSDOS
159 dbf->desc = open (dbf->name, O_RDWR|O_CREAT|O_BINARY, mode);
160#else
161 dbf->desc = open (dbf->name, O_RDWR|O_CREAT, mode);
162#endif
163 need_trunc = TRUE;
164 break;
165
166 default:
167#ifdef MSDOS
168 dbf->desc = open (dbf->name, O_RDWR|O_CREAT|O_BINARY, mode);
169#else
170 dbf->desc = open (dbf->name, O_RDWR|O_CREAT, mode);
171#endif
172 break;
173
174 }
175 if (dbf->desc < 0)
176 {
177 free (dbf->name);
178 free (dbf);
179 gdbm_errno = GDBM_FILE_OPEN_ERROR;
180 return NULL;
181 }
182
183 /* Get the status of the file. */
184 fstat (dbf->desc, &file_stat);
185
186 /* Lock the file in the approprate way. */
187 if ((flags & GDBM_OPENMASK) == GDBM_READER)
188 {
189 if (file_stat.st_size == 0)
190 {
191 close (dbf->desc);
192 free (dbf->name);
193 free (dbf);
194 gdbm_errno = GDBM_EMPTY_DATABASE;
195 return NULL;
196 }
197 if (dbf->file_locking)
198 {
199 /* Sets lock_val to 0 for success. See systems.h. */
200#ifdef MSDOS
201 if (dbf->need_filelock) {
202 READLOCK_FILE(dbf);
203 }
204 else {
205 lock_val = 0;
206 }
207#else
208 READLOCK_FILE(dbf);
209#endif
210 }
211 }
212 else if (dbf->file_locking)
213 {
214 /* Sets lock_val to 0 for success. See systems.h. */
215#ifdef MSDOS
216 if (dbf->need_filelock) {
217 WRITELOCK_FILE(dbf);
218 }
219 else {
220 lock_val = 0;
221 }
222#else
223 WRITELOCK_FILE(dbf);
224#endif
225 }
226 if (dbf->file_locking && (lock_val != 0))
227 {
228 close (dbf->desc);
229 free (dbf->name);
230 free (dbf);
231 if ((flags & GDBM_OPENMASK) == GDBM_READER)
232 gdbm_errno = GDBM_CANT_BE_READER;
233 else
234 gdbm_errno = GDBM_CANT_BE_WRITER;
235 return NULL;
236 }
237
238 /* Record the kind of user. */
239 dbf->read_write = (flags & GDBM_OPENMASK);
240
241 /* If we do have a write lock and it was a GDBM_NEWDB, it is
242 now time to truncate the file. */
243 if (need_trunc && file_stat.st_size != 0)
244 {
245 TRUNCATE (dbf);
246 fstat (dbf->desc, &file_stat);
247 }
248
249 /* Decide if this is a new file or an old file. */
250 if (file_stat.st_size == 0)
251 {
252
253 /* This is a new file. Create an empty database. */
254
255 /* Start with the blocksize. */
256 if (block_size < 512)
257 file_block_size = STATBLKSIZE;
258 else
259 file_block_size = block_size;
260
261 /* Get space for the file header. */
262 dbf->header = (gdbm_file_header *) malloc (file_block_size);
263 if (dbf->header == NULL)
264 {
265 gdbm_close (dbf);
266 gdbm_errno = GDBM_MALLOC_ERROR;
267 return NULL;
268 }
269
270 /* Set the magic number and the block_size. */
271 dbf->header->header_magic = 0x13579ace;
272 dbf->header->block_size = file_block_size;
273
274 /* Create the initial hash table directory. */
275 dbf->header->dir_size = 8 * sizeof (off_t);
276 dbf->header->dir_bits = 3;
277 while (dbf->header->dir_size < dbf->header->block_size)
278 {
279 dbf->header->dir_size <<= 1;
280 dbf->header->dir_bits += 1;
281 }
282
283 /* Check for correct block_size. */
284 if (dbf->header->dir_size != dbf->header->block_size)
285 {
286 gdbm_close (dbf);
287 gdbm_errno = GDBM_BLOCK_SIZE_ERROR;
288 return NULL;
289 }
290
291 /* Allocate the space for the directory. */
292 dbf->dir = (off_t *) malloc (dbf->header->dir_size);
293 if (dbf->dir == NULL)
294 {
295 gdbm_close (dbf);
296 gdbm_errno = GDBM_MALLOC_ERROR;
297 return NULL;
298 }
299 dbf->header->dir = dbf->header->block_size;
300
301 /* Create the first and only hash bucket. */
302 dbf->header->bucket_elems =
303 (dbf->header->block_size - sizeof (hash_bucket))
304 / sizeof (bucket_element) + 1;
305 dbf->header->bucket_size = dbf->header->block_size;
306 dbf->bucket = (hash_bucket *) malloc (dbf->header->bucket_size);
307 if (dbf->bucket == NULL)
308 {
309 gdbm_close (dbf);
310 gdbm_errno = GDBM_MALLOC_ERROR;
311 return NULL;
312 }
313 _gdbm_new_bucket (dbf, dbf->bucket, 0);
314 dbf->bucket->av_count = 1;
315 dbf->bucket->bucket_avail[0].av_adr = 3*dbf->header->block_size;
316 dbf->bucket->bucket_avail[0].av_size = dbf->header->block_size;
317
318 /* Set table entries to point to hash buckets. */
319 for (index = 0; index < dbf->header->dir_size / sizeof (off_t); index++)
320 dbf->dir[index] = 2*dbf->header->block_size;
321
322 /* Initialize the active avail block. */
323 dbf->header->avail.size
324 = ( (dbf->header->block_size - sizeof (gdbm_file_header))
325 / sizeof (avail_elem)) + 1;
326 dbf->header->avail.count = 0;
327 dbf->header->avail.next_block = 0;
328 dbf->header->next_block = 4*dbf->header->block_size;
329
330 /* Write initial configuration to the file. */
331 /* Block 0 is the file header and active avail block. */
332 num_bytes = write (dbf->desc, dbf->header, dbf->header->block_size);
333 if (num_bytes != dbf->header->block_size)
334 {
335 gdbm_close (dbf);
336 gdbm_errno = GDBM_FILE_WRITE_ERROR;
337 return NULL;
338 }
339
340 /* Block 1 is the initial bucket directory. */
341 num_bytes = write (dbf->desc, dbf->dir, dbf->header->dir_size);
342 if (num_bytes != dbf->header->dir_size)
343 {
344 gdbm_close (dbf);
345 gdbm_errno = GDBM_FILE_WRITE_ERROR;
346 return NULL;
347 }
348
349 /* Block 2 is the only bucket. */
350 num_bytes = write (dbf->desc, dbf->bucket, dbf->header->bucket_size);
351 if (num_bytes != dbf->header->bucket_size)
352 {
353 gdbm_close (dbf);
354 gdbm_errno = GDBM_FILE_WRITE_ERROR;
355 return NULL;
356 }
357
358 /* Wait for initial configuration to be written to disk. */
359 fsync (dbf->desc);
360
361 free (dbf->bucket);
362 }
363 else
364 {
365 /* This is an old database. Read in the information from the file
366 header and initialize the hash directory. */
367
368 gdbm_file_header partial_header; /* For the first part of it. */
369
370 /* Read the partial file header. */
371 num_bytes = read (dbf->desc, &partial_header, sizeof (gdbm_file_header));
372 if (num_bytes != sizeof (gdbm_file_header))
373 {
374 gdbm_close (dbf);
375 gdbm_errno = GDBM_FILE_READ_ERROR;
376 return NULL;
377 }
378
379 // GREENSTONE CUSTOMISATION: Check if the magic number is reversed
380 // If it is then the GDBM file is the wrong endianness for the current machine
381 wrong_endianness = (partial_header.header_magic == 0xCE9A5713);
382
383 // GREENSTONE CUSTOMISATION (thanks to Andrew Brooks): prevent write access to wrong-byte-order databases
384 if (wrong_endianness && ((flags & GDBM_OPENMASK) != GDBM_READER))
385 {
386 gdbm_close (dbf);
387 gdbm_errno = GDBM_CANT_BE_WRITER;
388 return NULL;
389 }
390
391 // GREENSTONE CUSTOMISATION: Swap each value in the partial header if the GDBM file is the wrong endianness
392 if (wrong_endianness)
393 {
394 endian_swap_array((int*) &partial_header, sizeof (gdbm_file_header));
395 }
396
397 /* Is the magic number good? */
398 if (partial_header.header_magic != 0x13579ace)
399 {
400 gdbm_close (dbf);
401 gdbm_errno = GDBM_BAD_MAGIC_NUMBER;
402 return NULL;
403 }
404
405 /* GREENSTONE CUSTOMISATION: Debugging output
406 printf("\n");
407 printf("Header magic: %x\n", partial_header.header_magic);
408 printf("Block size: %d\n", partial_header.block_size);
409 printf("Dir: %d\n", partial_header.dir);
410 printf("Dir size: %d\n", partial_header.dir_size);
411 printf("Dir bits: %d\n", partial_header.dir_bits);
412 printf("Bucket size: %d\n", partial_header.bucket_size);
413 printf("Bucket elems: %d\n", partial_header.bucket_elems);
414 printf("Next block: %d\n", partial_header.next_block);
415 printf("\n");
416 */
417
418 /* It is a good database, read the entire header. */
419 dbf->header = (gdbm_file_header *) malloc (partial_header.block_size);
420 if (dbf->header == NULL)
421 {
422 gdbm_close (dbf);
423 gdbm_errno = GDBM_MALLOC_ERROR;
424 return NULL;
425 }
426 bcopy (&partial_header, dbf->header, sizeof (gdbm_file_header));
427 num_bytes = read (dbf->desc, &dbf->header->avail.av_table[1],
428 dbf->header->block_size-sizeof (gdbm_file_header));
429
430 // GREENSTONE CUSTOMISATION: Swap each value in the header if the GDBM file is the wrong endianness
431 if (wrong_endianness)
432 {
433 endian_swap_array((int*) &dbf->header->avail.av_table[1],
434 dbf->header->block_size-sizeof(gdbm_file_header));
435 }
436
437 if (num_bytes != dbf->header->block_size-sizeof (gdbm_file_header))
438 {
439 gdbm_close (dbf);
440 gdbm_errno = GDBM_FILE_READ_ERROR;
441 return NULL;
442 }
443
444 /* Allocate space for the hash table directory. */
445 dbf->dir = (off_t *) malloc (dbf->header->dir_size);
446 if (dbf->dir == NULL)
447 {
448 gdbm_close (dbf);
449 gdbm_errno = GDBM_MALLOC_ERROR;
450 return NULL;
451 }
452
453 /* Read the hash table directory. */
454 file_pos = lseek (dbf->desc, dbf->header->dir, L_SET);
455 if (file_pos != dbf->header->dir)
456 {
457 gdbm_close (dbf);
458 gdbm_errno = GDBM_FILE_SEEK_ERROR;
459 return NULL;
460 }
461
462 num_bytes = read (dbf->desc, dbf->dir, dbf->header->dir_size);
463 if (num_bytes != dbf->header->dir_size)
464 {
465 gdbm_close (dbf);
466 gdbm_errno = GDBM_FILE_READ_ERROR;
467 return NULL;
468 }
469
470 // GREENSTONE CUSTOMISATION: Swap each value in the directory if the GDBM file is the wrong endianness
471 if (wrong_endianness)
472 {
473 endian_swap_array((int*) dbf->dir, dbf->header->dir_size);
474 }
475 }
476
477 /* Finish initializing dbf. */
478 dbf->last_read = -1;
479 dbf->bucket = NULL;
480 dbf->bucket_dir = 0;
481 dbf->cache_entry = NULL;
482 dbf->header_changed = FALSE;
483 dbf->directory_changed = FALSE;
484 dbf->bucket_changed = FALSE;
485 dbf->second_changed = FALSE;
486
487 /* Everything is fine, return the pointer to the file
488 information structure. */
489 return dbf;
490
491}
492
493/* initialize the bucket cache. */
494int
495_gdbm_init_cache(dbf, size)
496 gdbm_file_info *dbf;
497 int size;
498{
499register int index;
500
501 if (dbf->bucket_cache == NULL)
502 {
503 dbf->bucket_cache = (cache_elem *) malloc(sizeof(cache_elem) * size);
504 if(dbf->bucket_cache == NULL)
505 {
506 gdbm_errno = GDBM_MALLOC_ERROR;
507 return(-1);
508 }
509 dbf->cache_size = size;
510
511 for(index = 0; index < size; index++)
512 {
513 (dbf->bucket_cache[index]).ca_bucket
514 = (hash_bucket *) malloc (dbf->header->bucket_size);
515 if ((dbf->bucket_cache[index]).ca_bucket == NULL)
516 {
517 gdbm_errno = GDBM_MALLOC_ERROR;
518 return(-1);
519 }
520 (dbf->bucket_cache[index]).ca_adr = 0;
521 (dbf->bucket_cache[index]).ca_changed = FALSE;
522 (dbf->bucket_cache[index]).ca_data.hash_val = -1;
523 (dbf->bucket_cache[index]).ca_data.elem_loc = -1;
524 (dbf->bucket_cache[index]).ca_data.dptr = NULL;
525 }
526 dbf->bucket = dbf->bucket_cache[0].ca_bucket;
527 dbf->cache_entry = &dbf->bucket_cache[0];
528 }
529 return(0);
530}
Note: See TracBrowser for help on using the repository browser.