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
56gdbm_file_info *
57gdbm_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_WRITER:
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	need_trunc = TRUE;
139	break;
140
141      default:
142	dbf->desc = open (dbf->name, O_RDWR|O_CREAT, mode);
143	break;
144
145    }
146  if (dbf->desc < 0)
147    {
148      free (dbf->name);
149      free (dbf);
150      gdbm_errno = GDBM_FILE_OPEN_ERROR;
151      return NULL;
152    }
153
154  /* Get the status of the file. */
155  fstat (dbf->desc, &file_stat);
156
157  /* Lock the file in the approprate way. */
158  if ((flags & GDBM_OPENMASK) == GDBM_READER)
159    {
160      if (file_stat.st_size == 0)
161	{
162	  close (dbf->desc);
163	  free (dbf->name);
164	  free (dbf);
165	  gdbm_errno = GDBM_EMPTY_DATABASE;
166	  return NULL;
167	}
168      if (dbf->file_locking)
169	{
170          /* Sets lock_val to 0 for success.  See systems.h. */
171          READLOCK_FILE(dbf);
172	}
173    }
174  else if (dbf->file_locking)
175    {
176      /* Sets lock_val to 0 for success.  See systems.h. */
177      WRITELOCK_FILE(dbf);
178    }
179  if (dbf->file_locking && (lock_val != 0))
180    {
181      close (dbf->desc);
182      free (dbf->name);
183      free (dbf);
184      if ((flags & GDBM_OPENMASK) == GDBM_READER)
185	gdbm_errno = GDBM_CANT_BE_READER;
186      else
187	gdbm_errno = GDBM_CANT_BE_WRITER;
188      return NULL;
189    }
190
191  /* Record the kind of user. */
192  dbf->read_write = (flags & GDBM_OPENMASK);
193
194  /* If we do have a write lock and it was a GDBM_NEWDB, it is
195     now time to truncate the file. */
196  if (need_trunc && file_stat.st_size != 0)
197    {
198      TRUNCATE (dbf);
199      fstat (dbf->desc, &file_stat);
200    }
201
202  /* Decide if this is a new file or an old file. */
203  if (file_stat.st_size == 0)
204    {
205
206      /* This is a new file.  Create an empty database.  */
207
208      /* Start with the blocksize. */
209      if (block_size < 512)
210	file_block_size = STATBLKSIZE;
211      else
212	file_block_size = block_size;
213
214      /* Get space for the file header. */
215      dbf->header = (gdbm_file_header *) malloc (file_block_size);
216      if (dbf->header == NULL)
217	{
218	  gdbm_close (dbf);
219	  gdbm_errno = GDBM_MALLOC_ERROR;
220	  return NULL;
221	}
222
223      /* Set the magic number and the block_size. */
224      dbf->header->header_magic = 0x13579ace;
225      dbf->header->block_size = file_block_size;
226
227      /* Create the initial hash table directory.  */
228      dbf->header->dir_size = 8 * sizeof (off_t);
229      dbf->header->dir_bits = 3;
230      while (dbf->header->dir_size < dbf->header->block_size)
231	{
232	  dbf->header->dir_size <<= 1;
233	  dbf->header->dir_bits += 1;
234	}
235
236      /* Check for correct block_size. */
237      if (dbf->header->dir_size != dbf->header->block_size)
238	{
239	  gdbm_close (dbf);
240	  gdbm_errno = GDBM_BLOCK_SIZE_ERROR;
241	  return NULL;
242	}
243
244      /* Allocate the space for the directory. */
245      dbf->dir = (off_t *) malloc (dbf->header->dir_size);
246      if (dbf->dir == NULL)
247	{
248	  gdbm_close (dbf);
249	  gdbm_errno = GDBM_MALLOC_ERROR;
250	  return NULL;
251	}
252      dbf->header->dir = dbf->header->block_size;
253
254      /* Create the first and only hash bucket. */
255      dbf->header->bucket_elems =
256	(dbf->header->block_size - sizeof (hash_bucket))
257	/ sizeof (bucket_element) + 1;
258      dbf->header->bucket_size  = dbf->header->block_size;
259      dbf->bucket = (hash_bucket *) malloc (dbf->header->bucket_size);
260      if (dbf->bucket == NULL)
261	{
262	  gdbm_close (dbf);
263	  gdbm_errno = GDBM_MALLOC_ERROR;
264	  return NULL;
265	}
266      _gdbm_new_bucket (dbf, dbf->bucket, 0);
267      dbf->bucket->av_count = 1;
268      dbf->bucket->bucket_avail[0].av_adr = 3*dbf->header->block_size;
269      dbf->bucket->bucket_avail[0].av_size = dbf->header->block_size;
270
271      /* Set table entries to point to hash buckets. */
272      for (index = 0; index < dbf->header->dir_size / sizeof (off_t); index++)
273	dbf->dir[index] = 2*dbf->header->block_size;
274
275      /* Initialize the active avail block. */
276      dbf->header->avail.size
277	= ( (dbf->header->block_size - sizeof (gdbm_file_header))
278	 / sizeof (avail_elem)) + 1;
279      dbf->header->avail.count = 0;
280      dbf->header->avail.next_block = 0;
281      dbf->header->next_block  = 4*dbf->header->block_size;
282
283      /* Write initial configuration to the file. */
284      /* Block 0 is the file header and active avail block. */
285      num_bytes = write (dbf->desc, dbf->header, dbf->header->block_size);
286      if (num_bytes != dbf->header->block_size)
287	{
288	  gdbm_close (dbf);
289	  gdbm_errno = GDBM_FILE_WRITE_ERROR;
290	  return NULL;
291	}
292
293      /* Block 1 is the initial bucket directory. */
294      num_bytes = write (dbf->desc, dbf->dir, dbf->header->dir_size);
295      if (num_bytes != dbf->header->dir_size)
296	{
297	  gdbm_close (dbf);
298	  gdbm_errno = GDBM_FILE_WRITE_ERROR;
299	  return NULL;
300	}
301
302      /* Block 2 is the only bucket. */
303      num_bytes = write (dbf->desc, dbf->bucket, dbf->header->bucket_size);
304      if (num_bytes != dbf->header->bucket_size)
305	{
306	  gdbm_close (dbf);
307	  gdbm_errno = GDBM_FILE_WRITE_ERROR;
308	  return NULL;
309	}
310
311      /* Wait for initial configuration to be written to disk. */
312      fsync (dbf->desc);
313
314      free (dbf->bucket);
315    }
316  else
317    {
318      /* This is an old database.  Read in the information from the file
319	 header and initialize the hash directory. */
320
321      gdbm_file_header partial_header;  /* For the first part of it. */
322
323      /* Read the partial file header. */
324      num_bytes = read (dbf->desc, &partial_header, sizeof (gdbm_file_header));
325      if (num_bytes != sizeof (gdbm_file_header))
326	{
327	  gdbm_close (dbf);
328	  gdbm_errno = GDBM_FILE_READ_ERROR;
329	  return NULL;
330	}
331
332      /* Is the magic number good? */
333      if (partial_header.header_magic != 0x13579ace)
334	{
335	  gdbm_close (dbf);
336	  gdbm_errno = GDBM_BAD_MAGIC_NUMBER;
337	  return NULL;
338	}
339
340      /* It is a good database, read the entire header. */
341      dbf->header = (gdbm_file_header *) malloc (partial_header.block_size);
342      if (dbf->header == NULL)
343	{
344	  gdbm_close (dbf);
345	  gdbm_errno = GDBM_MALLOC_ERROR;
346	  return NULL;
347	}
348      bcopy (&partial_header, dbf->header, sizeof (gdbm_file_header));
349      num_bytes = read (dbf->desc, &dbf->header->avail.av_table[1],
350			dbf->header->block_size-sizeof (gdbm_file_header));
351      if (num_bytes != dbf->header->block_size-sizeof (gdbm_file_header))
352	{
353	  gdbm_close (dbf);
354	  gdbm_errno = GDBM_FILE_READ_ERROR;
355	  return NULL;
356	}
357
358      /* Allocate space for the hash table directory.  */
359      dbf->dir = (off_t *) malloc (dbf->header->dir_size);
360      if (dbf->dir == NULL)
361	{
362	  gdbm_close (dbf);
363	  gdbm_errno = GDBM_MALLOC_ERROR;
364	  return NULL;
365	}
366
367      /* Read the hash table directory. */
368      file_pos = lseek (dbf->desc, dbf->header->dir, L_SET);
369      if (file_pos != dbf->header->dir)
370	{
371	  gdbm_close (dbf);
372	  gdbm_errno = GDBM_FILE_SEEK_ERROR;
373	  return NULL;
374	}
375
376      num_bytes = read (dbf->desc, dbf->dir, dbf->header->dir_size);
377      if (num_bytes != dbf->header->dir_size)
378	{
379	  gdbm_close (dbf);
380	  gdbm_errno = GDBM_FILE_READ_ERROR;
381	  return NULL;
382	}
383
384    }
385
386  /* Finish initializing dbf. */
387  dbf->last_read = -1;
388  dbf->bucket = NULL;
389  dbf->bucket_dir = 0;
390  dbf->cache_entry = NULL;
391  dbf->header_changed = FALSE;
392  dbf->directory_changed = FALSE;
393  dbf->bucket_changed = FALSE;
394  dbf->second_changed = FALSE;
395
396  /* Everything is fine, return the pointer to the file
397     information structure.  */
398  return dbf;
399
400}
401
402/* initialize the bucket cache. */
403int
404_gdbm_init_cache(dbf, size)
405    gdbm_file_info *dbf;
406    int size;
407{
408register int index;
409
410  if (dbf->bucket_cache == NULL)
411    {
412      dbf->bucket_cache = (cache_elem *) malloc(sizeof(cache_elem) * size);
413      if(dbf->bucket_cache == NULL)
414        {
415          gdbm_errno = GDBM_MALLOC_ERROR;
416          return(-1);
417        }
418      dbf->cache_size = size;
419
420      for(index = 0; index < size; index++)
421        {
422          (dbf->bucket_cache[index]).ca_bucket
423            = (hash_bucket *) malloc (dbf->header->bucket_size);
424          if ((dbf->bucket_cache[index]).ca_bucket == NULL)
425	    {
426              gdbm_errno = GDBM_MALLOC_ERROR;
427	      return(-1);
428            }
429          (dbf->bucket_cache[index]).ca_adr = 0;
430          (dbf->bucket_cache[index]).ca_changed = FALSE;
431          (dbf->bucket_cache[index]).ca_data.hash_val = -1;
432          (dbf->bucket_cache[index]).ca_data.elem_loc = -1;
433          (dbf->bucket_cache[index]).ca_data.dptr = NULL;
434        }
435      dbf->bucket = dbf->bucket_cache[0].ca_bucket;
436      dbf->cache_entry = &dbf->bucket_cache[0];
437    }
438  return(0);
439}
440