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> 11SM_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 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 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 483int 484smdb_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