~rblasch/gdbm/1.8

« back to all changes in this revision

Viewing changes to gdbmopen.c

  • Committer: Ronald Blaschke
  • Date: 2008-11-09 15:55:44 UTC
  • Revision ID: ron@rblasch.org-20081109155544-ig1o30ikmghvvhzs
Initial import of GNU dbm 1.8.0.

Show diffs side-by-side

added added

removed removed

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