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_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 if (dbm_cursor == NULL) 442 return SMDBE_MALLOC; 443 dbm_cursor->smndbmc_db = db; 444 dbm_cursor->smndbmc_current_key.dptr = NULL; 445 dbm_cursor->smndbmc_current_key.dsize = 0; 446 447 cur = (SMDB_CURSOR*) malloc(sizeof(SMDB_CURSOR)); 448 if (cur == NULL) 449 { 450 free(dbm_cursor); 451 return SMDBE_MALLOC; 452 } 453 454 cur->smdbc_impl = dbm_cursor; 455 cur->smdbc_close = smdbm_cursor_close; 456 cur->smdbc_del = smdbm_cursor_del; 457 cur->smdbc_get = smdbm_cursor_get; 458 cur->smdbc_put = smdbm_cursor_put; 459 *cursor = cur; 460 461 return SMDBE_OK; 462 } 463 /* 464 ** SMDB_NDBM_OPEN -- Opens a ndbm database. 465 ** 466 ** Parameters: 467 ** database -- An unallocated database pointer to a pointer. 468 ** db_name -- The name of the database without extension. 469 ** mode -- File permisions on a created database. 470 ** mode_mask -- Mode bits that much match on an opened database. 471 ** sff -- Flags to safefile. 472 ** type -- The type of database to open. 473 ** Only SMDB_NDBM is supported. 474 ** user_info -- Information on the user to use for file 475 ** permissions. 476 ** db_params -- No params are supported. 477 ** 478 ** Returns: 479 ** SMDBE_OK -- Success, otherwise errno: 480 ** SMDBE_MALLOC -- Cannot allocate memory. 481 ** SMDBE_UNSUPPORTED -- The type is not supported. 482 ** SMDBE_GDBM_IS_BAD -- We have detected GDBM and we don't 483 ** like it. 484 ** SMDBE_BAD_OPEN -- dbm_open failed and errno was not set. 485 ** Anything else: errno 486 */ 487 488 int 489 smdb_ndbm_open(database, db_name, mode, mode_mask, sff, type, user_info, 490 db_params) 491 SMDB_DATABASE **database; 492 char *db_name; 493 int mode; 494 int mode_mask; 495 long sff; 496 SMDB_DBTYPE type; 497 SMDB_USER_INFO *user_info; 498 SMDB_DBPARAMS *db_params; 499 { 500 bool lockcreated = false; 501 int result; 502 int lock_fd; 503 SMDB_DATABASE *smdb_db; 504 SMDB_DBM_DATABASE *db; 505 DBM *dbm = NULL; 506 struct stat dir_stat_info; 507 struct stat pag_stat_info; 508 509 result = SMDBE_OK; 510 *database = NULL; 511 512 if (type == NULL) 513 return SMDBE_UNKNOWN_DB_TYPE; 514 515 result = smdb_setup_file(db_name, SMNDB_DIR_FILE_EXTENSION, mode_mask, 516 sff, user_info, &dir_stat_info); 517 if (result != SMDBE_OK) 518 return result; 519 520 result = smdb_setup_file(db_name, SMNDB_PAG_FILE_EXTENSION, mode_mask, 521 sff, user_info, &pag_stat_info); 522 if (result != SMDBE_OK) 523 return result; 524 525 if ((dir_stat_info.st_mode == ST_MODE_NOFILE || 526 pag_stat_info.st_mode == ST_MODE_NOFILE) && 527 bitset(mode, O_CREAT)) 528 lockcreated = true; 529 530 lock_fd = -1; 531 result = smdb_lock_file(&lock_fd, db_name, mode, sff, 532 SMNDB_DIR_FILE_EXTENSION); 533 if (result != SMDBE_OK) 534 return result; 535 536 if (lockcreated) 537 { 538 int pag_fd; 539 540 /* Need to pre-open the .pag file as well with O_EXCL */ 541 result = smdb_lock_file(&pag_fd, db_name, mode, sff, 542 SMNDB_PAG_FILE_EXTENSION); 543 if (result != SMDBE_OK) 544 { 545 (void) close(lock_fd); 546 return result; 547 } 548 (void) close(pag_fd); 549 550 mode |= O_TRUNC; 551 mode &= ~(O_CREAT|O_EXCL); 552 } 553 554 smdb_db = smdb_malloc_database(); 555 if (smdb_db == NULL) 556 result = SMDBE_MALLOC; 557 558 db = smdbm_malloc_database(); 559 if (db == NULL) 560 result = SMDBE_MALLOC; 561 562 /* Try to open database */ 563 if (result == SMDBE_OK) 564 { 565 db->smndbm_lock_fd = lock_fd; 566 567 errno = 0; 568 dbm = dbm_open(db_name, mode, DBMMODE); 569 if (dbm == NULL) 570 { 571 if (errno == 0) 572 result = SMDBE_BAD_OPEN; 573 else 574 result = errno; 575 } 576 db->smndbm_dbm = dbm; 577 } 578 579 /* Check for GDBM */ 580 if (result == SMDBE_OK) 581 { 582 if (dbm_dirfno(dbm) == dbm_pagfno(dbm)) 583 result = SMDBE_GDBM_IS_BAD; 584 } 585 586 /* Check for filechanged */ 587 if (result == SMDBE_OK) 588 { 589 result = smdb_filechanged(db_name, SMNDB_DIR_FILE_EXTENSION, 590 dbm_dirfno(dbm), &dir_stat_info); 591 if (result == SMDBE_OK) 592 { 593 result = smdb_filechanged(db_name, 594 SMNDB_PAG_FILE_EXTENSION, 595 dbm_pagfno(dbm), 596 &pag_stat_info); 597 } 598 } 599 600 /* XXX Got to get fchown stuff in here */ 601 602 /* Setup driver if everything is ok */ 603 if (result == SMDBE_OK) 604 { 605 *database = smdb_db; 606 607 smdb_db->smdb_close = smdbm_close; 608 smdb_db->smdb_del = smdbm_del; 609 smdb_db->smdb_fd = smdbm_fd; 610 smdb_db->smdb_lockfd = smdbm_lockfd; 611 smdb_db->smdb_get = smdbm_get; 612 smdb_db->smdb_put = smdbm_put; 613 smdb_db->smdb_set_owner = smndbm_set_owner; 614 smdb_db->smdb_sync = smdbm_sync; 615 smdb_db->smdb_cursor = smdbm_cursor; 616 617 smdb_db->smdb_impl = db; 618 619 return SMDBE_OK; 620 } 621 622 /* If we're here, something bad happened, clean up */ 623 if (dbm != NULL) 624 dbm_close(dbm); 625 626 smdb_unlock_file(db->smndbm_lock_fd); 627 free(db); 628 smdb_free_database(smdb_db); 629 630 return result; 631 } 632 #endif /* NDBM */ 633