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> 11SM_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 25struct smdb_dbm_database_struct 26{ 27 DBM *smndbm_dbm; 28 int smndbm_lock_fd; 29 bool smndbm_cursor_in_use; 30}; 31typedef struct smdb_dbm_database_struct SMDB_DBM_DATABASE; 32 33struct smdb_dbm_cursor_struct 34{ 35 SMDB_DBM_DATABASE *smndbmc_db; 36 datum smndbmc_current_key; 37}; 38typedef 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 54int 55smdb_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 74SMDB_DBM_DATABASE * 75smdbm_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 90int 91smdbm_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 107int 108smdbm_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 138int 139smdbm_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 152int 153smdbm_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 161int 162smdbm_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 195int 196smdbm_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 239int 240smndbm_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 270int 271smdbm_sync(database, flags) 272 SMDB_DATABASE *database; 273 unsigned int flags; 274{ 275 return SMDBE_UNSUPPORTED; 276} 277 278int 279smdbm_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 295int 296smdbm_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 322int 323smdbm_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 383int 384smdbm_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 426int 427smdbm_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 488int 489smdb_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