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