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