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

Last change on this file since 18097 was 18097, checked in by mdewsnip, 15 years ago

Added some debugging statements, commented out.

File size: 15.2 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: Swap each value in the partial header if the GDBM file is the wrong endianness
384 if (wrong_endianness)
385 {
386 endian_swap_array((int*) &partial_header, sizeof (gdbm_file_header));
387 }
388
389 /* Is the magic number good? */
390 if (partial_header.header_magic != 0x13579ace)
391 {
392 gdbm_close (dbf);
393 gdbm_errno = GDBM_BAD_MAGIC_NUMBER;
394 return NULL;
395 }
396
397 /* GREENSTONE CUSTOMISATION: Debugging output
398 printf("\n");
399 printf("Header magic: %x\n", partial_header.header_magic);
400 printf("Block size: %d\n", partial_header.block_size);
401 printf("Dir: %d\n", partial_header.dir);
402 printf("Dir size: %d\n", partial_header.dir_size);
403 printf("Dir bits: %d\n", partial_header.dir_bits);
404 printf("Bucket size: %d\n", partial_header.bucket_size);
405 printf("Bucket elems: %d\n", partial_header.bucket_elems);
406 printf("Next block: %d\n", partial_header.next_block);
407 printf("\n");
408 */
409
410 /* It is a good database, read the entire header. */
411 dbf->header = (gdbm_file_header *) malloc (partial_header.block_size);
412 if (dbf->header == NULL)
413 {
414 gdbm_close (dbf);
415 gdbm_errno = GDBM_MALLOC_ERROR;
416 return NULL;
417 }
418 bcopy (&partial_header, dbf->header, sizeof (gdbm_file_header));
419 num_bytes = read (dbf->desc, &dbf->header->avail.av_table[1],
420 dbf->header->block_size-sizeof (gdbm_file_header));
421
422 // GREENSTONE CUSTOMISATION: Swap each value in the header if the GDBM file is the wrong endianness
423 if (wrong_endianness)
424 {
425 endian_swap_array((int*) &dbf->header->avail.av_table[1],
426 dbf->header->block_size-sizeof(gdbm_file_header));
427 }
428
429 if (num_bytes != dbf->header->block_size-sizeof (gdbm_file_header))
430 {
431 gdbm_close (dbf);
432 gdbm_errno = GDBM_FILE_READ_ERROR;
433 return NULL;
434 }
435
436 /* Allocate space for the hash table directory. */
437 dbf->dir = (off_t *) malloc (dbf->header->dir_size);
438 if (dbf->dir == NULL)
439 {
440 gdbm_close (dbf);
441 gdbm_errno = GDBM_MALLOC_ERROR;
442 return NULL;
443 }
444
445 /* Read the hash table directory. */
446 file_pos = lseek (dbf->desc, dbf->header->dir, L_SET);
447 if (file_pos != dbf->header->dir)
448 {
449 gdbm_close (dbf);
450 gdbm_errno = GDBM_FILE_SEEK_ERROR;
451 return NULL;
452 }
453
454 num_bytes = read (dbf->desc, dbf->dir, dbf->header->dir_size);
455 if (num_bytes != dbf->header->dir_size)
456 {
457 gdbm_close (dbf);
458 gdbm_errno = GDBM_FILE_READ_ERROR;
459 return NULL;
460 }
461
462 // GREENSTONE CUSTOMISATION: Swap each value in the directory if the GDBM file is the wrong endianness
463 if (wrong_endianness)
464 {
465 endian_swap_array((int*) dbf->dir, dbf->header->dir_size);
466 }
467 }
468
469 /* Finish initializing dbf. */
470 dbf->last_read = -1;
471 dbf->bucket = NULL;
472 dbf->bucket_dir = 0;
473 dbf->cache_entry = NULL;
474 dbf->header_changed = FALSE;
475 dbf->directory_changed = FALSE;
476 dbf->bucket_changed = FALSE;
477 dbf->second_changed = FALSE;
478
479 /* Everything is fine, return the pointer to the file
480 information structure. */
481 return dbf;
482
483}
484
485/* initialize the bucket cache. */
486int
487_gdbm_init_cache(dbf, size)
488 gdbm_file_info *dbf;
489 int size;
490{
491register int index;
492
493 if (dbf->bucket_cache == NULL)
494 {
495 dbf->bucket_cache = (cache_elem *) malloc(sizeof(cache_elem) * size);
496 if(dbf->bucket_cache == NULL)
497 {
498 gdbm_errno = GDBM_MALLOC_ERROR;
499 return(-1);
500 }
501 dbf->cache_size = size;
502
503 for(index = 0; index < size; index++)
504 {
505 (dbf->bucket_cache[index]).ca_bucket
506 = (hash_bucket *) malloc (dbf->header->bucket_size);
507 if ((dbf->bucket_cache[index]).ca_bucket == NULL)
508 {
509 gdbm_errno = GDBM_MALLOC_ERROR;
510 return(-1);
511 }
512 (dbf->bucket_cache[index]).ca_adr = 0;
513 (dbf->bucket_cache[index]).ca_changed = FALSE;
514 (dbf->bucket_cache[index]).ca_data.hash_val = -1;
515 (dbf->bucket_cache[index]).ca_data.elem_loc = -1;
516 (dbf->bucket_cache[index]).ca_data.dptr = NULL;
517 }
518 dbf->bucket = dbf->bucket_cache[0].ca_bucket;
519 dbf->cache_entry = &dbf->bucket_cache[0];
520 }
521 return(0);
522}
Note: See TracBrowser for help on using the repository browser.