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