1 /* 2 ** Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers. 3 ** All rights reserved. 4 ** 5 ** By using this file, you agree to the terms and conditions set 6 ** forth in the LICENSE file which can be found at the top level of 7 ** the sendmail distribution. 8 */ 9 10 #include <sm/gen.h> 11 SM_RCSID("@(#)$Id: smndbm.c,v 8.52 2002/05/21 22:30:30 gshapiro Exp $") 12 13 #include <fcntl.h> 14 #include <stdlib.h> 15 #include <unistd.h> 16 17 #include <sendmail/sendmail.h> 18 #include <libsmdb/smdb.h> 19 20 #ifdef NDBM 21 22 # define SMNDB_DIR_FILE_EXTENSION "dir" 23 # define SMNDB_PAG_FILE_EXTENSION "pag" 24 25 struct smdb_dbm_database_struct 26 { 27 DBM *smndbm_dbm; 28 int smndbm_lock_fd; 29 bool smndbm_cursor_in_use; 30 }; 31 typedef struct smdb_dbm_database_struct SMDB_DBM_DATABASE; 32 33 struct smdb_dbm_cursor_struct 34 { 35 SMDB_DBM_DATABASE *smndbmc_db; 36 datum smndbmc_current_key; 37 }; 38 typedef struct smdb_dbm_cursor_struct SMDB_DBM_CURSOR; 39 40 /* 41 ** SMDB_PUT_FLAGS_TO_NDBM_FLAGS -- Translates smdb put flags to ndbm put flags. 42 ** 43 ** Parameters: 44 ** flags -- The flags to translate. 45 ** 46 ** Returns: 47 ** The ndbm flags that are equivalent to the smdb flags. 48 ** 49 ** Notes: 50 ** Any invalid flags are ignored. 51 ** 52 */ 53 54 int 55 smdb_put_flags_to_ndbm_flags(flags) 56 SMDB_FLAG flags; 57 { 58 int return_flags; 59 60 return_flags = 0; 61 if (bitset(SMDBF_NO_OVERWRITE, flags)) 62 return_flags = DBM_INSERT; 63 else 64 return_flags = DBM_REPLACE; 65 66 return return_flags; 67 } 68 69 /* 70 ** Except for smdb_ndbm_open, the rest of these function correspond to the 71 ** interface laid out in smdb.h. 72 */ 73 74 SMDB_DBM_DATABASE * 75 smdbm_malloc_database() 76 { 77 SMDB_DBM_DATABASE *db; 78 79 db = (SMDB_DBM_DATABASE *) malloc(sizeof(SMDB_DBM_DATABASE)); 80 if (db != NULL) 81 { 82 db->smndbm_dbm = NULL; 83 db->smndbm_lock_fd = -1; 84 db->smndbm_cursor_in_use = false; 85 } 86 87 return db; 88 } 89 90 int 91 smdbm_close(database) 92 SMDB_DATABASE *database; 93 { 94 SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl; 95 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; 96 97 dbm_close(dbm); 98 if (db->smndbm_lock_fd != -1) 99 close(db->smndbm_lock_fd); 100 101 free(db); 102 database->smdb_impl = NULL; 103 104 return SMDBE_OK; 105 } 106 107 int 108 smdbm_del(database, key, flags) 109 SMDB_DATABASE *database; 110 SMDB_DBENT *key; 111 unsigned int flags; 112 { 113 int result; 114 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; 115 datum dbkey; 116 117 (void) memset(&dbkey, '\0', sizeof dbkey); 118 dbkey.dptr = key->data; 119 dbkey.dsize = key->size; 120 121 errno = 0; 122 result = dbm_delete(dbm, dbkey); 123 if (result != 0) 124 { 125 int save_errno = errno; 126 127 if (dbm_error(dbm)) 128 return SMDBE_IO_ERROR; 129 130 if (save_errno != 0) 131 return save_errno; 132 133 return SMDBE_NOT_FOUND; 134 } 135 return SMDBE_OK; 136 } 137 138 int 139 smdbm_fd(database, fd) 140 SMDB_DATABASE *database; 141 int *fd; 142 { 143 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; 144 145 *fd = dbm_dirfno(dbm); 146 if (*fd <= 0) 147 return EINVAL; 148 149 return SMDBE_OK; 150 } 151 152 int 153 smdbm_lockfd(database) 154 SMDB_DATABASE *database; 155 { 156 SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl; 157 158 return db->smndbm_lock_fd; 159 } 160 161 int 162 smdbm_get(database, key, data, flags) 163 SMDB_DATABASE *database; 164 SMDB_DBENT *key; 165 SMDB_DBENT *data; 166 unsigned int flags; 167 { 168 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; 169 datum dbkey, dbdata; 170 171 (void) memset(&dbkey, '\0', sizeof dbkey); 172 (void) memset(&dbdata, '\0', sizeof dbdata); 173 dbkey.dptr = key->data; 174 dbkey.dsize = key->size; 175 176 errno = 0; 177 dbdata = dbm_fetch(dbm, dbkey); 178 if (dbdata.dptr == NULL) 179 { 180 int save_errno = errno; 181 182 if (dbm_error(dbm)) 183 return SMDBE_IO_ERROR; 184 185 if (save_errno != 0) 186 return save_errno; 187 188 return SMDBE_NOT_FOUND; 189 } 190 data->data = dbdata.dptr; 191 data->size = dbdata.dsize; 192 return SMDBE_OK; 193 } 194 195 int 196 smdbm_put(database, key, data, flags) 197 SMDB_DATABASE *database; 198 SMDB_DBENT *key; 199 SMDB_DBENT *data; 200 unsigned int flags; 201 { 202 int result; 203 int save_errno; 204 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; 205 datum dbkey, dbdata; 206 207 (void) memset(&dbkey, '\0', sizeof dbkey); 208 (void) memset(&dbdata, '\0', sizeof dbdata); 209 dbkey.dptr = key->data; 210 dbkey.dsize = key->size; 211 dbdata.dptr = data->data; 212 dbdata.dsize = data->size; 213 214 errno = 0; 215 result = dbm_store(dbm, dbkey, dbdata, 216 smdb_put_flags_to_ndbm_flags(flags)); 217 switch (result) 218 { 219 case 1: 220 return SMDBE_DUPLICATE; 221 222 case 0: 223 return SMDBE_OK; 224 225 default: 226 save_errno = errno; 227 228 if (dbm_error(dbm)) 229 return SMDBE_IO_ERROR; 230 231 if (save_errno != 0) 232 return save_errno; 233 234 return SMDBE_IO_ERROR; 235 } 236 /* NOTREACHED */ 237 } 238 239 int 240 smndbm_set_owner(database, uid, gid) 241 SMDB_DATABASE *database; 242 uid_t uid; 243 gid_t gid; 244 { 245 # if HASFCHOWN 246 int fd; 247 int result; 248 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; 249 250 fd = dbm_dirfno(dbm); 251 if (fd <= 0) 252 return EINVAL; 253 254 result = fchown(fd, uid, gid); 255 if (result < 0) 256 return errno; 257 258 fd = dbm_pagfno(dbm); 259 if (fd <= 0) 260 return EINVAL; 261 262 result = fchown(fd, uid, gid); 263 if (result < 0) 264 return errno; 265 # endif /* HASFCHOWN */ 266 267 return SMDBE_OK; 268 } 269 270 int 271 smdbm_sync(database, flags) 272 SMDB_DATABASE *database; 273 unsigned int flags; 274 { 275 return SMDBE_UNSUPPORTED; 276 } 277 278 int 279 smdbm_cursor_close(cursor) 280 SMDB_CURSOR *cursor; 281 { 282 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl; 283 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db; 284 285 if (!db->smndbm_cursor_in_use) 286 return SMDBE_NOT_A_VALID_CURSOR; 287 288 db->smndbm_cursor_in_use = false; 289 free(dbm_cursor); 290 free(cursor); 291 292 return SMDBE_OK; 293 } 294 295 int 296 smdbm_cursor_del(cursor, flags) 297 SMDB_CURSOR *cursor; 298 unsigned int flags; 299 { 300 int result; 301 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl; 302 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db; 303 DBM *dbm = db->smndbm_dbm; 304 305 errno = 0; 306 result = dbm_delete(dbm, dbm_cursor->smndbmc_current_key); 307 if (result != 0) 308 { 309 int save_errno = errno; 310 311 if (dbm_error(dbm)) 312 return SMDBE_IO_ERROR; 313 314 if (save_errno != 0) 315 return save_errno; 316 317 return SMDBE_NOT_FOUND; 318 } 319 return SMDBE_OK; 320 } 321 322 int 323 smdbm_cursor_get(cursor, key, value, flags) 324 SMDB_CURSOR *cursor; 325 SMDB_DBENT *key; 326 SMDB_DBENT *value; 327 SMDB_FLAG flags; 328 { 329 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl; 330 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db; 331 DBM *dbm = db->smndbm_dbm; 332 datum dbkey, dbdata; 333 334 (void) memset(&dbkey, '\0', sizeof dbkey); 335 (void) memset(&dbdata, '\0', sizeof dbdata); 336 337 if (flags == SMDB_CURSOR_GET_RANGE) 338 return SMDBE_UNSUPPORTED; 339 340 if (dbm_cursor->smndbmc_current_key.dptr == NULL) 341 { 342 dbm_cursor->smndbmc_current_key = dbm_firstkey(dbm); 343 if (dbm_cursor->smndbmc_current_key.dptr == NULL) 344 { 345 if (dbm_error(dbm)) 346 return SMDBE_IO_ERROR; 347 return SMDBE_LAST_ENTRY; 348 } 349 } 350 else 351 { 352 dbm_cursor->smndbmc_current_key = dbm_nextkey(dbm); 353 if (dbm_cursor->smndbmc_current_key.dptr == NULL) 354 { 355 if (dbm_error(dbm)) 356 return SMDBE_IO_ERROR; 357 return SMDBE_LAST_ENTRY; 358 } 359 } 360 361 errno = 0; 362 dbdata = dbm_fetch(dbm, dbm_cursor->smndbmc_current_key); 363 if (dbdata.dptr == NULL) 364 { 365 int save_errno = errno; 366 367 if (dbm_error(dbm)) 368 return SMDBE_IO_ERROR; 369 370 if (save_errno != 0) 371 return save_errno; 372 373 return SMDBE_NOT_FOUND; 374 } 375 value->data = dbdata.dptr; 376 value->size = dbdata.dsize; 377 key->data = dbm_cursor->smndbmc_current_key.dptr; 378 key->size = dbm_cursor->smndbmc_current_key.dsize; 379 380 return SMDBE_OK; 381 } 382 383 int 384 smdbm_cursor_put(cursor, key, value, flags) 385 SMDB_CURSOR *cursor; 386 SMDB_DBENT *key; 387 SMDB_DBENT *value; 388 SMDB_FLAG flags; 389 { 390 int result; 391 int save_errno; 392 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl; 393 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db; 394 DBM *dbm = db->smndbm_dbm; 395 datum dbdata; 396 397 (void) memset(&dbdata, '\0', sizeof dbdata); 398 dbdata.dptr = value->data; 399 dbdata.dsize = value->size; 400 401 errno = 0; 402 result = dbm_store(dbm, dbm_cursor->smndbmc_current_key, dbdata, 403 smdb_put_flags_to_ndbm_flags(flags)); 404 switch (result) 405 { 406 case 1: 407 return SMDBE_DUPLICATE; 408 409 case 0: 410 return SMDBE_OK; 411 412 default: 413 save_errno = errno; 414 415 if (dbm_error(dbm)) 416 return SMDBE_IO_ERROR; 417 418 if (save_errno != 0) 419 return save_errno; 420 421 return SMDBE_IO_ERROR; 422 } 423 /* NOTREACHED */ 424 } 425 426 int 427 smdbm_cursor(database, cursor, flags) 428 SMDB_DATABASE *database; 429 SMDB_CURSOR **cursor; 430 SMDB_FLAG flags; 431 { 432 SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl; 433 SMDB_CURSOR *cur; 434 SMDB_DBM_CURSOR *dbm_cursor; 435 436 if (db->smndbm_cursor_in_use) 437 return SMDBE_ONLY_SUPPORTS_ONE_CURSOR; 438 439 db->smndbm_cursor_in_use = true; 440 dbm_cursor = (SMDB_DBM_CURSOR *) malloc(sizeof(SMDB_DBM_CURSOR)); 441 dbm_cursor->smndbmc_db = db; 442 dbm_cursor->smndbmc_current_key.dptr = NULL; 443 dbm_cursor->smndbmc_current_key.dsize = 0; 444 445 cur = (SMDB_CURSOR*) malloc(sizeof(SMDB_CURSOR)); 446 if (cur == NULL) 447 return SMDBE_MALLOC; 448 449 cur->smdbc_impl = dbm_cursor; 450 cur->smdbc_close = smdbm_cursor_close; 451 cur->smdbc_del = smdbm_cursor_del; 452 cur->smdbc_get = smdbm_cursor_get; 453 cur->smdbc_put = smdbm_cursor_put; 454 *cursor = cur; 455 456 return SMDBE_OK; 457 } 458 /* 459 ** SMDB_NDBM_OPEN -- Opens a ndbm database. 460 ** 461 ** Parameters: 462 ** database -- An unallocated database pointer to a pointer. 463 ** db_name -- The name of the database without extension. 464 ** mode -- File permisions on a created database. 465 ** mode_mask -- Mode bits that much match on an opened database. 466 ** sff -- Flags to safefile. 467 ** type -- The type of database to open. 468 ** Only SMDB_NDBM is supported. 469 ** user_info -- Information on the user to use for file 470 ** permissions. 471 ** db_params -- No params are supported. 472 ** 473 ** Returns: 474 ** SMDBE_OK -- Success, otherwise errno: 475 ** SMDBE_MALLOC -- Cannot allocate memory. 476 ** SMDBE_UNSUPPORTED -- The type is not supported. 477 ** SMDBE_GDBM_IS_BAD -- We have detected GDBM and we don't 478 ** like it. 479 ** SMDBE_BAD_OPEN -- dbm_open failed and errno was not set. 480 ** Anything else: errno 481 */ 482 483 int 484 smdb_ndbm_open(database, db_name, mode, mode_mask, sff, type, user_info, 485 db_params) 486 SMDB_DATABASE **database; 487 char *db_name; 488 int mode; 489 int mode_mask; 490 long sff; 491 SMDB_DBTYPE type; 492 SMDB_USER_INFO *user_info; 493 SMDB_DBPARAMS *db_params; 494 { 495 bool lockcreated = false; 496 int result; 497 int lock_fd; 498 SMDB_DATABASE *smdb_db; 499 SMDB_DBM_DATABASE *db; 500 DBM *dbm = NULL; 501 struct stat dir_stat_info; 502 struct stat pag_stat_info; 503 504 result = SMDBE_OK; 505 *database = NULL; 506 507 if (type == NULL) 508 return SMDBE_UNKNOWN_DB_TYPE; 509 510 result = smdb_setup_file(db_name, SMNDB_DIR_FILE_EXTENSION, mode_mask, 511 sff, user_info, &dir_stat_info); 512 if (result != SMDBE_OK) 513 return result; 514 515 result = smdb_setup_file(db_name, SMNDB_PAG_FILE_EXTENSION, mode_mask, 516 sff, user_info, &pag_stat_info); 517 if (result != SMDBE_OK) 518 return result; 519 520 if ((dir_stat_info.st_mode == ST_MODE_NOFILE || 521 pag_stat_info.st_mode == ST_MODE_NOFILE) && 522 bitset(mode, O_CREAT)) 523 lockcreated = true; 524 525 lock_fd = -1; 526 result = smdb_lock_file(&lock_fd, db_name, mode, sff, 527 SMNDB_DIR_FILE_EXTENSION); 528 if (result != SMDBE_OK) 529 return result; 530 531 if (lockcreated) 532 { 533 int pag_fd; 534 535 /* Need to pre-open the .pag file as well with O_EXCL */ 536 result = smdb_lock_file(&pag_fd, db_name, mode, sff, 537 SMNDB_PAG_FILE_EXTENSION); 538 if (result != SMDBE_OK) 539 { 540 (void) close(lock_fd); 541 return result; 542 } 543 (void) close(pag_fd); 544 545 mode |= O_TRUNC; 546 mode &= ~(O_CREAT|O_EXCL); 547 } 548 549 smdb_db = smdb_malloc_database(); 550 if (smdb_db == NULL) 551 result = SMDBE_MALLOC; 552 553 db = smdbm_malloc_database(); 554 if (db == NULL) 555 result = SMDBE_MALLOC; 556 557 /* Try to open database */ 558 if (result == SMDBE_OK) 559 { 560 db->smndbm_lock_fd = lock_fd; 561 562 errno = 0; 563 dbm = dbm_open(db_name, mode, DBMMODE); 564 if (dbm == NULL) 565 { 566 if (errno == 0) 567 result = SMDBE_BAD_OPEN; 568 else 569 result = errno; 570 } 571 db->smndbm_dbm = dbm; 572 } 573 574 /* Check for GDBM */ 575 if (result == SMDBE_OK) 576 { 577 if (dbm_dirfno(dbm) == dbm_pagfno(dbm)) 578 result = SMDBE_GDBM_IS_BAD; 579 } 580 581 /* Check for filechanged */ 582 if (result == SMDBE_OK) 583 { 584 result = smdb_filechanged(db_name, SMNDB_DIR_FILE_EXTENSION, 585 dbm_dirfno(dbm), &dir_stat_info); 586 if (result == SMDBE_OK) 587 { 588 result = smdb_filechanged(db_name, 589 SMNDB_PAG_FILE_EXTENSION, 590 dbm_pagfno(dbm), 591 &pag_stat_info); 592 } 593 } 594 595 /* XXX Got to get fchown stuff in here */ 596 597 /* Setup driver if everything is ok */ 598 if (result == SMDBE_OK) 599 { 600 *database = smdb_db; 601 602 smdb_db->smdb_close = smdbm_close; 603 smdb_db->smdb_del = smdbm_del; 604 smdb_db->smdb_fd = smdbm_fd; 605 smdb_db->smdb_lockfd = smdbm_lockfd; 606 smdb_db->smdb_get = smdbm_get; 607 smdb_db->smdb_put = smdbm_put; 608 smdb_db->smdb_set_owner = smndbm_set_owner; 609 smdb_db->smdb_sync = smdbm_sync; 610 smdb_db->smdb_cursor = smdbm_cursor; 611 612 smdb_db->smdb_impl = db; 613 614 return SMDBE_OK; 615 } 616 617 /* If we're here, something bad happened, clean up */ 618 if (dbm != NULL) 619 dbm_close(dbm); 620 621 smdb_unlock_file(db->smndbm_lock_fd); 622 free(db); 623 smdb_free_database(smdb_db); 624 625 return result; 626 } 627 #endif /* NDBM */ 628