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

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

Code modifications to allow the GDBM library to read both little-endian and big-endian GDBM files, thus hopefully removing all the nasty endianness problems Greenstone has had with collections built on different architectures. By Michael Dewsnip at DL Consulting Ltd.

File size: 13.9 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 *
60gdbm_open (file, block_size, flags, mode, fatal_func)
61 char *file;
62 int block_size;
63 int flags;
64 int mode;
65 void (*fatal_func) ();
66{
67 gdbm_file_info *dbf; /* The record to return. */
68 struct stat file_stat; /* Space for the stat information. */
69 int len; /* Length of the file name. */
70 int num_bytes; /* Used in reading and writing. */
71 off_t file_pos; /* Used with seeks. */
72 int lock_val; /* Returned by the flock call. */
73 int file_block_size; /* Block size to use for a new file. */
74 int index; /* Used as a loop index. */
75 char need_trunc; /* Used with GDBM_NEWDB and locking to avoid
76 truncating a file from under a reader. */
77
78 /* Initialize the gdbm_errno variable. */
79 gdbm_errno = GDBM_NO_ERROR;
80
81 /* Allocate new info structure. */
82 dbf = (gdbm_file_info *) malloc (sizeof (gdbm_file_info));
83 if (dbf == NULL)
84 {
85 gdbm_errno = GDBM_MALLOC_ERROR;
86 return NULL;
87 }
88
89 /* Initialize some fields for known values. This is done so gdbm_close
90 will work if called before allocating some structures. */
91 dbf->dir = NULL;
92 dbf->bucket = NULL;
93 dbf->header = NULL;
94 dbf->bucket_cache = NULL;
95 dbf->cache_size = 0;
96
97 /* Save name of file. */
98 len = strlen (file);
99 dbf->name = (char *) malloc (len + 1);
100 if (dbf->name == NULL)
101 {
102 free (dbf);
103 gdbm_errno = GDBM_MALLOC_ERROR;
104 return NULL;
105 }
106 strcpy (dbf->name, file);
107
108 /* Initialize the fatal error routine. */
109 dbf->fatal_err = fatal_func;
110
111 dbf->fast_write = TRUE; /* Default to setting fast_write. */
112 dbf->file_locking = TRUE; /* Default to doing file locking. */
113 dbf->central_free = FALSE; /* Default to not using central_free. */
114 dbf->coalesce_blocks = FALSE; /* Default to not coalescing blocks. */
115
116 /* GDBM_FAST used to determine whethere or not we set fast_write. */
117 if (flags & GDBM_SYNC)
118 {
119 /* If GDBM_SYNC has been requested, don't do fast_write. */
120 dbf->fast_write = FALSE;
121 }
122 if (flags & GDBM_NOLOCK)
123 {
124 dbf->file_locking = FALSE;
125 }
126
127 /* Open the file. */
128 need_trunc = FALSE;
129 switch (flags & GDBM_OPENMASK)
130 {
131 case GDBM_READER:
132 dbf->desc = open (dbf->name, O_RDONLY, 0);
133 break;
134
135 case GDBM_WRITER:
136 dbf->desc = open (dbf->name, O_RDWR, 0);
137 break;
138
139 case GDBM_NEWDB:
140 dbf->desc = open (dbf->name, O_RDWR|O_CREAT, mode);
141 need_trunc = TRUE;
142 break;
143
144 default:
145 dbf->desc = open (dbf->name, O_RDWR|O_CREAT, mode);
146 break;
147
148 }
149 if (dbf->desc < 0)
150 {
151 free (dbf->name);
152 free (dbf);
153 gdbm_errno = GDBM_FILE_OPEN_ERROR;
154 return NULL;
155 }
156
157 /* Get the status of the file. */
158 fstat (dbf->desc, &file_stat);
159
160 /* Lock the file in the approprate way. */
161 if ((flags & GDBM_OPENMASK) == GDBM_READER)
162 {
163 if (file_stat.st_size == 0)
164 {
165 close (dbf->desc);
166 free (dbf->name);
167 free (dbf);
168 gdbm_errno = GDBM_EMPTY_DATABASE;
169 return NULL;
170 }
171 if (dbf->file_locking)
172 {
173 /* Sets lock_val to 0 for success. See systems.h. */
174 READLOCK_FILE(dbf);
175 }
176 }
177 else if (dbf->file_locking)
178 {
179 /* Sets lock_val to 0 for success. See systems.h. */
180 WRITELOCK_FILE(dbf);
181 }
182 if (dbf->file_locking && (lock_val != 0))
183 {
184 close (dbf->desc);
185 free (dbf->name);
186 free (dbf);
187 if ((flags & GDBM_OPENMASK) == GDBM_READER)
188 gdbm_errno = GDBM_CANT_BE_READER;
189 else
190 gdbm_errno = GDBM_CANT_BE_WRITER;
191 return NULL;
192 }
193
194 /* Record the kind of user. */
195 dbf->read_write = (flags & GDBM_OPENMASK);
196
197 /* If we do have a write lock and it was a GDBM_NEWDB, it is
198 now time to truncate the file. */
199 if (need_trunc && file_stat.st_size != 0)
200 {
201 TRUNCATE (dbf);
202 fstat (dbf->desc, &file_stat);
203 }
204
205 /* Decide if this is a new file or an old file. */
206 if (file_stat.st_size == 0)
207 {
208
209 /* This is a new file. Create an empty database. */
210
211 /* Start with the blocksize. */
212 if (block_size < 512)
213 file_block_size = STATBLKSIZE;
214 else
215 file_block_size = block_size;
216
217 /* Get space for the file header. */
218 dbf->header = (gdbm_file_header *) malloc (file_block_size);
219 if (dbf->header == NULL)
220 {
221 gdbm_close (dbf);
222 gdbm_errno = GDBM_MALLOC_ERROR;
223 return NULL;
224 }
225
226 /* Set the magic number and the block_size. */
227 dbf->header->header_magic = 0x13579ace;
228 dbf->header->block_size = file_block_size;
229
230 /* Create the initial hash table directory. */
231 dbf->header->dir_size = 8 * sizeof (off_t);
232 dbf->header->dir_bits = 3;
233 while (dbf->header->dir_size < dbf->header->block_size)
234 {
235 dbf->header->dir_size <<= 1;
236 dbf->header->dir_bits += 1;
237 }
238
239 /* Check for correct block_size. */
240 if (dbf->header->dir_size != dbf->header->block_size)
241 {
242 gdbm_close (dbf);
243 gdbm_errno = GDBM_BLOCK_SIZE_ERROR;
244 return NULL;
245 }
246
247 /* Allocate the space for the directory. */
248 dbf->dir = (off_t *) malloc (dbf->header->dir_size);
249 if (dbf->dir == NULL)
250 {
251 gdbm_close (dbf);
252 gdbm_errno = GDBM_MALLOC_ERROR;
253 return NULL;
254 }
255 dbf->header->dir = dbf->header->block_size;
256
257 /* Create the first and only hash bucket. */
258 dbf->header->bucket_elems =
259 (dbf->header->block_size - sizeof (hash_bucket))
260 / sizeof (bucket_element) + 1;
261 dbf->header->bucket_size = dbf->header->block_size;
262 dbf->bucket = (hash_bucket *) malloc (dbf->header->bucket_size);
263 if (dbf->bucket == NULL)
264 {
265 gdbm_close (dbf);
266 gdbm_errno = GDBM_MALLOC_ERROR;
267 return NULL;
268 }
269 _gdbm_new_bucket (dbf, dbf->bucket, 0);
270 dbf->bucket->av_count = 1;
271 dbf->bucket->bucket_avail[0].av_adr = 3*dbf->header->block_size;
272 dbf->bucket->bucket_avail[0].av_size = dbf->header->block_size;
273
274 /* Set table entries to point to hash buckets. */
275 for (index = 0; index < dbf->header->dir_size / sizeof (off_t); index++)
276 dbf->dir[index] = 2*dbf->header->block_size;
277
278 /* Initialize the active avail block. */
279 dbf->header->avail.size
280 = ( (dbf->header->block_size - sizeof (gdbm_file_header))
281 / sizeof (avail_elem)) + 1;
282 dbf->header->avail.count = 0;
283 dbf->header->avail.next_block = 0;
284 dbf->header->next_block = 4*dbf->header->block_size;
285
286 /* Write initial configuration to the file. */
287 /* Block 0 is the file header and active avail block. */
288 num_bytes = write (dbf->desc, dbf->header, dbf->header->block_size);
289 if (num_bytes != dbf->header->block_size)
290 {
291 gdbm_close (dbf);
292 gdbm_errno = GDBM_FILE_WRITE_ERROR;
293 return NULL;
294 }
295
296 /* Block 1 is the initial bucket directory. */
297 num_bytes = write (dbf->desc, dbf->dir, dbf->header->dir_size);
298 if (num_bytes != dbf->header->dir_size)
299 {
300 gdbm_close (dbf);
301 gdbm_errno = GDBM_FILE_WRITE_ERROR;
302 return NULL;
303 }
304
305 /* Block 2 is the only bucket. */
306 num_bytes = write (dbf->desc, dbf->bucket, dbf->header->bucket_size);
307 if (num_bytes != dbf->header->bucket_size)
308 {
309 gdbm_close (dbf);
310 gdbm_errno = GDBM_FILE_WRITE_ERROR;
311 return NULL;
312 }
313
314 /* Wait for initial configuration to be written to disk. */
315 fsync (dbf->desc);
316
317 free (dbf->bucket);
318 }
319 else
320 {
321 /* This is an old database. Read in the information from the file
322 header and initialize the hash directory. */
323
324 gdbm_file_header partial_header; /* For the first part of it. */
325
326 /* Read the partial file header. */
327 num_bytes = read (dbf->desc, &partial_header, sizeof (gdbm_file_header));
328 if (num_bytes != sizeof (gdbm_file_header))
329 {
330 gdbm_close (dbf);
331 gdbm_errno = GDBM_FILE_READ_ERROR;
332 return NULL;
333 }
334
335 // GREENSTONE CUSTOMISATION: Check if the magic number is reversed
336 // If it is then the GDBM file is the wrong endianness for the current machine
337 wrong_endianness = (partial_header.header_magic == 0xCE9A5713);
338
339 // GREENSTONE CUSTOMISATION: Swap each value in the partial header if the GDBM file is the wrong endianness
340 if (wrong_endianness)
341 {
342 endian_swap_array((int*) &partial_header, sizeof (gdbm_file_header));
343 }
344
345 /* Is the magic number good? */
346 if (partial_header.header_magic != 0x13579ace)
347 {
348 gdbm_close (dbf);
349 gdbm_errno = GDBM_BAD_MAGIC_NUMBER;
350 return NULL;
351 }
352
353 /* It is a good database, read the entire header. */
354 dbf->header = (gdbm_file_header *) malloc (partial_header.block_size);
355 if (dbf->header == NULL)
356 {
357 gdbm_close (dbf);
358 gdbm_errno = GDBM_MALLOC_ERROR;
359 return NULL;
360 }
361 bcopy (&partial_header, dbf->header, sizeof (gdbm_file_header));
362 num_bytes = read (dbf->desc, &dbf->header->avail.av_table[1],
363 dbf->header->block_size-sizeof (gdbm_file_header));
364
365 // GREENSTONE CUSTOMISATION: Swap each value in the header if the GDBM file is the wrong endianness
366 if (wrong_endianness)
367 {
368 endian_swap_array((int*) &dbf->header->avail.av_table[1],
369 dbf->header->block_size-sizeof(gdbm_file_header));
370 }
371
372 if (num_bytes != dbf->header->block_size-sizeof (gdbm_file_header))
373 {
374 gdbm_close (dbf);
375 gdbm_errno = GDBM_FILE_READ_ERROR;
376 return NULL;
377 }
378
379 /* Allocate space for the hash table directory. */
380 dbf->dir = (off_t *) malloc (dbf->header->dir_size);
381 if (dbf->dir == NULL)
382 {
383 gdbm_close (dbf);
384 gdbm_errno = GDBM_MALLOC_ERROR;
385 return NULL;
386 }
387
388 /* Read the hash table directory. */
389 file_pos = lseek (dbf->desc, dbf->header->dir, L_SET);
390 if (file_pos != dbf->header->dir)
391 {
392 gdbm_close (dbf);
393 gdbm_errno = GDBM_FILE_SEEK_ERROR;
394 return NULL;
395 }
396
397 num_bytes = read (dbf->desc, dbf->dir, dbf->header->dir_size);
398 if (num_bytes != dbf->header->dir_size)
399 {
400 gdbm_close (dbf);
401 gdbm_errno = GDBM_FILE_READ_ERROR;
402 return NULL;
403 }
404
405 // GREENSTONE CUSTOMISATION: Swap each value in the directory if the GDBM file is the wrong endianness
406 if (wrong_endianness)
407 {
408 endian_swap_array((int*) dbf->dir, dbf->header->dir_size);
409 }
410 }
411
412 /* Finish initializing dbf. */
413 dbf->last_read = -1;
414 dbf->bucket = NULL;
415 dbf->bucket_dir = 0;
416 dbf->cache_entry = NULL;
417 dbf->header_changed = FALSE;
418 dbf->directory_changed = FALSE;
419 dbf->bucket_changed = FALSE;
420 dbf->second_changed = FALSE;
421
422 /* Everything is fine, return the pointer to the file
423 information structure. */
424 return dbf;
425
426}
427
428/* initialize the bucket cache. */
429int
430_gdbm_init_cache(dbf, size)
431 gdbm_file_info *dbf;
432 int size;
433{
434register int index;
435
436 if (dbf->bucket_cache == NULL)
437 {
438 dbf->bucket_cache = (cache_elem *) malloc(sizeof(cache_elem) * size);
439 if(dbf->bucket_cache == NULL)
440 {
441 gdbm_errno = GDBM_MALLOC_ERROR;
442 return(-1);
443 }
444 dbf->cache_size = size;
445
446 for(index = 0; index < size; index++)
447 {
448 (dbf->bucket_cache[index]).ca_bucket
449 = (hash_bucket *) malloc (dbf->header->bucket_size);
450 if ((dbf->bucket_cache[index]).ca_bucket == NULL)
451 {
452 gdbm_errno = GDBM_MALLOC_ERROR;
453 return(-1);
454 }
455 (dbf->bucket_cache[index]).ca_adr = 0;
456 (dbf->bucket_cache[index]).ca_changed = FALSE;
457 (dbf->bucket_cache[index]).ca_data.hash_val = -1;
458 (dbf->bucket_cache[index]).ca_data.elem_loc = -1;
459 (dbf->bucket_cache[index]).ca_data.dptr = NULL;
460 }
461 dbf->bucket = dbf->bucket_cache[0].ca_bucket;
462 dbf->cache_entry = &dbf->bucket_cache[0];
463 }
464 return(0);
465}
Note: See TracBrowser for help on using the repository browser.