Coverage Report

Created: 2023-06-07 07:16

/src/gdbm/src/gdbmopen.c
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
}